Extension:RegistrationForm
From MediaWiki.org
| This extension stores its source code on a wiki page. Please be aware that this code may be unreviewed or maliciously altered. They may contain security holes, outdated interfaces that are no longer compatible etc. Note: No localisation updates are provided for this extension by translatewiki.net. |
|
RegistrationForm Release status: experimental |
|||
|---|---|---|---|
| Implementation | User interface | ||
| Description | Inserts a registration form and manages registration details | ||
| Author(s) | Dave (Clausekwistalk) | ||
| Last version | 0.1.1 (02.05.2010) | ||
| MediaWiki | 1.15+ | ||
| PHP | 5.2+ | ||
| License | GPLv3 | ||
| Download | see below 0.1.1: Add hash to notification mail |
||
| Example | HaxoGreen Camp Registration page | ||
|
|||
|
|||
|
|||
| Check usage and version matrix | |||
Contents |
What can this extension do? [edit]
This extension allows you to handle conference and other registrations.
Todo [edit]
- Internationalization
- Improve & simplify XML
- $wgParser->setHook('tag', array( &$object, 'method' ) );
- Different way of setting maxTickets and Payment Provider Details (We have WAY too many settings)
- Allow for various payment providers (i.e. outsource IONShop)
- Set PaymentInfo somewhere else (allowing people to use PayPal, Google Checkout, whatever)
Usage [edit]
On the page you want to have a registration form, insert a <registration/> tag.
You can use both options captcha and emailConfirm. I.e.:
<registration emailConfirm="true" captcha="true"></registration>
You should disable caching for those specific pages you're using registration on.
Download instructions [edit]
Please cut and paste the code found below and place it in $IP/extensions/Registration/Registration.php. Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.
Installation [edit]
Dependencies [edit]
- To use the recaptcha feature, you require a set of recaptcha keys.
- You will either need to install the ReCAPTCHA extension or manually supply the recaptcha library.
Instructions [edit]
To install this extension, add the following to LocalSettings.php:
$wgEventName = 'Your Event'; $wgNotifyRecipient = 'your email address'; $wgNotifySubject = 'Email Subject'; $wgRegistrationFile = 'path to your registration.xml file (should be outside DOCROOT)'; include_once( "$IP/extensions/Registration/Registration.php");
Configuration parameters [edit]
- captcha
- emailconfirm
Code [edit]
Registration.php [edit]
<?php /** * RegistrationForm.php * Written by David Rauber and David Raison * */ if ( !defined( 'MEDIAWIKI' ) ) die( 'This is a MediaWiki extension, and must be run from within MediaWiki.' ); define("REGNAME", "Registration"); $wgExtensionCredits['parserhook'][] = array( 'path' => __FILE__, 'name' => REGNAME, 'version' => '0.1', 'author' =>'David Rauber and David Raison', 'url' => 'http://www.mediawiki.org/wiki/Extension:RegistrationForm', 'description' => 'Inserts a registration form into a page' ); $wgAutoloadClasses['Registration'] = dirname(__FILE__) . '/Registration.body.php'; $wgExtensionFunctions[] = "wfRegistrationExtension"; $wgHooks['BeforePageDisplay'][] = 'wfAddFormScript'; /** * Tag extension * Could also use object directly: * $wgParser->setHook('addscript', array( &$asObj, 'pSet' ) ); * or $wgHooks['ParserAfterTidy'][] = array( $asObj, 'feedScripts' ); * What exactly is the difference between addHook and addFunctionHook? */ function wfRegistrationExtension() { global $wgParser; $wgParser->setHook(REGNAME, "handleRegistration" ); $wgParser->setHook("seatsleft", "showTicketsLeft"); return true; } function showTicketsLeft(){ $reg = new Registration(); return($reg->countOpenTickets()); } /** * See http://www.mediawiki.org/wiki/Manual:Tag_extensions#Example * <tag arg="$arg1">$input</tag> * $parser --> http://svn.wikimedia.org/doc/classParser.html * */ function handleRegistration($input, $argv, &$parser){ $reg = new Registration(); if($_POST['registered']) $output = $reg->saveRegistration($_POST['emailconfirm'],$_POST['captcha']); elseif($_GET['hash']) $output = $reg->confirmEmail($_GET['hash']); else $output = $reg->renderForm($argv['emailconfirm'],$argv['captcha']); return($output); } /** * BAD!! because not working everywhere! * But using the setHook(), this doesn't seem to work :( */ function wfAddFormScript(){ if (strpos($_SERVER['REQUEST_URI'],'Registration')){ global $wgOut, $wgScriptPath; $wgOut->addScript('<script type="text/javascript" src="'.$wgScriptPath.'/extensions/Registration/formSetup.js"></script>'."\n"); $wgOut->addInlineScript('addOnloadHook(shirts);'); } return true; }
Registration.body.php [edit]
<?php /** * * RegistrationForm.php * Written by David Rauber and David Raison */ class Registration{ private $_dbName; // OUTSOURCE to paymentprovider class private $_dbTable; // OUTSOURCE to paymentprovider class private $_header; // headers for emails private $_notifySubject; private $_notifyRecipient; private $_eventName; private $_maxTickets; private $_regFile; private $_paymentSiteURL; private $_paymentSiteName; public function __construct(){ global $IP, $wgEventName, $wgNotifySubject, $wgNotifyRecipient; global $wgRegistrationFile, $wgMaxTickets, $wgBillingDB, $wgBillingTable; global $wgPaymentSiteURL, $wgPaymentSiteName; $this->_recaptchaLib = $IP.'/extensions/recaptcha/recaptchalib.php'; // how to do if not having recaptcha? $this->_notifySubject = $wgNotifySubject; $this->_notifyRecipient = $wgNotifyRecipient; $this->_maxTickets = $wgMaxTickets; $this->_eventName = $wgEventName; $this->_regFile = $wgRegistrationFile; $this->_dbName = $wgBillingDB; $this->_dbTable = $wgBillingTable; $this->_header = 'From: '.$this->_notifyRecipient . "\r\n" . 'Reply-To: '.$this->_notifyRecipient . "\r\n" . 'X-Mailer: PHP/' . phpversion(); // add to localsettings? $this->_paymentSiteURL = $wgPaymentSiteURL; $this->_paymentSiteName = $wgPaymentSiteName; } public function countOpenTickets(){ $xml = simplexml_load_file($this->_regFile); $left = $this->_maxTickets - sizeof($xml); return $left; } /** * Insert payment info into dbcetrel.tblion_bill * idbill ass int, billdate ass date, amount ass decimal 10,2 an sin euro'en dei aner 3 sin varchar * * TODO: This function is specific to our payment provider, outsource it to a helping library or better yet use an encrypted API call * * */ private function _insertPaymentDetails($amount,$email,$hash){ global $wgDBuser, $wgDBpassword, $wgDBserver; $dbCetrel = new mysqli($wgDBserver,$wgDBuser,$wgDBpassword,$this->_dbName); $billDate = date("Y-m-d"); $query = "INSERT INTO ".$this->_dbTable."(dthash,dtlabel,dtbilldate,dtcustomer,dtamount) VALUES ('$hash','Camp fees $email','$billDate','HaxoGreen','$amount');"; if($dbCetrel->query($query)) return $dbCetrel->insert_id; else throw new Exception("Couldn't add information to ION Bill DB: ".$dbCetrel->error); } /** * TODO: Outsource payment info into config file?! */ private function _displayPaymentInfo($amount,$email,$hash){ if($this->_insertPaymentDetails($amount,$email,$hash)){ $info = '<p>Thank you for registering for '.$this->_eventName.'!</p>' .'[...payment system + hash...]' .'<p>Note that your registration is only final once your payment has been received. A copy of this message has been sent to your email address.</p>' .'<p>Mail <a href="mailto:'.$this->_notifyRecipient.'">the Organisers</a> if you require assistance.</p>'; $mailInfo = "Thank you for registering for ".$this->_eventName."!\r\n\r\n" .'[...payment system + hash...]' ."Note that your registration is only final once your payment has been received.\r\n" ."Reply to this eMail if you require assistance."; $this->_mailPaymentInfo($mailInfo,$email); } else $info = "<p>There was a problem creating your payment information. Please notify ".$this->_notifyRecipient."</p>"; return $info; } private function _mailPaymentInfo($msg,$address){ mail($address,$this->_eventName.' - your payment information.',$msg,$this->_header); } private function _sendConfirmationMail($address,$hash){ global $wgTitle; //generate hash and return it to the calling function, then send it $confirmText = "Thank you for registering for HaxoGreen 2010.\r\nPlease click the following link to confirm your email address:\r\n %s"; $link = $wgTitle->getFullUrl().'?hash='.$hash; $finalText = sprintf($confirmText,$link); return (mail($address,$this->_eventName.' Email Confirmation',$finalText,$this->_header)) ? true : false; } /** * load email address and price info and show payment information & send out notification * verify hash & change <confirmed> field * NOTE: using a single hash! to be able to find payment info! */ public function confirmEmail($mailHash){ $mail = "%s confirmed camp registration."; $xml = simplexml_load_file($this->_regFile); $element = 0; foreach($xml->registration as $reg){ if((string) $reg->hash == $mailHash){ // fetch data for payment information $address = (string) $reg->email; $price = (string) $reg->price; if((string) $reg->confirmed != 'true') { // only send email once $mail = "%s confirmed camp registration. Hash: %s"; $msg = sprintf($mail,$address,$mailHash); $this->_notifyOfRegistration($msg); } // set email to confirmed state and write back to file $xml->registration[$element]->confirmed = "true"; $xml->asXML($this->_regFile); return($this->_displayPaymentInfo($price,$address,$mailHash)); // returns string to be displayed } $element++; // got any better idea? } // getting here only if the hash wasn't found. $errMsg = '<p>Sorry, the hash you submitted doesn\'t exist.<br/>If you feel this to be in error,' .'please contact <a href="mailto:'.$this->_notifyRecipient.'?subject=Inexisting hash">the Organisers</a>'; return($errMsg); } public function saveRegistration($emailConfirm,$captcha){ if($captcha == 'true'){ require_once($this->_recaptchaLib); global $recaptcha_private_key; $resp = recaptcha_check_answer($recaptcha_private_key, $_SERVER["REMOTE_ADDR"],$_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"]); } // check captcha if($captcha == 'true' && !$resp->is_valid){ return("<p>Sorry, the captcha you typed was wrong, please try again.</p>".$this->renderForm($emailConfirm,$captcha)); } else { foreach($_POST as $arg => $value) ${$arg} = htmlspecialchars($value); // gives $firstname, $lastname, $email, $amount, $comment if(!(preg_match("/^[0-9]{1}$/",$amount) && preg_match("/^([^-><&>!]){1,20}$/",$firstname) && preg_match("/^([^-><&>!]){1,20}$/",$lastname) && preg_match("/^([a-zA-Z0-9_\-\.]{1,50})@([a-zA-Z0-9_\-\.]{1,50})$/",$email))) return('<p>Sorry, the data you entered didn\'t pass our checks. Please try again.</p><p>'.$this->renderForm($emailConfirm,$captcha)); // passed regexp, continue $price = $amount * 25 + 26; // prepare mail $mail = "Registration OK! \r\n"; $mail .= "Firstname: $firstname \r\n"; $mail .= "Lastname: $lastname \r\n"; $mail .= "Email: $email \r\n"; $mail .= "Price: $price EUR \r\n"; // prepare xml (wouldn't it be easier to just use DOM(SimpleXML from the beginning?) $xml = "\n<registration>\n"; $xml .= "<date>".date("r")."</date>\n"; $xml .= "<firstname>$firstname</firstname>\n"; $xml .= "<lastname>$lastname</lastname>\n"; $xml .= "<email>$email</email>\n"; $xml .= "<price>$price</price>\n"; // adding shirt data $mail .= "T-Shirts:\r\n"; $xml .= "<tshirts>\n"; // add each shirt separately (but combine them if they're the same) $order = array(); for($i = 1; $i <= $amount; $i++){ $thisSize = $_POST["size-$i"]; if(!preg_match("/^((S)|(M)|(L)|(XL)|(XXL))$/",$thisSize)) $thisSize = "?"; $order[$thisSize] += 1; } foreach($order as $size => $num){ $mail .= "$num x $size \r\n"; $xml .= "<shirt amount=\"$num\" size=\"$size\"/>\n"; } $xml .= "</tshirts>"; // comments? if(strlen($comment)>0) { $comment = htmlentities($comment); $mail .= "Comment: $comment \r\n"; $xml .= "<comment>$comment</comment>"; } else $xml .= "<comment/>"; // force utf8 encoding if(!(mb_detect_encoding($xml)=='UTF-8') || !(mb_check_encoding($xml,"UTF-8"))) $xml = utf8_encode($xml); // send a confirmation Mail (generate the hash here to have it available everywhere) $hash = 'hgcc'.substr(md5(date(r).mt_rand(0,99)),0,16); if($emailConfirm == "true"){ $this->_sendConfirmationMail($email,$hash); } else $this->_notifyOfRegistration($mail); $xml .= "<hash>$hash</hash>"; $xml .= "<confirmed/>"; $xml .= "</registration>"; $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; $doc->load($this->_regFile); // create DOMDocument from string in $xml $doc2 = new DOMDocument(); //$doc2->formatOutput = true; $doc2->preserveWhiteSpace = false; $doc2->loadXML($xml); // integrating $doc2 into $doc $doc->documentElement->appendChild($doc->importNode($doc2->documentElement,true)); $doc->formatOutput = true; $doc->saveXML(); $doc->save($this->_regFile); $response = "<p>Thank you for your registration!</p>"; if($emailConfirm == 'true') $response .= "<p>You will receive a confirmation email shortly.</p>"; else $respone .= $this->_displayPaymentInfo($price,$email,$hash); return $response; } // end valid captcha } private function _notifyOfRegistration($message){ return(mail($this->_notifyRecipient,$this->_notifySubject,$message)); } public function renderForm($emailConfirm=false,$captcha=false){ //$input, $argv, &$parser global $wgTitle, $recaptcha_public_key; if ($captcha == 'true') require_once($this->_recaptchaLib); if ( !($wgTitle->isProtected ('edit')) ) return ( REGNAME . " is only active on protected pages." ); else { $form ='<p><form action="'. $wgTitle->getFullURL() . '" method="post" onsubmit="return checkForm">' .'<input type="hidden" name="registered" value="true">'; if ($emailConfirm == 'true') $form .= '<input type="hidden" name="emailconfirm" value="true">'; if ($captcha == 'true') $form .= '<input type="hidden" name="captcha" value="true">'; $form .= '<table>' .'<tr><td><label for="firstname">First name:</label></td><td><input type="text" name="firstname" /></td></tr>' .'<tr><td><label for="lastname">Last name:</label></td><td><input type="text" name="lastname" /></td></tr>' .'<tr><td><label for="email">Email</label></td><td><input type="text" name="email" /></td></tr>' .'<tr><td><label for="amount">T-Shirts Amount</label></td><td><input size="1" maxlength="1" value="1" type="text" id="amount" name="amount" onkeyup="shirts()"/></td></tr>' .'<tr><td colspan="2"><div id="sizes"></div></td></tr>' .'<tr><td/><td><div style="margin:10px 0 10px 0;font-weight:bold;" id="price"></div></td></tr>' .'<tr><td><label for="comment">Comment:</label></td><td><textarea name="comment" rows="12" cols="50"/></textarea></td></tr>'; if ($captcha == 'true') $form .= '<tr><td>Please enter the captcha</td><td>'.recaptcha_get_html($recaptcha_public_key).'</td></tr>'; $form .= '<tr><td/><td><input type="submit" name="Submit" value="Proceed" /></td></tr>' .'</table></form></p>'; return($form); } } }
formSetup.js [edit]
// Todo: turn into OO code function checkForm() { var price = 26 + document.getElementById("amount").value * 25; return confirm("Submit? Price: " + price +" EUR"); } // called by hookEvent function shirts() { var amount = document.getElementById("amount").value; var pattern = /^[0-9]{1}$/; if (!pattern.test(amount)) { var pattern2 = /^$/; if (pattern2.test(amount)) { //OK! BACKSPACE USAGE! amount = 0; } else { //NO ALERTS! document.getElementById("amount").value = 1; amount = 1; } } var sizes = document.getElementById("sizes"); sizes.innerHTML = ""; var html = ""; for(var i = 1; i<= amount; i++) { html += '<label for="size-'+ i +'">T-Shirt '+ i +' Size</label> '; html += '<select name="size-'+ i +'">'; html += ' <option value="S">S</option>'; html += ' <option value="M">M</option>'; html += ' <option value="L">L</option>'; html += ' <option value="XL">XL</option>'; html += ' <option value="XXL">XXL</option>'; html += '</select><br/>'; } sizes.innerHTML = html; var price = 26 + amount * 25; var priceDiv = document.getElementById("price"); priceDiv.innerHTML = ""; priceDiv.innerHTML = "Price: " + price + " € <br/>"; }