3 * A helper for constructing web forms
5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright(c) 2007, 2008 Roland Haeder, this is free software
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.ship-simu.org
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 class WebFormHelper extends BaseHelper implements HelpableTemplate {
26 * Instance to the class which provides field values
28 private $valueInstance = null;
31 * Wether the form tag is opened (keep at false or else your forms will
34 private $formOpened = false;
39 private $formName = "";
42 * Wether the group is opened or not
44 private $groupOpened = false;
47 * Wether the sub group is opened or not
49 private $subGroupOpened = false;
54 private $groupName = "";
57 * Name of the sub group
59 private $subGroupName = "";
62 * Wether form tag is enabled (default: true)
64 private $formEnabled = true;
67 const EXCEPTION_FORM_NAME_INVALID = 0x120;
68 const EXCEPTION_CLOSED_FORM = 0x121;
69 const EXCEPTION_OPENED_FORM = 0x122;
70 const EXCEPTION_UNEXPECTED_CLOSED_GROUP = 0x123;
73 * Protected constructor
77 protected function __construct () {
78 // Call parent constructor
79 parent::__construct(__CLASS__);
81 // Set part description
82 $this->setObjectDescription("Helper class for HTML forms");
84 // Create unique ID number
85 $this->generateUniqueId();
89 * Creates the helper class with the given template engine instance and form name
91 * @param $templateInstance An instance of a valid template engine
92 * @param $formName Name of the form
93 * @param $formId Value for "id" attribute (default: $formName)
94 * @param $withForm Wether include the form tag
95 * @return $helperInstance A preparedf instance of this class
97 public final static function createWebFormHelper (CompileableTemplate $templateInstance, $formName, $formId = false, $withForm = true) {
99 $helperInstance = new WebFormHelper();
101 // Set template instance
102 $helperInstance->setTemplateInstance($templateInstance);
104 // Is the form id not set?
105 if ($formId === false) {
106 // Use form id from form name
111 $helperInstance->setFormName($formName);
112 // A form-less field may say "false" here...
113 if ($withForm === true) {
115 $helperInstance->addFormTag($formName, $formId);
118 $helperInstance->enableForm(false);
121 // Return the prepared instance
122 return $helperInstance;
126 * Pre-fetches field default values from the given registry key instance into this class
128 * @param $registryKey
130 * @throws NullPointerException If an instance from registry is null
132 public function prefetchFieldValues ($registryKey) {
133 // Get the required instance
134 $this->valueInstance = Registry::getRegistry()->getInstance($registryKey);
136 // Is the instance valid?
137 if (is_null($this->valueInstance)) {
138 // Throw an exception
139 throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
144 * Add the form tag or close it an already opened form tag
146 * @param $formName Name of the form (default: false)
147 * @param $formId Id of the form (attribute "id"; default: false)
149 * @throws InvalidFormNameException If the form name is invalid (=false)
150 * @todo Add some unique PIN here to bypass problems with some browser and/or extensions
152 public function addFormTag ($formName = false, $formId = false) {
153 // When the form is not yet opened at least form name must be valid
154 if (($this->formOpened === false) && ($formName === false)) {
155 // Thrown an exception
156 throw new InvalidFormNameException ($this, self::EXCEPTION_FORM_NAME_INVALID);
159 // Close the form is default
160 $formContent = "</form>";
162 // Check wether we shall open or close the form
163 if (($this->formOpened === false) && ($this->formEnabled === true)) {
165 $formContent = sprintf("<form name=\"%s\" class=\"forms\" action=\"%s/%s\" method=\"%s\" target=\"%s\"",
167 $this->getConfigInstance()->readConfig('base_url'),
168 $this->getConfigInstance()->readConfig('form_action'),
169 $this->getConfigInstance()->readConfig('form_method'),
170 $this->getConfigInstance()->readConfig('form_target')
173 // Add form id as well
174 $formContent .= sprintf(" id=\"%s_form\"",
181 // Open the form and remeber the form name
182 $this->formOpened = true;
184 // Add the hidden field required to identify safely this form
185 $this->addInputHiddenField('form', $this->getFormName());
188 if ($this->groupOpened === true) {
189 // Then automatically close it here
190 $this->addFormGroup();
194 $this->formOpened = false;
197 // Add it to the content
198 $this->addContent($formContent);
202 * Add a text input tag to the form or throw an exception if it is not yet
203 * opened. The field's name will be set as id.
205 * @param $fieldName Input field name
206 * @param $fieldValue Input default value (default: empty)
208 * @throws FormClosedException If the form is not yet opened
210 public function addInputTextField ($fieldName, $fieldValue = "") {
211 // Is the form opened?
212 if (($this->formOpened === false) && ($this->formEnabled === true)) {
213 // Throw an exception
214 throw new FormClosedException (array($this, $fieldName), self::EXCEPTION_CLOSED_FORM);
217 // Generate the content
218 $inputContent = sprintf("<input type=\"text\" class=\"textfield %s_field\" name=\"%s\" value=\"%s\" />",
224 // And add it maybe with a "li" tag
225 $this->addContent($inputContent);
229 * Add a text input tag to the form with pre-loaded default value
231 * @param $fieldName Input field name
234 public function addInputTextFieldWithDefault ($fieldName) {
235 // Get the value from instance
236 $fieldValue = $this->getField($fieldName);
237 //* DEBUG: */ echo __METHOD__.":".$fieldName."=".$fieldValue."<br />\n";
239 // Add the text field
240 $this->addInputTextField($fieldName, $fieldValue);
244 * Add a password input tag to the form or throw an exception if it is not
245 * yet opened. The field's name will be set as id.
247 * @param $fieldName Input field name
248 * @param $fieldValue Input default value (default: empty)
250 * @throws FormClosedException If the form is not yet opened
252 public function addInputPasswordField ($fieldName, $fieldValue = "") {
253 // Is the form opened?
254 if (($this->formOpened === false) && ($this->formEnabled === true)) {
255 // Throw an exception
256 throw new FormClosedException (array($this, $fieldName), self::EXCEPTION_CLOSED_FORM);
259 // Generate the content
260 $inputContent = sprintf("<input type=\"password\" class=\"password %s_field\" name=\"%s\" value=\"%s\" />",
267 $this->addContent($inputContent);
271 * Add a hidden input tag to the form or throw an exception if it is not
272 * yet opened. The field's name will be set as id.
274 * @param $fieldName Input field name
275 * @param $fieldValue Input default value (default: empty)
277 * @throws FormClosedException If the form is not yet opened
279 public function addInputHiddenField ($fieldName, $fieldValue = "") {
280 // Is the form opened?
281 if (($this->formOpened === false) && ($this->formEnabled === true)) {
282 // Throw an exception
283 throw new FormClosedException (array($this, $fieldName), self::EXCEPTION_CLOSED_FORM);
286 // Generate the content
287 $inputContent = sprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\" />",
293 $this->addContent($inputContent);
297 * Add a hidden input tag to the form with pre-loaded default value
299 * @param $fieldName Input field name
302 public function addInputHiddenFieldWithDefault ($fieldName) {
303 // Get the value from instance
304 $fieldValue = $this->getField($fieldName);
305 //* DEBUG: */ echo __METHOD__.":".$fieldName."=".$fieldValue."<br />\n";
307 // Add the text field
308 $this->addInputHiddenField($fieldName, $fieldValue);
312 * Add a hidden input tag to the form with configuration value
314 * @param $fieldName Input field name
315 * @param $prefix Prefix for configuration without trailing _
318 public function addInputHiddenConfiguredField ($fieldName, $prefix) {
319 // Get the value from instance
320 $fieldValue = $this->getConfigInstance()->readConfig("{$prefix}_{$fieldName}");
321 //* DEBUG: */ echo __METHOD__.":".$fieldName."=".$fieldValue."<br />\n";
323 // Add the text field
324 $this->addInputHiddenField($fieldName, $fieldValue);
328 * Add a checkbox input tag to the form or throw an exception if it is not
329 * yet opened. The field's name will be set as id.
331 * @param $fieldName Input field name
332 * @param $fieldChecked Wether the field is checked (defaut: checked)
334 * @throws FormClosedException If the form is not yet opened
336 public function addInputCheckboxField ($fieldName, $fieldChecked = true) {
337 // Is the form opened?
338 if (($this->formOpened === false) && ($this->formEnabled === true)) {
339 // Throw an exception
340 throw new FormClosedException (array($this, $fieldName), self::EXCEPTION_CLOSED_FORM);
343 // Set wether the check box is checked...
344 $checked = " checked=\"checked\"";
345 if ($fieldChecked === false) $checked = " ";
347 // Generate the content
348 $inputContent = sprintf("<input type=\"checkbox\" name=\"%s\" class=\"checkbox %s_field\" value=\"1\"%s/>",
355 $this->addContent($inputContent);
359 * Add a reset input tag to the form or throw an exception if it is not
360 * yet opened. The field's name will be set as id.
362 * @param $buttonText Text displayed on the button
364 * @throws FormClosedException If the form is not yet opened
366 public function addInputResetButton ($buttonText) {
367 // Is the form opened?
368 if (($this->formOpened === false) && ($this->formEnabled === true)) {
369 // Throw an exception
370 throw new FormClosedException (array($this, "reset"), self::EXCEPTION_CLOSED_FORM);
373 // Generate the content
374 $inputContent = sprintf("<input type=\"reset\" class=\"reset_button\" id=\"%s_reset\" value=\"%s\" />",
375 $this->getFormName(),
380 $this->addContent($inputContent);
384 * Add a reset input tag to the form or throw an exception if it is not
385 * yet opened. The field's name will be set as id.
387 * @param $buttonText Text displayed on the button
389 * @throws FormClosedException If the form is not yet opened
391 public function addInputSubmitButton ($buttonText) {
392 // Is the form opened?
393 if (($this->formOpened === false) && ($this->formEnabled === true)) {
394 // Throw an exception
395 throw new FormClosedException (array($this, "submit"), self::EXCEPTION_CLOSED_FORM);
398 // Generate the content
399 $inputContent = sprintf("<input type=\"submit\" class=\"submit_button\" id=\"%s_submit\" name=\"%s_button\" value=\"%s\" />",
400 $this->getFormName(),
401 $this->getFormName(),
406 $this->addContent($inputContent);
410 * Add a form group or close an already opened and open a new one
412 * @param $groupName Name of the group or last opened if empty
413 * @param $groupText Text including HTML to show above this group
415 * @throws FormClosedException If no form has been opened before
416 * @throws EmptyVariableException If $groupName is not set
418 public function addFormGroup ($groupName = "", $groupText = "") {
420 if (($this->formOpened === false) && ($this->formEnabled === true)) {
421 // Throw exception here
422 throw new FormClosedException(array($this, $groupName), self::EXCEPTION_CLOSED_FORM);
425 // At least the group name should be set
426 if ((empty($groupName)) && ($this->groupOpened === false)) {
427 // Throw exception here
428 throw new EmptyVariableException(array($this, 'groupName'), self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
429 } elseif (empty($groupName)) {
430 // Close the last opened
431 $groupName = $this->groupName;
434 // Same group to open?
435 if (($this->groupOpened === false) && ($groupName == $this->groupName)) {
436 // Abort here silently
440 // Initialize content with closing div by default
441 $content = " </div>\n</div><!-- Group - CLOSE //-->";
443 // Is this group opened?
444 if ($this->groupOpened === false) {
445 // Begin the div/span blocks
446 $content = sprintf("<!-- Group %s - OPEN //-->
447 <div class=\"group_box\" id=\"%s_group_box\">
448 <span class=\"group_text\" id=\"%s_group_text\">
451 <div class=\"group_field\" id=\"%s_group_field\">",
460 $this->addContent($content);
463 $this->groupName = $groupName;
464 $this->groupOpened = true;
466 // Is a sub group opened?
467 if ($this->subGroupOpened === true) {
469 $this->addFormSubGroup();
473 $this->addContent($content);
476 $this->groupOpened = false;
478 // All call it again if the group name is not empty
479 if (!empty($groupName)) {
480 $this->addFormGroup($groupName, $groupText);
486 * Add a form sub group or close an already opened and open a new one or
487 * throws an exception if no group has been opened before or if the sub
488 * group name is empty.
490 * @param $subGroupName Name of the group or last opened if empty
491 * @param $subGroupText Text including HTML to show above this group
493 * @throws FormGroupClosedException If no group has been opened before
494 * @throws EmptyVariableException If $subGroupName is not set
496 public function addFormSubGroup ($subGroupName = "", $subGroupText = "") {
497 // Is a group opened?
498 if ($this->groupOpened === false) {
499 // Throw exception here
500 throw new FormGroupClosedException(array($this, $subGroupName), self::EXCEPTION_UNEXPECTED_CLOSED_GROUP);
503 // At least the sub group name should be set
504 if ((empty($subGroupName)) && ($this->subGroupOpened === false)) {
505 // Throw exception here
506 throw new EmptyVariableException(array($this, 'groupName'), self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
507 } elseif (empty($subGroupName)) {
508 // Close the last opened
509 $subGroupName = $this->subGroupName;
512 // Same sub group to open?
513 if (($this->subGroupOpened === false) && ($subGroupName == $this->subGroupName)) {
514 // Abort here silently
518 // Initialize content with closing div by default
519 $content = " </div>\n</div><!-- Sub group- CLOSE //-->";
521 // Is this group opened?
522 if ($this->subGroupOpened === false) {
523 // Begin the span block
524 $content = sprintf("<!-- Sub group %s - OPEN //-->
525 <div class=\"subgroup_box\" id=\"%s_subgroup_box\">
526 <span class=\"subgroup_text\" id=\"%s_subgroup_text\">
529 <div class=\"subgroup_field\" id=\"%s_subgroup_field\">",
538 $this->addContent($content);
540 // Switch the state and remeber the name
541 $this->subGroupOpened = true;
542 $this->subGroupName = $subGroupName;
545 $this->addContent($content);
548 $this->subGroupOpened = false;
550 // All call it again if sub group name is not empty
551 if (!empty($subGroupName)) {
552 $this->addFormSubGroup($subGroupName, $subGroupText);
558 * Add text surrounded by a span block when there is a group opened before
559 * or else by a div block.
561 * @param $fieldName Field name
562 * @param $fieldText Text for the field
564 * @throws FormClosedException If the form is not yet opened
566 public function addFieldText ($fieldName, $fieldText) {
567 // Is the form opened?
568 if (($this->formOpened === false) && ($this->formEnabled === true)) {
569 // Throw an exception
570 throw new FormClosedException (array($this, $fieldName), self::EXCEPTION_CLOSED_FORM);
573 // Set the block type
575 if ($this->groupOpened === true) $block = "span";
577 // Generate the content
578 $inputContent = sprintf(" <%s id=\"%s_text\">
588 $this->addContent($inputContent);
592 * Add text (notes) surrounded by a div block. Still opened groups or sub
593 * groups will be automatically closed.
595 * @param $noteId Id for this note
596 * @param $formNotes The form notes we shell addd
598 * @throws FormClosedException If the form is not yet opened
600 public function addFormNote ($noteId, $formNotes) {
601 // Is the form opened?
602 if (($this->formOpened === false) && ($this->formEnabled === true)) {
603 // Throw an exception
604 throw new FormClosedException (array($this, "form_notes"), self::EXCEPTION_CLOSED_FORM);
608 if ($this->groupOpened === true) {
609 // Then automatically close it here
610 $this->addFormGroup();
613 // Generate the content
614 $inputContent = sprintf(" <div id=\"form_note_%s\">
622 $this->addContent($inputContent);
626 * Checks wether the registration requires a valid email address
628 * @return $required Wether the email address is required
630 public function ifRegisterRequiresEmailVerification () {
631 $required = ($this->getConfigInstance()->readConfig('register_requires_email') == "Y");
636 * Checks wether profile data shall be asked
638 * @return $required Wether profile shall be asked
640 public function ifRegisterIncludesProfile () {
641 $required = ($this->getConfigInstance()->readConfig('register_includes_profile') == "Y");
646 * Checks wether personal data shall be asked
648 * @return $required Wether personal data shall be asked
650 public function ifRegisterIncludesPersonaData () {
651 $required = ($this->getConfigInstance()->readConfig('register_personal_data') == "Y");
656 * Checks wether email addresses can only be once used
660 public function ifEmailMustBeUnique () {
661 $isUnique = ($this->getConfigInstance()->readConfig('register_email_unique') == "Y");
666 * Checks wether the specified chat protocol is enabled in this form
668 * @return $required Wether the specified chat protocol is enabled
670 public function ifChatEnabled ($chatProtocol) {
671 $required = ($this->getConfigInstance()->readConfig(sprintf("chat_enabled_%s", $chatProtocol)) == "Y");
676 * Checks wether login is enabled or disabled
678 * @return $isEnabled Wether the login is enabled or disabled
680 public function ifLoginIsEnabled () {
681 $isEnabled = ($this->getConfigInstance()->readConfig('login_enabled') == "Y");
686 * Checks wether login shall be done by username
688 * @return $isEnabled Wether the login shall be done by username
690 public function ifLoginWithUsername () {
691 $isEnabled = ($this->getConfigInstance()->readConfig('login_type') == "username");
696 * Checks wether login shall be done by email
698 * @return $isEnabled Wether the login shall be done by email
700 public function ifLoginWithEmail () {
701 $isEnabled = ($this->getConfigInstance()->readConfig('login_type') == "email");
706 * Checks wether guest login is allowed
708 * @return $isAllowed Wether guest login is allowed
710 public function ifGuestLoginAllowed () {
711 $isAllowed = ($this->getConfigInstance()->readConfig('guest_login_allowed') == "Y");
716 * Checks wether the email address change must be confirmed
718 * @return $requireConfirm Wether email change must be confirmed
720 public function ifEmailChangeRequireConfirmation () {
721 $requireConfirm = ($this->getConfigInstance()->readConfig('email_change_confirmation') == "Y");
722 return $requireConfirm;
726 * Checks wether the rules has been updated
728 * @return $rulesUpdated Wether rules has been updated
729 * @todo Implement check if rules have been changed
731 public function ifRulesHaveChanged () {
736 * Checks wether email change is allowed
738 * @return $emailChange Wether changing email address is allowed
740 public function ifEmailChangeAllowed () {
741 $emailChange = ($this->getConfigInstance()->readConfig('email_change_allowed') == "Y");
746 * Checks wether the user account is unconfirmed
748 * @return $isUnconfirmed Wether the user account is unconfirmed
750 public function ifUserAccountUnconfirmed () {
751 $isUnconfirmed = ($this->getField('user_status') === $this->getConfigInstance()->readConfig('user_status_unconfirmed'));
752 return $isUnconfirmed;
756 * Checks wether the user account is locked
758 * @return $isUnconfirmed Wether the user account is locked
760 public function ifUserAccountLocked () {
761 $isUnconfirmed = ($this->getField('user_status') === $this->getConfigInstance()->readConfig('user_status_locked'));
762 return $isUnconfirmed;
766 * Checks wether the user account is a guest
768 * @return $isUnconfirmed Wether the user account is a guest
770 public function ifUserAccountGuest () {
771 $isUnconfirmed = ($this->getField('user_status') === $this->getConfigInstance()->readConfig('user_status_guest'));
772 return $isUnconfirmed;
776 * Checks wether this form is secured by a CAPTCHA
778 * @return $isSecured Wether this form is secured by a CAPTCHA
780 public function ifFormSecuredWithCaptcha () {
781 $isSecured = ($this->getConfigInstance()->readConfig($this->getFormName().'_captcha_secured') === "Y");
786 * Flushs the content out (not yet secured against open forms, etc.!) or
787 * close the form automatically
790 * @throws FormOpenedException If the form is still open
792 public function flushContent () {
793 // Is the form still open?
794 if (($this->formOpened === true) && ($this->formEnabled === true)) {
795 // Close the form automatically
797 } elseif ($this->formEnabled === false) {
798 if ($this->subGroupOpened === true) {
800 $this->addFormSubGroup();
801 } elseif ($this->groupOpened === true) {
803 $this->addFormGroup();
807 // Send content to template engine
808 //* DEBUG: */ echo __METHOD__.": form=".$this->getFormName().", size=".strlen($this->getContent())."<br />\n";
809 $this->getTemplateInstance()->assignVariable($this->getFormName(), $this->getContent());
813 * Getter for direct field values
815 * @param $fieldName Name of the field we shall fetch
816 * @return $fieldValue Value from field
818 public function getField ($fieldName) {
819 // Get the field value
820 $fieldValue = call_user_func_array(array($this->valueInstance, 'getField'), array($fieldName));
827 * Adds a pre-configured CAPTCHA
831 public function addCaptcha () {
832 // Get last executed pre filter
833 $extraInstance = Registry::getRegistry()->getInstance('extra');
835 // Get a configured instance
836 $captchaInstance = ObjectFactory::createObjectByConfiguredName($this->getFormName()."_captcha", array($this, $extraInstance));
838 // Initiate the CAPTCHA
839 $captchaInstance->initiateCaptcha();
841 // Render the CAPTCHA code
842 $captchaInstance->renderCode();
844 // Get the content and add it to the helper
845 $this->addContent($captchaInstance->getContent());
849 * Enables/disables the form tag usage
851 * @param $formEnabled Wether form is enabled or disabled
854 public final function enableForm ($formEnabled = true) {
855 $this->formEnabled = (bool) $formEnabled;
859 * Setter for form name
861 * @param $formName Name of this form
864 public final function setFormName ($formName) {
865 $this->formName = (string) $formName;
869 * Getter for form name
871 * @return $formName Name of this form
873 public final function getFormName () {
874 return $this->formName;