http://www.mediawiki.org/wiki/Special:Code/MediaWiki/89637
Revision: 89637
Author: nagelp
Date: 2011-06-07 09:20:13 +0000 (Tue, 07 Jun 2011)
Log Message:
-----------
Initial commit of new extension Notificator
Added Paths:
-----------
trunk/extensions/Notificator/
trunk/extensions/Notificator/Notificator.body.php
trunk/extensions/Notificator/Notificator.i18n.php
trunk/extensions/Notificator/Notificator.php
trunk/extensions/Notificator/Notificator.sql
trunk/extensions/Notificator/SpecialNotificator.php
trunk/extensions/Notificator/diff-in-mail.css
Added: trunk/extensions/Notificator/Notificator.body.php
===================================================================
--- trunk/extensions/Notificator/Notificator.body.php
(rev 0)
+++ trunk/extensions/Notificator/Notificator.body.php 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,200 @@
+<?php
+
+if (!defined('MEDIAWIKI')) {
+ echo "Not a valid entry point";
+ exit(1);
+}
+
+class Notificator {
+
+public static function notificator_Render($parser, $receiver = '',
$receiverLabel = '') {
+ global $wgScript, $wgTitle;
+
+ if(! $receiverLabel) {
+ $receiverLabel = $receiver;
+ }
+
+ // Check that the database table is in place
+ if(! Notificator::checkDatabaseTableExists()) {
+ $output = '<span class="error">' .
wfMsg('notificator-db-table-does-not-exist') . '</span>';
+ return array($output, 'noparse' => true, 'isHTML' => true);
+ }
+
+ // Check whether the parameter is a valid e-mail address or not
+ if($receiver && Notificator::checkEmailAddress($receiver)) {
+ // Valid e-mail address available, so just show a button
+ $output = '<form action="' . $wgScript . '/Special:Notificator"
method="post" enctype="multipart/form-data">
+<input type="hidden" name="pageId" value="' . $wgTitle->getArticleID() . '" />
+<input type="hidden" name="revId" value="' . $wgTitle->getLatestRevID() . '" />
+<input type="hidden" name="receiver" value="' . $receiver . '" />
+<input type="submit" value="' . wfMsg('notify-address-or-name',
htmlspecialchars($receiverLabel)) . '" />
+</form>';
+ } else {
+ // No valid e-mail address available, show text entry field and
button
+ $output = '<form action="' . $wgScript . '/Special:Notificator"
method="post" enctype="multipart/form-data">
+<input type="hidden" name="pageId" value="' . $wgTitle->getArticleID() . '" />
+<input type="hidden" name="revId" value="' . $wgTitle->getLatestRevID() . '" />
+<input type="text" name="receiver" value="' . wfMsg('e-mail-address') . '"
onfocus="if (this.value == \'' . wfMsg('e-mail-address') . '\')
{this.value=\'\'}" />
+<input type="submit" value="' . wfMsg('notify') . '" />
+</form>';
+ }
+
+ return $parser->insertStripItem($output, $parser->mStripState);
+}
+
+private function checkDatabaseTableExists() {
+ $dbr = wfGetDB(DB_SLAVE);
+ $res = $dbr->tableExists('notificator');
+ return $res;
+}
+
+private function getDiffCss() {
+ $ret = '';
+ $file = fopen(dirname(__FILE__) . '/diff-in-mail.css', 'r');
+ while(!feof($file)) {
+ $ret = $ret . fgets($file, 4096);
+ }
+ fclose ($file);
+ return $ret;
+}
+
+public static function checkEmailAddress($string) {
+// from http://www.linuxjournal.com/article/9585
+ $isValid = true;
+ $atIndex = strrpos($string, "@");
+ if (is_bool($atIndex) && !$atIndex) {
+ $isValid = false;
+ } else {
+ $domain = substr($string, $atIndex+1);
+ $local = substr($string, 0, $atIndex);
+ $localLen = strlen($local);
+ $domainLen = strlen($domain);
+ if ($localLen < 1 || $localLen > 64) {
+ // local part length exceeded
+ $isValid = false;
+ }
+ else if ($domainLen < 1 || $domainLen > 255) {
+ // domain part length exceeded
+ $isValid = false;
+ }
+ else if ($local[0] == '.' || $local[$localLen-1] == '.') {
+ // local part starts or ends with '.'
+ $isValid = false;
+ }
+ else if (preg_match('/\\.\\./', $local)) {
+ // local part has two consecutive dots
+ $isValid = false;
+ }
+ else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) {
+ // character not valid in domain part
+ $isValid = false;
+ }
+ else if (preg_match('/\\.\\./', $domain)) {
+ // domain part has two consecutive dots
+ $isValid = false;
+ }
+ else if
(!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/',
str_replace("\\\\","",$local))) {
+ // character not valid in local part unless
+ // local part is quoted
+ if (!preg_match('/^"(\\\\"|[^"])+"$/',
str_replace("\\\\","",$local))) {
+ $isValid = false;
+ }
+ }
+ if ($isValid && !(checkdnsrr($domain,"MX") ||
checkdnsrr($domain,"A"))) {
+ // domain not found in DNS
+ $isValid = false;
+ }
+ }
+ return $isValid;
+}
+
+public static function getLastNotifiedRevId($pageId, $revId, $receiver) {
+ // Returns -1 if any parameter is missing
+ // Returns -2 if the database revId is the same as the given revId (=
notified already)
+ // Returns revId from the database - if there is no record, return 0
+
+ if (! $pageId || ! $revId || ! $receiver) {
+ return -1;
+ }
+
+ // Get $oldRevId from database
+ $dbr = wfGetDB(DB_SLAVE);
+ $res = $dbr->select('notificator', // table
+ 'rev_id', // vars (columns of
the table)
+ array('page_id' => (int)$pageId, 'receiver_email' =>
$receiver) // conds
+ );
+
+ $row = $dbr->fetchRow($res);
+
+ $oldRevId = $row[rev_id];
+
+ if(! $oldRevId) {
+ $oldRevId = 0;
+ } elseif ($oldRevId == $revId) {
+ $oldRevId = -2;
+ }
+
+ return $oldRevId;
+}
+
+public static function getNotificationDiffHtml($oldRevId, $revId) {
+ $oldRevisionObj = Revision::newFromId($oldRevId);
+ $newRevisionObj = Revision::newFromId($revId);
+
+ if($oldRevisionObj->getTitle() != $newRevisionObj->getTitle()) {
+ return '<span class="error">' .
wfMsg('revs-not-from-same-title') . '</span>';
+ }
+
+ $titleObj = $oldRevisionObj->getTitle();
+
+ $differenceEngineObj = new DifferenceEngine($titleObj, $oldRevId,
$revId);
+
+ $notificationDiffHtml = '<style media="screen" type="text/css">' .
Notificator::getDiffCss() . '</style><table class="diff">
+<col class="diff-marker" />
+<col class="diff-content" />
+<col class="diff-marker" />
+<col class="diff-content" />
+' . $differenceEngineObj->getDiffBody() . '
+</table>';
+
+ return $notificationDiffHtml;
+}
+
+public static function sendNotificationMail($receiver, $mailSubject,
$notificationText) {
+ global $ngFromAddress;
+ $headers = 'From: ' . $ngFromAddress . "\r\n" .
+ 'X-Mailer: PHP/' . phpversion() . "\r\n" .
+ 'MIME-Version: 1.0' . "\r\n" .
+ 'Content-type: text/html; charset=utf-8' . "\r\n";
+ $encodedMailSubject = mb_encode_mimeheader($mailSubject,"UTF-8", "B",
"\n");
+
+ return mail($receiver, $encodedMailSubject, $notificationText,
$headers);
+}
+
+public static function recordNotificationInDatabase($pageId, $revId,
$receiver) {
+ $lastNotifiedRevId = Notificator::getLastNotifiedRevId($pageId, $revId,
$receiver);
+ if($lastNotifiedRevId > 0) {
+ $dbw = wfGetDB(DB_MASTER);
+ $res = $dbw->update('notificator', // table
+ array('rev_id' => (int)$revId),
// vars (columns of the table)
+ array('page_id' => (int)$pageId, 'receiver_email' =>
$receiver) // conds
+ );
+ return $res;
+ } elseif($lastNotifiedRevId == 0) {
+ $dbw = wfGetDB(DB_MASTER);
+ $res = $dbw->insert('notificator', // table
+ array('page_id' => (int)$pageId,
+ 'rev_id' => (int)$revId,
+ 'receiver_email' => $receiver) // "$a"
+ );
+ return $res;
+ } elseif($lastNotifiedRevId < 0) {
+ return false;
+ }
+}
+
+public static function getReturnToText($linkToPage, $pageTitle) {
+ return '<p style="margin-top: 2em;">' . wfMsg('return-to') . ' <a
href="' . $linkToPage . '">' . $pageTitle . '</a>.';
+}
+
+}
Added: trunk/extensions/Notificator/Notificator.i18n.php
===================================================================
--- trunk/extensions/Notificator/Notificator.i18n.php
(rev 0)
+++ trunk/extensions/Notificator/Notificator.i18n.php 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,54 @@
+<?php
+
+$aliases = array();
+$messages = array();
+
+$aliases['en'] = array(
+ 'Notificator' => array( 'Notificator' ),
+);
+
+$messages['en'] = array(
+ 'notificator' => 'Notificator',
+ 'notificator-description' => 'Notifies someone by e-mail about changes
to a page when a button on that page gets clicked.',
+ 'notificator-db-table-does-not-exist' => 'Database table
\'notificator\' does not exist. The update.php maintenance script needs to be
run before the Notificator extension can be used.',
+ 'e-mail-address' => 'e-mail address',
+ 'notify' => 'Notify', // Button label
+ 'notify-address-or-name' => 'Notify $1', // Button label
+ 'revs-not-from-same-title' => 'Revision IDs are not from the same
title/article', // Unlikely to be seen by the user
+ 'return-to' => 'Return to', // Return to <link> - at the bottom of the
special page
+ 'special-page-accessed-directly' => 'This special page cannot be
accessed directly. It is intended to be used through a Notificator button.',
+ 'e-mail-address-invalid' => 'The provided e-mail address is invalid.',
+ 'notification-not-sent' => 'Notification <em>not</em> sent.',
+ 'change-tag' => 'change',
+ 'new-tag' => 'new',
+ 'notification-text-changes' => '$1 wants to notify you about the
following changes to $2:',
+ 'notification-text-new' => '$1 wants to notify you about $2.',
+ 'following-e-mail-sent-to' => 'The following e-mail has been sent to
<em>$1</em>:',
+ 'subject' => 'Subject:', // The subject line of an e-mail
+ 'error-sending-e-mail' => 'There was an error when sending the
notification e-mail to <em>$1</em>.',
+ 'error-parameter-missing' => 'Error: Missing parameter.', // Unlikely
to be seen by the user
+ 'notified-already' => '$1 has been notified about this page or page
change before.',
+);
+
+$messages['de'] = array(
+ 'notificator' => 'Notificator',
+ 'notificator-description' => 'Sendet Benachrichtungs-Mails über
Seitenänderungen, wenn ein Knopf auf der entsprechenden Seite betätigt wird.',
+ 'notificator-db-table-does-not-exist' => 'Datenbanktabelle
\'notificator\' existiert nicht. Das update.php Maintenance Script muss
ausgeführt werden, bevor die Notificator-Extension verwendet werden kann.',
+ 'e-mail-address' => 'E-Mail-Adresse',
+ 'notify' => 'Benachrichtigen',
+ 'notify-address-or-name' => '$1 benachrichtigen',
+ 'revs-not-from-same-title' => 'Revision-IDs gehören nicht zum selben
Titel/Artikel',
+ 'return-to' => 'Zurück zu',
+ 'special-page-accessed-directly' => 'Auf diese Spezial-Seite kann nicht
direkt zugegriffen werden. Sie sollte über einen Notificator-Knopf verwendet
werden.',
+ 'e-mail-address-invalid' => 'Die angegebene E-Mail-Adresse ist
ungültig.',
+ 'notification-not-sent' => 'Benachrichtigung <em>nicht</em> gesendet.',
+ 'change-tag' => 'Änderung',
+ 'new-tag' => 'Neu',
+ 'notification-text-changes' => '$1 möchte Sie zu folgenden Änderungen
an $2 benachrichtigen:',
+ 'notification-text-new' => '$1 möchte Sie zu $2 benachrichtigen.',
+ 'following-e-mail-sent-to' => 'Folgende E-Mail wurde an <em>$1</em>
gesendet:',
+ 'subject' => 'Betreff:',
+ 'error-sending-e-mail' => 'Beim Versenden der Benachrichtigungs-Mail an
<em>$1</em> ist ein Fehler aufgetreten.',
+ 'error-parameter-missing' => 'Fehler: Fehlender Parameter.',
+ 'notified-already' => '$1 wurde bereits zu dieser Seite oder
Seitenänderung benachrichtigt.',
+);
\ No newline at end of file
Added: trunk/extensions/Notificator/Notificator.php
===================================================================
--- trunk/extensions/Notificator/Notificator.php
(rev 0)
+++ trunk/extensions/Notificator/Notificator.php 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,51 @@
+<?php
+
+if (!defined('MEDIAWIKI')) {
+ echo "Not a valid entry point";
+ exit(1);
+}
+
+$wgExtensionCredits['parserhook'][] = array(
+ 'path' => __FILE__, // Magic so that svn revision number can be
shown
+ 'name' => 'Notificator',
+ 'descriptionmsg' => 'notificator-description',
+ 'version' => '1.0.2',
+ 'author' => 'Patrick Nagel',
+ 'url' => "http://www.mediawiki.org/wiki/Extension:Notificator",
+);
+
+$wgAutoloadClasses['Notificator'] = dirname(__FILE__) .
'/Notificator.body.php';
+$wgAutoloadClasses['SpecialNotificator'] = dirname(__FILE__) .
'/SpecialNotificator.php';
+
+$wgHooks['LoadExtensionSchemaUpdates'][] = 'notificator_AddDatabaseTable';
+$wgHooks['ParserTestTables'][] = 'notificator_ParserTestTables';
+$wgHooks['ParserFirstCallInit'][] = 'notificator_Setup';
+$wgHooks['LanguageGetMagic'][] = 'notificator_Magic';
+
+$wgSpecialPages['Notificator'] = 'SpecialNotificator';
+
+$wgExtensionMessagesFiles['Notificator'] = dirname( __FILE__ ) .
'/Notificator.i18n.php';
+
+global $wgPasswordSender, $ngFromAddress;
+if(! $ngFromAddress) $ngFromAddress = $wgPasswordSender;
+
+function notificator_AddDatabaseTable() {
+ global $wgExtNewTables;
+ $wgExtNewTables[] = array('notificator', dirname( __FILE__ ) .
'/Notificator.sql');
+ return true;
+}
+
+function notificator_ParserTestTables(&$tables) {
+ $tables[] = 'notificator';
+ return true;
+}
+
+function notificator_Setup(&$parser) {
+ $parser->setFunctionHook('notificator',
'Notificator::notificator_Render');
+ return true;
+}
+
+function notificator_Magic(&$magicWords, $langCode) {
+ $magicWords['notificator'] = array( 0, 'notificator' );
+ return true;
+}
Added: trunk/extensions/Notificator/Notificator.sql
===================================================================
--- trunk/extensions/Notificator/Notificator.sql
(rev 0)
+++ trunk/extensions/Notificator/Notificator.sql 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS notificator (
+ page_id int(10) NOT NULL,
+ rev_id int(10) NOT NULL,
+ receiver_email tinytext NOT NULL,
+ PRIMARY KEY (page_id, rev_id, receiver_email(10))
+);
\ No newline at end of file
Added: trunk/extensions/Notificator/SpecialNotificator.php
===================================================================
--- trunk/extensions/Notificator/SpecialNotificator.php
(rev 0)
+++ trunk/extensions/Notificator/SpecialNotificator.php 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,74 @@
+<?php
+
+if (!defined('MEDIAWIKI')) {
+ echo "Not a valid entry point";
+ exit(1);
+}
+
+class SpecialNotificator extends SpecialPage {
+
+function __construct() {
+ parent::__construct( 'Notificator' );
+ wfLoadExtensionMessages('Notificator');
+}
+
+function execute($par) {
+ global $wgRequest, $wgOut, $wgUser;
+
+ $this->setHeaders();
+
+ # Get request data from, e.g.
+ $pageId = $wgRequest->getText('pageId');
+ $revId = $wgRequest->getText('revId');
+ $receiver = $wgRequest->getText('receiver');
+
+ if(! $pageId || ! $revId || ! $receiver ) {
+ $output = '<span class="error">' .
wfMsg('special-page-accessed-directly') . '</span>';
+ } else {
+ $titleObj = Title::newFromID($pageId);
+ $pageTitle = $titleObj->getFullText();
+ $linkToPage = $titleObj->getFullURL();
+
+ if(! Notificator::checkEmailAddress($receiver)) {
+ $output = '<span class="error">' .
wfMsg('e-mail-address-invalid') . ' ' . wfMsg('notification-not-sent') .
'</span>';
+ $output .= Notificator::getReturnToText($linkToPage,
$pageTitle);
+ $wgOut->addHTML($output);
+ return;
+ }
+
+ $oldRevId = Notificator::getLastNotifiedRevId($pageId, $revId,
$receiver);
+
+ if($oldRevId >= 0) {
+ if($oldRevId > 0) {
+ // Receiver has been notified before - send the
diff to the last notified revision
+ $mailSubjectPrefix = '[' . wfMsg('change-tag')
. '] ';
+
+ $wgOut->addModules('mediawiki.legacy.diff');
+ $diff =
Notificator::getNotificationDiffHtml($oldRevId, $revId);
+ $notificationText =
wfMsg('notification-text-changes', htmlspecialchars($wgUser->getName()), '<a
href="' . $linkToPage . '">' . $pageTitle . '</a>') . '<div style="margin-top:
1em;">' . $diff . '</div>';
+ } else {
+ // Receiver has never been notified about this
page - so don't send a diff, just the link
+ $mailSubjectPrefix = '[' . wfMsg('new-tag') .
'] ';
+ $notificationText =
wfMsg('notification-text-new', htmlspecialchars($wgUser->getName()), '<a
href="' . $linkToPage . '">' . $pageTitle . '</a>');
+ }
+ $mailSubject = htmlspecialchars($mailSubjectPrefix .
$pageTitle);
+
+ if (Notificator::sendNotificationMail($receiver,
$mailSubject, $notificationText)) {
+ $output = '<strong>' .
wfMsg('following-e-mail-sent-to', htmlspecialchars($receiver)) . '</strong><div
style="margin-top: 1em;"><h3>' . wfMsg('subject') . ' ' . $mailSubject .
'</h3><p>' . $notificationText . '</p></div>';
+
Notificator::recordNotificationInDatabase($pageId, $revId, $receiver);
+ } else {
+ $output = '<span class="error">' .
wfMsg('error-sending-e-mail', htmlspecialchars($receiver)) . '</span>';
+ }
+ } elseif($oldRevId == -1) {
+ $output = '<span class="error">' .
wfMsg('error-parameter-missing') . '</span>';
+ } elseif($oldRevId == -2) {
+ $output = '<strong>' . wfMsg('notified-already',
htmlspecialchars($receiver)) . ' ' . wfMsg('notification-not-sent') .
'</strong>';
+ }
+
+ $output .= Notificator::getReturnToText($linkToPage,
$pageTitle);
+ }
+
+ $wgOut->addHTML($output);
+}
+
+}
Added: trunk/extensions/Notificator/diff-in-mail.css
===================================================================
--- trunk/extensions/Notificator/diff-in-mail.css
(rev 0)
+++ trunk/extensions/Notificator/diff-in-mail.css 2011-06-07 09:20:13 UTC
(rev 89637)
@@ -0,0 +1,49 @@
+td.diff-otitle,
+td.diff-ntitle {
+ text-align: center;
+}
+td.diff-marker {
+ text-align: right;
+}
+td.diff-lineno {
+ font-weight: bold;
+}
+td.diff-addedline {
+ background: #cfc;
+ font-size: smaller;
+}
+td.diff-deletedline {
+ background: #ffa;
+ font-size: smaller;
+}
+td.diff-context {
+ background: #eee;
+ font-size: smaller;
+}
+.diffchange {
+ color: red;
+ font-weight: bold;
+ text-decoration: none;
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap;
+}
+
+table.diff {
+ border: none;
+ width: 98%;
+ border-spacing: 4px;
+ table-layout: fixed;
+}
+table.diff td {
+ padding: 0;
+}
+table.diff col.diff-marker {
+ width: 2%;
+}
+table.diff col.diff-content {
+ width: 48%;
+}
+table.diff td div {
+ word-wrap: break-word;
+ overflow: auto;
+}
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs