Difference between revisions of "Extension:EmailPage"

From Organic Design wiki
m (Method)
 
(31 intermediate revisions by the same user not shown)
Line 1: Line 1:
<?php
+
{{svn|extensions|MediaWiki/EmailPage/}}<br />
# Extension:EmailArticle{{Category:Extensions|EmailArticle}}{{php}}
 
# - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
 
# - Author: [http://www.organicdesign.co.nz/nad User:Nad]{{Category:Extensions created with Template:SpecialPage|EmailArticle}}
 
# - See http://www.mediawiki.org/wiki/Extension:EmailArticle for installation and usage details
 
# - Started: 2007-05-25 (based on http://www.organicdesign.co.nz/email-article.php from XmlWiki)
 
  
if (!defined('MEDIAWIKI')) die('Not an entry point.');
+
== Using an SMW query result as the recipient list ==
 +
The idea here is that we'd like to have a form with an input box that allows us to enter a value to be used for part of a query, and the result of the query will be a list of email addresses. For example we may have a query structure that results in the following query when the term "duck" is entered into the form.
 +
<source>
 +
{{#ask:[[User:+]][[Species::~*{!duck!}*]][[Cur::!N]]
 +
|?Email
 +
}}
 +
</source>
 +
When the form is submitted, this query is executed and we're taken to the EmailPage special page with the recipient list automatically populated with the results from the query.
  
define('EMAILARTICLE_VERSION','1.0.8, 2007-09-28');
+
=== Method ===
 +
All of the fields in the EmailPage special page can be populated from the query-string or POST data. The ''ea-title'' variable is set to the page title that will be sent to all the recipients, and the ''ea-to'' variable can be set to the list of recipient's email addresses.
  
$wgEmailArticleGroup          = 'sysop';            # Users must belong to this group to send emails (empty string means anyone can send)
+
We can have some JavaScript which is executed on the clicking of the form's button that builds the query and then sends it to the wiki's API to be executed, and then the results entered into a hidden ''ea-to'' value in the form and then the form is submitted to the EmailPage special page.
$wgEmailArticleContactsCat    = '';                # This specifies the name of a category containing categories of contact articles
 
$wgEmailArticleCss            = 'EmailArticle.css'; # A minimal CSS article to embed in the email (eg. monobook/main.css without portlets, actions etc)
 
$wgEmailArticleAllowRemoteAddr = array($_SERVER['SERVER_ADDR'],'127.0.0.1'); # Allow anonymous sending from these addresses
 
$wgEmailArticleAllowAllUsers  = false;              # Whether to allow sending to all users (the "user" group)
 
$wgEmailArticleToolboxLink    = 'Send to email';    # Link title for toolbox link (set to "" to not have any link in toolbox)
 
$wgEmailArticleActionLink      = 'email';            # Link title for action link (set to "" to not have any action link)
 
$wgPhpMailerClass              = dirname(__FILE__).'/phpmailer/class.phpmailer.php'; # From http://phpmailer.sourceforge.net/
 
  
if ($wgEmailArticleGroup) $wgGroupPermissions['sysop'][$wgEmailArticleGroup] = true;
+
For example, the HTML part of this mechanism might be as follows:
 +
<source lang="html">
 +
<form id="send-email-form">
  
$wgExtensionFunctions[] = 'wfSetupEmailArticle';
+
<!-- This is the parameter that will be part of the SMW query -->
 +
<input id="send-email-param" />
  
$wgExtensionCredits['specialpage'][] = array(
+
<!-- This will execute the JavaScript that populates the following two hidden inputs -->
'name'        => 'Special:EmailArticle',
+
<input type="button" id="send-email-button" value="Send" />
'author'      => '[http://www.organicdesign.co.nz/nad User:Nad]',
 
'description' => 'Send rendered article to an email address or list of addresses',
 
'url'        => 'http://www.mediawiki.org/wiki/Extension:EmailArticle',
 
'version'    => EMAILARTICLE_VERSION
 
);
 
  
# If form has been posted, include the phpmailer class
+
<!-- Special:EmailPage will use this as the page to send to the recipients -->
if (isset($_REQUEST['ea_send'])) require_once($wgPhpMailerClass);
+
<input type="hidden" id="send-email-page" name="ea-title" />
  
# Add toolbox and action links
+
<!-- Special:EmailPage will use this as the recipient list -->
if ($wgEmailArticleToolboxLink) {
+
<input type="hidden" id="send-email-to" name="ea-to" />
$wgHooks['MonoBookTemplateToolboxEnd'][] = 'wfEmailArticleToolboxLink';
+
</form>
function wfEmailArticleToolboxLink() {
+
</source>
global $wgEmailArticleToolboxLink,$wgTitle;
 
if (is_object($wgTitle)) {
 
$url = Title::makeTitle(NS_SPECIAL,'EmailArticle')->getLocalURL('ea_title='.$wgTitle->getPrefixedText());
 
echo("<li><a href=\"$url\">$wgEmailArticleToolboxLink</li>");
 
}
 
return true;
 
}
 
}
 
if ($wgEmailArticleActionLink) {
 
$wgHooks['SkinTemplateTabs'][] = 'wfEmailArticleActionLink';
 
function wfEmailArticleActionLink(&$skin,&$actions) {
 
global $wgEmailArticleActionLink,$wgTitle;
 
if (is_object($wgTitle)) {
 
$url = Title::makeTitle(NS_SPECIAL,'EmailArticle')->getLocalURL('ea_title='.$wgTitle->getPrefixedText());
 
$actions['email'] = array('text' => $wgEmailArticleActionLink, 'class' => false, 'href' => $url);
 
}
 
return true;
 
}
 
}
 
 
 
 
 
# Define a new class based on the SpecialPage class
 
require_once("$IP/includes/SpecialPage.php");
 
class SpecialEmailArticle extends SpecialPage {
 
 
 
var $recipients = array();
 
var $title;
 
var $subject;
 
var $header;
 
var $cat;
 
var $group;
 
var $list;
 
var $textonly;
 
var $css;
 
 
 
# Constructor
 
function SpecialEmailArticle() {
 
global $wgEmailArticleGroup;
 
SpecialPage::SpecialPage('EmailArticle',$wgEmailArticleGroup);
 
}
 
 
 
 
 
# Override SpecialPage::execute($param = '')
 
function execute($param) {
 
global $wgOut,$wgUser,$wgEmailArticleContactsCat,$wgGroupPermissions,$wgSitename,$wgEmailArticleCss,$wgEmailArticleAllowAllUsers;
 
$db =& wfGetDB(DB_SLAVE);
 
$param = str_replace('_',' ',$param);
 
$this->setHeaders();
 
 
 
# Get info from request or set to defaults
 
$this->title    = isset($_REQUEST['ea_title'])    ? $_REQUEST['ea_title']    : $param;
 
$this->subject  = isset($_REQUEST['ea_subject'])  ? $_REQUEST['ea_subject']  : "\"{$this->title}\" article sent from $wgSitename";
 
$this->header  = isset($_REQUEST['ea_header'])  ? $_REQUEST['ea_header']  : '';
 
$this->cat      = isset($_REQUEST['ea_cat'])      ? $_REQUEST['ea_cat']      : '';
 
$this->group    = isset($_REQUEST['ea_group'])    ? $_REQUEST['ea_group']    : '';
 
$this->list    = isset($_REQUEST['ea_list'])    ? $_REQUEST['ea_list']    : '';
 
$this->textonly = isset($_REQUEST['ea_textonly']) ? $_REQUEST['ea_textonly'] : false;
 
$this->css      = isset($_REQUEST['ea_css'])      ? $_REQUEST['ea_css']      : $wgEmailArticleCss;
 
  
# Bail if no article title to send has been specified
+
The corresponding JavaScript to match the above HTML and the example SMW query might be as follows.
if ($this->title) $wgOut->addWikiText(wfMsg('ea_heading',$this->title));
+
<source lang="js">
else return $wgOut->addWikiText(wfMsg('ea_noarticle'));
+
// When our button is clicked, do the following...
 +
$('#send-email-button').click(function() {
  
# If the send button was clicked, attempt to send and exit
+
// Get the parameter from the form input and force to lowercase
if (isset($_REQUEST['ea_send'])) return $this->send();
+
var param = $('#send-email-param').val(){!.toLowerCase()!};
  
# Render form
+
// Construct our SMW query with the parameter in it
$special = Title::makeTitle(NS_SPECIAL,'EmailArticle');
+
var query = '{{#ask:[[User:+]][[Species::~*' + param + '*]][[Cur::!N]]|?Email}}';
$wgOut->addHTML(wfElement('form',array(
 
'class'  => 'EmailArticle',
 
'action' => $special->getLocalURL('action=submit'),
 
'method' => 'POST'
 
),null));
 
$wgOut->addHTML('<fieldset><legend>'.wfMsg('ea_selectrecipients').'</legend>');
 
$wgOut->addHTML('<table style="padding:0;margin:0;border:none;">');
 
  
# If $wgEmailArticleContactsCat is set, create a select list of all categories
+
// Send the query to the MediaWiki API to be parsed
if ($wgEmailArticleContactsCat) {
+
$.ajax({
$cl = $db->tableName('categorylinks');
+
type: 'post',
$cats = '';
+
url: mw.util.wikiScript('api'),
$result = $db->query("SELECT cl_from FROM $cl WHERE cl_to = '$wgEmailArticleContactsCat' ORDER BY cl_sortkey");
+
dataType: 'json',
if ($result instanceof ResultWrapper) $result = $result->result;
+
data: {
if ($result) while ($row = $db->fetchRow($result)) {
+
action: 'parse',
$t = Title::newFromID($row[0]);
+
text: query,
if ($t->getNamespace() == NS_CATEGORY) {
+
contentmodel: 'wikitext',
$cat = $t->getText();
+
disablelimitreport: true,
$selected = $cat == $this->cat ? ' selected' : '';
+
disabletidy: true,
$cats .= "<option$selected>$cat</option>";
+
format: 'json'
}
+
},
}
 
if ($cats) $wgOut->addHTML("<tr><td>From category:</td><td><select name=\"ea_cat\"><option/>$cats</select></td></tr>\n");
 
}
 
  
# Allow selection of a group
+
// When the API response arrives...
$groups = '<option/>';
+
success: function(json) {
foreach (array_keys($wgGroupPermissions) as $group) if ($group != '*') {
 
$selected = $group == $this->group ? ' selected' : '';
 
if ($wgEmailArticleAllowAllUsers || $group != 'user') $groups .= "<option$selected>$group</option>";
 
}
 
$wgOut->addHTML("<tr><td>From group:</td><td><select name=\"ea_group\">$groups</select></td></tr>\n");
 
$wgOut->addHTML('</table>');
 
  
# Addition of named list
+
// Extract the raw email addresses from the parsed result
$wgOut->addWikiText(wfMsg('ea_selectlist'));
+
var result = json.parse.text['*']{!.match(/(\S+@[^,]+)/g).join(';');!}
$wgOut->addHTML("<textarea name=\"ea_list\" rows=\"5\">{$this->list}</textarea><br />\n");
 
$wgOut->addHTML('</fieldset>');
 
  
$wgOut->addHTML('<fieldset><legend>'.wfMsg('ea_compose').'</legend>');
+
// Tell EmailPage to use the query result as the recipient list
 +
$('#send-email-to').val(result);
  
# Subject
+
// Tell EmailPage to use the current page to send
$wgOut->addWikiText(wfMsg('ea_subject'));
+
$('#send-email-page').val(mw.config.get('wgPageName'));
$wgOut->addHTML(wfElement('input',array('type' => 'text', 'name' => 'ea_subject', 'value' => $this->subject, 'style' => "width:100%")));
 
 
 
# Header
 
$wgOut->addWikiText(wfMsg('ea_header'));
 
$wgOut->addHTML("<textarea name=\"ea_header\" rows=\"5\">{$this->header}</textarea><br />\n");
 
 
 
# CSS
 
$page = $db->tableName('page');
 
$csss = '';
 
$result = $db->query("SELECT page_id FROM $page WHERE page_title REGEXP('\\.css$') ORDER BY page_title");
 
if ($result instanceof ResultWrapper) $result = $result->result;
 
if ($result) while ($row = $db->fetchRow($result)) {
 
$t = Title::newFromID($row[0])->getPrefixedText();
 
$selected = $t == $this->css ? ' selected' : '';
 
$csss .= "<option$selected>$t</option>";
 
}
 
if ($csss) {
 
$wgOut->addWikiText(wfMsg('ea_selectcss'));
 
$wgOut->addHTML("<select name=\"ea_css\"><option/>$csss</select>\n");
 
}
 
 
 
$wgOut->addHTML("</fieldset>");
 
 
 
# Submit buttons & hidden values
 
$wgOut->addHTML(wfElement('input',array('type' => 'submit','name' => 'ea_send', 'value' => wfMsg('ea_send'))));
 
$wgOut->addHTML(wfElement('input',array('type' => 'submit','name' => 'ea_show', 'value' => wfMsg('ea_show'))));
 
$wgOut->addHTML(wfElement('input',array('type' => 'hidden','name' => 'ea_title','value' => $this->title)));
 
 
 
$wgOut->addHTML('</form>');
 
 
 
# If the show button was clicked, render the list
 
if (isset($_REQUEST['ea_show'])) return $this->send(false);
 
}
 
 
 
 
 
# Send the message to the recipients (or just list them if arg = false)
 
function send($send = true) {
 
global $wgOut,$wgUser,$wgParser,$wgServer,$wgScript,$wgArticlePath,$wgScriptPath,
 
$wgEmailArticleCss,$wgEmailArticleGroup,$wgEmailArticleAllowRemoteAddr,$wgEmailArticleAllowAllUsers;
 
 
 
# Set error and bail if user not in postmaster group, and request not from trusted address
 
if ($wgEmailArticleGroup && !in_array($wgEmailArticleGroup,$wgUser->getGroups()) && !in_array($_SERVER['REMOTE_ADDR'],$wgEmailArticleAllowRemoteAddr)) {
 
$wgOut->addWikiText(wfMsg('ea_error',$this->title,'Permission denied'));
 
return false;
 
}
 
 
 
$db      = &wfGetDB(DB_SLAVE);
 
$title    = Title::newFromText($this->title);
 
$opt      = new ParserOptions;
 
 
 
# Get contact article titles from selected cat
 
if ($this->cat) {
 
$cl    = $db->tableName('categorylinks');
 
$result = $db->query("SELECT cl_from FROM $cl WHERE cl_to = '{$this->cat}' ORDER BY cl_sortkey");
 
while ($row = mysql_fetch_row($result)) $this->addRecipient(Title::newFromID($row[0]));
 
}
 
 
 
# Get email addresses from users in selected group
 
if ($this->group && ($wgEmailArticleAllowAllUsers || $this->group != 'user')) {
 
$u  = str_replace('`','',$db->tableName('user'));
 
$ug = str_replace('`','',$db->tableName('user_groups'));
 
if ($this->group == 'user') $sql = "SELECT user_email FROM $u WHERE user_email != ''";
 
else $sql = "SELECT $u.user_email FROM $u,$ug WHERE $ug.ug_user = $u.user_id AND $ug.ug_group = '{$this->group}'";
 
$result = $db->query($sql);
 
if ($result instanceof ResultWrapper) $result = $result->result;
 
while ($row = $db->fetchRow($result)) $this->addRecipient($row[0]);
 
}
 
 
 
# Recipients from list (expand templates in wikitext)
 
$list = $wgParser->preprocess($this->list,$title,$opt);
 
foreach (preg_split("/[\\x00-\\x1f,;*]+/",$list) as $item) $this->addRecipient($item);
 
 
 
# Compose the wikitext content of the article to send
 
$article = new Article($title);
 
$message = $article->getContent();
 
if ($this->header) $message = "{$this->header}\n\n$message";
 
 
 
# Convert the message text to html unless textonly
 
if ($this->textonly == '') {
 
 
 
# Parse the wikitext using absolute URL's for local article links
 
$tmp          = array($wgArticlePath,$wgScriptPath,$wgScript);
 
$wgArticlePath = $wgServer.$wgArticlePath;
 
$wgScriptPath  = $wgServer.$wgScriptPath;
 
$wgScript      = $wgServer.$wgScript;
 
$message      = $wgParser->parse($message,$title,$opt,true,true)->getText();
 
list($wgArticlePath,$wgScriptPath,$wgScript) = $tmp;
 
 
 
# Get CSS content if any
 
if ($this->css) {
 
$article = new Article(Title::newFromText($this->css));
 
$css = '<style type="text/css">'.$article->getContent().'</style>';
 
}
 
 
 
# Create a html wrapper for the message
 
$head    = "<head>$css</head>";
 
$message = "<html>$head<body style=\"margin:10px\"><div id=\"#bodyContent\">$message</div></body></html>";
 
 
 
}
 
 
# Send message or list recipients
 
$count = count($this->recipients);
 
if ($count > 0) {
 
 
 
# Set up new mailer instance if sending
 
if ($send) {
 
$mail          = new PHPMailer();
 
$mail->From    = $wgUser->isValidEmailAddr($wgUser->getEmail()) ? $wgUser->getEmail() : "wiki@$wgServer";
 
$mail->FromName = User::whoIsReal($wgUser->getId());
 
$mail->Subject  = $this->subject;
 
$mail->Body    = $message;
 
$mail->IsHTML(!$this->textonly);
 
}
 
else $msg = wfMsg('ea_listrecipients',$count);
 
 
# Loop through recipients sending or adding to list
 
foreach ($this->recipients as $recipient) $send ? $mail->AddAddress($recipient) : $msg .= "\n*[mailto:$recipient $recipient]";
 
 
if ($send) {
 
if ($state = $mail->Send()) $msg = wfMsg('ea_sent',$this->title,$count,$wgUser->getName());
 
else $msg = wfMsg('ea_error',$this->title,$mail->ErrorInfo);
 
}
 
else $state = $count;
 
}
 
else $msg = wfMsg('ea_error',$this->title,wfMsg('ea_norecipients'));
 
 
 
$wgOut->addWikiText($msg);
 
return $state;
 
}
 
 
 
# Add a recipient the list
 
# - accepts title objects for article containing email address, or string of actual address
 
function addRecipient($recipient) {
 
if (is_object($recipient) && $recipient->exists()) {
 
$article = new Article($recipient);
 
if (preg_match('/[a-z0-9_.-]+@[a-z0-9_.-]+/i',$article->getContent(),$emails)) $recipient = $emails[0];
 
else $recipient = '';
 
}
 
if ($valid = User::isValidEmailAddr($recipient)) $this->recipients[] = $recipient;
 
return $valid;
 
}
 
  
}
+
// Set the forms submission URL to Special:EmailPage
 +
$('#send-email-form').attr('action', mw.util.wikiScript() + '?title=Special:EmailPage');
  
# Called from $wgExtensionFunctions array when initialising extensions
+
// Best POST it since the email list might be long
function wfSetupEmailArticle() {
+
$('#send-email-form').attr('method', 'post');
global $wgLanguageCode,$wgMessageCache;
 
  
# Add the messages used by the specialpage
+
// Submit the form to Special:EmailPage
if ($wgLanguageCode == 'en') {
+
$('#send-email-form').submit();
$wgMessageCache->addMessages(array(
 
'emailarticle'        => "EmailArticle",
 
'ea_heading'          => "=== Emailing [[$1]] article ===",
 
'ea_noarticle'        => "Please specify an article to send, for example [[Special:EmailArticle/Main Page]].",
 
'ea_norecipients'    => "No valid email addresses found!",
 
'ea_listrecipients'  => "=== List of $1 {{PLURAL:$1|recipient|recipients}} ===",
 
'ea_error'            => "'''Error sending [[$1]]:''' ''$2''",
 
'ea_sent'            => "Article [[$1]] sent successfully to '''$2''' {{PLURAL:$2|recipient|recipients}} by [[User:$3|$3]].",
 
'ea_selectrecipients' => "Select recipients",
 
'ea_compose'          => "Compose content",
 
'ea_selectlist'      => "Additional recipients as article titles or email addresses\n"
 
                      . "*''separate items with , ; * \\n\n"
 
                      . "*''list can contain templates and parser-functions''",
 
'ea_show'            => "Show recipients",
 
'ea_send'            => "Send!",
 
'ea_subject'          => "Enter a subject line for the email",
 
'ea_header'          => "Prepend content with optional message (wikitext)",
 
'ea_selectcss'        => "Select a CSS stylesheet"
 
));
 
 
}
 
}
 +
});
 +
});
 +
</source>
  
# Add the specialpage to the environment
+
Note that if you don't want to have raw HTML enabled in your wiki, you can use JavaScript to add the form. The code should go '''before''' the click handler code since the click handler refers to the form which needs to already exist. You could add the form to some part of the skin on every page, or you could add it to the content of a specific page. In the following example I'm using a ''jQuery'' CSS-selector (highlighted) to add the form to the "Sandbox" article just after the page heading.
SpecialPage::addPage(new SpecialEmailArticle());
+
<source lang="js">
}
+
$('{!body.page-Sandbox h1.firstHeading!}').after(
?>
+
'<form id="send-email-form">'
 +
+ '<input id="send-email-param" value="JavaScript" />'
 +
+ '<input type="button" id="send-email-button" value="Send" />'
 +
+ '<input type="hidden" id="send-email-page" name="ea-title" />'
 +
+ '<input type="hidden" id="send-email-to" name="ea-to" />'
 +
+ '</form>'
 +
);
 +
</source>
 +
[[Category:Extensions|EmailPage]]

Latest revision as of 20:10, 8 February 2018

Info.svg This code is in our Git repository here.

Note: If there is no information in this page about this code and it's a MediaWiki extension, there may be something at mediawiki.org.


Using an SMW query result as the recipient list

The idea here is that we'd like to have a form with an input box that allows us to enter a value to be used for part of a query, and the result of the query will be a list of email addresses. For example we may have a query structure that results in the following query when the term "duck" is entered into the form.

{{#ask:[[User:+]][[Species::~*duck*]][[Cur::!N]]
|?Email
}}

When the form is submitted, this query is executed and we're taken to the EmailPage special page with the recipient list automatically populated with the results from the query.

Method

All of the fields in the EmailPage special page can be populated from the query-string or POST data. The ea-title variable is set to the page title that will be sent to all the recipients, and the ea-to variable can be set to the list of recipient's email addresses.

We can have some JavaScript which is executed on the clicking of the form's button that builds the query and then sends it to the wiki's API to be executed, and then the results entered into a hidden ea-to value in the form and then the form is submitted to the EmailPage special page.

For example, the HTML part of this mechanism might be as follows:

<form id="send-email-form">

	<!-- This is the parameter that will be part of the SMW query -->
	<input id="send-email-param" />

	<!-- This will execute the JavaScript that populates the following two hidden inputs -->
	<input type="button" id="send-email-button" value="Send" />

	<!-- Special:EmailPage will use this as the page to send to the recipients -->
	<input type="hidden" id="send-email-page" name="ea-title" />

	<!-- Special:EmailPage will use this as the recipient list -->
	<input type="hidden" id="send-email-to" name="ea-to" />
</form>

The corresponding JavaScript to match the above HTML and the example SMW query might be as follows.

// When our button is clicked, do the following...
$('#send-email-button').click(function() {

	// Get the parameter from the form input and force to lowercase
	var param = $('#send-email-param').val().toLowerCase();

	// Construct our SMW query with the parameter in it
	var query = '{{#ask:[[User:+]][[Species::~*' + param + '*]][[Cur::!N]]|?Email}}';

	// Send the query to the MediaWiki API to be parsed
	$.ajax({
		type: 'post',
		url: mw.util.wikiScript('api'),
		dataType: 'json',
		data: {
			action: 'parse',
			text: query,
			contentmodel: 'wikitext',
			disablelimitreport: true,
			disabletidy: true,
			format: 'json'
		},

		// When the API response arrives...
		success: function(json) {

			// Extract the raw email addresses from the parsed result
			var result = json.parse.text['*'].match(/(\S+@[^,]+)/g).join(';');

			// Tell EmailPage to use the query result as the recipient list
			$('#send-email-to').val(result);

			// Tell EmailPage to use the current page to send
			$('#send-email-page').val(mw.config.get('wgPageName'));

			// Set the forms submission URL to Special:EmailPage
			$('#send-email-form').attr('action', mw.util.wikiScript() + '?title=Special:EmailPage');

			// Best POST it since the email list might be long
			$('#send-email-form').attr('method', 'post');

			// Submit the form to Special:EmailPage
			$('#send-email-form').submit();
		}
	});
});

Note that if you don't want to have raw HTML enabled in your wiki, you can use JavaScript to add the form. The code should go before the click handler code since the click handler refers to the form which needs to already exist. You could add the form to some part of the skin on every page, or you could add it to the content of a specific page. In the following example I'm using a jQuery CSS-selector (highlighted) to add the form to the "Sandbox" article just after the page heading.

$('body.page-Sandbox h1.firstHeading').after(
	'<form id="send-email-form">'
	+ '<input id="send-email-param" value="JavaScript" />'
	+ '<input type="button" id="send-email-button" value="Send" />'
	+ '<input type="hidden" id="send-email-page" name="ea-title" />'
	+ '<input type="hidden" id="send-email-to" name="ea-to" />'
	+ '</form>'
);