Merge pull request #285 from totten/community-messages
[civicrm-core.git] / CRM / Core / Form.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
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
39 require_once 'HTML/QuickForm/Page.php';
40 class CRM_Core_Form extends HTML_QuickForm_Page {
41
42 /**
43 * The state object that this form belongs to
44 * @var object
45 */
46 protected $_state;
47
48 /**
49 * The name of this form
50 * @var string
51 */
52 protected $_name;
53
54 /**
55 * The title of this form
56 * @var string
57 */
58 protected $_title = NULL;
59
60 /**
61 * The options passed into this form
62 * @var mixed
63 */
64 protected $_options = NULL;
65
66 /**
67 * The mode of operation for this form
68 * @var int
69 */
70 protected $_action;
71
72 /**
73 * the renderer used for this form
74 *
75 * @var object
76 */
77 protected $_renderer;
78
79 /**
80 * cache the smarty template for efficiency reasons
81 *
82 * @var CRM_Core_Smarty
83 */
84 static protected $_template;
85
86 /**
87 * constants for attributes for various form elements
88 * attempt to standardize on the number of variations that we
89 * use of the below form elements
90 *
91 * @var const string
92 */
93 CONST ATTR_SPACING = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
94
95 /**
96 * All checkboxes are defined with a common prefix. This allows us to
97 * have the same javascript to check / clear all the checkboxes etc
98 * If u have multiple groups of checkboxes, you will need to give them different
99 * ids to avoid potential name collision
100 *
101 * @var const string / int
102 */
103 CONST CB_PREFIX = 'mark_x_', CB_PREFIY = 'mark_y_', CB_PREFIZ = 'mark_z_', CB_PREFIX_LEN = 7;
104
105 /**
106 * Constructor for the basic form page
107 *
108 * We should not use QuickForm directly. This class provides a lot
109 * of default convenient functions, rules and buttons
110 *
111 * @param object $state State associated with this form
112 * @param enum $action The mode the form is operating in (None/Create/View/Update/Delete)
113 * @param string $method The type of http method used (GET/POST)
114 * @param string $name The name of the form if different from class name
115 *
116 * @return object
117 * @access public
118 */
119 function __construct(
120 $state = NULL,
121 $action = CRM_Core_Action::NONE,
122 $method = 'post',
123 $name = NULL
124 ) {
125
126 if ($name) {
127 $this->_name = $name;
128 }
129 else {
130 $this->_name = CRM_Utils_String::getClassName(CRM_Utils_System::getClassName($this));
131 }
132
133 $this->HTML_QuickForm_Page($this->_name, $method);
134
135 $this->_state =& $state;
136 if ($this->_state) {
137 $this->_state->setName($this->_name);
138 }
139 $this->_action = (int) $action;
140
141 $this->registerRules();
142
143 // let the constructor initialize this, should happen only once
144 if (!isset(self::$_template)) {
145 self::$_template = CRM_Core_Smarty::singleton();
146 }
147 }
148
149 static function generateID() {
150 }
151
152 /**
153 * register all the standard rules that most forms potentially use
154 *
155 * @return void
156 * @access private
157 *
158 */
159 function registerRules() {
160 static $rules = array(
161 'title', 'longTitle', 'variable', 'qfVariable',
162 'phone', 'integer', 'query',
163 'url', 'wikiURL',
164 'domain', 'numberOfDigit',
165 'date', 'currentDate',
166 'asciiFile', 'htmlFile', 'utf8File',
167 'objectExists', 'optionExists', 'postalCode', 'money', 'positiveInteger',
168 'xssString', 'fileExists', 'autocomplete', 'validContact',
169 );
170
171 foreach ($rules as $rule) {
172 $this->registerRule($rule, 'callback', $rule, 'CRM_Utils_Rule');
173 }
174 }
175
176 /**
177 * Simple easy to use wrapper around addElement. Deal with
178 * simple validation rules
179 *
180 * @param string type of html element to be added
181 * @param string name of the html element
182 * @param string display label for the html element
183 * @param string attributes used for this element.
184 * These are not default values
185 * @param bool is this a required field
186 *
187 * @return object html element, could be an error object
188 * @access public
189 *
190 */
191 function &add($type, $name, $label = '',
192 $attributes = '', $required = FALSE, $javascript = NULL
193 ) {
194 $element = $this->addElement($type, $name, $label, $attributes, $javascript);
195 if (HTML_QuickForm::isError($element)) {
196 CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
197 }
198
199 if ($required) {
200 if ($type == 'file') {
201 $error = $this->addRule($name, ts('%1 is a required field.', array(1 => $label)), 'uploadedfile');
202 }
203 else {
204 $error = $this->addRule($name, ts('%1 is a required field.', array(1 => $label)), 'required');
205 }
206 if (HTML_QuickForm::isError($error)) {
207 CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
208 }
209 }
210
211 return $element;
212 }
213
214 /**
215 * This function is called before buildForm. Any pre-processing that
216 * needs to be done for buildForm should be done here
217 *
218 * This is a virtual function and should be redefined if needed
219 *
220 * @access public
221 *
222 * @return void
223 *
224 */
225 function preProcess() {}
226
227 /**
228 * This function is called after the form is validated. Any
229 * processing of form state etc should be done in this function.
230 * Typically all processing associated with a form should be done
231 * here and relevant state should be stored in the session
232 *
233 * This is a virtual function and should be redefined if needed
234 *
235 * @access public
236 *
237 * @return void
238 *
239 */
240 function postProcess() {}
241
242 /**
243 * This function is just a wrapper, so that we can call all the hook functions
244 */
245 function mainProcess() {
246 $this->postProcess();
247
248 $this->postProcessHook();
249 }
250
251 /**
252 * The postProcess hook is typically called by the framework
253 * However in a few cases, the form exits or redirects early in which
254 * case it needs to call this function so other modules can do the needful
255 * Calling this function directly should be avoided if possible. In general a
256 * better way is to do setUserContext so the framework does the redirect
257 *
258 */
259 function postProcessHook() {
260 CRM_Utils_Hook::postProcess(get_class($this), $this);
261 }
262
263 /**
264 * This virtual function is used to build the form. It replaces the
265 * buildForm associated with QuickForm_Page. This allows us to put
266 * preProcess in front of the actual form building routine
267 *
268 * @access public
269 *
270 * @return void
271 *
272 */
273 function buildQuickForm() {}
274
275 /**
276 * This virtual function is used to set the default values of
277 * various form elements
278 *
279 * access public
280 *
281 * @return array reference to the array of default values
282 *
283 */
284 function setDefaultValues() {}
285
286 /**
287 * This is a virtual function that adds group and global rules to
288 * the form. Keeping it distinct from the form to keep code small
289 * and localized in the form building code
290 *
291 * @access public
292 *
293 * @return void
294 *
295 */
296 function addRules() {}
297
298 function validate() {
299 $error = parent::validate();
300
301 $hookErrors = CRM_Utils_Hook::validate(
302 get_class($this),
303 $this->_submitValues,
304 $this->_submitFiles,
305 $this
306 );
307
308 if (!is_array($hookErrors)) {
309 $hookErrors = array();
310 }
311
312 CRM_Utils_Hook::validateForm(
313 get_class($this),
314 $this->_submitValues,
315 $this->_submitFiles,
316 $this,
317 $hookErrors
318 );
319
320 if (!empty($hookErrors)) {
321 $this->_errors += $hookErrors;
322 }
323
324 return (0 == count($this->_errors));
325 }
326
327 /**
328 * Core function that builds the form. We redefine this function
329 * here and expect all CRM forms to build their form in the function
330 * buildQuickForm.
331 *
332 */
333 function buildForm() {
334 $this->_formBuilt = TRUE;
335
336 $this->preProcess();
337
338 $this->assign('translatePermission', CRM_Core_Permission::check('translate CiviCRM'));
339
340 if (
341 $this->controller->_key &&
342 $this->controller->_generateQFKey
343 ) {
344 $this->addElement('hidden', 'qfKey', $this->controller->_key);
345 $this->assign('qfKey', $this->controller->_key);
346 }
347
348
349 $this->buildQuickForm();
350
351 $defaults = $this->setDefaultValues();
352 unset($defaults['qfKey']);
353
354 if (!empty($defaults)) {
355 $this->setDefaults($defaults);
356 }
357
358 // call the form hook
359 // also call the hook function so any modules can set thier own custom defaults
360 // the user can do both the form and set default values with this hook
361 CRM_Utils_Hook::buildForm(get_class($this), $this);
362
363 $this->addRules();
364 }
365
366 /**
367 * Add default Next / Back buttons
368 *
369 * @param array array of associative arrays in the order in which the buttons should be
370 * displayed. The associate array has 3 fields: 'type', 'name' and 'isDefault'
371 * The base form class will define a bunch of static arrays for commonly used
372 * formats
373 *
374 * @return void
375 *
376 * @access public
377 *
378 */
379 function addButtons($params) {
380 $prevnext = array();
381 $spacing = array();
382 foreach ($params as $button) {
383 $js = CRM_Utils_Array::value('js', $button);
384 $isDefault = CRM_Utils_Array::value('isDefault', $button, FALSE);
385 if ($isDefault) {
386 $attrs = array('class' => 'form-submit default');
387 }
388 else {
389 $attrs = array('class' => 'form-submit');
390 }
391
392 if ($js) {
393 $attrs = array_merge($js, $attrs);
394 }
395
396 if ($button['type'] === 'reset') {
397 $prevnext[] = $this->createElement($button['type'], 'reset', $button['name'], $attrs);
398 }
399 else {
400 if (CRM_Utils_Array::value('subName', $button)) {
401 $buttonName = $this->getButtonName($button['type'], $button['subName']);
402 }
403 else {
404 $buttonName = $this->getButtonName($button['type']);
405 }
406
407 if (in_array($button['type'], array(
408 'next', 'upload')) && $button['name'] === 'Save') {
409 $attrs = array_merge($attrs, (array('accesskey' => 'S')));
410 }
411 $prevnext[] = $this->createElement('submit', $buttonName, $button['name'], $attrs);
412 }
413 if (CRM_Utils_Array::value('isDefault', $button)) {
414 $this->setDefaultAction($button['type']);
415 }
416
417 // if button type is upload, set the enctype
418 if ($button['type'] == 'upload') {
419 $this->updateAttributes(array('enctype' => 'multipart/form-data'));
420 $this->setMaxFileSize();
421 }
422
423 // hack - addGroup uses an array to express variable spacing, read from the last element
424 $spacing[] = CRM_Utils_Array::value('spacing', $button, self::ATTR_SPACING);
425 }
426 $this->addGroup($prevnext, 'buttons', '', $spacing, FALSE);
427 }
428
429 /**
430 * getter function for Name
431 *
432 * @return string
433 * @access public
434 */
435 function getName() {
436 return $this->_name;
437 }
438
439 /**
440 * getter function for State
441 *
442 * @return object
443 * @access public
444 */
445 function &getState() {
446 return $this->_state;
447 }
448
449 /**
450 * getter function for StateType
451 *
452 * @return int
453 * @access public
454 */
455 function getStateType() {
456 return $this->_state->getType();
457 }
458
459 /**
460 * getter function for title. Should be over-ridden by derived class
461 *
462 * @return string
463 * @access public
464 */
465 function getTitle() {
466 return $this->_title ? $this->_title : ts('ERROR: Title is not Set');
467 }
468
469 /**
470 * setter function for title.
471 *
472 * @param string $title the title of the form
473 *
474 * @return void
475 * @access public
476 */
477 function setTitle($title) {
478 $this->_title = $title;
479 }
480
481 /**
482 * Setter function for options
483 *
484 * @param mixed
485 *
486 * @return void
487 * @access public
488 */
489 function setOptions($options) {
490 $this->_options = $options;
491 }
492
493 /**
494 * getter function for link.
495 *
496 * @return string
497 * @access public
498 */
499 function getLink() {
500 $config = CRM_Core_Config::singleton();
501 return CRM_Utils_System::url($_GET[$config->userFrameworkURLVar],
502 '_qf_' . $this->_name . '_display=true'
503 );
504 }
505
506 /**
507 * boolean function to determine if this is a one form page
508 *
509 * @return boolean
510 * @access public
511 */
512 function isSimpleForm() {
513 return $this->_state->getType() & (CRM_Core_State::START | CRM_Core_State::FINISH);
514 }
515
516 /**
517 * getter function for Form Action
518 *
519 * @return string
520 * @access public
521 */
522 function getFormAction() {
523 return $this->_attributes['action'];
524 }
525
526 /**
527 * setter function for Form Action
528 *
529 * @param string
530 *
531 * @return void
532 * @access public
533 */
534 function setFormAction($action) {
535 $this->_attributes['action'] = $action;
536 }
537
538 /**
539 * render form and return contents
540 *
541 * @return string
542 * @access public
543 */
544 function toSmarty() {
545 $renderer = $this->getRenderer();
546 $this->accept($renderer);
547 $content = $renderer->toArray();
548 $content['formName'] = $this->getName();
549 return $content;
550 }
551
552 /**
553 * getter function for renderer. If renderer is not set
554 * create one and initialize it
555 *
556 * @return object
557 * @access public
558 */
559 function &getRenderer() {
560 if (!isset($this->_renderer)) {
561 $this->_renderer = CRM_Core_Form_Renderer::singleton();
562 }
563 return $this->_renderer;
564 }
565
566 /**
567 * Use the form name to create the tpl file name
568 *
569 * @return string
570 * @access public
571 */
572 function getTemplateFileName() {
573 $ext = CRM_Extension_System::singleton()->getMapper();
574 if ($ext->isExtensionClass(CRM_Utils_System::getClassName($this))) {
575 $filename = $ext->getTemplateName(CRM_Utils_System::getClassName($this));
576 $tplname = $ext->getTemplatePath(CRM_Utils_System::getClassName($this)) . DIRECTORY_SEPARATOR . $filename;
577 }
578 else {
579 $tplname = str_replace('_',
580 DIRECTORY_SEPARATOR,
581 CRM_Utils_System::getClassName($this)
582 ) . '.tpl';
583 }
584 return $tplname;
585 }
586
587 /**
588 * Default extra tpl file basically just replaces .tpl with .extra.tpl
589 * i.e. we dont override
590 *
591 * @return string
592 * @access public
593 */
594 function overrideExtraTemplateFileName() {
595 return NULL;
596 }
597
598 /**
599 * Error reporting mechanism
600 *
601 * @param string $message Error Message
602 * @param int $code Error Code
603 * @param CRM_Core_DAO $dao A data access object on which we perform a rollback if non - empty
604 *
605 * @return void
606 * @access public
607 */
608 function error($message, $code = NULL, $dao = NULL) {
609 if ($dao) {
610 $dao->query('ROLLBACK');
611 }
612
613 $error = CRM_Core_Error::singleton();
614
615 $error->push($code, $message);
616 }
617
618 /**
619 * Store the variable with the value in the form scope
620 *
621 * @param string name : name of the variable
622 * @param mixed value : value of the variable
623 *
624 * @access public
625 *
626 * @return void
627 *
628 */
629 function set($name, $value) {
630 $this->controller->set($name, $value);
631 }
632
633 /**
634 * Get the variable from the form scope
635 *
636 * @param string name : name of the variable
637 *
638 * @access public
639 *
640 * @return mixed
641 *
642 */
643 function get($name) {
644 return $this->controller->get($name);
645 }
646
647 /**
648 * getter for action
649 *
650 * @return int
651 * @access public
652 */
653 function getAction() {
654 return $this->_action;
655 }
656
657 /**
658 * setter for action
659 *
660 * @param int $action the mode we want to set the form
661 *
662 * @return void
663 * @access public
664 */
665 function setAction($action) {
666 $this->_action = $action;
667 }
668
669 /**
670 * assign value to name in template
671 *
672 * @param array|string $name name of variable
673 * @param mixed $value value of varaible
674 *
675 * @return void
676 * @access public
677 */
678 function assign($var, $value = NULL) {
679 self::$_template->assign($var, $value);
680 }
681
682 /**
683 * assign value to name in template by reference
684 *
685 * @param array|string $name name of variable
686 * @param mixed $value value of varaible
687 *
688 * @return void
689 * @access public
690 */
691 function assign_by_ref($var, &$value) {
692 self::$_template->assign_by_ref($var, $value);
693 }
694
695 function &addRadio($name, $title, &$values, $attributes = NULL, $separator = NULL, $required = FALSE) {
696 $options = array();
697 if (empty($attributes)) {
698 $attributes = array('id_suffix' => $name);
699 }
700 else {
701 $attributes = array_merge($attributes, array('id_suffix' => $name));
702 }
703 foreach ($values as $key => $var) {
704 $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
705 }
706 $group = $this->addGroup($options, $name, $title, $separator);
707 if ($required) {
708 $this->addRule($name, ts('%1 is a required field.', array(1 => $title)), 'required');
709 }
710 return $group;
711 }
712
713 function addYesNo($id, $title, $dontKnow = NULL, $required = NULL, $attribute = NULL) {
714 if (empty($attribute)) {
715 $attribute = array('id_suffix' => $id);
716 }
717 else {
718 $attribute = array_merge($attribute, array('id_suffix' => $id));
719 }
720 $choice = array();
721 $choice[] = $this->createElement('radio', NULL, '11', ts('Yes'), '1', $attribute);
722 $choice[] = $this->createElement('radio', NULL, '11', ts('No'), '0', $attribute);
723 if ($dontKnow) {
724 $choice[] = $this->createElement('radio', NULL, '22', ts("Don't Know"), '2', $attribute);
725 }
726 $this->addGroup($choice, $id, $title);
727
728 if ($required) {
729 $this->addRule($id, ts('%1 is a required field.', array(1 => $title)), 'required');
730 }
731 }
732
733 function addCheckBox($id, $title, $values, $other = NULL,
734 $attributes = NULL, $required = NULL,
735 $javascriptMethod = NULL,
736 $separator = '<br />', $flipValues = FALSE
737 ) {
738 $options = array();
739
740 if ($javascriptMethod) {
741 foreach ($values as $key => $var) {
742 if (!$flipValues) {
743 $options[] = $this->createElement('checkbox', $var, NULL, $key, $javascriptMethod);
744 }
745 else {
746 $options[] = $this->createElement('checkbox', $key, NULL, $var, $javascriptMethod);
747 }
748 }
749 }
750 else {
751 foreach ($values as $key => $var) {
752 if (!$flipValues) {
753 $options[] = $this->createElement('checkbox', $var, NULL, $key);
754 }
755 else {
756 $options[] = $this->createElement('checkbox', $key, NULL, $var);
757 }
758 }
759 }
760
761 $this->addGroup($options, $id, $title, $separator);
762
763 if ($other) {
764 $this->addElement('text', $id . '_other', ts('Other'), $attributes[$id . '_other']);
765 }
766
767 if ($required) {
768 $this->addRule($id,
769 ts('%1 is a required field.', array(1 => $title)),
770 'required'
771 );
772 }
773 }
774
775 function resetValues() {
776 $data = $this->controller->container();
777 $data['values'][$this->_name] = array();
778 }
779
780 /**
781 * simple shell that derived classes can call to add buttons to
782 * the form with a customized title for the main Submit
783 *
784 * @param string $title title of the main button
785 * @param string $type button type for the form after processing
786 * @param string $submitOnce If true, add javascript to next button submit which prevents it from being clicked more than once
787 *
788 * @return void
789 * @access public
790 */
791 function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) {
792 $buttons = array();
793 if ($backType != NULL) {
794 $buttons[] = array(
795 'type' => $backType,
796 'name' => ts('Previous'),
797 );
798 }
799 if ($nextType != NULL) {
800 $nextButton = array(
801 'type' => $nextType,
802 'name' => $title,
803 'isDefault' => TRUE,
804 );
805 if ($submitOnce) {
806 $nextButton['js'] = array('onclick' => "return submitOnce(this,'{$this->_name}','" . ts('Processing') . "');");
807 }
808 $buttons[] = $nextButton;
809 }
810 $this->addButtons($buttons);
811 }
812
813 function addDateRange($name, $from = '_from', $to = '_to', $label = 'From:', $dateFormat = 'searchDate', $required = FALSE, $displayTime = FALSE) {
814 if ($displayTime) {
815 $this->addDateTime($name . $from, $label, $required, array('formatType' => $dateFormat));
816 $this->addDateTime($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
817 } else {
818 $this->addDate($name . $from, $label, $required, array('formatType' => $dateFormat));
819 $this->addDate($name . $to, ts('To:'), $required, array('formatType' => $dateFormat));
820 }
821 }
822
823 function addSelect($name, $label, $prefix = NULL, $required = NULL, $extra = NULL, $select = '- select -') {
824 if ($prefix) {
825 $this->addElement('select', $name . '_id' . $prefix, $label,
826 array(
827 '' => $select) + CRM_Core_OptionGroup::values($name), $extra
828 );
829 if ($required) {
830 $this->addRule($name . '_id' . $prefix, ts('Please select %1', array(1 => $label)), 'required');
831 }
832 }
833 else {
834 $this->addElement('select', $name . '_id', $label,
835 array(
836 '' => $select) + CRM_Core_OptionGroup::values($name), $extra
837 );
838 if ($required) {
839 $this->addRule($name . '_id', ts('Please select %1', array(1 => $label)), 'required');
840 }
841 }
842 }
843
844 /**
845 * Add a widget for selecting/editing/creating/copying a profile form
846 *
847 * @param string $name HTML form-element name
848 * @param string $label Printable label
849 * @param string $allowCoreTypes only present a UFGroup if its group_type includes a subset of $allowCoreTypes; e.g. 'Individual', 'Activity'
850 * @param string $allowSubTypes only present a UFGroup if its group_type is compatible with $allowSubypes
851 * @param array $entities
852 */
853 function addProfileSelector($name, $label, $allowCoreTypes, $allowSubTypes, $entities) {
854 // Output widget
855 // FIXME: Instead of adhoc serialization, use a single json_encode()
856 CRM_UF_Page_ProfileEditor::registerProfileScripts();
857 CRM_UF_Page_ProfileEditor::registerSchemas(CRM_Utils_Array::collect('entity_type', $entities));
858 $this->add('text', $name, $label, array(
859 'class' => 'crm-profile-selector',
860 // Note: client treats ';;' as equivalent to \0, and ';;' works better in HTML
861 'data-group-type' => CRM_Core_BAO_UFGroup::encodeGroupType($allowCoreTypes, $allowSubTypes, ';;'),
862 'data-entities' => json_encode($entities),
863 ));
864 }
865
866 function addWysiwyg($name, $label, $attributes, $forceTextarea = FALSE) {
867 // 1. Get configuration option for editor (tinymce, ckeditor, pure textarea)
868 // 2. Based on the option, initialise proper editor
869 $editorID = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
870 'editor_id'
871 );
872 $editor = strtolower(CRM_Utils_Array::value($editorID,
873 CRM_Core_PseudoConstant::wysiwygEditor()
874 ));
875 if (!$editor || $forceTextarea) {
876 $editor = 'textarea';
877 }
878 if ($editor == 'joomla default editor') {
879 $editor = 'joomlaeditor';
880 }
881
882 if ($editor == 'drupal default editor') {
883 $editor = 'drupalwysiwyg';
884 }
885
886 //lets add the editor as a attribute
887 $attributes['editor'] = $editor;
888
889 $this->addElement($editor, $name, $label, $attributes);
890 $this->assign('editor', $editor);
891
892 // include wysiwyg editor js files
893 $includeWysiwygEditor = FALSE;
894 $includeWysiwygEditor = $this->get('includeWysiwygEditor');
895 if (!$includeWysiwygEditor) {
896 $includeWysiwygEditor = TRUE;
897 $this->set('includeWysiwygEditor', $includeWysiwygEditor);
898 }
899
900 $this->assign('includeWysiwygEditor', $includeWysiwygEditor);
901 }
902
903 function addCountry($id, $title, $required = NULL, $extra = NULL) {
904 $this->addElement('select', $id, $title,
905 array(
906 '' => ts('- select -')) + CRM_Core_PseudoConstant::country(), $extra
907 );
908 if ($required) {
909 $this->addRule($id, ts('Please select %1', array(1 => $title)), 'required');
910 }
911 }
912
913 function addSelectOther($name, $label, $options, $attributes, $required = NULL, $javascriptMethod = NULL) {
914
915 $this->addElement('select', $name . '_id', $label, $options, $javascriptMethod);
916
917 if ($required) {
918 $this->addRule($name . '_id', ts('Please select %1', array(1 => $label)), 'required');
919 }
920 }
921
922 function buildAddressBlock($locationId, $title, $phone,
923 $alternatePhone = NULL, $addressRequired = NULL,
924 $phoneRequired = NULL, $altPhoneRequired = NULL,
925 $locationName = NULL
926 ) {
927 if (!$locationName) {
928 $locationName = "location";
929 }
930
931 $config = CRM_Core_Config::singleton();
932 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_Address');
933
934 $location[$locationId]['address']['street_address'] = $this->addElement('text', "{$locationName}[$locationId][address][street_address]", $title,
935 $attributes['street_address']
936 );
937 if ($addressRequired) {
938 $this->addRule("{$locationName}[$locationId][address][street_address]", ts("Please enter the Street Address for %1.", array(1 => $title)), 'required');
939 }
940
941 $location[$locationId]['address']['supplemental_address_1'] = $this->addElement('text', "{$locationName}[$locationId][address][supplemental_address_1]", ts('Additional Address 1'),
942 $attributes['supplemental_address_1']
943 );
944 $location[$locationId]['address']['supplemental_address_2'] = $this->addElement('text', "{$locationName}[$locationId][address][supplemental_address_2]", ts('Additional Address 2'),
945 $attributes['supplemental_address_2']
946 );
947
948 $location[$locationId]['address']['city'] = $this->addElement('text', "{$locationName}[$locationId][address][city]", ts('City'),
949 $attributes['city']
950 );
951 if ($addressRequired) {
952 $this->addRule("{$locationName}[$locationId][address][city]", ts("Please enter the City for %1.", array(1 => $title)), 'required');
953 }
954
955 $location[$locationId]['address']['postal_code'] = $this->addElement('text', "{$locationName}[$locationId][address][postal_code]", ts('Zip / Postal Code'),
956 $attributes['postal_code']
957 );
958 if ($addressRequired) {
959 $this->addRule("{$locationName}[$locationId][address][postal_code]", ts("Please enter the Zip/Postal Code for %1.", array(1 => $title)), 'required');
960 }
961
962 $location[$locationId]['address']['postal_code_suffix'] = $this->addElement('text', "{$locationName}[$locationId][address][postal_code_suffix]", ts('Add-on Code'),
963 array('size' => 4, 'maxlength' => 12)
964 );
965 $this->addRule("{$locationName}[$locationId][address][postal_code_suffix]", ts('Zip-Plus not valid.'), 'positiveInteger');
966
967 if ($config->includeCounty) {
968 $location[$locationId]['address']['county_id'] = $this->addElement('select', "{$locationName}[$locationId][address][county_id]", ts('County'),
969 array('' => ts('- select -')) + CRM_Core_PseudoConstant::county()
970 );
971 }
972
973 $location[$locationId]['address']['state_province_id'] = $this->addElement('select', "{$locationName}[$locationId][address][state_province_id]", ts('State / Province'),
974 array('' => ts('- select -')) + CRM_Core_PseudoConstant::stateProvince()
975 );
976
977 $location[$locationId]['address']['country_id'] = $this->addElement('select', "{$locationName}[$locationId][address][country_id]", ts('Country'),
978 array('' => ts('- select -')) + CRM_Core_PseudoConstant::country()
979 );
980 if ($addressRequired) {
981 $this->addRule("{$locationName}[$locationId][address][country_id]", ts("Please select the Country for %1.", array(1 => $title)), 'required');
982 }
983
984
985 if ($phone) {
986 $location[$locationId]['phone'][1]['phone'] = $this->addElement('text',
987 "{$locationName}[$locationId][phone][1][phone]",
988 $phone,
989 CRM_Core_DAO::getAttribute('CRM_Core_DAO_Phone',
990 'phone'
991 )
992 );
993 if ($phoneRequired) {
994 $this->addRule("{$locationName}[$locationId][phone][1][phone]", ts('Please enter a value for %1', array(1 => $phone)), 'required');
995 }
996 $this->addRule("{$locationName}[$locationId][phone][1][phone]", ts('Please enter a valid number for %1', array(1 => $phone)), 'phone');
997 }
998
999 if ($alternatePhone) {
1000 $location[$locationId]['phone'][2]['phone'] = $this->addElement('text',
1001 "{$locationName}[$locationId][phone][2][phone]",
1002 $alternatePhone,
1003 CRM_Core_DAO::getAttribute('CRM_Core_DAO_Phone',
1004 'phone'
1005 )
1006 );
1007 if ($alternatePhoneRequired) {
1008 $this->addRule("{$locationName}[$locationId][phone][2][phone]", ts('Please enter a value for %1', array(1 => $alternatePhone)), 'required');
1009 }
1010 $this->addRule("{$locationName}[$locationId][phone][2][phone]", ts('Please enter a valid number for %1', array(1 => $alternatePhone)), 'phone');
1011 }
1012 }
1013
1014 public function getRootTitle() {
1015 return NULL;
1016 }
1017
1018 public function getCompleteTitle() {
1019 return $this->getRootTitle() . $this->getTitle();
1020 }
1021
1022 static function &getTemplate() {
1023 return self::$_template;
1024 }
1025
1026 function addUploadElement($elementName) {
1027 $uploadNames = $this->get('uploadNames');
1028 if (!$uploadNames) {
1029 $uploadNames = array();
1030 }
1031 if (is_array($elementName)) {
1032 foreach ($elementName as $name) {
1033 if (!in_array($name, $uploadNames)) {
1034 $uploadNames[] = $name;
1035 }
1036 }
1037 }
1038 else {
1039 if (!in_array($elementName, $uploadNames)) {
1040 $uploadNames[] = $elementName;
1041 }
1042 }
1043 $this->set('uploadNames', $uploadNames);
1044
1045 $config = CRM_Core_Config::singleton();
1046 if (!empty($uploadNames)) {
1047 $this->controller->addUploadAction($config->customFileUploadDir, $uploadNames);
1048 }
1049 }
1050
1051 function buttonType() {
1052 $uploadNames = $this->get('uploadNames');
1053 $buttonType = (is_array($uploadNames) && !empty($uploadNames)) ? 'upload' : 'next';
1054 $this->assign('buttonType', $buttonType);
1055 return $buttonType;
1056 }
1057
1058 function getVar($name) {
1059 return isset($this->$name) ? $this->$name : NULL;
1060 }
1061
1062 function setVar($name, $value) {
1063 $this->$name = $value;
1064 }
1065
1066 /**
1067 * Function to add date
1068 * @param string $name name of the element
1069 * @param string $label label of the element
1070 * @param array $attributes key / value pair
1071 *
1072 // if you need time
1073 * $attributes = array ( 'addTime' => true,
1074 * 'formatType' => 'relative' or 'birth' etc check advanced date settings
1075 * );
1076 * @param boolean $required true if required
1077 *
1078 */
1079 function addDate($name, $label, $required = FALSE, $attributes = NULL) {
1080 if (CRM_Utils_Array::value('formatType', $attributes)) {
1081 // get actual format
1082 $params = array('name' => $attributes['formatType']);
1083 $values = array();
1084
1085 // cache date information
1086 static $dateFormat;
1087 $key = "dateFormat_" . str_replace(' ', '_', $attributes['formatType']);
1088 if (!CRM_Utils_Array::value($key, $dateFormat)) {
1089 CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, $values);
1090 $dateFormat[$key] = $values;
1091 }
1092 else {
1093 $values = $dateFormat[$key];
1094 }
1095
1096 if ($values['date_format']) {
1097 $attributes['format'] = $values['date_format'];
1098 }
1099
1100 if (CRM_Utils_Array::value('time_format', $values)) {
1101 $attributes['timeFormat'] = $values['time_format'];
1102 }
1103 $attributes['startOffset'] = $values['start'];
1104 $attributes['endOffset'] = $values['end'];
1105 }
1106
1107 $config = CRM_Core_Config::singleton();
1108 if (!CRM_Utils_Array::value('format', $attributes)) {
1109 $attributes['format'] = $config->dateInputFormat;
1110 }
1111
1112 if (!isset($attributes['startOffset'])) {
1113 $attributes['startOffset'] = 10;
1114 }
1115
1116 if (!isset($attributes['endOffset'])) {
1117 $attributes['endOffset'] = 10;
1118 }
1119
1120 $this->add('text', $name, $label, $attributes);
1121
1122 if (CRM_Utils_Array::value('addTime', $attributes) ||
1123 CRM_Utils_Array::value('timeFormat', $attributes)
1124 ) {
1125
1126 if (!isset($attributes['timeFormat'])) {
1127 $timeFormat = $config->timeInputFormat;
1128 }
1129 else {
1130 $timeFormat = $attributes['timeFormat'];
1131 }
1132
1133 // 1 - 12 hours and 2 - 24 hours, but for jquery widget it is 0 and 1 respectively
1134 if ($timeFormat) {
1135 $show24Hours = TRUE;
1136 if ($timeFormat == 1) {
1137 $show24Hours = FALSE;
1138 }
1139
1140 //CRM-6664 -we are having time element name
1141 //in either flat string or an array format.
1142 $elementName = $name . '_time';
1143 if (substr($name, -1) == ']') {
1144 $elementName = substr($name, 0, strlen($name) - 1) . '_time]';
1145 }
1146
1147 $this->add('text', $elementName, ts('Time'), array('timeFormat' => $show24Hours));
1148 }
1149 }
1150
1151 if ($required) {
1152 $this->addRule($name, ts('Please select %1', array(1 => $label)), 'required');
1153 if (CRM_Utils_Array::value('addTime', $attributes) && CRM_Utils_Array::value('addTimeRequired', $attributes)) {
1154 $this->addRule($elementName, ts('Please enter a time.'), 'required');
1155 }
1156 }
1157 }
1158
1159 /**
1160 * Function that will add date and time
1161 */
1162 function addDateTime($name, $label, $required = FALSE, $attributes = NULL) {
1163 $addTime = array('addTime' => TRUE);
1164 if (is_array($attributes)) {
1165 $attributes = array_merge($attributes, $addTime);
1166 }
1167 else {
1168 $attributes = $addTime;
1169 }
1170
1171 $this->addDate($name, $label, $required, $attributes);
1172 }
1173
1174 /**
1175 * add a currency and money element to the form
1176 */
1177 function addMoney($name,
1178 $label,
1179 $required = FALSE,
1180 $attributes = NULL,
1181 $addCurrency = TRUE,
1182 $currencyName = 'currency',
1183 $defaultCurrency = NULL,
1184 $freezeCurrency = FALSE
1185 ) {
1186 $element = $this->add('text', $name, $label, $attributes, $required);
1187 $this->addRule($name, ts('Please enter a valid amount.'), 'money');
1188
1189 if ($addCurrency) {
1190 $ele = $this->addCurrency($currencyName, NULL, TRUE, $defaultCurrency, $freezeCurrency);
1191 }
1192
1193 return $element;
1194 }
1195
1196 /**
1197 * add currency element to the form
1198 */
1199 function addCurrency($name = 'currency',
1200 $label = NULL,
1201 $required = TRUE,
1202 $defaultCurrency = NULL,
1203 $freezeCurrency = FALSE
1204 ) {
1205 $currencies = CRM_Core_OptionGroup::values('currencies_enabled');
1206 if (!$required) {
1207 $currencies = array(
1208 '' => ts('- select -')) + $currencies;
1209 }
1210 $ele = $this->add('select', $name, $label, $currencies, $required);
1211 if ($freezeCurrency) {
1212 $ele->freeze();
1213 }
1214 if (!$defaultCurrency) {
1215 $config = CRM_Core_Config::singleton();
1216 $defaultCurrency = $config->defaultCurrency;
1217 }
1218 $this->setDefaults(array($name => $defaultCurrency));
1219 }
1220
1221 function removeFileRequiredRules($elementName) {
1222 $this->_required = array_diff($this->_required, array($elementName));
1223 if (isset($this->_rules[$elementName])) {
1224 foreach ($this->_rules[$elementName] as $index => $ruleInfo) {
1225 if ($ruleInfo['type'] == 'uploadedfile') {
1226 unset($this->_rules[$elementName][$index]);
1227 }
1228 }
1229 if (empty($this->_rules[$elementName])) {
1230 unset($this->_rules[$elementName]);
1231 }
1232 }
1233 }
1234
1235 /**
1236 * Function that can be defined in Form to override or
1237 * perform specific action on cancel action
1238 *
1239 * @access public
1240 */
1241 function cancelAction() {}
1242 }
1243