Extension:SpecialForm

From MediaWiki.org

Jump to: navigation, search
Zeichen 206.svg WARNING: the code or configuration described here poses a major security risk.

Problem: Possible injection of raw SQL
Solution: Don't get $select directly from $wgRequest
Signed: -- Bryan (talk

         

Manual on MediaWiki Extensions
List of MediaWiki Extensions
Crystal Clear action run.png
SpecialForm

Release status: beta

Implementation  Special page, Database
Description Creates a register form that will insert elements into the database and send it by e-mail.
Author(s)  sbrunner
Last Version  2007-06-22
MediaWiki  1.9.0
License No license specified
Download see below

check usage (experimental)

Contents

[edit] Documentation

This extension is intended to easily create a register form and will create a new table in your MySQL database to store the input of the form. This extension provides no way to retrieve the data again.

[edit] Parameters

$wgFormTablePrefix 
DBPrefix of the table, e.g. form_ will create all tables with the prefix form_
$wgAutoCreateFormTable 
Will create a new table if the table does not exist.
$wgFormSender 
The sender of the email (can be $wgFormSender = "No reply <noreply@site.com>"; or for backward compatibility: $wgFormSender = $wgPasswordSender)

[edit] Form parameters

database table-suffix="first" 
Use/create a table with the name form_first ($wgFormTablePrefix + database table-suffix).
field name 
Use/create table columns with that field's description and, of course, create the HTML form.

[edit] Install

Note: This extension supports MySQL only.

  • Copy source in file named SpecialForm.php.
  • Copy SpecialForm.i18n.php the file named SpecialForm.i18n.php
  • Copy these files in the extensions directory.
  • Add include_once('extensions/SpecialForm.php'); in the LocalSettings.php file.
  • In file define for example  :
$wgFormTablePrefix = "form_";
$wgAutoCreateFormTable = true;
$wgFormSender = "No reply <noreply@site.com>";
Introduction
<field name="Name" type="text" size="50" label="Name" value="" />
<field name="Firstname" type="text" size="50" label="Firstname" value="" />
<field name="Sex" type="select" label="Sex">
  <option value="Male" />
  <option value="Female" />
</field>
<field name="Email" type="text" size="50" label="E-mail" value="" />
some text ;-)
<to user="Sbrunner" />
<database table-suffix="first" />
<confirmation field="Email" text="Register of {Firstname} {Name} reserved" />
<send subject="Inscription" />
  • Go to Special:Form/FirstForm to view the result.
  • The First register will create the table form_first.
  • To view the result go to Special:ShowForm/FirstForm (you must have showform rights), to allow to show the forme to all registered people add to the LocalSettings.php:
 
$wgGroupPermissions['user']['showform'] = true;

To create a new group:

$wgGroupPermissions['showform']['showform'] = true;

[edit] Problems

I use this extension, and I note 2 problems :

  • line 504 :
if (preg_match ("/" + $pattern + "/", $line)) {

I have to desactivate it to work

  • line 598 :
$res = $dbr->query("INSERT INTO `".$table."` (`isread`, ".$sqlcolumns.") VALUES (0, ".$values.")" ) or die(wfMsg("formquerryfailed"));

It seems to not be at the good line ! If we put $wgAutoCreateFormTable to false, this line is ignored. I suppose this line has to be at line 599 and } (the line 599) at line 598

[edit] Documentation

The tag field is the most important tag since it defines the input of the form. This attributes are:

  • name the name of the fields, used to store in the database
  • type is the type of the field, possible valuesare:
  • textarea for textarea, custom attribute are: rows and cols
  • text for simple field, custom attribute: maxlength and accesskey
  • password for password field (unrecommended for security reasons)
  • checkbox for a check box field
  • select for combo box
  • radio for radio buttons
  • submit and reset for buttons
  • file, image and buttons can work but untested
  • label the field label
  • value the default value

The option tag is a subtag for fields of type select or' radio, its attributes are:

  • name the name oft the option
  • label its label
  • select is selected
  • disabled is disable (only for select)

The to tag defines its user attribute the MediaWiki user (who should have a validated email address) where the form is sent by email (which can be defined more than one time).

The database tag defines its table suffix (the second part of the table name). The table will be automatically created and the missing columns will automatically be added.

The confirmation tag defines the confirmation email. Attributes:

  • field the field where to find the email to send the confirmation.
  • text the content of the confirmation email; it can contain fields as {field name} or {all-fields}

The send tag defines the subject attribute of the title of the register (not really a good name!).

[edit] History

9 June 2007
  • Check box really supported!
3 May 2007
  • Add support of tag <p />, <div />, <span /> and <br />
  • Add multi language support
  • Add support of &nbsp; char
  • Add support of radio button
  • Add support of fieldset and legend
  • Don't use any more the URL attribute form (conflict
  • Rename acion submit to formsubmit (conflict),
  • Add new parameter $wgFormSender
22 June(?) 2007
  • Fix for the reset button!

[edit] Known problems

  • Doesn't work with template with parameter(s).
  • Doesn't work with two (or more) levels of templates.
  • Some problems with <div /> end tag.

[edit] Licence

GNU General Public License (GPL)

Author: User:sbrunner

[edit] Code

[edit] SpecialForm.php

<?php
/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *  
 * Parts of the program are from the file 'Image.php' from the MediaWiki project. The respective source can be acquired from http://wikipedia.sourceforge.net/.
 * 
 * @author sbrunner
 */
 
if (!defined('MEDIAWIKI')) die();
$wgExtensionFunctions[] = "wfFormExtension";
 
require_once( "$IP/includes/SpecialPage.php" );
 
function wfFormExtension() {
	global $IP, $wgMessageCache, $myForm, $wgI18nMessages;
 
        require_once( 'SpecialForm.i18n.php');
 
        foreach( $wgI18nMessages as $key => $value ) {
                $wgMessageCache->addMessages( $value, $key );
        }
 
	SpecialPage::addPage( new FormSpecialPage() );
	SpecialPage::addPage( new ShowFormSpecialPage() );
}
 
class FormSpecialPage extends SpecialPage {
 
	function FormSpecialPage() {
		SpecialPage::SpecialPage( 'Form', '', false );
	}
 
	function execute( $par ) {
		global $wgUser, $wgOut, $wgTitle, $wgRequest;
 
		if( !isset( $_SESSION ) ) { session_start(); }
		$this->setHeaders();
 
		if ( $this->userCanExecute( $wgUser ) ) {
			$action = $wgRequest->getVal( 'action' );
			$form = isset($par) ? $par : $wgRequest->getVal( 'form' );
 
			$text = wfMsg($form);
			if ( "success" == $action ) {
				showSuccess($text, $form);
			} else if ( "formsubmit" == $action ) {
				doSubmit($text, $form);
			} else {
				renderForm($text, $form);
			}
		} else {
			$this->displayRestrictionError();
		}
	}
}	
 
global $subject, $tablesuffix;
$subject = '';
$tablesuffix = NULL;
 
class ShowFormSpecialPage extends SpecialPage {
 
	function ShowFormSpecialPage() {
		SpecialPage::SpecialPage( 'ShowForm', 'showform', false );
	}
 
	function execute( $par = NULL ) {
		global $wgRequest, $wgOut, $tablesuffix, $subject, $wgFormTablePrefix, $wgUser, $wgTitle;
 
		$this->setHeaders();
 
		if ( $this->userCanExecute( $wgUser ) ) {
			$action = $wgRequest->getVal( 'action' );
			$select = $wgRequest->getVal( 'select' );
			$id = $wgRequest->getVal( 'id' );
			$form = isset($par) ? $par : $wgRequest->getVal( 'form' );
			$text = wfMsg($form);
 
			$xml_parser = xml_parser_create();
			xml_set_element_handler($xml_parser, 'startShowElement', 'endShowElement');
 
			if (!xml_parse($xml_parser, '<form>'._translateLiteral2NumericEntities($text).'</form>'))  {
				return 'Parce error<br />Code: '.xml_get_error_code($xml_parser).
					'<br />Error: '.xml_error_string($xml_parser).
					'<br />Line: '.xml_get_current_line_number($xml_parser).
					'<br />Column: '.xml_get_current_column_number($xml_parser).
					'<br />Content: <form>'.$input.'</form>';
			}
			xml_parser_free($xml_parser);
 
			$wgOut->setPagetitle( $subject );
			if ($tablesuffix != null) {
				$dbr =& wfGetDB( DB_MASTER );
				$table = $tablesuffix;
				if (isset($wgFormTablePrefix)) {
					$table = $wgFormTablePrefix.$tablesuffix;
				}
				$titleObj = Title::makeTitle( NS_SPECIAL, "ShowForm/$form" );
				if ( "markread" == $action ) {
					$res = $dbr->query( "UPDATE ".$table." SET `isread` = 1, `updated` = CURRENT_TIMESTAMP WHERE `id` = ".$id, $fname ) or die(wfMsg("formquerryfailed"));
 
					$wgOut->redirect( $titleObj->getFullURL( 'select='.urlencode( $select ) ) );
				} else if ( "markunread" == $action ) {
					$res = $dbr->query( "UPDATE ".$table." SET `isread` = 0, `updated` = CURRENT_TIMESTAMP WHERE `id` = ".$id, $fname ) or die(wfMsg("formquerryfailed"));
 
					$wgOut->redirect( $titleObj->getFullURL( 'select='.urlencode( $select ) ) );
				} else {
					$result = null;
					if (isset($select) && strlen($select) != 0) {
	//echo "SELECT * FROM ".$table." WHERE ".$select;
						$result = $dbr->query( "SELECT * FROM `".$table."` WHERE ".$select, $fname ) or die(wfMsg("formquerryfailed"));
					}
					else {
						$result = $dbr->query( "SELECT * FROM `".$table."`", $fname ) or die(wfMsg("formquerryfailed"));
					}
					$columns = $dbr->query("SHOW COLUMNS FROM `$table`;", $fname) or die(wfMsg("formquerryfailed"));
 
					if ( "raw" == $action ) {
						$wgOut->disable();
						header( "Content-type: application/form; charset=UTF-8" );
						echo "<table>\n";
						echo "\t<tr>\n";
						while ($column = mysql_fetch_row($columns)) {
							echo "\t\t<th>$column[0]</th>\n";
						}
						echo "\t</tr>\n";
 
						while ($line = mysql_fetch_assoc($result)) {
							echo "\t<tr>\n";
							foreach ($line as $col_value) {
								echo "\t\t<td>$col_value</td>\n";
							}
							echo "\t</tr>\n";
						}
						echo "</table>\n";
					}
					else {
						$action = $titleObj->escapeLocalURL();
						$wgOut->addHTML('<p><a href="'.$titleObj->escapeLocalURL('select='.urlencode( $select ).'&action=raw' ).'">'.wfMsg('formdownload').'</a></p>');
						$wgOut->addHTML("<form id=\"form\" method=\"post\" action=\"{$action}\">\n");
						$wgOut->addHTML('<p><b>'.wfMsg('formselect').'</b> <input name="select" type="text" size="50" value="'.$select.'"/> ');
						$wgOut->addHTML('<input value="View" type="submit"></input></p>');
						$wgOut->addHTML("</form>");
 
						$cols = array();
						$i = 0;
						$wgOut->addHTML("<table border=\"1\">\n");
						$wgOut->addHTML("\t<tr>\n");
						while ($column = mysql_fetch_row($columns)) {
							if ($i <= 3) {
								$wgOut->addHTML("\t\t<th>".wfMsg("form".$column[0])."</th>\n");
							}
							else {
								$wgOut->addHTML("\t\t<th>$column[0]</th>\n");
							}
							$cols[$i] = $column[0];
							$i++;
						}
						$wgOut->addHTML("\t</tr>\n");
 
						$preselect = '';
						if (strlen($select) != 0) {
							$preselect = $select.' AND ';
						}
						while ($line = mysql_fetch_assoc($result)) {
							$wgOut->addHTML("\t<tr>\n");
							$i = 0;
							$id = null;
							foreach ($line as $col_value) {
								if ($i == 0) {
									$id = $col_value;
								}
 
								if (strlen($col_value)>255) {
									$wgOut->addHTML("\t\t<td>$col_value");
								}
								else {
									$href = $titleObj->escapeLocalURL('select='.$preselect.urlencode( "`".$cols[$i]."` LIKE '".$col_value."'" ) );
									$wgOut->addHTML("\t\t<td><a href=\"$href\">$col_value</a>");
								}
 
								if ($i == 0) {
									$wgOut->addHTML('<br /><a href="'.$titleObj->escapeLocalURL('select='.urlencode( "`id` LIKE '".$col_value."'" ).'&action=raw' ).'">'.wfMsg('formdownloadshort').'</a>');
 
								}
								if ($i == 1 && $col_value == 0) {
									$href = $titleObj->escapeLocalURL('id='.$id.'&action=markread'.'&select='.urlencode( $select ) );
 
									$wgOut->addHTML(" <a href=\"$href\" title=\"".wfMsg('formmarkread')."\">√</a>");
								}
								$wgOut->addHTML("</td>\n");
 
								$i++;
							}
 
							$wgOut->addHTML("\t</tr>\n");
						}
						$wgOut->addHTML("</table>\n");
					}
				}
			}
		} else {
			$this->displayRestrictionError();
		}
	}
}	
 
global $submitValue, $resetValue, $option, $wiki;
$submitValue = 'Send';
$resetValue = null;
$option = FALSE;
$wiki = '';
$fieldtype = '';
$fieldname = '';
 
function renderForm( $input, $form ) {
	global $wgScript, $wgOut, $subject, $submitValue, $resetValue, $wiki, $wgRequest;
 
/*$wgOut->addHTML("<!-- input\n");
$wgOut->addHTML($input);
$wgOut->addHTML("\n-->");*/
 
	$titleObj = Title::makeTitle( NS_MEDIAWIKI, $form );
	$edit = $titleObj->escapeLocalURL( "action=edit" );
 
	$titleObj = Title::makeTitle( NS_SPECIAL, "ShowForm/".$form);
	$show = $titleObj->escapeFullURL( 'select='.urlencode("`isread` LIKE '0'" ));
 
	$wgOut->addHTML('<h1 class="editor forceedit"><div class="editsection" style="float: right; margin-left: 5px;">[<a href="'.$show.'">'.wfMsg('showform').'</a>, <a href="'.$edit.'">'.wfMsg('editsection').'</a>]</div><br clear="right" /></h1>');
 
 
	$titleObj = Title::makeTitle( NS_SPECIAL, "Form/".$form );
	$action = $titleObj->escapeLocalURL("action=formsubmit" );
 
	$error = $wgRequest->getVal( 'error' );
	if (strlen($error) > 0) {
		$wgOut->addHTML('<p style="error">'.$error."</p>");
	}
 
	$wgOut->addHTML("<form id=\"form\" method=\"post\" action=\"{$action}\"><table>\n");
 
	$xml_parser = xml_parser_create();
	xml_set_element_handler($xml_parser, 'startRenderElement', 'endRenderElement');
	xml_set_character_data_handler($xml_parser, "characterRenderData");
 
	if (!xml_parse($xml_parser, '<form>'._translateLiteral2NumericEntities($input).'</form>'))  {
		return 'Parce error<br />Code: '.xml_get_error_code($xml_parser).
			'<br />Error: '.xml_error_string($xml_parser).
			'<br />Line: '.xml_get_current_line_number($xml_parser).
			'<br />Column: '.xml_get_current_column_number($xml_parser).
			'<br />Content: <form>'.$input.'</form>';
	}
	xml_parser_free($xml_parser);
 
	$wgOut->addHTML('<tr><td colspan="2" style="text-align:center;">');
 
	$wgOut->addHTML('<input type="submit" value="'.$submitValue.'" />');
	if ($resetValue != null) {
		$wgOut->addHTML('<input type="reset" value="'.$resetValue.'" />');
	}
	$wgOut->addHTML('</td></tr>');
 
	$wgOut->addHTML('</table>');
	$wgOut->addHTML("</form>\n");
 
	$wgOut->setPagetitle( $subject );
 
	if (strlen($wiki) > 0) {
		$wgOut->addHTML( '<tr><td colspan="2">' );
		$wgOut->addWikiText( $wiki );
		$wgOut->addHTML( '</td></tr>' );
	}
	$wiki = '';
 
	$wgOut->addHTML("</div>\n");
}
 
function characterRenderData($parser, $data) {
	global $wgOut, $wiki;
 
//	if (strlen(preg_replace('/[^[:alnum:]]/', '', $data)) > 0) {
		$wiki.=$data;//."\n";
//	}
}
 
function startRenderElement($parser, $name, $attrs) {
    global $submitValue, $resetValue, $subject, $option, $wgOut, $wiki, $wgRequest, $fieldtype, $fieldname;
 
    if ($name == 'BR' || $name == 'P' || $name == 'SPAN' || $name == 'DIV') {
            $wiki .= '<'.strtolower($name);
            if ($attrs['CLEAR']) {
                    $wiki .= " clear=\"{$attrs['CLEAR']}\"";
            }
            if ($attrs['CLASS']) {
                    $wiki .= " class=\"{$attrs['CLASS']}\"";
            }
            if ($attrs['STYLE']) {
                    $wiki .= " style=\"{$attrs['STYLE']}\"";
            }
            if ($attrs['ID']) {
                    $wiki .= " id=\"{$attrs['ID']}\"";
            }
            if ($name == 'BR') {
                    $wiki .= " />";
            }
            else {
                    $wiki .= ">";
            }
    }
    else {
	if (strlen(preg_replace('/[^[:alnum:]]/', '', $wiki)) > 0) {
		$wgOut->addHTML( '<tr><td colspan="2">' );
		if (substr($wiki, 0, 1) == "\n") {
			$wiki = substr($wiki, 1);
		}
		$wgOut->addWikiText($wiki);
		$wgOut->addHTML( '</td></tr>' );
	}
	$wiki = '';
 
        if ($name == 'FIELDSET') {
                $wgOut->addHTML('</table>');
                $wgOut->addHTML('<fieldset');
                if ($attrs['CLASS']) {
                    $wgOut->addHTML(" class=\"{$attrs['CLASS']}\"");
                }
                if ($attrs['STYLE']) {
                    $wgOut->addHTML(" style=\"{$attrs['STYLE']}\"");
                }
                if ($attrs['TITLE']) {
                    $wgOut->addHTML(" title=\"{$attrs['TITLE']}\"");
                }
                if ($attrs['ID']) {
                    $wgOut->addHTML(" id=\"{$attrs['ID']}\"");
                }
                $wgOut->addHTML('>');
                if ($attrs['LEGEND']) {
                    $wgOut->addHTML("<legend>{$attrs['LEGEND']}</legend>");
                }
                $wgOut->addHTML('<table>');
        }
	if ($name == 'FIELD') {
                $fieldtype = $attrs['TYPE'];
                $fieldname = $attrs['NAME'];
 
		if ($attrs['TYPE'] == 'textarea') {
			$wgOut->addHTML('<tr><td>'.$attrs['LABEL'].'</td><td>');
			$wgOut->addHTML('<textarea name="'.str_replace(" ", "_", $attrs['NAME']).'"');
			if ($attrs['ROWS'] != null) {
				$wgOut->addHTML(' rows="'.$attrs['ROWS'].'"');
			}
			if ($attrs['COLS'] != null) {
				$wgOut->addHTML(' cols="'.$attrs['COLS'].'"');
			}
			else {
				$wgOut->addHTML(' cols="100"');
			}
			if ($attrs['WIDTH'] != null) {
				$wgOut->addHTML(' style="width:'.$attrs['WIDTH'].'px;"');
			}
			$wgOut->addHTML('>');
			if ($attrs['VALUE'] != null) {
				$wgOut->addHTML($attrs['VALUE']);
			}
			$wgOut->addHTML("</textarea></td></tr>\n");
		}
                else if ($attrs['TYPE'] == 'select') {
                        $option = TRUE;
                        $wgOut->addHTML('<tr><td>'.$attrs['LABEL'].'</td><td>');
                        $wgOut->addHTML('<select name="'.str_replace(" ", "_", $attrs['NAME']).'">'."\n");
                }
                else if ($attrs['TYPE'] == 'radio') {
                        $option = TRUE;
                        $wgOut->addHTML('<tr><td>'.$attrs['LABEL'].'</td><td>');
                }
		else if ($attrs['TYPE'] == 'submit') {
			$submitValue = $attrs['LABEL'];
		}
		else if ($attrs['TYPE'] == 'reset') {
			$resetValue = $attrs['LABEL'];
		}
                else if ($attrs['TYPE'] == 'checkbox') {
                        $option = TRUE;
                        $wgOut->addHTML('<tr><td colspan="2">');
			$wgOut->addHTML('<input name="'.str_replace(" ", "_", $attrs['NAME']).'" type="'.$attrs['TYPE'].'"');
			if ($attrs['ID'] != null) {
				$wgOut->addHTML(' id="'.$attrs['ID'].'"');
			}
			if ($attrs['SIZE'] != null) {
				$wgOut->addHTML(' size="'.$attrs['SIZE'].'"');
			}
			if ($attrs['WIDTH'] != null) {
				$wgOut->addHTML(' style="width:'.$attrs['WIDTH'].'px;"');
			}
			if ($attrs['VALUE'] != null) {
				$wgOut->addHTML(' value="'.$attrs['VALUE'].'"');
			}
			if ($attrs['MAXLENGTH'] != null) {
				$wgOut->addHTML(' maxlength="'.$attrs['MAXLENGTH'].'"');
			}
			if ($attrs['ACCESSKEY'] != null) {
				$wgOut->addHTML(' accesskey="'.$attrs['ACCESSKEY'].'"');
			}
			$wgOut->addHTML('>'.$attrs['LABEL'].'</input>');
                        $wgOut->addHTML('</td><td>');
                }
		else {
			$wgOut->addHTML('<tr><td>'.$attrs['LABEL'].'</td><td>');
			$wgOut->addHTML('<input name="'.str_replace(" ", "_", $attrs['NAME']).'" type="'.$attrs['TYPE'].'"');
			if ($attrs['ID'] != null) {
				$wgOut->addHTML(' id="'.$attrs['ID'].'"');
			}
			if ($attrs['SIZE'] != null) {
				$wgOut->addHTML(' size="'.$attrs['SIZE'].'"');
			}
			if ($attrs['WIDTH'] != null) {
				$wgOut->addHTML(' style="width:'.$attrs['WIDTH'].'px;"');
			}
			if ($attrs['VALUE'] != null) {
				$wgOut->addHTML(' value="'.$attrs['VALUE'].'"');
			}
			if ($attrs['MAXLENGTH'] != null) {
				$wgOut->addHTML(' maxlength="'.$attrs['MAXLENGTH'].'"');
			}
			if ($attrs['ACCESSKEY'] != null) {
				$wgOut->addHTML(' accesskey="'.$attrs['ACCESSKEY'].'"');
			}
			$wgOut->addHTML('/>');
			$wgOut->addHTML("</td></tr>\n");
		}
	}
	else if ($name == 'SEND') {
		$subject = $attrs['SUBJECT'];
	}
	else if ($name == 'OPTION') {
                $value = $attrs['VALUE'] != null ? $attrs['VALUE'] : $attrs['LABEL'];
                $label = $attrs['LABEL'] != null ? $attrs['LABEL'] : $attrs['VALUE'];
                if ($fieldtype == 'radio') {
                        $wgOut->addHTML("<input type=\"radio\" value=\"{$value}\" name=\"{$fieldname}\" >$label</input><br />");
                }
                else {
                        $wgOut->addHTML('<option');
                        if ($attrs['SELECTED'] != null) {
                                $wgOut->addHTML(' selected="'.$attrs['SELECTED'].'"');
                        }
                        if ($attrs['DISABLED'] != null) {
                                $wgOut->addHTML(' disabled="'.$attrs['DISABLED'].'"');
                        }
                        $wgOut->addHTML(' label="'.$label.'"');
                        $wgOut->addHTML('>'.$value."</option>\n");
                }
	}
    }
}
 
function endRenderElement($parser, $name) {
	global $option, $wgOut, $wiki, $fieldname;
 
	if ($name == 'FIELD') {
                if ($option === TRUE) {
		        $wgOut->addHTML("</select></td></tr>\n");
		        $option = FALSE;
                }
                else if($fieldname == 'radio') {
                        $wgOut->addHTML("</td></tr>\n");
                }
	}
        if ($name == 'FIELDSET') {
                $wgOut->addHTML('</table>');
                $wgOut->addHTML('</fieldset>');
                $wgOut->addHTML('<table>');
        }
        else if ($name == 'P' || $name == 'SPAN' || $name == 'div') {
                $wiki .= '</'.strtolower($name).'>';
        }
}
 
global $to, $confirmation, $confirmationField, $fields, $type;
$to = array();
$confirmation = '';
$confirmationField = '';
$fields = array();
$type = array();
 
function test($pattern, $text, $field) {
//  $pattern = str_replace('[', '\[', $pattern);
  $pattern = $pattern == null || strlen($pattern) == 0 ? wfMsg('formallowedstring') : $pattern;
  $pattern = $pattern == null || strlen($pattern) == 0 ? '[a-zA-Z0-9 ]*' : $pattern;
  $lines = preg_split('/[\n\r]/',$text);
  foreach ($lines as $line) {
    if (preg_match ("/" + $pattern + "/", $line)) {
      $args = array($field, $pattern);
//      return trim(wfMsgReplaceArgs(wfMsg('formtypeerror'), $args)).'<br />';
      //return wfMsg('formtypeerror').'<br />';
    }
  }
  return '';
}
 
function doSubmit( $input, $form ) {
	global $wgFormSender, $to, $confirmation, $confirmationField, $fields, $subject, $wgOut, $type;
 
	$xml_parser = xml_parser_create();
	xml_set_element_handler($xml_parser, 'startSubmitElement', 'endSubmitElement');
	if (!xml_parse($xml_parser, '<form>'._translateLiteral2NumericEntities($input).'</form>'))  {
		return 'Parce error<br />Code: '.xml_get_error_code($xml_parser).
			'<br />Error: '.xml_error_string($xml_parser).
			'<br />Line: '.xml_get_current_line_number($xml_parser).
			'<br />Column: '.xml_get_current_column_number($xml_parser).
			'<br />Content: <form>'.$input.'</form>';
	}
	xml_parser_free($xml_parser);
 
	global $wgOut, $wgUser, $wgLang, $wgOutputEncoding, $wgRequest;
	$wgOut->setPagetitle( $subject );
 
	$emailTo = '';
 
	$sqlcolumns = null;
	$values = null;
	$result = "<table>\n";
	$allFields = "<table>\n";
	$error = '';
	foreach ($fields as $field) {
		$value = $wgRequest->getVal($field);
		$result.="<tr><th>".$field."</th><td>".$value."</td><tr>\n";
		//if ($field !== 'navigateurform') {
		if (strtolower($type[$field]) !== 'hidden') {
			$allFields.="<tr><th>".$field."</th><td>".$value."</td><tr>\n";
		}
		$value = str_replace("'", "''", $value);
		//$value = str_replace("|", "", $value);
		$error .= test($type[$field], $value, $field);
		if ($sqlcolumns == null) {
			$sqlcolumns = '`'.$field.'`';
			$values = '\''.$value.'\'';
		}
		else {
			$sqlcolumns.=', `'.$field.'`';
			$values.=', \''.$value.'\'';
		}
		$confirmation = str_replace('{'.$field.'}', $value, $confirmation);
	}
	$result.="</table>\n";
	$allFields.="</table>\n";
	$confirmation = str_replace('{all-fields}', $allFields, $confirmation);
	$confirmation = str_replace('&lt;', '<', $confirmation);
	$confirmation = str_replace('&gt;', '>', $confirmation);
	$result = "<html><body>\n".$result."</body></html>\n";
 
	if (strlen($error) > 0) {
		$titleObj = Title::makeTitle( NS_SPECIAL, "Form/".$form );
		$wgOut->redirect($titleObj->getFullURL("&error=". wfUrlencode( $error )));
		return;
	}
 
	global $wgFormTablePrefix, $wgAutoCreateFormTable, $tablesuffix, $wgDBname;
	if ($tablesuffix != null) {
		$table = $tablesuffix;
		if (isset($wgFormTablePrefix)) {
			$table = $wgFormTablePrefix.$tablesuffix;
		}
		$dbr =& wfGetDB( DB_MASTER );
		if (isset($wgAutoCreateFormTable) && $wgAutoCreateFormTable === TRUE) {
			if (!mysql_table_exists($fname, $wgDBname, $table)) {
				$sql = "CREATE TABLE `".$table."` (`id` INT(15) NOT NULL AUTO_INCREMENT, `isread` INT(1) NOT NULL, ".
					"`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated` TIMESTAMP";
 
				foreach ($fields as $field) {
					$sql.=", `".$field."` VARCHAR(255)";
				}
				$sql.=", PRIMARY KEY (id)) TYPE = MYISAM ;";
				$res = $dbr->query( $sql, $fname ) or die(wfMsg("formquerryfailed"));
			}
			else {
				$columns = array();
				$columnsResult = $dbr->query("SHOW COLUMNS FROM $table;", $dbLink) or die(wfMsg("formquerryfailed"));
				while ($row = mysql_fetch_row($columnsResult)) $columns[] = $row[0];
				foreach ($fields as $field) {
					if (!in_array($field, $columns)) {
						$res = $dbr->query("ALTER TABLE `".$table."` ADD `".$field."` VARCHAR(255) ;", $fname ) or die(wfMsg("formquerryfailed"));
					}
				}
			}
 
			$res = $dbr->query("INSERT INTO `".$table."` (`isread`, ".$sqlcolumns.") VALUES (0, ".$values.")" ) or die(wfMsg("formquerryfailed"));
		}
	}
 
	foreach ($to as $name) {
		$nu = User::newFromName( $name );
		if ( is_null( $nu ) || !$nu->canReceiveEmail() ) {
//			wfDebug( "Target is invalid user or can't receive.\n" );
//			$wgOut->errorpage( "noemailtitle", "noemailtext" );
//			return;
		}
		else {
			$emailTo.=$nu->getName()." <".$nu->getEmail().">,";
		}
	}
	$mailConfirmResult = sendMail( $emailTo, $wgFormSender, $subject, $result, 'text/html' );
 
	if( WikiError::isError( $mailResult ) ) {
		$wgOut->addHTML( $mailResult );
	} else {
		$mailResult = sendMail( $wgRequest->getVal( $confirmationField ), $wgFormSender, $subject, '<html><body>'.$confirmation.'</body></html>', 'text/html' );
 
		if( WikiError::isError( $mailConfirmResult ) ) {
			$wgOut->addHTML( $mailConfirmResult );
		} else {
			$titleObj = Title::makeTitle( NS_SPECIAL, "Form/$form" );
 
			$_SESSION['message'] = $confirmation;
			$wgOut->redirect( $titleObj->getFullURL("action=success" ) );
		}
	}
}
 
function mysql_table_exists($dbLink, $database, $tableName) {
	$dbr =& wfGetDB( DB_MASTER );
 
	$tables = array();
	$tablesResult = $dbr->query("SHOW TABLES FROM $database;", $dbLink) or die(wfMsg("formquerryfailed"));
	while ($row = mysql_fetch_row($tablesResult)) $tables[] = $row[0];
	return(in_array($tableName, $tables));
}
 
function startSubmitElement($parser, $name, $attrs) {
	global $fields, $subject, $to, $confirmation, $confirmationField, $tablesuffix, $type;
 
	if ($name == 'FIELD') {
		if (isset($attrs['NAME'])) {
			$name = str_replace(" ", "_", $attrs['NAME']);
			array_push($fields, $name);
			if (isset($attrs['TYPE'])) {
				$type[$name] = $attrs['TYPE'];
			}
		}
	}
	if ($name == 'SEND') {
		$subject = $attrs['SUBJECT'];
	}
	if ($name == 'DATABASE') {
		$tablesuffix = $attrs['TABLE-SUFFIX'];
	}
	if ($name == 'TO') {
		array_push($to, $attrs['USER']);
	}
	else if ($name == 'CONFIRMATION') {
		$confirmation = $attrs['TEXT'];
		$confirmationField = $attrs['FIELD'];
	}
}
 
function endSubmitElement($parser, $name) {
}
 
function showSuccess( $input, $form ) {
	global $wgOut, $subject, $wgRequest;
 
	$xml_parser = xml_parser_create();
	xml_set_element_handler($xml_parser, 'startSuccessElement', 'endSuccessElement');
	if (!xml_parse($xml_parser, '<form>'._translateLiteral2NumericEntities($input).'</form>'))  {
		return 'Parce error<br />Code: '.xml_get_error_code($xml_parser).
			'<br />Error: '.xml_error_string($xml_parser).
			'<br />Line: '.xml_get_current_line_number($xml_parser).
			'<br />Column: '.xml_get_current_column_number($xml_parser).
			'<br />Content: <form>'.$input.'</form>';
	}
	xml_parser_free($xml_parser);
	$wgOut->setPagetitle($subject);
 
//	$wgOut->addHTML($wgRequest->getVal('message'));
	$wgOut->addHTML($_SESSION['message']);
}
 
function startSuccessElement($parser, $name, $attrs) {
	global $subject, $confirmation;
 
	if ($name == 'SEND') {
		$subject = $attrs['SUBJECT'];
	}
	else if ($name == 'CONFIRMATION') {
		$confirmation = $attrs['TEXT'];
	}
}
 
function endSuccessElement($parser, $name) {
}
 
function startShowElement($parser, $name, $attrs) {
	global $fields, $subject, $to, $confirmation, $confirmationField, $tablesuffix;
 
	if ($name == 'DATABASE') {
		$tablesuffix = $attrs['TABLE-SUFFIX'];
	}
	else if ($name == 'SEND') {
		$subject = $attrs['SUBJECT'];
	}
}
 
function endShowElement($parser, $name) {
}
 
/**
 * This function will perform a direct (authenticated) login to
 * a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
 * array of parameters. It requires PEAR:Mail to do that.
 * Otherwise it just uses the standard PHP 'mail' function.
 *
 * @param string $to recipient's email
 * @param string $from sender's email
 * @param string $subject email's subject
 * @param string $body email's text
 * @param string $replyto optional reply-to email (default : false)
 */
function sendMail( $to, $from, $subject, $body, $contenttype='text/plain', $replyto=false ) {
	global $wgUser, $wgSMTP, $wgOutputEncoding, $wgErrorString, $wgEmergencyContact;
 
	if (is_array( $wgSMTP )) {
		require_once( 'Mail.php' );
 
		$timestamp = time();
 
		$headers['From'] = $from;
		if ( $replyto ) {
			$headers['Reply-To'] = $replyto;
		}
		$headers['Subject'] = $subject;
		$headers['MIME-Version'] = '1.0';
		$headers['Content-type'] = $contenttype.'; charset='.$wgOutputEncoding;
		$headers['Content-transfer-encoding'] = '8bit';
		$headers['Message-ID'] = "<{$timestamp}" . $wgUser->getName() . '@' . $wgSMTP['IDHost'] . '>';
		$headers['X-Mailer'] = 'MediaWiki mailer';
 
		// Create the mail object using the Mail::factory method
		$mail_object =& Mail::factory('smtp', $wgSMTP);
		wfDebug( "Sending mail via PEAR::Mail\n" );
		$mailResult =& $mail_object->send($to, $headers, $body);
 
		# Based on the result return an error string, 
		if ($mailResult === true)
			return '';
		else if (is_object($mailResult))
			return $mailResult->getMessage();
		else
			return 'Mail object return unknown error.';
	} else	{
		# In the following $headers = expression we removed "Reply-To: {$from}\r\n" , because it is treated differently
		# (fifth parameter of the PHP mail function, see some lines below)
		$headers =
			"MIME-Version: 1.0\n" .
			"Content-type: ".$contenttype."; charset={$wgOutputEncoding}\n" .
			"Content-Transfer-Encoding: 8bit\n" .
			"X-Mailer: MediaWiki mailer\n".
			'From: ' . $from . "\n";
		if ($replyto) {
			$headers .= "Reply-To: $replyto\n";
		}
 
		$wgErrorString = '';
		set_error_handler( 'mailError' );
		wfDebug( "Sending mail via internal mail() function\n" );
		mail( $to, $subject, $body, $headers );
		restore_error_handler();
 
		if ( $wgErrorString ) {
			wfDebug( "Error sending mail: $wgErrorString\n" );
		}
		return $wgErrorString;
	}
}
 
/**
 * @todo document
 */
function mailError( $code, $string ) {
	global $wgErrorString;
	$wgErrorString = preg_replace( "/^mail\(\): /", "", $string );
}
 
  /**
  * get from http://bugs.php.net/bug.php?id=15092
  *
  * Translate literal entities to their numeric equivalents and vice
versa.
  *
  * PHP's XML parser (in V 4.1.0) has problems with entities! The only
one's that are recognized
  * are &amp;, &lt; &gt; and &quot;. *ALL* others (like &nbsp; &copy;
a.s.o.) cause an 
  * XML_ERROR_UNDEFINED_ENTITY error. I reported this as bug at
http://bugs.php.net/bug.php?id=15092
  * The work around is to translate the entities found in the XML source
to their numeric equivalent
  * E.g. &nbsp; to &#160; / &copy; to &#169; a.s.o.
  * 
  * NOTE: Entities &amp;, &lt; &gt; and &quot; are left 'as is'
  * 
  * @author Sam Blum bs_php@users.sourceforge.net
  * @param string $xmlSource The XML string
  * @param bool   $reverse (default=FALSE) Translate numeric entities to
literal entities.
  * @return The XML string with translatet entities.
  */
  function _translateLiteral2NumericEntities($xmlSource, $reverse =
FALSE) {
    static $literal2NumericEntity;
 
    $xmlSource = str_replace('&amp;amp;', '&amp;', str_replace('&', '&amp;', $xmlSource)); // an add !
 
    if (empty($literal2NumericEntity)) {
      $transTbl = get_html_translation_table(HTML_ENTITIES);
      foreach ($transTbl as $char => $entity) {
        if (strpos('&"<>', $char) !== FALSE) continue;
        $literal2NumericEntity[$entity] = '&#'.ord($char).';';
      }
    }
    if ($reverse) {
      return strtr($xmlSource, array_flip($literal2NumericEntity));
    } else {
      return strtr($xmlSource, $literal2NumericEntity);
    }
  }
?>

[edit] SpecialForm.i18n.php

<?php
if ( !defined( 'MEDIAWIKI' ) ) {
        echo "This file is part of MediaWiki, it is not a valid entry point.\n";
        exit( 1 );
}
 
$wgI18nMessages = array();
 
$wgI18nMessages['en'] = array(
        'form' => 'Register',
        'showform' => 'Register result',
        'formquerryfailed' => 'Query failed',
        'formselect' => 'Select',
        'formmarkread' => 'Mark read',
        'formdownload' => 'Download',
        'formdownloadshort' => '(download)',
        'formid' => 'ID',
        'formisread' => 'isread',
        'formcreated' => 'created',
        'formupdated' => 'updated',
//        'formtypeerror' => "The field '$1' is wrong <small>(pattern: $2)</small>",
//        'formallowedstring' => '[a-zA-Z0-9 ]*',
);
 
$wgI18nMessages['fr'] = array(
        'form' => 'Inscription',
        'showform' => 'Résultat',
        'formquerryfailed' => 'Erreur de base de donnée',
        'formselect' => 'Selectionne',
        'formmarkread' => 'Marquer comme lu',
        'formdownload' => 'Télécharge',
        'formdownloadshort' => '(Télécharge)',
        'formid' => 'ID',
        'formisread' => 'est lu',
        'formcreated' => 'création',
        'formupdated' => 'modification',
//        'formtypeerror' => "Le champ '$1' est incorrecte <small>(pattern : $2)</small>",
);
 
$wgI18nMessages['nl'] = array(
        'form' => 'Register',
        'showform' => 'Resultaat',
        'formquerryfailed' => 'Mislukt',
        'formmarkread' => 'Markeren als gelezen',
        'formdownload' => 'Downloaden',
        'formdownloadshort' => '(downloaden)',
        'formid' => 'ID',
        'formisread' => 'is gelezen',
        'formcreated' => 'gemaakt',
        'formupdated' => 'geüpdate',
//        'formtypeerror' => "Het veld  '$1' is verkeerd <small>(pattern: $2)</small>",
);
 
$wgI18nMessages['lt'] = array(
        'form' => 'Registracija',
        'showform' => 'Registracijos rezultatas',
        'formquerryfailed' => 'Užklausa nesuveikė',
        'formselect' => 'Pasirinkti',
        'formmarkread' => 'Pažymėti kaip perskaityta',
        'formdownload' => 'Siųstis',
        'formdownloadshort' => '(siųstis)',
        'formid' => 'ID',
        'formisread' => 'perskaitytas',
        'formcreated' => 'sukurtas',
        'formupdated' => 'atnaujintas',
//        'formtypeerror' => "Laukelis '$1' yra neteisingas <small>(dalelytė: $2)</small>",
//        'formallowedstring' => '[a-zA-Z0-9 ]*',
);
?>