r6696 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r6695‎ | r6696 | r6697 >
Date:03:47, 18 December 2004
Author:vibber
Status:old
Tags:
Comment:
(bug 454) Merge e-notif 2.00
From http://bugzilla.wikipedia.org/attachment.cgi?id=171&action=view
and extra files from http://www.tgries.de/mw/cvs15+enea200-minus-cvs.tgz

Undid a couple minor bits like the broken changing of minoreditletter.
Have not yet made general corrections/reworkings. Since this changes
the user and watchlist tables, you must run the updaters (installer or
update.php) on an existing database.
Modified paths:
  • /trunk/phase3/README (modified) (history)
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/config/index.php (modified) (history)
  • /trunk/phase3/includes/Article.php (modified) (history)
  • /trunk/phase3/includes/ChangesList.php (modified) (history)
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/PageHistory.php (modified) (history)
  • /trunk/phase3/includes/RecentChange.php (modified) (history)
  • /trunk/phase3/includes/Setup.php (modified) (history)
  • /trunk/phase3/includes/Skin.php (modified) (history)
  • /trunk/phase3/includes/SkinTemplate.php (modified) (history)
  • /trunk/phase3/includes/SpecialEmailuser.php (modified) (history)
  • /trunk/phase3/includes/SpecialPreferences.php (modified) (history)
  • /trunk/phase3/includes/SpecialRecentchanges.php (modified) (history)
  • /trunk/phase3/includes/SpecialRecentchangeslinked.php (modified) (history)
  • /trunk/phase3/includes/SpecialUserlevels.php (modified) (history)
  • /trunk/phase3/includes/SpecialUserlogin.php (modified) (history)
  • /trunk/phase3/includes/SpecialWatchlist.php (modified) (history)
  • /trunk/phase3/includes/User.php (modified) (history)
  • /trunk/phase3/includes/UserMailer.php (modified) (history)
  • /trunk/phase3/includes/WatchedItem.php (modified) (history)
  • /trunk/phase3/includes/templates/Userlogin.php (modified) (history)
  • /trunk/phase3/languages/Language.php (modified) (history)
  • /trunk/phase3/languages/LanguageDe.php (modified) (history)
  • /trunk/phase3/maintenance/archives/patch-drop-user_newtalk.sql (added) (history)
  • /trunk/phase3/maintenance/archives/patch-email-authentication.sql (added) (history)
  • /trunk/phase3/maintenance/archives/patch-email-notification.sql (added) (history)
  • /trunk/phase3/maintenance/tables.sql (modified) (history)
  • /trunk/phase3/maintenance/updaters.inc (modified) (history)
  • /trunk/phase3/skins/MonoBook.php (modified) (history)
  • /trunk/phase3/skins/amethyst/main.css (modified) (history)
  • /trunk/phase3/skins/chick/main.css (modified) (history)
  • /trunk/phase3/skins/common/common.css (modified) (history)
  • /trunk/phase3/skins/monobook/main.css (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/archives/patch-email-notification.sql
@@ -0,0 +1,11 @@
 2+-- Patch for email notification on page changes T.Gries/M.Arndt 11.09.2004
 3+
 4+-- A new column 'wl_notificationtimestamp' is added to the table 'watchlist'.
 5+-- When a page watched by a user X is changed by someone else, an email is sent to the watching user X
 6+-- if and only if the field 'wl_notificationtimestamp' is '0'. The time/date of sending the mail is then stored in that field.
 7+-- Further pages changes do not trigger new notification mails as long as user X has not re-visited that page.
 8+-- The field is reset to '0' when user X re-visits the page or when he or she resets all notification timestamps
 9+-- ("notification flags") at once by clicking the new button on his/her watchlist page.
 10+-- T. Gries/M. Arndt 11.09.2004 - December 2004
 11+
 12+ALTER TABLE /*$wgDBprefix*/watchlist ADD (wl_notificationtimestamp varchar(14) binary NOT NULL default '0');
Property changes on: trunk/phase3/maintenance/archives/patch-email-notification.sql
___________________________________________________________________
Added: svn:eol-style
113 + native
Added: svn:keywords
214 + Author Date Id Revision
Index: trunk/phase3/maintenance/archives/patch-email-authentication.sql
@@ -0,0 +1,3 @@
 2+-- Patch for email authentication T.Gries/M.Arndt 27.11.2004
 3+-- A new column is added to the table 'user'.
 4+ALTER TABLE /*$wgDBprefix*/user ADD (user_emailauthenticationtimestamp varchar(14) binary NOT NULL default '0');
Property changes on: trunk/phase3/maintenance/archives/patch-email-authentication.sql
___________________________________________________________________
Added: svn:eol-style
15 + native
Added: svn:keywords
26 + Author Date Id Revision
Index: trunk/phase3/maintenance/archives/patch-drop-user_newtalk.sql
@@ -0,0 +1,3 @@
 2+-- Patch for email authentication T.Gries/M.Arndt 27.11.2004
 3+-- Table user_newtalk is dropped, as the table watchlist is now also used for storing user_talk-page notifications
 4+DROP TABLE /*$wgDBprefix*/user_newtalk;
Property changes on: trunk/phase3/maintenance/archives/patch-drop-user_newtalk.sql
___________________________________________________________________
Added: svn:eol-style
15 + native
Added: svn:keywords
26 + Author Date Id Revision
Index: trunk/phase3/maintenance/updaters.inc
@@ -146,6 +146,82 @@
147147 }
148148 }
149149
 150+function do_watchlist_update() {
 151+ global $wgDatabase;
 152+ if( $wgDatabase->fieldExists( 'watchlist', 'wl_notificationtimestamp' ) ) {
 153+ echo "ENOTIF: The watchlist table is already set up for email notification.\n";
 154+ } else {
 155+ echo "ENOTIF: Adding wl_notificationtimestamp field for email notification management.";
 156+ /* ALTER TABLE watchlist ADD (wl_notificationtimestamp varchar(14) binary NOT NULL default '0'); */
 157+ dbsource( "maintenance/archives/patch-email-notification.sql", $wgDatabase );
 158+ echo "ok\n";
 159+ }
 160+}
 161+
 162+function do_copy_newtalk_to_watchlist() {
 163+ global $wgDatabase;
 164+ global $wgCommandLineMode; # this needs to be saved while getID() and getName() are called
 165+
 166+ $CommandLineMode_save = $wgCommandLineMode;
 167+ $wgCommandLineMode = false; # otherwise User:loadfromDatabase() early returns, but we need it herein
 168+
 169+ if ( $wgDatabase->tableExists( 'user_newtalk' ) ) {
 170+ $res = $wgDatabase->safeQuery( 'SELECT user_id, user_ip FROM !',
 171+ $wgDatabase->tableName( 'user_newtalk' ) );
 172+ $num_newtalks=$wgDatabase->numRows($res);
 173+ echo "ENOTIF: Now converting ".$num_newtalks." user_newtalk entries to watchlist table entries ... \n";
 174+
 175+ $user = new User();
 176+ for ( $i = 1; $i <= $num_newtalks; $i++ ) {
 177+ $wluser = $wgDatabase->fetchObject( $res );
 178+ echo 'ENOTIF: <= user_newtalk: user_id='.$wluser->user_id.' user_ip='.$wluser->user_ip."\n";
 179+ if ($wluser->user_id == 0) { # anonymous users ... have IP numbers as "names"
 180+ if ($user->isIP($wluser->user_ip)) { # do only if it really looks like an IP number (double checked)
 181+ $wgDatabase->replace( 'watchlist',
 182+ array(array('wl_user','wl_namespace', 'wl_title', 'wl_notificationtimestamp' )),
 183+ array('wl_user' => 0,
 184+ 'wl_namespace' => NS_USER_TALK,
 185+ 'wl_title' => $wluser->user_ip,
 186+ 'wl_notificationtimestamp' => '19700101000000'
 187+ ), 'updaters.inc::do_watchlist_update2'
 188+ );
 189+ echo 'ENOTIF: ====> watchlist: user_id=0 '.$wluser->user_ip."\n";
 190+ }
 191+ } else { # normal users ... have user_ids
 192+ $user->setID($wluser->user_id);
 193+ $wgDatabase->replace( 'watchlist',
 194+ array(array('wl_user','wl_namespace', 'wl_title', 'wl_notificationtimestamp' )),
 195+ array('wl_user' => $user->getID(),
 196+ 'wl_namespace' => NS_USER_TALK,
 197+ 'wl_title' => $user->getName(),
 198+ 'wl_notificationtimestamp' => '19700101000000'
 199+ ), 'updaters.inc::do_watchlist_update3'
 200+ );
 201+ echo 'ENOTIF: ====> watchlist: user_id='.$user->getID().' '.$user->getName()."\n";
 202+ }
 203+ }
 204+ echo "ENOTIF: The watchlist table has got the former user_newtalk entries.\n";
 205+ dbsource( "maintenance/archives/patch-drop-user_newtalk.sql", $wgDatabase );
 206+ echo "ENOTIF: Deleting the user_newtalk table as its entries are now in the watchlist table.\n";
 207+ } else {
 208+ echo "ENOTIF: No user_newtalk table found. Nothing to convert to watchlist table entries.\n";
 209+ }
 210+ $wgCommandLineMode = $CommandLineMode_save;
 211+}
 212+
 213+
 214+function do_user_update() {
 215+ global $wgDatabase;
 216+ if( $wgDatabase->fieldExists( 'user', 'user_emailauthenticationtimestamp' ) ) {
 217+ echo "EAUTHENT: The user table is already set up for email authentication.\n";
 218+ } else {
 219+ echo "EAUTHENT: Adding user_emailauthenticationtimestamp field for email authentication management.";
 220+ /* ALTER TABLE user ADD (user_emailauthenticationtimestamp varchar(14) binary NOT NULL default '0'); */
 221+ dbsource( "maintenance/archives/patch-email-authentication.sql", $wgDatabase );
 222+ echo "ok\n";
 223+ }
 224+}
 225+
150226 # Assumes that the group table has been added.
151227 function do_group_update() {
152228 global $wgDatabase;
@@ -199,6 +275,9 @@
200276 do_linkscc_1_3_update(); flush();
201277 convertLinks(); flush();
202278 do_image_name_unique_update(); flush();
 279+ do_watchlist_update(); flush();
 280+ do_copy_newtalk_to_watchlist(); flush();
 281+ do_user_update(); flush();
203282
204283 if ( isTemplateInitialised() ) {
205284 print "Template namespace already initialised\n";
Index: trunk/phase3/maintenance/tables.sql
@@ -11,6 +11,7 @@
1212 user_password tinyblob NOT NULL default '',
1313 user_newpassword tinyblob NOT NULL default '',
1414 user_email tinytext NOT NULL default '',
 15+ user_emailauthenticationtimestamp varchar(14) binary NOT NULL default '0',
1516 user_options blob NOT NULL default '',
1617 user_touched char(14) binary NOT NULL default '',
1718 user_token char(32) binary NOT NULL default '',
@@ -25,12 +26,14 @@
2627 UNIQUE KEY ur_user (ur_user)
2728 );
2829
29 -CREATE TABLE /*$wgDBprefix*/user_newtalk (
30 - user_id int(5) NOT NULL default '0',
31 - user_ip varchar(40) NOT NULL default '',
32 - INDEX user_id (user_id),
33 - INDEX user_ip (user_ip)
34 -);
 30+-- The following table is no longer needed with Enotif >= 2.00
 31+-- Entries for newtalk on user_talk page are handled like in the watchlist table
 32+-- CREATE TABLE /*$wgDBprefix*/user_newtalk (
 33+-- user_id int(5) NOT NULL default '0',
 34+-- user_ip varchar(40) NOT NULL default '',
 35+-- INDEX user_id (user_id),
 36+-- INDEX user_ip (user_ip)
 37+-- );
3538
3639 CREATE TABLE /*$wgDBprefix*/cur (
3740 cur_id int(8) unsigned NOT NULL auto_increment,
@@ -241,6 +244,7 @@
242245 wl_user int(5) unsigned NOT NULL,
243246 wl_namespace tinyint(2) unsigned NOT NULL default '0',
244247 wl_title varchar(255) binary NOT NULL default '',
 248+ wl_notificationtimestamp varchar(14) binary NOT NULL default '0',
245249 UNIQUE KEY (wl_user, wl_namespace, wl_title),
246250 KEY namespace_title (wl_namespace,wl_title)
247251 );
Index: trunk/phase3/skins/chick/main.css
@@ -430,3 +430,18 @@
431431 span.newpage, span.minor {
432432 font-weight: bold;
433433 }
 434+
 435+span.updatedmarker {
 436+ color:black;
 437+ background-color:#00FF00;
 438+}
 439+span.newpageletter {
 440+ font-weight:bold
 441+ color:black;
 442+ background-color:yellow;
 443+}
 444+span.minoreditletter {
 445+ color:black;
 446+ background-color:#C5FFE6;
 447+}
 448+
Index: trunk/phase3/skins/monobook/main.css
@@ -935,3 +935,17 @@
936936 .sharedUploadNotice {
937937 font-style: italic;
938938 }
 939+
 940+span.updatedmarker {
 941+ color:black;
 942+ background-color:#00FF00;
 943+}
 944+span.newpageletter {
 945+ font-weight:bold
 946+ color:black;
 947+ background-color:yellow;
 948+}
 949+span.minoreditletter {
 950+ color:black;
 951+ background-color:#C5FFE6;
 952+}
Index: trunk/phase3/skins/common/common.css
@@ -212,3 +212,16 @@
213213 color:red;
214214 }
215215
 216+span.updatedmarker {
 217+ color:black;
 218+ background-color:#00FF00;
 219+}
 220+span.newpageletter {
 221+ font-weight:bold
 222+ color:black;
 223+ background-color:yellow;
 224+}
 225+span.minoreditletter {
 226+ color:black;
 227+ background-color:#C5FFE6;
 228+}
Index: trunk/phase3/skins/MonoBook.php
@@ -180,11 +180,13 @@
181181 <ul id="f-list">
182182 <?php if($this->data['lastmod' ]) { ?><li id="f-lastmod"><?php $this->html('lastmod') ?></li><?php } ?>
183183 <?php if($this->data['viewcount' ]) { ?><li id="f-viewcount"><?php $this->html('viewcount') ?></li><?php } ?>
 184+ <?php if($this->data['numberofwatchingusers' ]) { ?><li id="f-numberofwatchingusers"><?php $this->html('numberofwatchingusers') ?></li><?php } ?>
184185 <?php if($this->data['credits' ]) { ?><li id="f-credits"><?php $this->html('credits') ?></li><?php } ?>
185186 <?php if($this->data['copyright' ]) { ?><li id="f-copyright"><?php $this->html('copyright') ?></li><?php } ?>
186187 <?php if($this->data['about' ]) { ?><li id="f-about"><?php $this->html('about') ?></li><?php } ?>
187188 <?php if($this->data['disclaimer']) { ?><li id="f-disclaimer"><?php $this->html('disclaimer') ?></li><?php } ?>
188189 </ul>
 190+ <?php if($data['tagline']) { ?><li id="f-tagline"><?php echo $data['tagline'] ?></li><?php } ?>
189191 </div>
190192 </div>
191193 <?php $this->html('reporttime') ?>
Index: trunk/phase3/skins/amethyst/main.css
@@ -967,3 +967,17 @@
968968 span.searchmatch {
969969 color: red;
970970 }
 971+
 972+span.updatedmarker {
 973+ color:black;
 974+ background-color:#00FF00;
 975+}
 976+span.newpageletter {
 977+ font-weight:bold
 978+ color:black;
 979+ background-color:yellow;
 980+}
 981+span.minoreditletter {
 982+ color:black;
 983+ background-color:#C5FFE6;
 984+}
Index: trunk/phase3/README
@@ -59,6 +59,10 @@
6060 http://meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide
6161 http://meta.wikipedia.org/wiki/MediaWiki_development
6262
 63+Documentation about the E-Mail notification can be found on:
 64+
 65+ http://meta.wikipedia.org/Enotif
 66+
6367 If you are setting up your own wiki based on this software, it is highly
6468 recommended that you subscribe to mediawiki-l:
6569
Index: trunk/phase3/config/index.php
@@ -80,7 +80,7 @@
8181
8282 <b><a href="http://www.mediawiki.org/">MediaWiki</a></b> is
8383 Copyright (C) 2001-2004 by Magnus Manske, Brion Vibber, Lee Daniel Crocker,
84 - Tim Starling, Erik M&ouml;ller, Gabriel Wicke and others.</p>
 84+ Tim Starling, Erik M&ouml;ller, Gabriel Wicke, Thomas Gries and others.</p>
8585
8686 <ul>
8787 <li><a href="../README">Readme</a></li>
@@ -387,6 +387,12 @@
388388 $errs["MCServers"] = "Please specify at least one server if you wish to use memcached";
389389 }
390390
 391+/* default values for installation */
 392+$conf->Email =importRequest("Email", "email_enabled");
 393+$conf->Emailuser=importRequest("Emailuser", "emailuser_enabled");
 394+$conf->Enotif =importRequest("Enotif", "enotif_allpages");
 395+$conf->Eauthent =importRequest("Eauthent", "eauthent_enabled");
 396+
391397 if( $conf->posted && ( 0 == count( $errs ) ) ) {
392398 do { /* So we can 'continue' to end prematurely */
393399 $conf->Root = ($conf->RootPW != "");
@@ -636,7 +642,8 @@
637643 <dt>
638644 This will be used as the return address for password reminders and
639645 may be displayed in some error conditions so visitors can get in
640 - touch with you.
 646+ touch with you. It is also be used as the default sender address of e-mail
 647+ notifications (enotifs).
641648 </dt>
642649
643650 <dd>
@@ -733,6 +740,87 @@
734741 use Turck shared memory if the wiki will be running on a single Apache server.
735742 </dl>
736743
 744+<h2>E-mail, e-mail notification and authentification setup</h2>
 745+
 746+<dl class="setup">
 747+ <dd>
 748+ <label class='column'>E-mail (general)</label>
 749+ <div>Select one:</div>
 750+
 751+ <ul class="plain">
 752+ <li><?php aField( $conf, "Email", "enabled", "radio", "email_enabled" ); ?></li>
 753+ <li><?php aField( $conf, "Email", "disabled", "radio", "email_disabled" ); ?></li>
 754+ </ul>
 755+ </dd>
 756+ <dt>
 757+ Use this to disable all e-mail functions (send a password reminder, user-to-user e-mail and e-mail notification),
 758+ if sending e-mails on your server doesn't work.
 759+ </dt>
 760+ <dd>
 761+ <label class='column'>User-to-user e-mail</label>
 762+ <div>Select one:</div>
 763+
 764+ <ul class="plain">
 765+ <li><?php aField( $conf, "Emailuser", "enabled", "radio", "emailuser_enabled" ); ?></li>
 766+ <li><?php aField( $conf, "Emailuser", "disabled", "radio", "emailuser_disabled" ); ?></li>
 767+ </ul>
 768+ </dd>
 769+ <dt>
 770+ Use this to disable only the user-to-user e-mail function (EmailUser).
 771+ </dt>
 772+ <dd>
 773+ <label class='column'>E-mail notification</label>
 774+ <div>Select one:</div>
 775+
 776+ <ul class="plain">
 777+ <li><?php aField( $conf, "Enotif", "disabled", "radio", "enotif_disabled" ); ?></li>
 778+ <li><?php aField( $conf, "Enotif", "enabled for changes of watch-listed and user_talk pages (recommended for small wikis; perhaps not suited for large wikis)", "radio", "enotif_allpages" ); ?></li>
 779+ <li><?php aField( $conf, "Enotif", "enabled for changes of user_talk pages only (suited for small and large wikis)", "radio", "enotif_usertalk" ); ?></li>
 780+ </ul>
 781+ </dd>
 782+ <dt>
 783+ <p><?php
 784+ $ccEnotif = htmlspecialchars( 'http://meta.wikipedia.org/Enotif' );
 785+ print "<a href=\"$ccEnotif\">E-mail notification</a>";
 786+ ?>
 787+ sends a notification e-mail to a user, when the user_talk page is changed
 788+ and/or when watch-listed pages are changed, depending on the above settings.
 789+ When testing this feature, be reminded, that obviously an e-mail address must be present in your preferences
 790+ and that your own changes never trigger notifications to be sent to yourself.</p>
 791+
 792+ <p>Users get corresponding options to select or deselect in their users' preferences.
 793+ The user options are not shown on the preference page, if e-mail notification is disabled.</p>
 794+
 795+ <p>There are additional options for fine tuning in /includes/DefaultSettings.php .</p>
 796+ </dt>
 797+
 798+ <dd>
 799+ <label class='column'>E-mail address authentication</label>
 800+ <div>Select one:</div>
 801+
 802+ <ul class="plain">
 803+ <li><?php aField( $conf, "Eauthent", "disabled", "radio", "eauthent_disabled" ); ?></li>
 804+ <li><?php aField( $conf, "Eauthent", "enabled", "radio", "eauthent_enabled" ); ?></li>
 805+ </ul>
 806+ </dd>
 807+ <dt>
 808+ <p><?php
 809+ $ccEauthent = htmlspecialchars( 'http://meta.wikipedia.org/Eauthent' );
 810+ print "<a href=\"$ccEnotif\">E-mail address authentication</a>";
 811+ ?>
 812+ uses a scheme to authenticate e-mail addresses of the users. The user who initially enters or who changes his/her stored e-mail address
 813+ gets a one-time temporary password mailed to that address. The user can use the original password as long as wanted, however, the stored e-mail address
 814+ is only authenticated at the moment when the user logs in with the one-time temporary password.<p>
 815+
 816+ <p>The e-mail address stays authenticated as long as the user does not change it; the time of authentication is indicated
 817+ on the user preference page.</p>
 818+
 819+ <p>If the option is enabled, only authenticated e-mail addresses can receive EmailUser mails and/or
 820+ e-mail notification mails.</p>
 821+ </dt>
 822+
 823+ </dl>
 824+
737825 <h2>Database config</h2>
738826
739827 <dl class="setup">
@@ -853,6 +941,30 @@
854942 $turck = '#';
855943 }
856944
 945+ if ( $conf->Email == 'email_enabled' ) {
 946+ $enableemail = 'true';
 947+ $enableuseremail = ( $conf->Emailuser == 'emailuser_enabled' ) ? 'true' : 'false' ;
 948+ $eauthent = ( $conf->Eauthent == 'eauthent_enabled' ) ? 'true' : 'false' ;
 949+ switch ( $conf->Enotif ) {
 950+ case 'enotif_usertalk':
 951+ $enotifusertalk = 'true';
 952+ $enotifwatchlist = 'false';
 953+ break;
 954+ case 'enotif_allpages':
 955+ $enotifusertalk = 'true';
 956+ $enotifwatchlist = 'true';
 957+ break;
 958+ default:
 959+ $enotifusertalk = 'false';
 960+ $enotifwatchlist = 'false';
 961+ }
 962+ } else {
 963+ $enableuseremail = 'false';
 964+ $enableemail = 'false';
 965+ $eauthent = 'false';
 966+ $enotifusertalk = 'false';
 967+ $enotifwatchlist = 'false';
 968+ }
857969
858970 $file = @fopen( "/dev/urandom", "r" );
859971 if ( $file ) {
@@ -912,9 +1024,21 @@
9131025 \$wgUploadPath = \"\$wgScriptPath/images\";
9141026 \$wgUploadDirectory = \"\$IP/images\";
9151027
 1028+\$wgEnableEmail = $enableemail;
 1029+\$wgEnableUserEmail = $enableuseremail;
 1030+
9161031 \$wgEmergencyContact = \"{$slconf['EmergencyContact']}\";
9171032 \$wgPasswordSender = \"{$slconf['PasswordSender']}\";
9181033
 1034+## For a detailed description of the following switches see
 1035+## http://meta.wikimedia.org/Enotif and http://meta.wikimedia.org/Eauthent
 1036+## There are many more options for fine tuning available see
 1037+## /includes/DefaultSettings.php
 1038+## UPO means: this is also a user preference option
 1039+\$wgEmailNotificationForUserTalkPages = $enotifusertalk; # UPO
 1040+\$wgEmailNotificationForWatchlistPages = $enotifwatchlist; # UPO
 1041+\$wgEmailAuthentication = $eauthent;
 1042+
9191043 \$wgDBserver = \"{$slconf['DBserver']}\";
9201044 \$wgDBname = \"{$slconf['DBname']}\";
9211045 \$wgDBuser = \"{$slconf['DBuser']}\";
Index: trunk/phase3/includes/SpecialRecentchangeslinked.php
@@ -82,6 +82,7 @@
8383 "GROUP BY cur_id,cur_namespace,cur_title,cur_user,cur_comment,cur_user_text," .
8484 "cur_timestamp,cur_minor_edit,cur_is_new,inverse_timestamp ORDER BY inverse_timestamp LIMIT {$limit}";
8585 }
 86+
8687 $res = $dbr->query( $sql, $fname );
8788
8889 $wgOut->addHTML("&lt; ".$sk->makeKnownLinkObj($nt, "", "redirect=no" )."<br />\n");
Index: trunk/phase3/includes/User.php
@@ -23,6 +23,7 @@
2424 * @access private
2525 */
2626 var $mId, $mName, $mPassword, $mEmail, $mNewtalk;
 27+ var $mEmailAuthenticationtimestamp;
2728 var $mRights, $mOptions;
2829 var $mDataLoaded, $mNewpassword;
2930 var $mSkin;
@@ -116,6 +117,15 @@
117118 }
118119
119120 /**
 121+ * does the string match roughly an email address ?
 122+ * @param string $addr email address
 123+ * @static
 124+ */
 125+ function isValidEmailAddr ( $addr ) {
 126+ return preg_match( '/^([a-z0-9_.-]+([a-z0-9_.-]+)*\@[a-z0-9_-]+([a-z0-9_.-]+)*([a-z.]{2,})+)$/', strtolower($addr));
 127+ }
 128+
 129+ /**
120130 * probably return a random password
121131 * @return string probably a random password
122132 * @static
@@ -150,10 +160,10 @@
151161 $this->mNewtalk = -1;
152162 $this->mName = $wgIP;
153163 $this->mRealName = $this->mEmail = '';
 164+ $this->mEmailAuthenticationtimestamp = 0;
154165 $this->mPassword = $this->mNewpassword = '';
155166 $this->mRights = array();
156167 $this->mGroups = array();
157 -
158168 // Getting user defaults only if we have an available language
159169 if( isset( $wgContLang ) ) {
160170 $this->loadDefaultFromLanguage();
@@ -394,12 +404,14 @@
395405
396406 $dbr =& wfGetDB( DB_SLAVE );
397407 $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
 408+ 'user_emailauthenticationtimestamp',
398409 'user_real_name','user_options','user_touched', 'user_token' ),
399410 array( 'user_id' => $this->mId ), $fname );
400411
401412 if ( $s !== false ) {
402413 $this->mName = $s->user_name;
403414 $this->mEmail = $s->user_email;
 415+ $this->mEmailAuthenticationtimestamp = $s->user_emailauthenticationtimestamp;
404416 $this->mRealName = $s->user_real_name;
405417 $this->mPassword = $s->user_password;
406418 $this->mNewpassword = $s->user_newpassword;
@@ -458,8 +470,12 @@
459471 if ( $this->mNewtalk == -1 ) {
460472 $this->mNewtalk=0; # reset talk page status
461473 $dbr =& wfGetDB( DB_SLAVE );
 474+ extract( $dbr->tableNames( 'watchlist' ) );
462475 if($this->mId) {
463 - $res = $dbr->select( 'user_newtalk', 1, array( 'user_id' => $this->mId ), $fname );
 476+ $sql = "SELECT wl_user FROM $watchlist
 477+ WHERE wl_title='" . $dbr->strencode( str_replace( ' ', '_', $this->mName) )."' AND wl_namespace = " . NS_USER_TALK .
 478+ " AND wl_user=" . $this->mId . " AND wl_notificationtimestamp != 0";
 479+ $res = $dbr->query( $sql,'User::get:Newtalk');
464480
465481 if ( $dbr->numRows($res)>0 ) {
466482 $this->mNewtalk= 1;
@@ -470,7 +486,11 @@
471487 $key = "$wgDBname:newtalk:ip:{$this->mName}";
472488 $newtalk = $wgMemc->get( $key );
473489 if( ! is_integer( $newtalk ) ){
474 - $res = $dbr->select( 'user_newtalk', 1, array( 'user_ip' => $this->mName ), $fname );
 490+ extract( $dbr->tableNames( 'watchlist' ) );
 491+ $sql = "SELECT wl_user FROM $watchlist
 492+ WHERE wl_title='" . $dbr->strencode( str_replace( ' ', '_', $this->mName) )."' AND wl_namespace = " . NS_USER_TALK .
 493+ " AND wl_user =" . 0 . " AND wl_notificationtimestamp != 0";
 494+ $res = $dbr->query( $sql,'User::getNewtalk');
475495
476496 $this->mNewtalk = $dbr->numRows( $res ) > 0 ? 1 : 0;
477497 $dbr->freeResult( $res );
@@ -565,6 +585,11 @@
566586 return $this->mEmail;
567587 }
568588
 589+ function getEmailAuthenticationtimestamp() {
 590+ $this->loadFromDatabase();
 591+ return $this->mEmailAuthenticationtimestamp;
 592+ }
 593+
569594 function setEmail( $str ) {
570595 $this->loadFromDatabase();
571596 $this->mEmail = $str;
@@ -839,12 +864,19 @@
840865 global $wgMemc, $wgDBname;
841866 $fname = 'User::saveSettings';
842867
843 - $this->saveNewtalk();
 868+ $dbw =& wfGetDB( DB_MASTER );
 869+ if ( ! $this->getNewtalk() ) {
 870+ # Delete the watchlist entry for user_talk page X watched by user X
 871+ if( $this->mId ) {
 872+ $dbw->delete( 'watchlist', array( 'wl_user' => $this->mId, 'wl_title' => str_replace('_', ' ', $this->mName) ,'wl_namespace' => NS_USER_TALK ), $fname );
 873+ } else {
 874+ $dbw->delete( 'watchlist', array( 'wl_user' => 0, 'wl_title' => $this->mName, 'wl_namespace' => NS_USER_TALK ), $fname );
 875+ $wgMemc->delete( "$wgDBname:newtalk:ip:{$this->mName}" );
 876+ }
 877+ }
844878
845879 if ( 0 == $this->mId ) { return; }
846880
847 - $dbw =& wfGetDB( DB_MASTER );
848 -
849881 $dbw->update( 'user',
850882 array( /* SET */
851883 'user_name' => $this->mName,
@@ -852,6 +884,7 @@
853885 'user_newpassword' => $this->mNewpassword,
854886 'user_real_name' => $this->mRealName,
855887 'user_email' => $this->mEmail,
 888+ 'user_emailauthenticationtimestamp' => $this->mEmailAuthenticationtimestamp,
856889 'user_options' => $this->encodeOptions(),
857890 'user_touched' => $dbw->timestamp($this->mTouched),
858891 'user_token' => $this->mToken
@@ -878,38 +911,6 @@
879912 }
880913 }
881914
882 - /**
883 - * Save value of new talk flag.
884 - */
885 - function saveNewtalk() {
886 - global $wgDBname, $wgMemc;
887 -
888 - $fname = 'User::saveNewtalk';
889 -
890 - if ($this->getID() != 0) {
891 - $field = 'user_id';
892 - $value = $this->getID();
893 - $key = "$wgDBname:user:id:$this->mId";
894 - } else {
895 - $field = 'user_ip';
896 - $value = $this->mName;
897 - $key = "$wgDBname:newtalk:ip:$this->mName";
898 - }
899 -
900 - $dbr =& wfGetDB( DB_SLAVE );
901 - $dbw =& wfGetDB( DB_MASTER );
902 -
903 - $res = $dbr->selectField('user_newtalk', $field,
904 - array($field => $value), $fname);
905 -
906 - if ($res !== false && $this->mNewtalk == 0) {
907 - $dbw->delete('user_newtalk', array($field => $value), $fname);
908 - $wgMemc->delete($key);
909 - } else if ($res === false && $this->mNewtalk == 1) {
910 - $dbw->insert('user_newtalk', array($field => $value), $fname);
911 - $wgMemc->delete($key);
912 - }
913 - }
914915
915916 /**
916917 * Checks if a user with the given name exists, returns the ID
@@ -943,6 +944,7 @@
944945 'user_password' => $this->mPassword,
945946 'user_newpassword' => $this->mNewpassword,
946947 'user_email' => $this->mEmail,
 948+ 'user_emailauthenticationtimestamp' => $this->mEmailAuthenticationtimestamp,
947949 'user_real_name' => $this->mRealName,
948950 'user_options' => $this->encodeOptions(),
949951 'user_token' => $this->mToken
@@ -1087,20 +1089,22 @@
10881090 * @return bool True if the given password is correct otherwise False.
10891091 */
10901092 function checkPassword( $password ) {
 1093+ global $wgAuth;
10911094 $this->loadFromDatabase();
10921095
1093 - global $wgAuth;
10941096 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
10951097 return true;
10961098 } elseif( $wgAuth->strict() ) {
10971099 /* Auth plugin doesn't allow local authentication */
10981100 return false;
10991101 }
1100 -
11011102 $ep = $this->encryptPassword( $password );
11021103 if ( 0 == strcmp( $ep, $this->mPassword ) ) {
11031104 return true;
1104 - } elseif ( 0 == strcmp( $ep, $this->mNewpassword ) ) {
 1105+ } elseif ( ($this->mNewpassword != '') && (0 == strcmp( $ep, $this->mNewpassword )) ) {
 1106+ $this->mEmailAuthenticationtimestamp = wfTimestampNow();
 1107+ $this->mNewpassword = ''; # use the temporary one-time password only once: clear it now !
 1108+ $this->saveSettings();
11051109 return true;
11061110 } elseif ( function_exists( 'iconv' ) ) {
11071111 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
Index: trunk/phase3/includes/Article.php
@@ -681,6 +681,7 @@
682682 function view() {
683683 global $wgUser, $wgOut, $wgRequest, $wgOnlySysopsCanPatrol, $wgLang;
684684 global $wgLinkCache, $IP, $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol;
 685+ global $wgEnotif;
685686 $sk = $wgUser->getSkin();
686687
687688 $fname = 'Article::view';
@@ -806,6 +807,10 @@
807808
808809 $this->viewUpdates();
809810 wfProfileOut( $fname );
 811+
 812+ include_once( "UserMailer.php" );
 813+ $wgEnotif = new EmailNotification ();
 814+ $wgEnotif->Clear( $wgUser->getID(), $this->mTitle->getDBkey(), $this->mTitle->getNamespace() );
810815 }
811816
812817 /**
@@ -878,9 +883,10 @@
879884 array( 'cur_namespace' => $talkns, 'cur_title' => $ttl ), $fname );
880885
881886 # standard deferred updates
882 - $this->editUpdates( $text );
 887+ $this->editUpdates( $text, $summary, $isminor, $now );
883888
884 - $this->showArticle( $text, wfMsg( 'newarticle' ) );
 889+ $oldid = 0; # new article
 890+ $this->showArticle( $text, wfMsg( 'newarticle' ), false, $isminor, $now, $summary, $oldid );
885891 }
886892
887893
@@ -1094,7 +1100,7 @@
10951101 }
10961102 }
10971103 # standard deferred updates
1098 - $this->editUpdates( $text );
 1104+ $this->editUpdates( $text, $summary, $minor, $now );
10991105
11001106
11011107 $urls = array();
@@ -1117,7 +1123,7 @@
11181124 $u->doUpdate();
11191125 }
11201126
1121 - $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor );
 1127+ $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor, $me2, $now, $summary, $oldid );
11221128 }
11231129 return $good;
11241130 }
@@ -1126,8 +1132,8 @@
11271133 * After we've either updated or inserted the article, update
11281134 * the link tables and redirect to the new page.
11291135 */
1130 - function showArticle( $text, $subtitle , $sectionanchor = '' ) {
1131 - global $wgOut, $wgUser, $wgLinkCache;
 1136+ function showArticle( $text, $subtitle , $sectionanchor = '', $me2, $now, $summary, $oldid ) {
 1137+ global $wgOut, $wgUser, $wgLinkCache, $wgEnotif;
11321138
11331139 $wgLinkCache = new LinkCache();
11341140 # Select for update
@@ -1149,6 +1155,13 @@
11501156 else
11511157 $r = '';
11521158 $wgOut->redirect( $this->mTitle->getFullURL( $r ).$sectionanchor );
 1159+
 1160+ # this call would better fit into RecentChange::notifyEdit and RecentChange::notifyNew .
 1161+ # this will be improved later (to-do)
 1162+
 1163+ include_once( "UserMailer.php" );
 1164+ $wgEnotif = new EmailNotification ();
 1165+ $wgEnotif->NotifyOnPageChange( $wgUser->getID(), $this->mTitle->getDBkey(), $this->mTitle->getNamespace(),$now, $summary, $me2, $oldid );
11531166 }
11541167
11551168 /**
@@ -1857,12 +1870,10 @@
18581871 # talk page
18591872
18601873 global $wgUser;
1861 -
18621874 if ($this->mTitle->getNamespace() == NS_USER_TALK &&
1863 - $this->mTitle->getText() == $wgUser->getName())
1864 - {
1865 - $wgUser->setNewtalk(0);
1866 - $wgUser->saveNewtalk();
 1875+ $this->mTitle->getText() == $wgUser->getName()) {
 1876+ require_once( 'UserTalkUpdate.php' );
 1877+ $u = new UserTalkUpdate( 0, $this->mTitle->getNamespace(), $this->mTitle->getDBkey(), false, false, false );
18671878 }
18681879 }
18691880
@@ -1872,7 +1883,7 @@
18731884 * @private
18741885 * @param string $text
18751886 */
1876 - function editUpdates( $text ) {
 1887+ function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange) {
18771888 global $wgDeferredUpdateList, $wgDBname, $wgMemc;
18781889 global $wgMessageCache, $wgUser;
18791890
@@ -1897,15 +1908,13 @@
18981909 $u = new SearchUpdate( $id, $title, $text );
18991910 array_push( $wgDeferredUpdateList, $u );
19001911
1901 - # If this is another user's talk page, save a
1902 - # newtalk notification for them
 1912+ # If this is another user's talk page,
 1913+ # create a watchlist entry for this page
19031914
19041915 if ($this->mTitle->getNamespace() == NS_USER_TALK &&
1905 - $shortTitle != $wgUser->getName())
1906 - {
1907 - $other = User::newFromName($shortTitle);
1908 - $other->setNewtalk(1);
1909 - $other->saveNewtalk();
 1916+ $shortTitle != $wgUser->getName()) {
 1917+ require_once( 'UserTalkUpdate.php' );
 1918+ $u = new UserTalkUpdate( 1, $this->mTitle->getNamespace(), $shortTitle, $summary, $minoredit, $timestamp_of_pagechange);
19101919 }
19111920
19121921 if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
Index: trunk/phase3/includes/RecentChange.php
@@ -40,6 +40,10 @@
4141 * lastTimestamp timestamp of previous entry, used in WHERE clause during update
4242 * lang the interwiki prefix, automatically set in save()
4343 *
 44+ * temporary: not stored in the database
 45+ * notificationtimestamp
 46+ * numberofWatchingusers
 47+ *
4448 * @todo document functions and variables
4549 * @package MediaWiki
4650 */
@@ -61,6 +65,8 @@
6266 {
6367 $rc = new RecentChange;
6468 $rc->loadFromCurRow( $row );
 69+ $rc->notificationtimestamp = false;
 70+ $rc->numberofWatchingusers = false;
6571 return $rc;
6672 }
6773
Index: trunk/phase3/includes/SpecialUserlevels.php
@@ -142,7 +142,7 @@
143143
144144 /**
145145 * The entry form
146 - * It allow a user to select or eventually add a group as well as looking up
 146+ * It allows a user to select or eventually add a group as well as looking up
147147 * for a username.
148148 */
149149 function switchForm() {
Index: trunk/phase3/includes/WatchedItem.php
@@ -17,7 +17,12 @@
1818 $wl->mUser =& $user;
1919 $wl->mTitle =& $title;
2020 $wl->id = $user->getId();
21 - $wl->ns = $title->getNamespace() & ~1;
 21+# Patch (also) for email notification on page changes T.Gries/M.Arndt 11.09.2004
 22+# TG patch: here we do not consider pages and their talk pages equivalent - why should we ?
 23+# The change results in talk-pages not automatically included in watchlists, when their parent page is included
 24+# $wl->ns = $title->getNamespace() & ~1;
 25+ $wl->ns = $title->getNamespace();
 26+
2227 $wl->ti = $title->getDBkey();
2328 return $wl;
2429 }
@@ -57,13 +62,25 @@
5863 # REPLACE instead of INSERT because occasionally someone
5964 # accidentally reloads a watch-add operation.
6065 $dbw =& wfGetDB( DB_MASTER );
61 - $dbw->replace( 'watchlist', array(array('wl_user', 'wl_namespace', 'wl_title')),
 66+ $dbw->replace( 'watchlist', array(array('wl_user', 'wl_namespace', 'wl_title', 'wl_notificationtimestamp')),
6267 array(
6368 'wl_user' => $this->id,
64 - 'wl_namespace' => $this->ns,
 69+ 'wl_namespace' => ($this->ns & ~1),
6570 'wl_title' => $this->ti,
 71+ 'wl_notificationtimestamp' => '0'
6672 ), $fname );
6773
 74+ # the following code compensates the new behaviour, introduced by the enotif patch,
 75+ # that every single watched page needs now to be listed in watchlist
 76+ # namespace:page and namespace_talk:page need separate entries: create them
 77+ $dbw->replace( 'watchlist', array(array('wl_user', 'wl_namespace', 'wl_title', 'wl_notificationtimestamp')),
 78+ array(
 79+ 'wl_user' => $this->id,
 80+ 'wl_namespace' => ($this->ns | 1 ),
 81+ 'wl_title' => $this->ti,
 82+ 'wl_notificationtimestamp' => '0'
 83+ ), $fname );
 84+
6885 global $wgMemc;
6986 $wgMemc->set( $this->watchkey(), 1 );
7087 return true;
@@ -76,10 +93,21 @@
7794 $dbw->delete( 'watchlist',
7895 array(
7996 'wl_user' => $this->id,
80 - 'wl_namespace' => $this->ns,
 97+ 'wl_namespace' => ($this->ns & ~1),
8198 'wl_title' => $this->ti
8299 ), $fname
83100 );
 101+
 102+ # the following code compensates the new behaviour, introduced by the enotif patch,
 103+ # that every single watched page needs now to be listed in watchlist
 104+ # namespace:page and namespace_talk:page had separate entries: clear them
 105+ $dbw->delete( 'watchlist',
 106+ array(
 107+ 'wl_user' => $this->id,
 108+ 'wl_namespace' => ($this->ns | 1),
 109+ 'wl_title' => $this->ti
 110+ ), $fname
 111+ );
84112
85113 if ( $dbw->affectedRows() ) {
86114 global $wgMemc;
Index: trunk/phase3/includes/SpecialUserlogin.php
@@ -6,13 +6,8 @@
77 */
88
99 /**
10 - *
 10+ * constructor
1111 */
12 -require_once('UserMailer.php');
13 -
14 -/**
15 - * consutrctor
16 - */
1712 function wfSpecialUserlogin() {
1813 global $wgCommandLineMode;
1914 global $wgRequest;
@@ -36,6 +31,7 @@
3732
3833 function LoginForm( &$request ) {
3934 global $wgLang, $wgAllowRealName, $wgEnableEmail;
 35+ global $wgEmailAuthentication;
4036
4137 $this->mName = $request->getText( 'wpName' );
4238 $this->mPassword = $request->getText( 'wpPassword' );
@@ -92,6 +88,7 @@
9389 */
9490 function addNewAccountMailPassword() {
9591 global $wgOut;
 92+ global $wgEmailAuthentication;
9693
9794 if ('' == $this->mEmail) {
9895 $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
@@ -104,20 +101,37 @@
105102 return;
106103 }
107104
108 - $u->saveSettings();
109 - $error = $this->mailPasswordInternal($u);
 105+ $newadr = strtolower($this->mEmail);
110106
 107+ # prepare for authentication and mail a temporary password to newadr
 108+ if ( !$u->isValidEmailAddr( $newadr ) ) {
 109+ return $this->mainLoginForm( wfMsg( 'invalidemailaddress', $error ) );
 110+ }
 111+ $u->mEmail = $newadr; # new behaviour: set this new emailaddr from login-page into user database record
 112+ $u->mEmailAuthenticationtimestamp = 0; # but flag as "dirty" = unauthenticated
 113+
 114+ if ($wgEmailAuthentication) {
 115+ $error = $this->mailPasswordInternal( $u, true, $dummy ); # mail a temporary password to the dirty address
 116+ }
 117+
111118 $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
112119 $wgOut->setRobotpolicy( 'noindex,nofollow' );
113120 $wgOut->setArticleRelated( false );
114121
115 - if ( $error === '' ) {
116 - $wgOut->addWikiText( wfMsg( 'accmailtext', $u->getName(), $u->getEmail() ) );
117 - $wgOut->returnToMain( false );
 122+ if ($wgEmailAuthentication) {
 123+ if ($error === '') {
 124+ return $this->mainLoginForm( wfMsg( 'passwordsentforemailauthentication', $u->getName() ) );
118125 } else {
119 - $this->mainLoginForm( wfMsg( 'mailerror', $error ) );
 126+ return $this->mainLoginForm( wfMsg( 'mailerror', $error ) );
120127 }
121 -
 128+ # if user returns, that new email address gets authenticated in checkpassword()
 129+ }
 130+# if ( $error === '' ) {
 131+# $wgOut->addWikiText( wfMsg( 'accmailtext', $u->getName(), $u->getEmail() ) );
 132+# $wgOut->returnToMain( false );
 133+# } else {
 134+# $this->mainLoginForm( wfMsg( 'mailerror', $error ) );
 135+# }
122136 $u = 0;
123137 }
124138
@@ -127,6 +141,7 @@
128142 */
129143 function addNewAccount() {
130144 global $wgUser, $wgOut;
 145+ global $wgEmailAuthentication;
131146
132147 $u = $this->addNewAccountInternal();
133148
@@ -134,6 +149,27 @@
135150 return;
136151 }
137152
 153+ $newadr = strtolower($this->mEmail);
 154+ if ($newadr != '') { # prepare for authentication and mail a temporary password to newadr
 155+ if ( !$u->isValidEmailAddr( $newadr ) ) {
 156+ return $this->mainLoginForm( wfMsg( 'invalidemailaddress', $error ) );
 157+ }
 158+ $u->mEmail = $newadr; # new behaviour: set this new emailaddr from login-page into user database record
 159+ $u->mEmailAuthenticationtimestamp = 0; # but flag as "dirty" = unauthenticated
 160+
 161+ if ($wgEmailAuthentication) {
 162+ # mail a temporary password to the dirty address
 163+
 164+ $error = $this->mailPasswordInternal( $u, true, $dummy );
 165+ if ($error === '') {
 166+ return $this->mainLoginForm( wfMsg( 'passwordsentforemailauthentication', $u->getName() ) );
 167+ } else {
 168+ return $this->mainLoginForm( wfMsg( 'mailerror', $error ) );
 169+ }
 170+ # if user returns, that new email address gets authenticated in checkpassword()
 171+ }
 172+ }
 173+
138174 $wgUser = $u;
139175 $wgUser->setCookies();
140176
@@ -168,7 +204,7 @@
169205 $u = User::newFromName( $name );
170206 if ( is_null( $u ) ||
171207 ( '' == $name ) ||
172 - preg_match( "/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/", $name ) ||
 208+ $wgUser->isIP( $name ) ||
173209 (strpos( $name, "/" ) !== false) ||
174210 (strlen( $name ) > $wgMaxNameChars) ||
175211 ucFirst($name) != $u->getName() )
@@ -229,7 +265,8 @@
230266 * @access private
231267 */
232268 function processLogin() {
233 - global $wgUser;
 269+ global $wgUser, $wgLang;
 270+ global $wgEmailAuthentication;
234271
235272 if ( '' == $this->mName ) {
236273 $this->mainLoginForm( wfMsg( 'noname' ) );
@@ -258,6 +295,15 @@
259296 } else {
260297 $u->loadFromDatabase();
261298 }
 299+
 300+ # store temporarily the status before the password check is performed
 301+ $mailmsg = '';
 302+ $oldadr = strtolower($u->getEmail());
 303+ $newadr = strtolower($this->mEmail);
 304+ $alreadyauthenticated = (( $u->mEmailAuthenticationtimestamp != 0 ) || ($oldadr == '')) ;
 305+
 306+ # checkPassword sets EmailAuthenticationtimestamp, if the newPassword is used
 307+
262308 if (!$u->checkPassword( $this->mPassword )) {
263309 $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
264310 return;
@@ -272,13 +318,54 @@
273319 }
274320 $u->setOption( 'rememberpassword', $r );
275321
 322+ /* check if user with correct password has entered a new email address */
 323+ if (($newadr <> '') && ($newadr <> $oldadr)) { # the user supplied a new email address on the login page
 324+
 325+ # prepare for authentication and mail a temporary password to newadr
 326+ if ( !$u->isValidEmailAddr( $newadr ) ) {
 327+ return $this->mainLoginForm( wfMsg( 'invalidemailaddress', $error ) );
 328+ }
 329+ $u->mEmail = $newadr; # new behaviour: store this new emailaddr from login-page now into user database record ...
 330+ $u->mEmailAuthenticationtimestamp = 0; # ... but flag the address as "dirty" (unauthenticated)
 331+ $alreadyauthenticated = false;
 332+
 333+ if ($wgEmailAuthentication) {
 334+
 335+ # mail a temporary one-time password to the dirty address and return here to complete the user login
 336+ # if the user returns now or later using this temp. password, then the new email address $newadr
 337+ # - which is already stored in his user record - gets authenticated in checkpassword()
 338+
 339+ $error = $this->mailPasswordInternal( $u, false, $newpassword_temp);
 340+ $u->mNewpassword = $newpassword_temp;
 341+
 342+ # The temporary password is mailed. The user is logged-in as he entered his correct password
 343+ # This appears to be more intuitive than alternative 2.
 344+
 345+ if ($error === '') {
 346+ $mailmsg = '<br>' . wfMsg( 'passwordsentforemailauthentication', $u->getName() );
 347+ } else {
 348+ $mailmsg = '<br>' . wfMsg( 'mailerror', $error ) ;
 349+ }
 350+ }
 351+ }
 352+
276353 $wgUser = $u;
277354 $wgUser->setCookies();
278355
 356+ # save all settings (incl. new email address and/or temporary password, if applicable)
279357 $wgUser->saveSettings();
280358
 359+ if ( !$wgEmailAuthentication || $alreadyauthenticated ) {
 360+ $authenticated = '';
 361+ $mailmsg = '';
 362+ } elseif ($u->mEmailAuthenticationtimestamp != 0) {
 363+ $authenticated = ' ' . wfMsg( 'emailauthenticated', $wgLang->timeanddate( $u->mEmailAuthenticationtimestamp, true ) );
 364+ } else {
 365+ $authenticated = ' ' . wfMsg( 'emailnotauthenticated' );
 366+ }
 367+
281368 if( $this->hasSessionCookie() ) {
282 - return $this->successfulLogin( wfMsg( 'loginsuccess', $wgUser->getName() ) );
 369+ return $this->successfulLogin( wfMsg( 'loginsuccess', $wgUser->getName() ) . $authenticated . $mailmsg );
283370 } else {
284371 return $this->cookieRedirectCheck( 'login' );
285372 }
@@ -307,39 +394,47 @@
308395
309396 $u->loadFromDatabase();
310397
311 - $error = $this->mailPasswordInternal( $u );
 398+ $error = $this->mailPasswordInternal( $u, true, $dummy );
312399 if ($error === '') {
313400 $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ) );
314401 } else {
315402 $this->mainLoginForm( wfMsg( 'mailerror', $error ) );
316403 }
317 -
 404+ return;
318405 }
319406
320407
321408 /**
322409 * @access private
323410 */
324 - function mailPasswordInternal( $u ) {
325 - global $wgDeferredUpdateList, $wgOutputEncoding;
 411+ function mailPasswordInternal( $u, $savesettings = true, &$newpassword_out ) {
326412 global $wgPasswordSender, $wgDBname, $wgIP;
327413 global $wgCookiePath, $wgCookieDomain;
328414
329415 if ( '' == $u->getEmail() ) {
330416 return wfMsg( 'noemail', $u->getName() );
331417 }
332 - $np = User::randomPassword();
 418+
 419+ $np = $u->randomPassword();
333420 $u->setNewpassword( $np );
334421
 422+ # we want to store this new password together with other values in the calling function
 423+ $newpassword_out = $u->mNewpassword;
 424+
 425+ # WHY IS THIS HERE ? SHOULDN'T IT BE User::setcookie ???
335426 setcookie( "{$wgDBname}Token", '', time() - 3600, $wgCookiePath, $wgCookieDomain );
 427+
 428+ if ($savesettings) {
336429 $u->saveSettings();
 430+ }
337431
338432 $ip = $wgIP;
339433 if ( '' == $ip ) { $ip = '(Unknown)'; }
340434
341 - $m = wfMsg( 'passwordremindertext', $ip, $u->getName(), $np );
 435+ $m = wfMsg( 'passwordremindermailbody', $ip, $u->getName(), wfUrlencode($u->getName()), $np );
342436
343 - $error = userMailer( $u->getEmail(), $wgPasswordSender, wfMsg( 'passwordremindertitle' ), $m );
 437+ require_once('UserMailer.php');
 438+ $error = userMailer( $u->getEmail(), $wgPasswordSender, wfMsg( 'passwordremindermailsubject' ), $m );
344439
345440 return htmlspecialchars( $error );
346441 }
@@ -350,7 +445,6 @@
351446 */
352447 function successfulLogin( $msg ) {
353448 global $wgUser;
354 - global $wgDeferredUpdateList;
355449 global $wgOut;
356450
357451 # Run any hooks; ignore results
@@ -382,6 +476,7 @@
383477 function mainLoginForm( $err ) {
384478 global $wgUser, $wgOut, $wgLang;
385479 global $wgDBname, $wgAllowRealName, $wgEnableEmail;
 480+ global $wgEmailAuthentication;
386481
387482 if ( '' == $this->mName ) {
388483 if ( 0 != $wgUser->getID() ) {
@@ -397,7 +492,6 @@
398493 }
399494 $titleObj = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
400495
401 -
402496 require_once( 'templates/Userlogin.php' );
403497 $template =& new UserloginTemplate();
404498
@@ -410,9 +504,10 @@
411505 $template->set( 'action', $titleObj->getLocalUrl( $q ) );
412506 $template->set( 'error', $err );
413507 $template->set( 'create', $wgUser->isAllowedToCreateAccount() );
414 - $template->set( 'createemail', $wgEnableEmail && $wgUser->getID() != 0 );
 508+ $template->set( 'createemail', $wgEnableEmail && ($wgUser->getID() != 0) );
415509 $template->set( 'userealname', $wgAllowRealName );
416510 $template->set( 'useemail', $wgEnableEmail );
 511+ $template->set( 'useemailauthent', $wgEmailAuthentication );
417512 $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) );
418513
419514 $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
Index: trunk/phase3/includes/SpecialRecentchanges.php
@@ -18,6 +18,7 @@
1919 global $wgUser, $wgOut, $wgLang, $wgContLang, $wgTitle, $wgMemc, $wgDBname;
2020 global $wgRequest, $wgSitename, $wgLanguageCode, $wgContLanguageCode;
2121 global $wgFeedClasses, $wgUseRCPatrol;
 22+ global $wgRCUseModStyle, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
2223 $fname = 'wfSpecialRecentchanges';
2324
2425 # Get query parameters
@@ -115,10 +116,13 @@
116117 $patrLink = $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchanges' ),
117118 $showhide[1-$hidepatrolled], wfArrayToCGI( array( 'hidepatrolled' => 1-$hidepatrolled ), $urlparams ) );
118119
 120+ $RCUseModStyle = ($wgRCUseModStyle && $wgUser->getOption('rcusemodstyle')) ? 'AND rc_this_oldid=0 ' : '' ;
 121+
119122 $uid = $wgUser->getID();
120 - $sql2 = "SELECT $recentchanges.*" . ($uid ? ",wl_user" : "") . " FROM $recentchanges " .
121 - ($uid ? "LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace & 65534 " : "") .
122 - "WHERE rc_timestamp > '{$cutoff}' {$hidem} " .
 123+ # Patch for showing "updated since last visit" marker
 124+ $sql2 = "SELECT $recentchanges.*" . ($uid ? ",wl_user,wl_notificationtimestamp" : "") . " FROM $recentchanges " .
 125+ ($uid ? "LEFT OUTER JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") .
 126+ "WHERE rc_timestamp > '{$cutoff}' {$hidem} " . $RCUseModStyle .
123127 "ORDER BY rc_timestamp DESC LIMIT {$limit}";
124128
125129 $res = $dbr->query( $sql2, DB_SLAVE, $fname );
@@ -161,6 +165,24 @@
162166 ! ( $hidepatrolled && $obj->rc_patrolled ) ) {
163167 $rc = RecentChange::newFromRow( $obj );
164168 $rc->counter = $counter++;
 169+
 170+ if ($wgShowUpdatedMarker
 171+ && $wgUser->getOption( 'showupdated' )
 172+ && $obj->wl_notificationtimestamp
 173+ && ($obj->rc_timestamp >= $obj->wl_notificationtimestamp)) {
 174+ $rc->notificationtimestamp = true;
 175+ } else {
 176+ $rc->notificationtimestamp = false;
 177+ }
 178+
 179+ if ($wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
 180+ $sql3 = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_title='" . $dbr->strencode($obj->rc_title) ."' AND wl_namespace=$obj->rc_namespace" ;
 181+ $res3 = $dbr->query( $sql3, 'wfSpecialRecentChanges');
 182+ $x = $dbr->fetchObject( $res3 );
 183+ $rc->numberofWatchingusers = $x->n;
 184+ } else {
 185+ $rc->numberofWatchingusers = 0;
 186+ }
165187 $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ) );
166188 --$limit;
167189 }
Index: trunk/phase3/includes/Setup.php
@@ -80,8 +80,6 @@
8181
8282 $wgRequest = new WebRequest();
8383
84 -
85 -
8684 wfProfileOut( $fname.'-includes' );
8785 wfProfileIn( $fname.'-misc1' );
8886 global $wgUser, $wgLang, $wgContLang, $wgOut, $wgTitle;
Index: trunk/phase3/includes/UserMailer.php
@@ -1,10 +1,59 @@
22 <?php
 3+/** Copyright (C) 2004 Thomas Gries <mail@tgries.de>
 4+# http://www.mediawiki.org/
 5+#
 6+# This program is free software; you can redistribute it and/or modify
 7+# it under the terms of the GNU General Public License as published by
 8+# the Free Software Foundation; either version 2 of the License, or
 9+# (at your option) any later version.
 10+#
 11+# This program is distributed in the hope that it will be useful,
 12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+# GNU General Public License for more details.
 15+#
 16+# You should have received a copy of the GNU General Public License along
 17+# with this program; if not, write to the Free Software Foundation, Inc.,
 18+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 19+# http://www.gnu.org/copyleft/gpl.html
 20+**/
 21+
322 /**
423 * Provide mail capabilities
524 *
625 * @package MediaWiki
726 */
827
 28+function wfQuotedPrintable_name_and_emailaddr( $string ) {
 29+ /* it takes formats like "name <emailaddr>" into account,
 30+ for which the partial string "name" will be quoted-printable converted
 31+ but not "<emailaddr>".
 32+
 33+ The php mail() function does not accept the email address partial string
 34+ to be quotedprintable, it does not accept inputs as quotedprintable("name <emailaddr>") .
 35+
 36+ case 1:
 37+ input: "name <emailaddr> rest"
 38+ output: "quoted-printable(name) <emailaddr>"
 39+
 40+ case 2: (should not happen)
 41+ input: "<emailaddr> rest"
 42+ output: "<emailaddr>"
 43+
 44+ case 3: (should not happen)
 45+ input: "name"
 46+ output: "quoted-printable(name)"
 47+
 48+ T. Gries 18.11.2004
 49+ */
 50+
 51+ /* do not quote printable for email address string <emailaddr>, but only for the (leading) string, usually the name */
 52+ preg_match( '/^([^<]*)?(<([A-z0-9_.-]+([A-z0-9_.-]+)*\@[A-z0-9_-]+([A-z0-9_.-]+)*([A-z.]{2,})+)>)?$/', $string, $part );
 53+ if ( !isset($part[1]) ) return $part[2];
 54+ if ( !isset($part[2]) ) return wfQuotedprintable($part[1]);
 55+ return wfQuotedprintable($part[1]) . $part[2] ;
 56+}
 57+
958 /**
1059 * This function will perform a direct (authenticated) login to
1160 * a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
@@ -16,13 +65,12 @@
1766 * @param string $subject email's subject
1867 * @param string $body email's text
1968 */
20 -function userMailer( $to, $from, $subject, $body ) {
21 - global $wgUser, $wgSMTP, $wgOutputEncoding, $wgErrorString;
 69+function userMailer( $to, $from, $subject, $body, $replyto=false ) {
 70+ global $wgUser, $wgSMTP, $wgOutputEncoding, $wgErrorString, $wgEmergencyContact;
2271
23 - $qto = wfQuotedPrintable( $to );
 72+ $qto = wfQuotedPrintable_name_and_emailaddr( $to );
2473
25 - if (is_array( $wgSMTP ))
26 - {
 74+ if (is_array( $wgSMTP )) {
2775 require_once( 'Mail.php' );
2876
2977 $timestamp = time();
@@ -30,13 +78,16 @@
3179 $headers['From'] = $from;
3280 /* removing to: field as it should be set by the send() function below
3381 UNTESTED - Hashar */
34 -// $headers["To"] = $qto;
 82+// $headers['To'] = $qto;
 83+/* Reply-To:
 84+ UNTESTED - Tom Gries */
 85+ $headers['Reply-To'] = $replyto;
3586 $headers['Subject'] = $subject;
3687 $headers['MIME-Version'] = '1.0';
3788 $headers['Content-type'] = 'text/plain; charset='.$wgOutputEncoding;
3889 $headers['Content-transfer-encoding'] = '8bit';
3990 $headers['Message-ID'] = "<{$timestamp}" . $wgUser->getName() . '@' . $wgSMTP['IDHost'] . '>';
40 - $headers['X-Mailer'] = 'MediaWiki interuser e-mailer';
 91+ $headers['X-Mailer'] = 'MediaWiki mailer';
4192
4293 // Create the mail object using the Mail::factory method
4394 $mail_object =& Mail::factory('smtp', $wgSMTP);
@@ -50,20 +101,23 @@
51102 return $mailResult->getMessage();
52103 else
53104 return 'Mail object return unknown error.';
54 - }
55 -
56 - else
57 - {
 105+ } else {
 106+ # In the following $headers = expression we removed "Reply-To: {$from}\r\n" , because it is treated differently
 107+ # (fifth parameter of the PHP mail function, see some lines below)
58108 $headers =
59109 "MIME-Version: 1.0\n" .
60110 "Content-type: text/plain; charset={$wgOutputEncoding}\n" .
61 - "Content-transfer-encoding: 8bit\n" .
62 - "From: {$from}\n" .
63 - "X-Mailer: MediaWiki interuser e-mailer";
 111+ "Content-Transfer-Encoding: 8bit\n" .
 112+ "X-Mailer: MediaWiki mailer\n".
 113+ 'From:' . wfQuotedPrintable_name_and_emailaddr($from) . "\n";
 114+ if ($replyto) {
 115+ $headers .= 'Reply-To: '.wfQuotedPrintable_name_and_emailaddr($replyto)."\n";
 116+ }
64117
65118 $wgErrorString = '';
66119 set_error_handler( 'mailErrorHandler' );
67 - mail( $to, $subject, $body, $headers );
 120+ # added -f parameter, see PHP manual for the fifth parameter when using the mail function
 121+ mail( wfQuotedPrintable_name_and_emailaddr($to), $subject, $body, $headers, "-f{$wgEmergencyContact}\n");
68122 restore_error_handler();
69123
70124 return $wgErrorString;
@@ -77,4 +131,244 @@
78132 global $wgErrorString;
79133 $wgErrorString = preg_replace( "/^mail\(\): /", "", $string );
80134 }
 135+
 136+class EmailNotification {
 137+
 138+var $to, $subject, $body, $replyto, $from;
 139+
 140+# Patch for email notification on page changes T.Gries/M.Arndt 11.09.2004
 141+#
 142+# This new module processes the email notifications when the current page is changed.
 143+# It looks up the table watchlist to find out which users are watching that page.
 144+#
 145+# The current implementation sends independent emails to each watching user for the following reason:
 146+#
 147+# - Each watching user will be notified about the page edit time expressed in his/her local time (UTC is shown additionally).
 148+# To achieve this, we need to find the individual timeoffset of each watching user from the preferences..
 149+#
 150+# Suggested improvement to slack down the number of sent emails:
 151+# We could think of sending out bulk mails (bcc:user1,user2...) for all these users having the same timeoffset in their preferences.
 152+#
 153+# - Visit the documentation pages under http://meta.wikipedia.com/Enotif
 154+
 155+function NotifyOnPageChange($currentUser, $currentPage, $currentNs, $timestamp, $currentSummary, $currentMinorEdit, $oldid=false) {
 156+
 157+ # we use $wgEmergencyContact as sender's address
 158+ global $wgUser, $wgLang, $wgEmergencyContact;
 159+ global $wgEmailNotificationForWatchlistPages, $wgEmailNotificationForMinorEdits;
 160+ global $wgEmailNotificationSystembeep, $wgEmailNotificationForUserTalkPages;
 161+ global $wgEmailNotificationRevealPageEditorAddress;
 162+ global $wgEmailNotificationMailsSentFromPageEditor;
 163+ global $wgEmailAuthentication;
 164+ global $beeped;
 165+
 166+ # The following code is only run, if several conditions are met:
 167+ # 1. EmailNotification for pages (other than user_talk pages) must be enabled
 168+ # 2. minor edits (changes) are only regarded if the global flag indicates so
 169+
 170+ $isUserTalkPage = ($currentNs == NS_USER_TALK);
 171+ $enotifusertalkpage = ($isUserTalkPage && $wgEmailNotificationForUserTalkPages);
 172+ $enotifwatchlistpage = (!$isUserTalkPage && $wgEmailNotificationForWatchlistPages);
 173+
 174+ if ( ($enotifusertalkpage || $enotifwatchlistpage)
 175+ && (!$currentMinorEdit || $wgEmailNotificationForMinorEdits) ) {
 176+
 177+ $dbr =& wfGetDB( DB_MASTER );
 178+ extract( $dbr->tableNames( 'watchlist' ) );
 179+ $sql = "SELECT wl_user FROM $watchlist
 180+ WHERE wl_title='" . $dbr->strencode($currentPage)."' AND wl_namespace = " . $currentNs .
 181+ " AND wl_user <>" . $currentUser . " AND wl_notificationtimestamp <= 1";
 182+ $res = $dbr->query( $sql,'UserMailer::NotifyOnChange');
 183+
 184+ if ( $dbr->numRows( $res ) > 0 ) { # if anyone is watching ... set up the email message text which is common for all receipients ...
 185+
 186+ # This is a switch for one beep on the server when sending notification mails
 187+ $beeped = false;
 188+
 189+ $article->mTimestamp = $timestamp;
 190+ $article->mSummary = $currentSummary;
 191+ $article->mMinorEdit = $currentMinorEdit;
 192+ $article->mNamespace = $currentNs;
 193+ $article->mTitle = $currentPage;
 194+ $article->mOldid = $oldid;
 195+
 196+ $mail = $this->ComposeCommonMailtext( $wgUser, $article );
 197+ $watchingUser = new User();
 198+
 199+ for ($i = 1; $i <= $dbr->numRows( $res ); $i++) { # ... now do for all watching users ... if the options fit
 200+
 201+ $wuser = $dbr->fetchObject( $res );
 202+ $watchingUser->setID($wuser->wl_user);
 203+ if ( ( $enotifwatchlistpage && $watchingUser->getOption('enotifwatchlistpages') ) ||
 204+ ( $enotifusertalkpage && $watchingUser->getOption('enotifusertalkpages') )
 205+ && (!$currentMinorEdit || ($wgEmailNotificationForMinorEdits && $watchingUser->getOption('enotifminoredits') ) )
 206+ && ($watchingUser->getEmail() != '')
 207+ && (!$wgEmailAuthentication || ($watchingUser->getEmailAuthenticationtimestamp() != 0 ) ) ) {
 208+ # ... adjust remaining text and page edit time placeholders
 209+ # which needs to be personalized for each user
 210+ $sent = $this->ComposeAndSendPersonalisedMail( $watchingUser, $mail, $article );
 211+ /* the beep here beeps once when a watched-listed page is changed */
 212+ if ($sent && !$beeped && ($wgEmailNotificationSystembeep != '') ) {
 213+ $last_line = system($wgEmailNotificationSystembeep);
 214+ $beeped=true;
 215+ }
 216+ } # if the watching user has an email address in the preferences
 217+ # mark the changed watch-listed page with a timestamp, so that the page is listed with an "updated since your last visit" icon in the watch list, ...
 218+ # ... no matter, if the watching user has or has not indicated an email address in his/her preferences.
 219+ # We memorise the event of sending out a notification and use this as a flag to suppress further mails for changes on the same page for that watching user
 220+ $dbw =& wfGetDB( DB_MASTER );
 221+ $succes = $dbw->update( 'watchlist',
 222+ array( /* SET */
 223+ 'wl_notificationtimestamp' => $article->mTimestamp
 224+ ), array( /* WHERE */
 225+ 'wl_title' => $currentPage,
 226+ 'wl_namespace' => $currentNs,
 227+ 'wl_user' => $wuser->wl_user
 228+ ), 'UserMailer::NotifyOnChange'
 229+ );
 230+ } # for every watching user
 231+ } # if anyone is watching
 232+ } # if $wgEmailNotificationForWatchlistPages = true
 233+} # function NotifyOnChange
 234+
 235+function ComposeCommonMailtext ( $pageeditorUser, $article ) {
 236+
 237+ global $wgLang, $wgEmergencyContact;
 238+ global $wgEmailNotificationRevealPageEditorAddress, $wgEmailNotificationMailsSentFromPageEditor;
 239+ global $wgNoReplyAddress;
 240+
 241+ $summary = ($article->mSummary == '') ? ' - ' : $article->mSummary;
 242+ $medit = ($article->mMinorEdit) ? wfMsg( 'minoredit' ) : '';
 243+
 244+ # You as the WikiAdmin and Sysops can make use of plenty of named variables when composing
 245+ # your notification emails while simply editing the Meta pages
 246+
 247+ $to = wfMsg( 'email_notification_to' );
 248+ $subject = wfMsg( 'email_notification_subject' );
 249+ $body = wfMsg( 'email_notification_body' );
 250+ $from = ''; /* fail safe */
 251+ $replyto = ''; /* fail safe */
 252+
 253+ # regarding the use of oldid as an indicator for the last visited version, see also
 254+ # http://bugzilla.wikipeda.org/show_bug.cgi?id=603 "Delete + undelete cycle doesn't preserve old_id"
 255+ # However, in the case of a new page which is already watched, we have no previous version to compare
 256+ if ($article->mOldid) {
 257+ $body = str_replace('$NEWPAGE', wfMsg( 'email_notification_lastvisitedrevisiontext' ), $body);
 258+ $body = str_replace('$OLDID', $article->mOldid , $body);
 259+ } else {
 260+ $body = str_replace('$NEWPAGE', wfMsg( 'email_notification_newpagetext' ), $body );
 261+ $body = str_replace('$OLDID', '', $body); # clear $OLDID placeholder in the message template
 262+ }
 263+
 264+ $pagetitle = $article->mTitle;
 265+ if ($article->mNamespace != 0) { $pagetitle = $wgLang->getNsText($article->mNamespace).':'.$pagetitle; };
 266+ $subject = str_replace('$PAGETITLE_QP', wfQuotedPrintable(str_replace( '_', ' ', $pagetitle)), $subject);
 267+ $body = str_replace('%24PAGETITLE_RAWURL', wfUrlencode($pagetitle), $body);
 268+ $body = str_replace('$PAGETITLE_RAWURL', wfUrlencode($pagetitle), $body);
 269+ $body = str_replace('%24PAGETITLE', $pagetitle, $body); # needed for the {{localurl:$PAGETITLE}} in the messagetext, "$" appears here as "%24"
 270+ $body = str_replace('$PAGETITLE', $pagetitle, $body);
 271+ $body = str_replace('$PAGETIMESTAMP', $article->mTimestamp, $body); # this is the raw internal timestamp - can be useful, too
 272+ $body = str_replace('$PAGEEDITDATEUTC', $wgLang->timeanddate( $article->mTimestamp, false, false, false, true ), $body);
 273+ $body = str_replace('$PAGEMINOREDIT', $medit,$body);
 274+ $body = str_replace('$PAGESUMMARY', $summary, $body);
 275+
 276+ $pageeditor_qp = wfQuotedPrintable($pageeditorUser->getName());
 277+ # the user who edited is $pageeditorUser->getName():
 278+ # reveal the page editor's address as REPLY-TO address only if the user has not opted-out
 279+ if (($pageeditorUser->getEmail() != '')
 280+ && $pageeditorUser->getOption('enotifrevealaddr')
 281+ && $wgEmailNotificationRevealPageEditorAddress ) {
 282+ if ($wgEmailNotificationMailsSentFromPageEditor) {
 283+ $from = $pageeditorUser->getName().' <'.$pageeditorUser->getEmail().'>';
 284+ } else {
 285+ $from = 'WikiAdmin <'.$wgEmergencyContact.'>';
 286+ if ($wgEmailNotificationRevealPageEditorAddress) {
 287+ $replyto = $pageeditorUser->getName().' <'.$pageeditorUser->getEmail().'>';
 288+ }
 289+ }
 290+ $body = str_replace('$PAGEEDITORNAMEANDEMAILADDR', $pageeditorUser->getName().' <'.$pageeditorUser->getEmail().'>', $body);
 291+ } else {
 292+ $from = 'WikiAdmin <'.$wgEmergencyContact.'>';
 293+ $replyto = $wgNoReplyAddress;
 294+ $body = str_replace('$PAGEEDITORNAMEANDEMAILADDR', $replyto, $body);
 295+ }
 296+
 297+ if ($pageeditorUser->isIP($pageeditorUser->getName())) { #real anon (user:xxx.xxx.xxx.xxx)
 298+ $subject = str_replace('$PAGEEDITOR_QP', 'anonymous user ' . $pageeditorUser->getName(), $subject);
 299+ $body = str_replace('$PAGEEDITOR_RAWURL', wfUrlencode($pageeditorUser->getName()) . ' (anonymous user)' , $body);
 300+ $body = str_replace('%24PAGEEDITOR_RAWURL', wfUrlencode($pageeditorUser->getName()) . ' (anonymous user)' , $body);
 301+ $body = str_replace('%24PAGEEDITORE', $pageeditorUser->getName() . ' (anonymous user)' , $body);
 302+ $body = str_replace('$PAGEEDITORE', $pageeditorUser->getName() . ' (anonymous user)' , $body);
 303+ $body = str_replace('$PAGEEDITOR', 'anonymous user ' . $pageeditorUser->getName(), $body);
 304+ } else {
 305+ $subject = str_replace('$PAGEEDITOR_QP', $pageeditor_qp, $subject);
 306+ $body = str_replace('$PAGEEDITOR_RAWURL', wfUrlencode($pageeditorUser->getName()), $body);
 307+ $body = str_replace('%24PAGEEDITOR_RAWURL', wfUrlencode($pageeditorUser->getName()), $body);
 308+ $body = str_replace('%24PAGEEDITORE', str_replace( ' ', '_', $pageeditorUser->getName()), $body);
 309+ $body = str_replace('$PAGEEDITORE', str_replace( ' ', '_', $pageeditorUser->getName()), $body);
 310+ $body = str_replace('$PAGEEDITOR',$pageeditorUser->getName(), $body);
 311+ }
 312+
 313+ # now save this as the constant user-independent part of the message
 314+ $this->to = $to;
 315+ $this->from = $from;
 316+ $this->replyto = $replyto;
 317+ $this->subject = $subject;
 318+ $this->body = $body;
 319+ return $this;
 320+}
 321+
 322+
 323+function ComposeAndSendPersonalisedMail( $watchingUser, $mail, $article) {
 324+
 325+ /* returns true if the mail was sent successfully */
 326+ global $wgLang;
 327+
 328+ $to = $watchingUser->getName().' <'.$watchingUser->getEmail().'>';
 329+ $body = str_replace('$WATCHINGUSERNAME', $watchingUser->getName() , $mail->body);
 330+ $body = str_replace('$WATCHINGUSEREMAILADDR', $watchingUser->getEmail(), $body);
 331+
 332+ $timecorrection = $watchingUser->getOption('timecorrection');
 333+ if (!$timecorrection) { $timecorrection = '00:00'; } # fail safe - I prefer it. TomGries
 334+ # $PAGEEDITDATE is the time and date of the page change expressed in terms of individual local time of the notification recipient, i.e. watching user
 335+ $body = str_replace('$PAGEEDITDATE', $wgLang->timeanddate($article->mTimestamp, true, false, $timecorrection, true), $body);
 336+ return (userMailer($to, $mail->from, $mail->subject, $body, $mail->replyto) == '');
 337+}
 338+
 339+function Clear($currentUser, $currentPage, $currentNs) {
 340+ $dbw =& wfGetDB( DB_MASTER );
 341+ $success = $dbw->update( 'watchlist',
 342+ array( /* SET */
 343+ 'wl_notificationtimestamp' => 0
 344+ ), array( /* WHERE */
 345+ 'wl_title' => $currentPage,
 346+ 'wl_namespace' => $currentNs,
 347+ 'wl_user' => $currentUser
 348+ ), 'UserMailer::Clear'
 349+ );
 350+}
 351+
 352+function ClearAll($currentUser) {
 353+ global $_REQUEST;
 354+
 355+ if ($currentUser != 0) {
 356+
 357+ if( $_REQUEST['reset'] == 'all') {
 358+
 359+ $dbw =& wfGetDB( DB_MASTER );
 360+ $success = $dbw->update( 'watchlist',
 361+ array( /* SET */
 362+ 'wl_notificationtimestamp' => 0
 363+ ), array( /* WHERE */
 364+ 'wl_user' => $currentUser
 365+ ), 'UserMailer::ClearAll'
 366+ );
 367+
 368+ # we also need to clear here the "you have new message" notification for the own user_talk page
 369+ # This is cleared one page view later in Article::viewUpdates();
 370+ }
 371+ }
 372+}
 373+
 374+} # end of class EmailNotification
81375 ?>
Index: trunk/phase3/includes/SpecialWatchlist.php
@@ -17,6 +17,7 @@
1818 function wfSpecialWatchlist() {
1919 global $wgUser, $wgOut, $wgLang, $wgTitle, $wgMemc, $wgRequest;
2020 global $wgUseWatchlistCache, $wgWLCacheTimeout, $wgDBname;
 21+ global $wgEnotif, $wgShowUpdatedMarker, $wgRCShowWatchingUsers;
2122 $fname = "wfSpecialWatchlist";
2223
2324 $wgOut->setPagetitle( wfMsg( "watchlist" ) );
@@ -38,6 +39,12 @@
3940 $remove = $wgRequest->getVal( 'remove' );
4041 $id = $wgRequest->getVal( 'id' );
4142
 43+ $wgOut->addHTML( wfMsg( "email_notification_infotext" ) );
 44+
 45+ include_once( "UserMailer.php" );
 46+ $wgEnotif = new EmailNotification ();
 47+ $wgEnotif->ClearAll($wgUser->getID());
 48+
4249 if(($action == "submit") && isset($remove) && is_array($id)) {
4350 $wgOut->addHTML( wfMsg( "removingchecked" ) );
4451 foreach($id as $one) {
@@ -70,9 +77,14 @@
7178 extract( $dbr->tableNames( 'cur', 'watchlist', 'recentchanges' ) );
7279
7380 $sql = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_user=$uid";
74 - $res = $dbr->query( $sql );
 81+ $res = $dbr->query( $sql, $fname );
7582 $s = $dbr->fetchObject( $res );
 83+
 84+# Patch *** A1 *** (see A2 below)
 85+# adjust for page X, talk:page X, which are both stored separately, but treated together
 86+# $nitems = $s->n / 2;
7687 $nitems = $s->n;
 88+
7789 if($nitems == 0) {
7890 $wgOut->addHTML( wfMsg( "nowatchlist" ) );
7991 return;
@@ -99,7 +111,7 @@
100112 ( $cutoff = $dbr->timestamp( time() - intval( $days * 86400 ) ) )
101113 . "'";
102114 $sql = "SELECT COUNT(*) AS n FROM $cur WHERE cur_timestamp>'$cutoff'";
103 - $res = $dbr->query( $sql );
 115+ $res = $dbr->query( $sql, $fname );
104116 $s = $dbr->fetchObject( $res );
105117 $npages = $s->n;
106118
@@ -113,8 +125,13 @@
114126 $specialTitle->escapeLocalUrl( "action=submit" ) .
115127 "' method='post'>\n" .
116128 "<ul>\n" );
 129+
 130+# Patch A2
 131+# The following was proposed by KTurner 07.11.2004 to T.Gries
 132+# $sql = "SELECT distinct (wl_namespace & ~1),wl_title FROM $watchlist WHERE wl_user=$uid";
117133 $sql = "SELECT wl_namespace,wl_title FROM $watchlist WHERE wl_user=$uid";
118 - $res = $dbr->query( $sql );
 134+
 135+ $res = $dbr->query( $sql, $fname );
119136 $sk = $wgUser->getSkin();
120137 while( $s = $dbr->fetchObject( $res ) ) {
121138 $t = Title::makeTitle( $s->wl_namespace, $s->wl_title );
@@ -145,11 +162,17 @@
146163 if( $cutoff && ( $nitems*1.15 > $npages ) ) {
147164 $x = "cur_timestamp";
148165 $y = wfMsg( "watchmethod-recent" );
149 - $z = "wl_namespace=cur_namespace&65534";
 166+ # TG patch: here we do not consider pages and their talk pages equivalent - why should we ?
 167+ # The change results in talk-pages not automatically included in watchlists, when their parent page is included
 168+ # $z = "wl_namespace=cur_namespace & ~1";
 169+ $z = "wl_namespace=cur_namespace";
150170 } else {
151171 $x = "name_title_timestamp";
152172 $y = wfMsg( "watchmethod-list" );
153 - $z = "(wl_namespace=cur_namespace OR wl_namespace+1=cur_namespace)";
 173+ # TG patch: here we do not consider pages and their talk pages equivalent - why should we ?
 174+ # The change results in talk-pages not automatically included in watchlists, when their parent page is included
 175+ # $z = "(wl_namespace=cur_namespace OR wl_namespace+1=cur_namespace)";
 176+ $z = "wl_namespace=cur_namespace";
154177 }
155178
156179
@@ -160,7 +183,7 @@
161184 $use_index = $dbr->useIndexClause( $x );
162185 $sql = "SELECT
163186 cur_namespace,cur_title,cur_comment, cur_id,
164 - cur_user,cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new
 187+ cur_user,cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new,wl_notificationtimestamp
165188 FROM $watchlist,$cur $use_index
166189 WHERE wl_user=$uid
167190 AND $z
@@ -194,7 +217,23 @@
195218 # Make fake RC entry
196219 $rc = RecentChange::newFromCurRow( $obj );
197220 $rc->counter = $counter++;
198 - $s .= $list->recentChangesLine( $rc, true );
 221+
 222+ if ($wgShowUpdatedMarker && $wgUser->getOption( 'showupdated' )) {
 223+ $rc->notificationtimestamp = $obj->wl_notificationtimestamp;
 224+ } else {
 225+ $rc->notificationtimestamp = false;
 226+ }
 227+
 228+ if ($wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
 229+ $sql3 = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_title='" .wfStrencode($obj->cur_title). "' AND wl_namespace='{$obj->cur_namespace}'" ;
 230+ $res3 = $dbr->query( $sql3, DB_READ, $fname );
 231+ $x = $dbr->fetchObject( $res3 );
 232+ $rc->numberofWatchingusers = $x->n;
 233+ } else {
 234+ $rc->numberofWatchingusers = 0;
 235+ }
 236+
 237+ $s .= $list->recentChangesLine( $rc, true);
199238 }
200239 $s .= $list->endRecentChangesList();
201240
Index: trunk/phase3/includes/SpecialEmailuser.php
@@ -19,7 +19,7 @@
2020 }
2121
2222 if ( 0 == $wgUser->getID() ||
23 - ( false === strpos( $wgUser->getEmail(), "@" ) ) ) {
 23+ ( !$wgUser->isValidEmailAddr( $wgUser->getEmail() ) ) ) {
2424 $wgOut->errorpage( "mailnologin", "mailnologintext" );
2525 return;
2626 }
@@ -48,8 +48,9 @@
4949
5050 $address = $nu->getEmail();
5151
52 - if ( ( false === strpos( $address, "@" ) ) ||
53 - ( 1 == $nu->getOption( "disablemail" ) ) ) {
 52+ if ( ( !$nu->isValidEmailAddr( $address ) ) ||
 53+ ( 1 == $nu->getOption( "disablemail" ) ) ||
 54+ ( 0 == $nu->getEmailauthenticationtimestamp() ) ) {
5455 $wgOut->errorpage( "noemailtitle", "noemailtext" );
5556 return;
5657 }
Index: trunk/phase3/includes/ChangesList.php
@@ -79,6 +79,10 @@
8080 if ( $rcObj->watched ) $link = '<strong>'.$link.'</strong>' ;
8181 $r .= $link ;
8282
 83+ if ($rcObj->notificationtimestamp) {
 84+ $r .= wfMsg( 'updatedmarker' );
 85+ }
 86+
8387 # Diff
8488 $r .= ' (' ;
8589 $r .= $rcObj->difflink ;
@@ -97,6 +101,10 @@
98102 $r .= $wgContLang->emphasize( ' ('.$rc_comment.')' );
99103 }
100104
 105+ if ($rcObj->numberofWatchingusers > 0) {
 106+ $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rcObj->numberofWatchingusers));
 107+ }
 108+
101109 $r .= "<br />\n" ;
102110 return $r ;
103111 }
@@ -158,9 +166,9 @@
159167 else $r .= '&nbsp;' ;
160168 $r .= '&nbsp;' ; # Minor
161169 if ( $unpatrolled ) {
162 - $r .= "!";
 170+ $r .= '!';
163171 } else {
164 - $r .= "&nbsp;";
 172+ $r .= '&nbsp;';
165173 }
166174
167175 # Timestamp
@@ -172,6 +180,10 @@
173181 if ( $block[0]->watched ) $link = '<strong>'.$link.'</strong>' ;
174182 $r .= $link ;
175183
 184+ if ($block[0]->notificationtimestamp) {
 185+ $r .= wfMsg( 'updatedmarker' );
 186+ }
 187+
176188 $curIdEq = 'curid=' . $block[0]->mAttribs['rc_cur_id'];
177189 if ( $block[0]->mAttribs['rc_type'] != RC_LOG ) {
178190 # Changes
@@ -187,6 +199,10 @@
188200 }
189201
190202 $r .= $users ;
 203+
 204+ if ($block[0]->numberofWatchingusers > 0) {
 205+ $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($block[0]->numberofWatchingusers));
 206+ }
191207 $r .= "<br />\n" ;
192208
193209 # Sub-entries
@@ -210,9 +226,9 @@
211227 }
212228
213229 if ( $rcObj->unpatrolled ) {
214 - $r .= "!";
 230+ $r .= '!';
215231 } else {
216 - $r .= "&nbsp;";
 232+ $r .= '&nbsp;';
217233 }
218234
219235 $r .= '&nbsp;</tt>' ;
@@ -271,7 +287,7 @@
272288 * Either returns the line, or caches it for later use
273289 */
274290 function recentChangesLine( &$rc, $watched = false ) {
275 - global $wgUser ;
 291+ global $wgUser;
276292 $usenew = $wgUser->getOption( 'usenewrc' );
277293 if ( $usenew )
278294 $line = $this->recentChangesLineNew ( $rc, $watched ) ;
@@ -280,6 +296,7 @@
281297 return $line ;
282298 }
283299
 300+
284301 function recentChangesLineOld( &$rc, $watched = false ) {
285302 $fname = 'Skin::recentChangesLineOld';
286303 wfProfileIn( $fname );
@@ -350,9 +367,9 @@
351368 $s .= ') . . ';
352369
353370 # M, N and ! (minor, new and unpatrolled)
354 - if ( $rc_minor ) { $s .= ' <span class="minor">'.$message["minoreditletter"].'</span>'; }
355 - if ( $rc_type == RC_NEW ) { $s .= '<span class="newpage">'.$message["newpageletter"].'</span>'; }
356 - if ( $unpatrolled ) { $s .= ' <span class="unpatrolled">!</span>'; }
 371+ if ( $rc_minor ) { $s .= ' <span class="minoreditletter">'.$message["minoreditletter"].'</span>'; }
 372+ if ( $rc_type == RC_NEW ) { $s .= '<span class="newpageletter">'.$message["newpageletter"].'</span>'; }
 373+ if ( !$rc_patrolled ) { $s .= ' <span class="unpatrolled">!</span>'; }
357374
358375 # Article link
359376 # If it's a new article, there is no diff link, but if it hasn't been
@@ -365,6 +382,11 @@
366383 if ( $watched ) {
367384 $articleLink = '<strong>'.$articleLink.'</strong>';
368385 }
 386+
 387+ if ($rc->notificationtimestamp) {
 388+ $articleLink .= wfMsg( 'updatedmarker' );
 389+ }
 390+
369391 $s .= ' '.$articleLink;
370392 wfProfileOut("$fname-page");
371393 }
@@ -412,6 +434,11 @@
413435 $rc_comment = $this->skin->formatComment($rc_comment,$rc->getTitle());
414436 $s .= $wgContLang->emphasize(' (' . $rc_comment . ')');
415437 }
 438+
 439+ if ($rc->numberofWatchingusers > 0) {
 440+ $s .= ' ' . wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rc->numberofWatchingusers));
 441+ }
 442+
416443 $s .= "</li>\n";
417444
418445 wfProfileOut( "$fname-rest" );
@@ -477,6 +504,8 @@
478505 $rc->watched = $watched ;
479506 $rc->link = $clink ;
480507 $rc->timestamp = $time;
 508+ $rc->notificationtimestamp = $baseRC->notificationtimestamp;
 509+ $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
481510
482511 # Make "cur" and "diff" links
483512 $titleObj = $rc->getTitle();
Index: trunk/phase3/includes/SkinTemplate.php
@@ -148,6 +148,7 @@
149149 global $wgMimeType, $wgOutputEncoding, $wgUseDatabaseMessages, $wgRequest;
150150 global $wgDisableCounters, $wgLogo, $action, $wgFeedClasses, $wgSiteNotice;
151151 global $wgMaxCredits, $wgShowCreditsIfMax;
 152+ global $wgPageShowWatchingUsers;
152153
153154 $fname = 'SkinTemplate::outputPage';
154155 wfProfileIn( $fname );
@@ -282,6 +283,25 @@
283284 $tpl->set('viewcount', false);
284285 }
285286 }
 287+
 288+ if ($wgPageShowWatchingUsers) {
 289+ $dbr =& wfGetDB( DB_SLAVE );
 290+ extract( $dbr->tableNames( 'watchlist' ) );
 291+ $sql = "SELECT COUNT(*) AS n FROM $watchlist
 292+ WHERE wl_title='" . $dbr->strencode($wgTitle->getDBKey()) .
 293+ "' AND wl_namespace=" . $wgTitle->getNamespace() ;
 294+ $res = $dbr->query( $sql, 'SkinPHPTal::outputPage');
 295+ $x = $dbr->fetchObject( $res );
 296+ $numberofwatchingusers = $x->n;
 297+ if ($numberofwatchingusers > 0) {
 298+ $tpl->set('numberofwatchingusers', wfMsg('number_of_watching_users_pageview', $numberofwatchingusers));
 299+ } else {
 300+ $tpl->set('numberofwatchingusers', false);
 301+ };
 302+ } else {
 303+ $tpl->set('numberofwatchingusers', false);
 304+ }
 305+
286306 $tpl->set('lastmod', $this->lastModified());
287307 $tpl->set('copyright',$this->getCopyright());
288308
@@ -316,6 +336,7 @@
317337 $tpl->setRef( 'debug', $out->mDebugtext );
318338 $tpl->set( 'reporttime', $out->reportTime() );
319339 $tpl->set( 'sitenotice', $wgSiteNotice );
 340+ $tpl->set( 'tagline', wfMsg('tagline') );
320341
321342 $printfooter = "<div class=\"printfooter\">\n" . $this->printSource() . "</div>\n";
322343 $out->mBodytext .= $printfooter ;
Index: trunk/phase3/includes/SpecialPreferences.php
@@ -166,6 +166,8 @@
167167 */
168168 function savePreferences() {
169169 global $wgUser, $wgLang, $wgOut;
 170+ global $wgEnableUserEmail, $wgEnableEmail;
 171+ global $wgEmailAuthentication;
170172
171173 if ( '' != $this->mNewpass ) {
172174 if ( $this->mNewpass != $this->mRetypePass ) {
@@ -179,10 +181,6 @@
180182 }
181183 $wgUser->setPassword( $this->mNewpass );
182184 }
183 - global $wgEnableEmail;
184 - if( $wgEnableEmail ) {
185 - $wgUser->setEmail( $this->mUserEmail );
186 - }
187185 $wgUser->setRealName( $this->mRealName );
188186 $wgUser->setOption( 'language', $this->mUserLanguage );
189187 $wgUser->setOption( 'variant', $this->mUserVariant );
@@ -206,7 +204,6 @@
207205 $wgUser->setOption( "searchNs{$i}", $value );
208206 }
209207
210 - global $wgEnableUserEmail;
211208 if( $wgEnableEmail && $wgEnableUserEmail ) {
212209 $wgUser->setOption( 'disablemail', $this->mEmailFlag );
213210 }
@@ -216,8 +213,38 @@
217214 $wgUser->setOption( $tname, $tvalue );
218215 }
219216 $wgUser->setCookies();
 217+ $wgUser->saveSettings();
220218
 219+ if( $wgEnableEmail ) {
 220+ $newadr = strtolower( $this->mUserEmail );
 221+ $oldadr = strtolower($wgUser->getEmail());
 222+ if (($newadr <> '') && ($newadr <> $oldadr)) { # the user has supplied a new email address on the login page
 223+ # prepare for authentication and mail a temporary password to newadr
 224+ require_once( 'SpecialUserlogin.php' );
 225+ if ( !$wgUser->isValidEmailAddr( $newadr ) ) {
 226+ $this->mainPrefsForm( wfMsg( 'invalidemailaddress' ) );
 227+ return;
 228+ }
 229+ $wgUser->mEmail = $newadr; # new behaviour: set this new emailaddr from login-page into user database record
 230+ $wgUser->mEmailAuthenticationtimestamp = 0; # but flag as "dirty" = unauthenticated
221231 $wgUser->saveSettings();
 232+ if ($wgEmailAuthentication) {
 233+ # mail a temporary password to the dirty address
 234+ # on "save options", this user will be logged-out automatically
 235+ $error = LoginForm::mailPasswordInternal( $wgUser, true, $dummy );
 236+ if ($error === '') {
 237+ return LoginForm::mainLoginForm( wfMsg( 'passwordsentforemailauthentication', $wgUser->getName() ) );
 238+ } else {
 239+ return LoginForm::mainLoginForm( wfMsg( 'mailerror', $error ) );
 240+ }
 241+ # if user returns, that new email address gets authenticated in checkpassword()
 242+ }
 243+ } else {
 244+ $wgUser->setEmail( strtolower($this->mUserEmail) );
 245+ $wgUser->setCookies();
 246+ $wgUser->saveSettings();
 247+ }
 248+ }
222249
223250 $wgOut->setParserOptions( ParserOptions::newFromUser( $wgUser ) );
224251 $po = ParserOptions::newFromUser( $wgUser );
@@ -232,6 +259,7 @@
233260
234261 $this->mOldpass = $this->mNewpass = $this->mRetypePass = '';
235262 $this->mUserEmail = $wgUser->getEmail();
 263+ $this->mUserEmailAuthenticationtimestamp = $wgUser->getEmailAuthenticationtimestamp();
236264 $this->mRealName = ($wgAllowRealName) ? $wgUser->getRealName() : '';
237265 $this->mUserLanguage = $wgUser->getOption( 'language' );
238266 if( empty( $this->mUserLanguage ) ) {
@@ -306,7 +334,7 @@
307335 }
308336
309337
310 - function getToggle( $tname ) {
 338+ function getToggle( $tname, $trailer = false) {
311339 global $wgUser, $wgLang;
312340
313341 $this->mUsedToggles[$tname] = true;
@@ -317,8 +345,9 @@
318346 } else {
319347 $checked = '';
320348 }
 349+ $trailer =($trailer) ? $trailer : '';
321350 return "<div><input type='checkbox' value=\"1\" "
322 - . "id=\"$tname\" name=\"wpOp$tname\"$checked /><label for=\"$tname\">$ttext</label></div>\n";
 351+ . "id=\"$tname\" name=\"wpOp$tname\"$checked /><label for=\"$tname\">$ttext</label>$trailer</div>\n";
323352 }
324353
325354 /**
@@ -328,7 +357,11 @@
329358 global $wgUser, $wgOut, $wgLang, $wgContLang, $wgUseDynamicDates, $wgValidSkinNames;
330359 global $wgAllowRealName, $wgImageLimits;
331360 global $wgLanguageNames, $wgDisableLangConversion;
 361+ global $wgEmailNotificationForWatchlistPages, $wgEmailNotificationForUserTalkPages,$wgEmailNotificationForMinorEdits;
 362+ global $wgRCUseModStyle, $wgRCShowWatchingUsers, $wgEmailNotificationRevealPageEditorAddress;
 363+ global $wgEnableEmail, $wgEnableUserEmail, $wgEmailAuthentication;
332364 global $wgContLanguageCode;
 365+
333366 $wgOut->setPageTitle( wfMsg( 'preferences' ) );
334367 $wgOut->setArticleRelated( false );
335368 $wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -396,21 +429,50 @@
397430 if ( $this->mEmailFlag ) { $emfc = 'checked="checked"'; }
398431 else { $emfc = ''; }
399432
 433+ if ($wgEmailAuthentication && ($this->mUserEmail != '') ) {
 434+ if ($wgUser->getEmailAuthenticationtimestamp() != 0) {
 435+ $emailauthenticated = wfMsg('emailauthenticated',$wgLang->timeanddate($wgUser->getEmailAuthenticationtimestamp(), true ) ).'<br>';
 436+ $disabled = '';
 437+ } else {
 438+ $emailauthenticated = wfMsg('emailnotauthenticated').'<br>';
 439+ $disabled = ' '.wfMsg('disableduntilauthent');
 440+ }
 441+ } else {
 442+ $emailauthenticated = '';
 443+ }
 444+
 445+ if ($this->mUserEmail == '') {
 446+ $disabled = ' '.wfMsg('disablednoemail');
 447+ }
 448+
400449 $ps = $this->namespacesCheckboxes();
401450
 451+ $enotifwatchlistpages = ($wgEmailNotificationForWatchlistPages) ? $this->getToggle( 'enotifwatchlistpages', $disabled) : '';
 452+ $enotifusertalkpages = ($wgEmailNotificationForUserTalkPages) ? $this->getToggle( 'enotifusertalkpages', $disabled) : '';
 453+ $enotifminoredits = ($wgEmailNotificationForMinorEdits) ? $this->getToggle( 'enotifminoredits', $disabled) : '';
 454+ $enotifrevealaddr = ($wgEmailNotificationRevealPageEditorAddress) ? $this->getToggle( 'enotifrevealaddr', $disabled) : '';
 455+ $prefs_help_email_enotif = ( $wgEmailNotificationForWatchlistPages || $wgEmailNotificationForUserTalkPages) ? ' ' . wfMsg('prefs-help-email-enotif') : '';
 456+ $prefs_help_realname = '';
 457+
402458 $wgOut->addHTML( "<fieldset>
403459 <legend>".wfMsg('prefs-personal')."</legend>");
 460+
404461 if ($wgAllowRealName) {
405462 $wgOut->addHTML("<div><label>$yrn: <input type='text' name=\"wpRealName\" value=\"{$this->mRealName}\" size='20' /></label></div>");
 463+ $prefs_help_realname = wfMsg('prefs-help-realname').'<br>';
406464 }
407465
408 - global $wgEnableEmail, $wgEnableUserEmail;
409466 if( $wgEnableEmail ) {
410467 $wgOut->addHTML("
411468 <div><label>$yem: <input type='text' name=\"wpUserEmail\" value=\"{$this->mUserEmail}\" size='20' /></label></div>" );
412469 if( $wgEnableUserEmail ) {
413 - $wgOut->addHTML("
414 - <div><label><input type='checkbox' $emfc value=\"1\" name=\"wpEmailFlag\" />$emf</label></div>" );
 470+ $wgOut->addHTML(
 471+ $emailauthenticated.
 472+ $enotifrevealaddr.
 473+ $enotifwatchlistpages.
 474+ $enotifusertalkpages.
 475+ $enotifminoredits.
 476+ "<div><label><input type='checkbox' $emfc value=\"1\" name=\"wpEmailFlag\" />$emf.$disabled</label></div>" );
415477 }
416478 }
417479
@@ -465,7 +527,7 @@
466528 <div><label>$rpw: <input type='password' name=\"wpRetypePass\" value=\"{$this->mRetypePass}\" size='20' /></label></div>
467529 " . $this->getToggle( "rememberpassword" ) . "
468530 </fieldset>
469 - <div class='prefsectiontip'>".wfMsg('prefs-help-userdata')."</div>\n</fieldset>\n" );
 531+ <div class='prefsectiontip'>".$prefs_help_realname.wfMsg('prefs-help-email').$prefs_help_email_enotif."</div>\n</fieldset>\n" );
470532
471533
472534 # Quickbar setting
@@ -559,12 +621,15 @@
560622 <div class='prefsectiontip'>* {$tzt}</div>
561623 </fieldset>\n\n" );
562624
 625+ $shownumberswatching = ($wgRCShowWatchingUsers) ? $this->getToggle('shownumberswatching') : '';
 626+ $rcusemodstyle = ($wgRCUseModStyle) ? $this->getToggle('rcusemodstyle') : '';
 627+
563628 $wgOut->addHTML( "
564629 <fieldset><legend>".wfMsg('prefs-rc')."</legend>
565 - <div><label>$rcc: <input type='text' name=\"wpRecent\" value=\"$this->mRecent\" size='6' /></label></div>
566 - " . $this->getToggle( "hideminor" ) .
567 - $this->getToggle( "usenewrc" ) . "
568 - <div><label>$stt: <input type='text' name=\"wpStubs\" value=\"$this->mStubs\" size='6' /></label></div>
 630+ <div><label>$rcc: <input type='text' name=\"wpRecent\" value=\"$this->mRecent\" size='6' /></label></div>" .
 631+ $this->getToggle( "hideminor" ) . $shownumberswatching .
 632+ $this->getToggle( "usenewrc" ) . $rcusemodstyle . $this->getToggle('showupdated', wfMsg('updatedmarker')) .
 633+ "<div><label>$stt: <input type='text' name=\"wpStubs\" value=\"$this->mStubs\" size='6' /></label></div>
569634 <div><label>".wfMsg('imagemaxsize')."<select name=\"wpImageSize\">");
570635
571636 $imageLimitOptions='';
Index: trunk/phase3/includes/DefaultSettings.php
@@ -165,6 +165,14 @@
166166 $wgPasswordSender = 'Wikipedia Mail <apache@' . $wgServerName . '>';
167167
168168 /**
 169+ * dummy address which should be accepted during mail send action
 170+ * It might be necessay to adapt the address or to set it equal
 171+ * to the $wgEmergencyContact address
 172+ */
 173+#$wgNoReplyAddress = $wgEmergencyContact;
 174+$wgNoReplyAddress = 'reply@not.possible';
 175+
 176+/**
169177 * Set to true to enable the e-mail basic features:
170178 * Password reminders, etc. If sending e-mail on your
171179 * server doesn't work, you might want to disable this.
@@ -495,10 +503,64 @@
496504 # Requires zlib support enabled in PHP.
497505 $wgUseGzip = false;
498506
 507+/* T. Gries Aug.-Nov.2004
499508
 509+ THESE ARE MY SUGGESTED FIRST TEST global admin options FOR ENOTIF.
 510+ Attention: the defaults might differ from standard media wiki distributions.
 511+
 512+ However, I suggest to start with these which allow to evaluate almost all new features quickly.
 513+ user preferences default options SEE AS USUAL /languages/Language.php
 514+*/
 515+
 516+########################### CAUTION ################# ATTENTION ###############################################
 517+#
 518+# T. Gries Aug.-Dec. 2004
 519+#
 520+# SECTION FOR DEVELOPERS and SERVER ADMINS
 521+#
 522+# HERE COME TWO OPTIONS, WHICH ALLOW ACOUSTIC SIGNALS on the server beeper WHEN AN EMAIL IS ACTUALLY SENT OUT.
 523+# YOU MIGHT WISH TO ENABLE THESE, BUT BE CAREFUL, AS A system() CALL IS PERFORMED using the shown parameters.
 524+# I FIND IT VERY USEFUL, but this is a very personal comment. T. Gries
 525+#
 526+# The system speaker beeps when the wiki actually sends out a notification mail (safe default = disabled).
 527+#
 528+# The following string is passed as parameter to a system() call in UserMailer.php
 529+# The system() calls call - in the shown example - the beep 1.2.2 program (LINUX) with frequency f [Hz] and length l [msec]
 530+#
 531+# Beep 1.2.2 can be found on http://freshmeat.net/projects/beep/ and I like it.
 532+#
 533+########################### SAFE #################### SAFE #####################################################
 534+$wgEmailNotificationSystembeep = ''; # empty string disables this feature; this appears to be safe
 535+######ENTER#AT#YOUR#OWN#RISK###CAUTION###### ATTENTION ###################################################
 536+#$wgEmailNotificationSystembeep = '/usr/bin/beep -f 4000 -l 20 &'; # a system() call with exactly this string as parameter is executed when such a mail is sent
 537+########################### CAUTION ################# ATTENTION ###############################################
 538+
 539+# For email notification on page changes T.Gries/M.Arndt 01.11.2004
 540+$wgPasswordSender = $wgEmergencyContact;
 541+$wgEmailNotificationMailsSentFromPageEditor = false; # false: Enotif mails appear to come from $wgEmergencyContact
 542+# # true: from PageEditor if s/he opted-in
 543+
 544+# If set to true, users get a corresponding option in their preferences and can choose to enable or disable at their discretion
 545+# If set to false, the corresponding input form on the user preference page is suppressed
 546+# It call this to be a "user-preferences-option (UPO)"
 547+$wgEmailAuthentication = true; # UPO (if this is set to false, texts referring to authentication are suppressed)
 548+$wgEmailNotificationForWatchlistPages = true; # UPO
 549+$wgEmailNotificationForUserTalkPages = true; # UPO
 550+$wgEmailNotificationRevealPageEditorAddress = true; # UPO; reply-to address may be filled with page editor's address (if user allowed this in the preferences)
 551+$wgEmailNotificationForMinorEdits = true; # UPO; false: "minor edits" on pages do not trigger notification mails.
 552+# # Attention: _every_ change on a user_talk page trigger a notification mail (if the user is not yet notified)
 553+
 554+# Show recent changes in UseMod style, i.e. only the recent change of a page is listed
 555+$wgRCUseModStyle = true; # UPO
 556+# Show watching users in recent changes, watchlist and page history views
 557+$wgRCShowWatchingUsers = true; # UPO
 558+# Show watching users in Page views
 559+$wgPageShowWatchingUsers = true;
 560+# Show "Updated (since my last visit)" marker in RC view, watchlist and history view for watched pages with new changes
 561+$wgShowUpdatedMarker = true; # UPO
 562+
500563 $wgCookieExpiration = 2592000;
501564
502 -
503565 # Squid-related settings
504566 #
505567
@@ -743,10 +805,10 @@
744806 $wgDefaultUserOptions = array();
745807
746808 # Whether or not to allow real name fields. Defaults to true.
 809+# If set to false, the corresponding input forms on the log-in page and on the user preference page is suppressed.
747810 $wgAllowRealName = true;
748811
749812 # Use XML parser?
750 -
751813 $wgUseXMLparser = false ;
752814
753815 # Extensions
Index: trunk/phase3/includes/PageHistory.php
@@ -22,7 +22,7 @@
2323 # This shares a lot of issues (and code) with Recent Changes
2424
2525 function history() {
26 - global $wgUser, $wgOut, $wgLang;
 26+ global $wgUser, $wgOut, $wgLang, $wgShowUpdatedMarker;
2727
2828 # If page hasn't changed, client can cache this
2929
@@ -59,8 +59,17 @@
6060
6161 $namespace = $this->mTitle->getNamespace();
6262 $title = $this->mTitle->getText();
 63+ $uid = $wgUser->getID();
 64+ $db =& wfGetDB( DB_SLAVE );
 65+ if ($wgShowUpdatedMarker && $wgUser->getOption( 'showupdated' )) {
 66+ $dbr =& wfGetDB( DB_MASTER );
 67+ $row = $dbr->selectRow( 'watchlist',
 68+ array( 'wl_notificationtimestamp' ),
 69+ array( 'wl_namespace' => $namespace, 'wl_title' => $this->mTitle->getDBkey(), 'wl_user' => $wgUser->getID() ),
 70+ $fname );
 71+ $notificationtimestamp = $row->wl_notificationtimestamp;
 72+ } else $notificationtimestamp = false ;
6373
64 - $db =& wfGetDB( DB_SLAVE );
6574 $use_index = $db->useIndexClause( 'name_title_timestamp' );
6675 $oldtable = $db->tableName( 'old' );
6776
@@ -103,7 +112,8 @@
104113 $this->mArticle->getUserText(), $namespace,
105114 $title, 0, $this->mArticle->getComment(),
106115 ( $this->mArticle->getMinorEdit() > 0 ),
107 - $counter++
 116+ $counter++,
 117+ $notificationtimestamp
108118 );
109119 }
110120 while ( $line = $db->fetchObject( $res ) ) {
@@ -112,7 +122,8 @@
113123 $line->old_user_text, $namespace,
114124 $title, $line->old_id,
115125 $line->old_comment, ( $line->old_minor_edit > 0 ),
116 - $counter++
 126+ $counter++,
 127+ $notificationtimestamp
117128 );
118129 }
119130 $s .= $this->endHistoryList( !$atend );
@@ -143,7 +154,7 @@
144155 return $s;
145156 }
146157
147 - function historyLine( $ts, $u, $ut, $ns, $ttl, $oid, $c, $isminor, $counter = '' ) {
 158+ function historyLine( $ts, $u, $ut, $ns, $ttl, $oid, $c, $isminor, $counter = '', $notificationtimestamp = false ) {
148159 global $wgLang, $wgContLang;
149160
150161 static $message;
@@ -206,6 +217,9 @@
207218 $c = $this->mSkin->formatcomment( $c, $this->mTitle );
208219 $s .= " <em>($c)</em>";
209220 }
 221+ if ($notificationtimestamp && ($ts >= $notificationtimestamp)) {
 222+ $s .= wfMsg( 'updatedmarker' );
 223+ }
210224 $s .= '</li>';
211225
212226 $this->lastline = $s;
Index: trunk/phase3/includes/Skin.php
@@ -797,8 +797,8 @@
798798 }
799799
800800 function pageStats() {
801 - global $wgOut, $wgLang, $wgArticle, $wgRequest;
802 - global $wgDisableCounters, $wgMaxCredits, $wgShowCreditsIfMax;
 801+ global $wgOut, $wgLang, $wgArticle, $wgRequest, $wgUser;
 802+ global $wgDisableCounters, $wgMaxCredits, $wgShowCreditsIfMax, $wgTitle, $wgPageShowWatchingUsers;
803803
804804 extract( $wgRequest->getValues( 'oldid', 'diff' ) );
805805 if ( ! $wgOut->isArticle() ) { return ''; }
@@ -820,6 +820,17 @@
821821 $s .= $this->lastModified();
822822 }
823823
 824+ if ($wgPageShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
 825+ $dbr =& wfGetDB( DB_SLAVE );
 826+ extract( $dbr->tableNames( 'watchlist' ) );
 827+ $sql = "SELECT COUNT(*) AS n FROM $watchlist
 828+ WHERE wl_title='" . $dbr->strencode($wgTitle->getDBKey()) .
 829+ "' AND wl_namespace=" . $wgTitle->getNamespace() ;
 830+ $res = $dbr->query( $sql, 'Skin::pageStats');
 831+ $x = $dbr->fetchObject( $res );
 832+ $s .= ' ' . wfMsg('number_of_watching_users_pageview', $x->n );
 833+ }
 834+
824835 return $s . ' ' . $this->getCopyright();
825836 }
826837
@@ -1101,7 +1112,10 @@
11021113 return $wgEnableEmail &&
11031114 $wgEnableUserEmail &&
11041115 0 != $wgUser->getID() && # show only to signed in users
1105 - 0 != $id; # can only email non-anons
 1116+ 0 != $id; # we can only email to non-anons ..
 1117+# '' != $id->getEmail() && # who must have an email address stored ..
 1118+# 0 != $id->getEmailauthenticationtimestamp() && # .. which is authenticated
 1119+# 1 != $wgUser->getOption('disablemail'); # and not disabled
11061120 }
11071121
11081122 function emailUserLink() {
@@ -1928,7 +1942,6 @@
19291943 return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
19301944 }
19311945
1932 -
19331946 /**
19341947 * This function is called by all recent changes variants, by the page history,
19351948 * and by the user contributions list. It is responsible for formatting edit
Index: trunk/phase3/includes/templates/Userlogin.php
@@ -94,7 +94,11 @@
9595 <p>
9696 <?php $this->msgHtml( 'emailforlost' ) ?><br />
9797 <input tabindex='10' type='submit' name="wpMailmypassword"
98 - value="<?php $this->msg('mailmypassword') ?>" />
 98+ value="<?php if ( $this->data['useemailauthent'] ) {
 99+ $this->msg('mailmypasswordauthent') ?>" />
 100+ <?php } else {
 101+ $this->msg('mailmypassword') ?>" />
 102+ <?php } ?>
99103 </p>
100104 </td>
101105 </tr>
Index: trunk/phase3/RELEASE-NOTES
@@ -3,7 +3,112 @@
44 Security reminder: MediaWiki does not require PHP's register_globals
55 setting since version 1.2.0. If you have it on, turn it *off* if you can.
66
 7+== Version Enotif+Eauthent EN+EA v2.00/CVS, 14.12.2004 ==
 8+written by Thomas Gries, Berlin and Markus Arndt, Munich
79
 10+Executive summary for the impatient reader:
 11+
 12+Enotif adds e-mail notification to MediaWiki and sends e-mails
 13+to watching users when a watch-listed page or user_talk page is changed
 14+http://bugzilla.wikipedia.org/show_bug.cgi?id=454
 15+Visit the complete documentation on http://meta.wikipedia.org/Enotif
 16+
 17+Eauthent is a mechanism to use a temporary one-time password cycle
 18+to check whether the email address a user has entered is a valid one.
 19+http://bugzilla.wikipedia.org/show_bug.cgi?id=866
 20+Visit the complete documentation on http://meta.wikipedia.org/Eauthent
 21+
 22+The current patch has only been checked for (see DefaultSettings.php):
 23+
 24+- php mail()
 25+ ( = not using PEAR:Mail() module --- I do not know anyone who uses that)
 26+ $wgSMTP = false;
 27+- MySQL database
 28+ ( = not using PostgreSQL --- I do not know anyone who uses that)
 29+ $wgDBtype = "mysql";
 30+ $wgSearchType = "MyISAM";
 31+
 32+- STILL TODO:
 33+ NEW (newpageletter) and CORR (minoreditletter) markers needs
 34+ corresponding "spacers"
 35+
 36+- table user_newtalk dropped; changes on usertalk pages and their
 37+ notifications are now fully handled via existing table watchlist
 38+ The user interface and behavious is unchanged to previous version.
 39+- updaters.inc for compatibility with older mediawiki tables:
 40+ the conversion script converts existing user_newtalk entries
 41+ watchlist table entries
 42+ (user_newtalk) id ==> (watchlist) NS_USER_TALK:namefromId(id) timestamp=1
 43+- minor bug fixes:
 44+ updated marker now correctly shown on watchlist page
 45+ watching users number display with enhanced RC view + RCUseModStyle
 46+- wfUrlencode() instead of rawurlencode() in enotif mails
 47+- duplicate enotif code moved from UserTalkUpdate.php and
 48+ merged into UserMailer.php and using usermailer() solely
 49+- fixed an enotif mail text error for user names with spaces
 50+- fixed missing part for suppressing watching user number
 51+
 52+v1.36:
 53+magic watchlist shows and counts now only the content page, notwithstanding
 54+the content and talk page are stored separately in watchlist.
 55+* bug fixed: rawurlencode for pagetitles in enotif mails
 56+* bug fixed: link to userpages of anonymous user are correct now
 57+
 58+* Enotif v1.34
 59+ bugs fixed regarding missing $oldid parameter
 60+
 61+* v1.33
 62+* Details:
 63+ Implements almost all enotif options as user preferences.
 64+ These are only shown on the user preference page, if they are globally
 65+ enabled by the corresponding admin option in DefaultSettings.php.
 66+ Added admin feature to let enotifs appear to come from the page editor.
 67+ This facilitates automatic mail sorting and anti-spam filtering; feature was
 68+ originally proposed by Nick Triantos, thank you !
 69+ Page editor's email address is however only shown, if this user enabled
 70+ the option "reveal my email address" in user preferences. Otherwise,
 71+ the enotifs appear to come from WikiAdmin as usual (tricky to program,
 72+ but simply trust the algorithm. or look into UserMailer.php and
 73+ UserTalkPage.php).
 74+
 75+* Changes from previous enotif versions
 76+* v1.31 is an improved version with many security and also cosmetic changes
 77+ applied after two first reviews by Brion Vibber. v1.31 is basically the same as
 78+ the older Enotif v1.30 and v1.22 versions.
 79+
 80+ Added UseMod style for recent changes view so that only the most recent
 81+ change of any page is listed. The (diff) and (hist) still allow to retrieve the
 82+ older versions at users' discretion, but the RC view is much cleaner for
 83+ trusted environments such as medium-size companies or family wikis.
 84+
 85+* Enotif v1.30 redesign after review by Brion Vibber 25.10.2003
 86+
 87+* v1.22 "updated (since my last visit)" also shown for users without stored
 88+ email address in preferences, so that they can see, what watched pages
 89+ have changed.
 90+* show "updated (since my last visit)" markers in RC, history and watchlist
 91+* Systemvariables to suppress updated marker in all views
 92+* show number of watching users in RC and on bottom of articles in
 93+ classic skin and in monobook skin
 94+* Systemvariables in DefaultSettings.php to enable or disable features
 95+* v1.21 now suppresses displaying the marker "updated (since my last visit)"
 96+ in recent changes view for the older (already visited) versions of watched
 97+ pages - i.e. page versions before the enotif was sent do not bear that
 98+ marker any longer.
 99+* enotif mails come with a link to the diff view between current and last
 100+ visited version of the watching user.
 101+*database structure is changed automatically when installing via the
 102+ recommended way (starting index.php and re-using the old database name).
 103+ run php /maintenance/update.php
 104+ OR see /maintenance/archives/patch-email-notification.sql and apply the
 105+ command
 106+ ALTER TABLE watchlist
 107+ ADD (wl_notificationtimestamp varchar(14) binary NOT NULL default '0');
 108+ manually to your database, which does not harm the non notification versions
 109+
 110+*adding a page x to the watchlist does automatically add a watch
 111+ for the accompanying talk_page talk:x and vice versa;
 112+
8113 == MediaWiki 1.4 BETA ==
9114
10115 [Not everything is 100% working in beta yet, the installer needs fixes still.]
Index: trunk/phase3/languages/LanguageDe.php
@@ -684,10 +684,13 @@
685685 "largefile" => "Bitte keine Bilder über 100 KByte hochladen.",
686686 'emptyfile' => "Die hochgeladene Datei ist leer. Der Grund kann ein Tippfehler im Dateinamen sein. Bitte kontrollieren Sie, ob Sie die Datei wirklich hochladen wollen.",
687687 "successfulupload" => "Erfolgreich hochgeladen",
688 -"fileuploaded" => "Die Datei \"$1\" wurde erfolgreich hochgeladen. Bitte
689 -verwenden Sie diesen ($2) Link zur Beschreibungsseite und füllen Sie die
690 -Informationen über die Datei aus, insbesondere seine Herkunft, von wem und wann es
691 -gemacht wurde und besondere Angaben zum Copyright, falls notwendig.",
 688+"fileuploaded" => "Die Datei \"$1\" wurde erfolgreich hochgeladen.
 689+Bitte verwenden Sie diesen ($2) Link zur Beschreibungsseite und füllen Sie die Informationen über die Datei
 690+ aus, insbesondere seine Herkunft, von wem und wann es
 691+ gemacht wurde und besondere Angaben zum Copyright, falls notwendig.
 692+ Falls es sich um ein Bild handelte, so können Sie mit
 693+ <tt><nowiki>[[Image:$1|thumb|Description]]</nowiki></tt> ein Vorschaubild
 694+ auf der Seite erzeugen lassen.",
692695 "uploadwarning" => "Warnung",
693696 "savefile" => "Datei speichern",
694697 "uploadedimage" => "\"$1\" hochgeladen",
Index: trunk/phase3/languages/Language.php
@@ -51,14 +51,35 @@
5252 }
5353
5454 /* private */ $wgDefaultUserOptionsEn = array(
55 - 'quickbar' => 1, 'underline' => 1, 'hover' => 1,
56 - 'cols' => 80, 'rows' => 25, 'searchlimit' => 20,
57 - 'contextlines' => 5, 'contextchars' => 50,
58 - 'skin' => $wgDefaultSkin, 'math' => 1, 'rcdays' => 7, 'rclimit' => 50,
59 - 'highlightbroken' => 1, 'stubthreshold' => 0,
60 - 'previewontop' => 1, 'editsection'=>1,'editsectiononrightclick'=>0, 'showtoc'=>1,
61 - 'showtoolbar' =>1,
62 - 'date' => 0, 'imagesize' => 2
 55+ 'quickbar' => 1,
 56+ 'underline' => 1,
 57+ 'hover' => 1,
 58+ 'cols' => 80,
 59+ 'rows' => 25,
 60+ 'searchlimit' => 20,
 61+ 'contextlines' => 5,
 62+ 'contextchars' => 50,
 63+ 'skin' => $wgDefaultSkin,
 64+ 'math' => 1,
 65+ 'rcdays' => 7,
 66+ 'rclimit' => 50,
 67+ 'highlightbroken' => 1,
 68+ 'stubthreshold' => 0,
 69+ 'previewontop' => 1,
 70+ 'editsection' => 1,
 71+ 'editsectiononrightclick'=> 0,
 72+ 'showtoc' => 1,
 73+ 'showtoolbar' => 1,
 74+ 'date' => 0,
 75+ 'imagesize' => 2,
 76+ 'rememberpassword' => 0,
 77+ 'enotifwatchlistpages' => 1,
 78+ 'enotifusertalkpages' => 1,
 79+ 'enotifminoredits' => 0,
 80+ 'enotifrevealaddr' => 0,
 81+ 'shownumberswatching' => 1,
 82+ 'rcusemodstyle' => 1,
 83+ 'showupdated' => 1
6384 );
6485
6586 /* private */ $wgQuickbarSettingsEn = array(
@@ -127,6 +148,13 @@
128149 'previewontop',
129150 'previewonfirst',
130151 'nocache',
 152+ 'enotifwatchlistpages',
 153+ 'enotifusertalkpages',
 154+ 'enotifminoredits',
 155+ 'enotifrevealaddr',
 156+ 'shownumberswatching',
 157+ 'rcusemodstyle',
 158+ 'showupdated',
131159 );
132160
133161 /* private */ $wgBookstoreListEn = array(
@@ -229,11 +257,11 @@
230258 'tog-hideminor' => 'Hide minor edits in recent changes',
231259 'tog-usenewrc' => 'Enhanced recent changes (not for all browsers)',
232260 'tog-numberheadings' => 'Auto-number headings',
233 -'tog-showtoolbar'=>'Show edit toolbar',
 261+'tog-showtoolbar' => 'Show edit toolbar',
234262 'tog-editondblclick' => 'Edit pages on double click (JavaScript)',
235 -'tog-editsection'=>'Enable section editing via [edit] links',
236 -'tog-editsectiononrightclick'=>'Enable section editing by right clicking<br /> on section titles (JavaScript)',
237 -'tog-showtoc'=>'Show table of contents<br />(for pages with more than 3 headings)',
 263+'tog-editsection' => 'Enable section editing via [edit] links',
 264+'tog-editsectiononrightclick' => 'Enable section editing by right clicking<br /> on section titles (JavaScript)',
 265+'tog-showtoc' => 'Show table of contents<br />(for pages with more than 3 headings)',
238266 'tog-rememberpassword' => 'Remember password across sessions',
239267 'tog-editwidth' => 'Edit box has full width',
240268 'tog-watchdefault' => 'Add pages you edit to your watchlist',
@@ -241,6 +269,13 @@
242270 'tog-previewontop' => 'Show preview before edit box and not after it',
243271 'tog-previewonfirst' => 'Show preview on first edit',
244272 'tog-nocache' => 'Disable page caching',
 273+'tog-enotifwatchlistpages' => 'Send me an email on page changes (remark: existing notification flags need to be cleared manually in the watchlist)',
 274+'tog-enotifusertalkpages' => 'Send me an email when my user talk page is changed (remark: existing notification flags need to be cleared manually in the watchlist)',
 275+'tog-enotifminoredits' => 'Send me an email also for minor edits of pages (which usually do not trigger notification mails)',
 276+'tog-enotifrevealaddr' => 'Reveal my email address in notification mails (when I change a page, it allows watching users to reply quickly to me)',
 277+'tog-shownumberswatching' => 'Show the number of watching users (in recent changes view, watchlist and article page footers)',
 278+'tog-rcusemodstyle' => 'Show recent changes in UseMod style: only the most recent change of any page is listed.',
 279+'tog-showupdated' => 'Show update marker ',
245280
246281 # dates
247282 'sunday' => 'Sunday',
@@ -494,11 +529,11 @@
495530 # Login and logout pages
496531 #
497532 "logouttitle" => 'User logout',
498 -"logouttext" => "You are now logged out.
 533+"logouttext" => "You are now logged out.<br>
499534 You can continue to use {{SITENAME}} anonymously, or you can log in
500535 again as the same or as a different user. Note that some pages may
501536 continue to be displayed as if you were still logged in, until you clear
502 -your browser cache\n",
 537+your browser cache.\n",
503538
504539 'welcomecreation' => "== Welcome, $1! ==
505540
@@ -523,13 +558,17 @@
524559 'createaccountmail' => 'by email',
525560 'badretype' => 'The passwords you entered do not match.',
526561 'userexists' => 'The user name you entered is already in use. Please choose a different name.',
527 -'youremail' => 'Your email*',
 562+'youremail' => 'Your email**',
528563 'yourrealname' => 'Your real name*',
529564 'yourlanguage' => 'Interface language',
530565 'yourvariant' => 'Language variant',
531566 'yournick' => 'Your nickname (for signatures)',
532 -'emailforlost' => "Fields marked with a star (*) are optional. Storing an email address enables people to contact you through the website without you having to reveal your
 567+'emailforlost' => "Fields marked with stars (*, **) are optional. Storing an email address enables people to contact you through the website without you having to reveal your
533568 email address to them, and it can be used to send you a new password if you forget it.<br /><br />Your real name, if you choose to provide it, will be used for giving you attribution for your work.",
 569+'prefs-help-email' => '** <strong>Email</strong> (optional): Enables others to contact you through your user or user_talk page without the need of revealing your
 570+email address. It also allows the wiki to sent you a temporary password in case you forgot your current one.',
 571+'prefs-help-email-enotif' => 'This address is also used to send you email notifications if you enabled the options.',
 572+'prefs-help-realname' => '* <strong>Real name</strong> (optional): if you choose to provide it this will be used for giving you attribution for your work.',
534573 'prefs-help-userdata' => '* <strong>Real name</strong> (optional): if you choose to provide it this will be used for giving you attribution for your work.<br />
535574 * <strong>Email</strong> (optional): Enables people to contact you through the website without you having to reveal your
536575 email address to them, and it can be used to send you a new password if you forget it.',
@@ -542,20 +581,40 @@
543582 'nosuchuser' => "There is no user by the name \"$1\".
544583 Check your spelling, or use the form below to create a new user account.",
545584 'nosuchusershort' => "There is no user by the name \"$1\". Check your spelling.",
546 -'wrongpassword' => 'The password you entered is incorrect. Please try again.',
547 -'mailmypassword' => 'Mail me a new password',
548 -'passwordremindertitle' => "Password reminder from {{SITENAME}}",
549 -'passwordremindertext' => "Someone (probably you, from IP address $1)
550 -requested that we send you a new {{SITENAME}} login password.
551 -The password for user \"$2\" is now \"$3\".
552 -You should log in and change your password now.",
 585+'wrongpassword' => 'The password you entered is incorrect (or missing). Please try again.',
 586+'mailmypassword' => 'Mail me a temporary password
 587+because I forgot my password',
 588+'mailmypasswordauthent' => 'Mail me a temporary password
 589+because I forgot my password or
 590+for authentication of my email address',
 591+'passwordremindermailsubject' => "Email address authentication and temporary login password from {{SITENAME}}",
 592+'passwordremindermailbody' => "Someone, probably you from IP address $1,
 593+requested that we send you a temporary one-time login password for {{SITENAME}}.
 594+
 595+This mail is also be sent for the purpose of authentication of your email address.
 596+The password for user \"$2\" is now \"$4\".
 597+
 598+You can now log in with this temporary password, which is valid for only one login.
 599+You may wish to keep using your old password if you remember it or to set a new one.
 600+
 601+{{SERVER}}{{localurl:Special:Userlogin|wpName=$3&wpPassword=$4&returnto=Special:Preferences}}",
553602 'noemail' => "There is no e-mail address recorded for user \"$1\".",
554 -'passwordsent' => "A new password has been sent to the e-mail address
 603+'passwordsent' => "A temporary password has been sent to the e-mail address
555604 registered for \"$1\".
556605 Please log in again after you receive it.",
 606+'passwordsentforemailauthentication'
 607+ => "A temporary password has been sent to the e-mail address newly
 608+registered for \"$1\".
 609+Please re-login with that for authentication purposes.",
557610 'loginend' => '&nbsp;',
558611 'mailerror' => "Error sending mail: $1",
559612 'acct_creation_throttle_hit' => 'Sorry, you have already created $1 accounts. You can\'t make any more.',
 613+'emailauthenticated' => 'Your email address was authenticated on $1.',
 614+'emailnotauthenticated' => 'Your email address is <strong>not yet authenticated</strong> and the advanced email features are disabled until authentication <strong>(d.u.a.)</strong>.<br>
 615+To authenticate, please login in with the temporary password which has been mailed to you, or request a new one on the login page.',
 616+'invalidemailaddress' => 'The email address cannot be accepted as it appears to have an invalid format. Please enter a well-formatted address or empty that field.',
 617+'disableduntilauthent' => '<strong>(d.u.a.)</strong>',
 618+'disablednoemail' => '<strong>(disabled; no email address)</strong>',
560619
561620 # Edit page toolbar
562621 'bold_sample'=>'Bold text',
@@ -586,7 +645,7 @@
587646 #
588647 'summary' => 'Summary',
589648 'subject' => 'Subject/headline',
590 -'minoredit' => 'This is a minor edit',
 649+'minoredit' => 'This is a minor edit.',
591650 'watchthis' => 'Watch this page',
592651 'savearticle' => 'Save page',
593652 'preview' => 'Preview',
@@ -866,6 +925,8 @@
867926 'minoreditletter' => 'm',
868927 'newpageletter' => 'N',
869928 'sectionlink' => '&rarr;',
 929+'number_of_watching_users_RCview' => '[$1]',
 930+'number_of_watching_users_pageview' => '[$1 watching user/s]',
870931
871932 # Upload
872933 #
@@ -1145,9 +1206,8 @@
11461207 'removechecked' => 'Remove checked items from watchlist',
11471208 'watchlistcontains' => "Your watchlist contains $1 pages.",
11481209 'watcheditlist' => 'Here\'s an alphabetical list of your
1149 -watched pages. Check the boxes of pages you want to remove
1150 -from your watchlist and click the \'remove checked\' button
1151 -at the bottom of the screen.',
 1210+watched content pages. Check the boxes of pages you want to remove from your watchlist and click the \'remove checked\' button
 1211+at the bottom of the screen (deleting a content page also deletes the accompanying talk page and vice versa).',
11521212 'removingchecked' => 'Removing requested items from watchlist...',
11531213 'couldntremove' => "Couldn't remove item '$1'...",
11541214 'iteminvalidname' => "Problem with item '$1', invalid name...",
@@ -1155,7 +1215,49 @@
11561216 'wlshowlast' => "Show last $1 hours $2 days $3",
11571217 'wlsaved' => 'This is a saved version of your watchlist.',
11581218
 1219+'updatedmarker' => '<span class=\'updatedmarker\'>&nbsp;updated (since my last visit)&nbsp;</span>',
11591220
 1221+'email_notification_mailer' => '{{SITENAME}} Notification Mailer',
 1222+'email_notification_infotext' => '
 1223+<strong>Email Notification</strong><p>
 1224+<form action=\'{{localurl:Special:Watchlist|action=submit&magic=yes}}\' method=\'post\'>
 1225+<ul>
 1226+<li>You will be notified by email when someone changes a page which is listed in your watchlist.</li>
 1227+<li>A flag is set so that you receive only one email on the first change.</li>
 1228+<li>Your own edits do <b>not</b> trigger the sending of notifications.
 1229+<li>{{MediaWiki:updatedmarker}} means that the <b><i>page has changed since your last visit and that a notification mail has been sent to you.</i></b>.</li>
 1230+<li>The flag is automatically cleared when you visit such pages.</li>
 1231+<li> Alternatively, you can reset all flags at once by clicking on&nbsp; <input type="submit" name="dummy" value=\'Reset all notification flags (set their status to "visited")\'><input type="hidden" name="reset" value="all"></form></li>
 1232+</ul>
 1233+<hr>',
 1234+'email_notification_newpagetext'=> 'This is a new page.',
 1235+'email_notification_to' => '$WATCHINGUSERNAME_QP <$WATCHINGUSEREMAILADDR>',
 1236+'email_notification_subject' => '{{SITENAME}} page $PAGETITLE_QP has been changed by $PAGEEDITOR_QP',
 1237+'email_notification_lastvisitedrevisiontext' => 'See {{SERVER}}{{localurl:$PAGETITLE_RAWURL|diff=0&oldid=$OLDID}} for all changes since your last visit.',
 1238+'email_notification_body' => 'Dear $WATCHINGUSERNAME,
 1239+
 1240+the {{SITENAME}} page $PAGETITLE has been changed on $PAGEEDITDATE by $PAGEEDITOR,
 1241+see {{SERVER}}{{localurl:$PAGETITLE_RAWURL}} for the current version.
 1242+
 1243+$NEWPAGE
 1244+
 1245+Editor\'s summary: $PAGESUMMARY $PAGEMINOREDIT
 1246+Contact the editor:
 1247+mail {{SERVER}}{{localurl:Special:Emailuser|target=$PAGEEDITOR_RAWURL}}
 1248+wiki {{SERVER}}{{localurl:User:$PAGEEDITOR_RAWURL}}
 1249+
 1250+There will be no other notifications in case of further changes unless you visit this page.
 1251+You could also reset the notification flags for all your watched pages on your watchlist.
 1252+
 1253+ Your friendly {{SITENAME}} notification system
 1254+
 1255+--
 1256+To change your watchlist settings, visit
 1257+{{SERVER}}{{localurl:Special:Watchlist|magic=yes}}
 1258+
 1259+Feedback and further assistance:
 1260+{{SERVER}}{{localurl:WikiHelpdesk}}',
 1261+
11601262 # Delete/protect/revert
11611263 #
11621264 'deletepage' => 'Delete page',
@@ -1772,10 +1874,13 @@
17731875 return wfMsg($wgWeekdayNamesEn[$key-1]);
17741876 }
17751877
1776 - function userAdjust( $ts ) {
 1878+ function userAdjust( $ts, $tz = false ) {
17771879 global $wgUser, $wgLocalTZoffset;
17781880
 1881+ if (!$tz) {
17791882 $tz = $wgUser->getOption( 'timecorrection' );
 1883+ }
 1884+
17801885 if ( $tz === '' ) {
17811886 $hrDiff = isset( $wgLocalTZoffset ) ? $wgLocalTZoffset : 0;
17821887 $minDiff = 0;
@@ -1798,11 +1903,13 @@
17991904 return date( 'YmdHis', $t );
18001905 }
18011906
1802 - function date( $ts, $adj = false, $format = MW_DATE_USER_FORMAT ) {
 1907+ function date( $ts, $adj = false, $format = MW_DATE_USER_FORMAT, $timecorrection = false ) {
18031908 global $wgAmericanDates, $wgUser, $wgUseDynamicDates;
18041909
18051910 $ts=wfTimestamp(TS_MW,$ts);
1806 - if ( $adj ) { $ts = $this->userAdjust( $ts ); }
 1911+
 1912+ if ( $adj ) { $ts = $this->userAdjust( $ts, $timecorrection ); }
 1913+
18071914 if ( $wgUseDynamicDates ) {
18081915 if ( $format == MW_DATE_USER_FORMAT ) {
18091916 $datePreference = $wgUser->getOption( 'date' );
@@ -1828,10 +1935,10 @@
18291936 }
18301937 }
18311938
1832 - function time( $ts, $adj = false, $seconds = false ) {
 1939+ function time( $ts, $adj = false, $seconds = false, $timecorrection = false ) {
18331940 $ts=wfTimestamp(TS_MW,$ts);
18341941
1835 - if ( $adj ) { $ts = $this->userAdjust( $ts ); }
 1942+ if ( $adj ) { $ts = $this->userAdjust( $ts, $timecorrection ); }
18361943
18371944 $t = substr( $ts, 8, 2 ) . ':' . substr( $ts, 10, 2 );
18381945 if ( $seconds ) {
@@ -1840,10 +1947,14 @@
18411948 return $this->formatNum( $t );
18421949 }
18431950
1844 - function timeanddate( $ts, $adj = false, $format = MW_DATE_USER_FORMAT ) {
 1951+ function timeanddate( $ts, $adj = false, $format = MW_DATE_USER_FORMAT, $timecorrection = false, $dateandtime = false) {
18451952 $ts=wfTimestamp(TS_MW,$ts);
1846 -
1847 - return $this->time( $ts, $adj ) . ', ' . $this->date( $ts, $adj, $format );
 1953+ if ($dateandtime) {
 1954+ $ret = $this->date( $ts, $adj, $format, $timecorrection ) . ', ' . $this->time( $ts, $adj, false, $timecorrection );
 1955+ } else {
 1956+ $ret = $this->time( $ts, $adj, false, $timecorrection ) . ', ' . $this->date( $ts, $adj, $format, $timecorrection );
 1957+ }
 1958+ return $ret;
18481959 }
18491960
18501961 function rfc1123( $ts ) {

Status & tagging log