Extension talk:FormHandler send form by Email/Archive 1

To all who had problems with the toString() error: Use version 0.2, this should fix the problem. Dbu 14:08, 16 April 2008 (UTC)

Security

Perhaps a dumb question, but what's to stop someone from creating their own form and using that to send spam/malware/whatever?

As noted in the Installation section:
To avoid spam, you should make sure that only trusted users can edit the pages which use the extension.
The primary goal of this extension was to avoid writing full html forms inside the wiki, which would introduce the much larger risk of users injecting javascript or anything into a wiki page.
Better possibilities could be
* limit the number of emails to be sent through the form by the same address.
* implement a configuration setting to limit the possible target addresses of the form, so at worst the admins are mailbombed, which can be done using normal email software as well.
If anybody feels like doing this, contact me. -- Dbu, 2006-03-27

I may be misunderstanding how this extension works, but what's to stop a spammer from coming along and setting up their own page using this extension? They don't necessarily have to use pages you've set up, if they know the proper syntax they can set up their own.

Comment: Perhaps the extension should be changed to store source and target e-mail addresses somewhere in the MediaWiki: namespace, instead of accepting raw e-mail addresses as parameters? That way, the msg() code used to translate the user interface into other languages could be re-used to look up the e-mail addresses to be used. MediaWiki: is by default protected so that only +sysop have access to edit there, preventing the code from being used to send spam to arbitrary addresses other than those preconfigured by admins. --66.102.65.40 11:56, 16 June 2006 (UTC)
Anybody is welcome to do that. Please drop me a note if you did. Dbu 14:08, 16 April 2008 (UTC)

"Sender" vs "Email"

What's the difference between "Sender" and "Email"? --Yonghokim 06:58, 28 October 2006 (UTC)

The sender can be used as From part of the mail to be sent. If it is set, it is - as the target - never shown to the client but kept on the server. If sender is present, 'email' is only used as a reply-to address. It can be used to allowe a user to submit a form without specifying his email address. It was not very obvious in the doc, i will improve that.

Extension not working

I installed this extension and everything seems to be fine except when I click "Submit Query" I get forwarded to an invalid page, it looks as though I am being sent to the root directory of my domain such as domainname.com/Form, rather then an authentic wiki style link such as "domainname.com/index.php?title=Form."

Any ideas why?

Thanks!

Sorry, no idea. Anybody else has this problem? Any more details? Dbu 14:08, 16 April 2008 (UTC)


I am having this problem as well. When I click "Submit", I am redirected to http://<mydomain>/<form page name> which does not exist. Also, I received an email the first time I set this up, but it did not contain the data from the form. Since that first email, I have not received any others. Seamus6869


I have the same issue, when I submit it tries to navigate to a page by the same name, but gives Error 404 for some reason. Also worth mentioning, I had to disble the entire cleanup section (the part with breaking the $attr with regexp) because it also threw errors.


Sending Email from Form to Multiple Recipients

Is it possible to have the form sent to more than 1 recipient? If so, how? - Colin

It might be possible to just give all addresses in one target parameter, separated by ','. If this does not work: The UserMailer class checks if the $to parameter is an array. So you could think up a method to specify multiple targets and adapt the script to build an array from them. Dbu 14:08, 16 April 2008 (UTC)

',' separated email id's are allowed in the user mailer class. But in form handler it validates the email address in target field, so we need to change that logic according to multiple reciepients.--Shabnam Garg Here is the code:

  
/* 
   * Check Email for validity, using a regular expression.
   */  
  function isValidEmail($candidate) {
	/*SG250028 Validation Logic of Email Address in case of multiple reciepients...starts*/
	$token = strtok($candidate, ",");
	$result = false;
	while ($token !== false)
	{
		$result = eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $token);
		if($result==false){
			break;
		}
		$token = strtok(",");
	}
	return $result;
	/*SG250028 Validation Logic of Email Address in case of multiple reciepients...ends*/
  }


Radio Buttons/ Check Boxes

I have added both these features and also user can send mail to multiple reciepients.--Shabnam Garg Here is my code

 
<?php
 
/* FormHandler extension, Version 0.1
 * David Buchmann, 16.3.2006
 *
 * See http://meta.wikimedia.org/wiki/User:Dbu for explanations and updates.
 */
 
$wgExtensionFunctions[] = "wfFormHandler";
 
function wfFormHandler() {
  global $wgParser;
  # register the extension with the WikiText parser
  # the first parameter is the name of the new tag.
  # In this case it defines the tag <example> ... </example>
  # the second parameter is the callback function for
  # processing the text between the tags
 $wgParser->setHook( "form", "renderForm" );
 
}
 
/* The callback function for converting the input text to HTML output
 * $argv is an array containing any arguments passed to the
 * extension like <example argument="foo" bar>..
 * Put this on the sandbox page:  (works in MediaWiki 1.5.5)
 *   <example argument="foo" argument2="bar">Testing text **example** in between the new tags</example>
 */

function renderForm( $input, $argv) { // parser is not passed?? , &$wgParser ) {
  global $wgParser, $wgRequest;
  $handler = new FormHandler($wgParser, $wgRequest, $input, $argv);
  return $handler->render();

}
 
 
class FormHandler {
  var $defaultMethod = 'email';
  // Mediawiki objects
  var $request, $input, $argv;
  // Form parameters
  var $reset, $submit, $target, $sender, $email;
 
  /*
   * array of arrays with parsed field info.
   * type: text, hidden, textarea, select
   * name: name in form
   * required: whether this field is required
   * value: default value
   * option: array of options for select
   * prompt: text for prompt of that field
   */
  var $fields,$prompt;
 
  function FormHandler(&$parser, $request, $input, $argv) {
    $parser->disableCache();
    $this->request = $request;
    $this->input = $input;
    $this->argv = $argv;
  }
 
 
  function parseInput() {
    //parse form setup
    $argv = $this->argv;
 
    $this->reset = isset($argv['reset']) ? $argv['reset'] : false;
    $this->submit = isset($argv['submit']) ? $argv['submit'] : 'Submit';    
    $this->email = isset($argv['email']) ? $argv['email'] : false;
    $this->sender = isset($argv['sender']) ? $argv['sender'] : false;
    $this->target = isset($argv['target']) ? $argv['target'] : false;
    $this->method = isset($argv['method']) ? $argv['method'] : $this->defaultMethod;
 
    $lines = preg_split('/(\r\n|\n|\r)/', $this->input);
    foreach($lines as $num=>$line) {
      $line = trim($line);
      if (strlen($line)==0) continue;
 
      $pos = strpos($line, ':');
      if (! $pos) { //0 or false:not found
        $this->fields[$num]['type'] = 'invalid';
      $this->fields[$num]['value'] = $line;
        continue;
      }
 
      $type = substr($line, 0, $pos);
 
      if ($line{$pos+1}==='*') {
        $required = true;
        $pos++;
      } else {
        $required = false;
      }
 
      $pattern = '/(\S+="[^"]*")/';
      $attributes = preg_split ($pattern, trim(substr($line, $pos+1)), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
      $atar = array();
      foreach($attributes as $attr) {
        list($name, $value) = explode('=', $attr, 2); //limit to 2: not break if attribute value contains =
        
		if ($name==='option') {
          $atar['option'][] = substr($value, 1, strlen($value)-2); //remove quotation marks
        } else {
          $atar[$name] = substr($value, 1, strlen($value)-2); //remove quotation marks
        }
      }
 
      if (isset($atar['name'])) {
        $this->fields[$atar['name']] = $atar;
        $this->fields[$atar['name']]['type'] = $type;
        if ($required) $this->fields[$atar['name']]['required'] = $required;
      }else {
        $this->fields[$num]['type'] = 'invalid';
        $this->fields[$num]['value'] = "No name: '$line'";
      }
    }
  }
 
  function render() {
    $this->parseInput();
    if ($this->sender===false && $this->email===false) 
      return 'Sorry, this form is invalid. Either sender or caption to get user email have to be set.';
    if ($this->method==='email') {
      if ($this->target===false)
        return 'Sorry, this form is invalid. For the email method, a target email adress must be specified.';
      if (! $this->isValidEmail($this->target)) 
        return 'Sorry, this form is invalid. For the email method, the target must be a valid email adress, however, '.$this->target.' is not valid';
 
        if ($this->sender!==false && ! $this->isValidEmail($this->sender)) 
          return 'Sorry, this form is invalid. The sender adress is invalid: '.$this->sender;
    }
/*SG250028 Checking new hidden parameter which will be in every form to confirm that form has been submitted */
    if (($this->request->wasPosted())&&($_REQUEST['onformsubmit']=='yes')) {
   	  return $this->submit();
    } else {
	  return $this->show();
    }
 
  }
 
  function show($error=false) {
    global $wgOut, $wgTitle, $wgUser;

    $output='';
    if ($error !== false) $output = "<h2>An Error Occurred</h2><p>$error</p>";
 
    $output .= '<form action="'.$wgTitle->mTextform.'" method="post">
<input type="hidden" name="action" value="purge" />
              <table class="FormHandler">';
    if ($this->email !== false) $output .= '<tr><td>'.$this->email.'</td><td><input type="text" name="FormHandlerEmail" value="'. 
      (($this->request->getText('FormHandlerEmail') == '') ? $wgUser->mEmail : 
       $this->request->getText('FormHandlerEmail')) .
      '"/></td></tr>'."\n";
 
    foreach ($this->fields as $name => $field) {
 // $output .= '<tr><td>'.$wgOut->parse($field['prompt'], false)."</td><td>\n";
/*SG250028 We will not parse the field names(prompts) in the form*/
    	$output .= '<tr><td>'.$field['prompt']."</td><td>\n";
      switch($field['type']) {
        case 'text':
          $output .= '<input type="text" name="FormHandler_'.$field['name'].'" value="'.$field['value'].'" />';
          break;
        case 'hidden':
          //saver not to pass by client at all. $output .= '<input type="hidden" name="FormHandler_'.$field['name'].'" value="'.$field['value'].'" />';     
          break;
        case 'textarea':
          $output .= '<textarea name="FormHandler_'.$field['name'].'">'.$field['value'].'</textarea>';
          break;
        case 'select':
          $output .= '<select name="FormHandler_'.$field['name'].'">';
          foreach($field['option'] as $option) {
            $output .= '<option '.($option===$field['value'] ? 'selected="true"' : '').">$option</option>";
          }
          $output .= '</select>';
          break;
	  
          /*SG250028 Handling radiobuttons and checkboxes ...starts*/
    case 'radiobutton':
		  foreach($field['option'] as $option) {
			$output.=$option.':  <input type="radio" name="FormHandler_'.$field['name'].'" value="'.$option.'" />';
		  }
          break;
	case 'checkbox':
		  foreach($field['option'] as $option) {
			$output.=$option.':  <input type="checkbox" name="FormHandler_'.$field['name'].'[]" value="'.$option.'" />';
		  }
          break;
		/*SG250028 Handling radiobuttons and checkboxes ...ends*/
        case 'invalid':
          $output .= "Could not understand line $name: '".$field['value']."'";
          break;
        default:
          $output .= 'Unknown field type '.$field['type'];
          break;
      }
      if (isset($field['required'])) $output .= '*';
      $output .= "</td></tr>\n";
    }
 
    $output .= '<tr><td style="text-align:center; padding-top:15px;">';
    if (isset($this->argv['reset'])) $output .= '<input type="reset" value="'.$this->argv['reset'].'" />';
    /*Sg250028 Show the name of the submit button and include hidden parameter to specify that request is to submit the form*/
    $output .= '</td><td style="text-align:center; padding-top:15px;"><input type="submit" value="'.$this->argv['submit'].'" /></td></tr><input type="hidden" name="onformsubmit" value="yes"/></table></form>';
	return $output;
  }
 
  function submit() {
    global $wgUser, $wgDBname, $wgIP;
    $error = '';
    foreach($this->fields as $field) {
      $this->fields[$field['name']]['value'] = $this->request->getText('FormHandler_'.$field['name']);
      if (isset($field['required'])) {
        if (empty($_POST['FormHandler_'.$field['name']])) {
          $error .= $field['prompt'] . '<br />'; //todo: better would be to highlight the fields. for this we would keep a list of required fields here.
        }
      }
    }
    if (! empty($error)) {
	  return $this->show("Not all required fields have been filled out:<br />\n$error");
    }
 
    if ( 0 != $wgUser->getID() ) {
      $username = $wgUser->getName();
    } else {
      $username = '(not logged in)';
    }
 
    $usermail = $this->request->getText('FormHandlerEmail');
    if (empty($usermail)) $usermail=false;
/*SG250028 Removing the wiki server name which was displayed on top of the mail*/ 
 $message = 'Form '.$this->argv['name']." has been submitted by $username\n";
//$message = 'Form '.$this->argv['name']." has been submitted by $username (IP: $wgIP, Email: " . ($usermail ? $usermail : 'not specified') .') \n This Email is sent to you by MediaWiki FormHandler extension from http://'.$_SERVER['SERVER_NAME'].$_SERVER['PHP_SELF']."\n\n"; 
 
    foreach($this->fields as $field) {
      $message .= $field['name'] . ': ';
      switch($field['type']) {
        case 'text':
        case 'select':
        /*SG250028 Getting radio button values*/
        case 'radiobutton':
		case 'textarea':
          $value = $this->request->getText('FormHandler_'.$field['name']);
          break;
        /*SG250028 Getting check box values...starts*/
        case 'checkbox':
			$value = "";
			foreach($_POST['FormHandler_'.$field['name']] as $s){
				$value =$value ." ". $s." ";
			}
			break;
		/*SG250028 Getting check box values...ends*/
		case 'hidden':
          $value = $field['value']; //we do not put it into form and not treat it, but keep it at server side...
          break;
        case 'invalid':
          $value = 'There is an invalid line in the form: '.$field['value'];
          break;
        default:
          $value = 'Implementation Error in FormHandler: unexpected field type '.$field['type'];
          break;
      }
      $message .= (empty($value) ? '[not set]' : $value) . "\n";
    }
 
    switch ($this->argv['method']) {
      case 'email':
        require_once('UserMailer.php');
        if ($usermail!==false && ! $this->isValidEmail($usermail)) return $this->show('Your specified Email adress is invalid: '.$usermail); //sender is either == usermail or tested above
 
        if (! $this->sender) {
          if (! $usermail) return $this->show("The Email field is required, please fill in.");
          $this->sender=$usermail;
        }
 
        $error = userMailer( $this->target, 
                             $this->sender, 
                             $this->argv['name'].'form from FSDWiki',
                             $message,
                             $usermail);
        if (empty($error)) {
          return 'Thank you for sending a message to '.$this->target."<br /><br />\n".nl2br($message);
        } else {
          return "Sorry, sending the form failed.\n" . htmlspecialchars($error);
        }
        break;
      default:
        return 'Sorry, this is an invalid form, i do not know the method to store the information: '.$this->argv['method'];
    }
  }
  /* 
   * Check Email for validity, using a regular expression.
   */  
  function isValidEmail($candidate) {
	/*SG250028 Validation Logic of Email Address in case of multiple reciepients...starts*/
	$token = strtok($candidate, ",");
	$result = false;
	while ($token !== false)
	{
		$result = eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $token);
		if($result==false){
			break;
		}
		$token = strtok(",");
	}
	return $result;
	/*SG250028 Validation Logic of Email Address in case of multiple reciepients...ends*/
  }
}

No Email, End up on Non-existing page

I cobbled together the 0.2 release with the checkboxes/radio button version above, but I was not able to get it to work. The form rendered correctly, but when I submitted the form

  1. I never received the e-mail.
  2. It redirected me from User:Tnabtaf/Sandbox to User/Tnabtaf/Tnabtaf/Sandbox, which does not exist.

I'm going to give up on this one for the time being. Please let me know if you want the source for what I was using.

Thanks,

Tnabtaf 03:56, 30 August 2008 (UTC)

Problems with MediaWiki 1.12.0

I can't get this to work with MediaWiki 1.12.0.

If I use the code as-is, I get the following error:

Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING

At a guess, this might be caused by the lack of comma after new MailAddress($this->sender):

$error = UserMailer::send(new MailAddress($this->target), 
                            new MailAddress($this->sender)
                            'Contact form '.$this->argv['name'],
                            $message,
                            new MailAddress($usermail));

If I add the comma, I no longer get the parse error, but if I insert the <form> example as-is, I just get this displayed on the article page:

�UNIQ20ade97375a1674b-form-00000002-QINU� 

If I remove some of the parameters, I start to get other errors, e.g.:

Fatal error: Class 'UserMailer' not found in /home/www/sites/wiki/demo/mediawiki-1.12.0/extensions/FormHandler.php on line 251

Do I need to do something else to use this extension?

I'm watching that page, but I'm not using that extension (I wanted to do that once). Would be nice, if someone gets it working. --Wikinaut 22:37, 9 June 2009 (UTC)

Send Copy of Completed Form To Form Submitter

Is there any way of sending a copy of a completed form to the person who fills it in? We use the email="Enter your email" parameter to capture their email address, but I can't see any way of copying the form to that address. Any suggestions? Thanks Colin.

Return to "FormHandler send form by Email/Archive 1" page.