Rename all the special page class files back to their proper names.
authorBrion Vibber <brion@users.mediawiki.org>
Thu, 19 Jun 2008 21:12:45 +0000 (21:12 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Thu, 19 Jun 2008 21:12:45 +0000 (21:12 +0000)
1) This keeps the filename the same as the classname, which is always nice
2) This avoids duplicate filenames (such as includes/Export.php and includes/specials/Export.php)
So I've at least got a chance of figuring out what file is what still...

152 files changed:
includes/AutoLoader.php
includes/SpecialPage.php
includes/specials/Allmessages.php [deleted file]
includes/specials/Allpages.php [deleted file]
includes/specials/Ancientpages.php [deleted file]
includes/specials/Blockip.php [deleted file]
includes/specials/Blockme.php [deleted file]
includes/specials/Booksources.php [deleted file]
includes/specials/BrokenRedirects.php [deleted file]
includes/specials/Categories.php [deleted file]
includes/specials/Confirmemail.php [deleted file]
includes/specials/Contributions.php [deleted file]
includes/specials/Deadendpages.php [deleted file]
includes/specials/Disambiguations.php [deleted file]
includes/specials/DoubleRedirects.php [deleted file]
includes/specials/Emailuser.php [deleted file]
includes/specials/Export.php [deleted file]
includes/specials/Fewestrevisions.php [deleted file]
includes/specials/FileDuplicateSearch.php [deleted file]
includes/specials/Filepath.php [deleted file]
includes/specials/Imagelist.php [deleted file]
includes/specials/Import.php [deleted file]
includes/specials/Ipblocklist.php [deleted file]
includes/specials/Listgrouprights.php [deleted file]
includes/specials/Listredirects.php [deleted file]
includes/specials/Listusers.php [deleted file]
includes/specials/Lockdb.php [deleted file]
includes/specials/Log.php [deleted file]
includes/specials/Lonelypages.php [deleted file]
includes/specials/Longpages.php [deleted file]
includes/specials/MIMEsearch.php [deleted file]
includes/specials/MergeHistory.php [deleted file]
includes/specials/Mostcategories.php [deleted file]
includes/specials/Mostimages.php [deleted file]
includes/specials/Mostlinked.php [deleted file]
includes/specials/Mostlinkedcategories.php [deleted file]
includes/specials/Mostlinkedtemplates.php [deleted file]
includes/specials/Mostrevisions.php [deleted file]
includes/specials/Movepage.php [deleted file]
includes/specials/Newimages.php [deleted file]
includes/specials/Newpages.php [deleted file]
includes/specials/Popularpages.php [deleted file]
includes/specials/Preferences.php [deleted file]
includes/specials/Prefixindex.php [deleted file]
includes/specials/Protectedpages.php [deleted file]
includes/specials/Protectedtitles.php [deleted file]
includes/specials/Randompage.php [deleted file]
includes/specials/Randomredirect.php [deleted file]
includes/specials/Recentchanges.php [deleted file]
includes/specials/Recentchangeslinked.php [deleted file]
includes/specials/Resetpass.php [deleted file]
includes/specials/Revisiondelete.php [deleted file]
includes/specials/Search.php [deleted file]
includes/specials/Shortpages.php [deleted file]
includes/specials/SpecialAllmessages.php [new file with mode: 0644]
includes/specials/SpecialAllpages.php [new file with mode: 0644]
includes/specials/SpecialAncientpages.php [new file with mode: 0644]
includes/specials/SpecialBlockip.php [new file with mode: 0644]
includes/specials/SpecialBlockme.php [new file with mode: 0644]
includes/specials/SpecialBooksources.php [new file with mode: 0644]
includes/specials/SpecialBrokenRedirects.php [new file with mode: 0644]
includes/specials/SpecialCategories.php [new file with mode: 0644]
includes/specials/SpecialConfirmemail.php [new file with mode: 0644]
includes/specials/SpecialContributions.php [new file with mode: 0644]
includes/specials/SpecialDeadendpages.php [new file with mode: 0644]
includes/specials/SpecialDisambiguations.php [new file with mode: 0644]
includes/specials/SpecialDoubleRedirects.php [new file with mode: 0644]
includes/specials/SpecialEmailuser.php [new file with mode: 0644]
includes/specials/SpecialExport.php [new file with mode: 0644]
includes/specials/SpecialFewestrevisions.php [new file with mode: 0644]
includes/specials/SpecialFileDuplicateSearch.php [new file with mode: 0644]
includes/specials/SpecialFilepath.php [new file with mode: 0644]
includes/specials/SpecialImagelist.php [new file with mode: 0644]
includes/specials/SpecialImport.php [new file with mode: 0644]
includes/specials/SpecialIpblocklist.php [new file with mode: 0644]
includes/specials/SpecialListgrouprights.php [new file with mode: 0644]
includes/specials/SpecialListredirects.php [new file with mode: 0644]
includes/specials/SpecialListusers.php [new file with mode: 0644]
includes/specials/SpecialLockdb.php [new file with mode: 0644]
includes/specials/SpecialLog.php [new file with mode: 0644]
includes/specials/SpecialLonelypages.php [new file with mode: 0644]
includes/specials/SpecialLongpages.php [new file with mode: 0644]
includes/specials/SpecialMIMEsearch.php [new file with mode: 0644]
includes/specials/SpecialMergeHistory.php [new file with mode: 0644]
includes/specials/SpecialMostcategories.php [new file with mode: 0644]
includes/specials/SpecialMostimages.php [new file with mode: 0644]
includes/specials/SpecialMostlinked.php [new file with mode: 0644]
includes/specials/SpecialMostlinkedcategories.php [new file with mode: 0644]
includes/specials/SpecialMostlinkedtemplates.php [new file with mode: 0644]
includes/specials/SpecialMostrevisions.php [new file with mode: 0644]
includes/specials/SpecialMovepage.php [new file with mode: 0644]
includes/specials/SpecialNewimages.php [new file with mode: 0644]
includes/specials/SpecialNewpages.php [new file with mode: 0644]
includes/specials/SpecialPopularpages.php [new file with mode: 0644]
includes/specials/SpecialPreferences.php [new file with mode: 0644]
includes/specials/SpecialPrefixindex.php [new file with mode: 0644]
includes/specials/SpecialProtectedpages.php [new file with mode: 0644]
includes/specials/SpecialProtectedtitles.php [new file with mode: 0644]
includes/specials/SpecialRandompage.php [new file with mode: 0644]
includes/specials/SpecialRandomredirect.php [new file with mode: 0644]
includes/specials/SpecialRecentchanges.php [new file with mode: 0644]
includes/specials/SpecialRecentchangeslinked.php [new file with mode: 0644]
includes/specials/SpecialResetpass.php [new file with mode: 0644]
includes/specials/SpecialRevisiondelete.php [new file with mode: 0644]
includes/specials/SpecialSearch.php [new file with mode: 0644]
includes/specials/SpecialShortpages.php [new file with mode: 0644]
includes/specials/SpecialSpecialpages.php [new file with mode: 0644]
includes/specials/SpecialStatistics.php [new file with mode: 0644]
includes/specials/SpecialUncategorizedcategories.php [new file with mode: 0644]
includes/specials/SpecialUncategorizedimages.php [new file with mode: 0644]
includes/specials/SpecialUncategorizedpages.php [new file with mode: 0644]
includes/specials/SpecialUncategorizedtemplates.php [new file with mode: 0644]
includes/specials/SpecialUndelete.php [new file with mode: 0644]
includes/specials/SpecialUnlockdb.php [new file with mode: 0644]
includes/specials/SpecialUnusedcategories.php [new file with mode: 0644]
includes/specials/SpecialUnusedimages.php [new file with mode: 0644]
includes/specials/SpecialUnusedtemplates.php [new file with mode: 0644]
includes/specials/SpecialUnwatchedpages.php [new file with mode: 0644]
includes/specials/SpecialUpload.php [new file with mode: 0644]
includes/specials/SpecialUploadMogile.php [new file with mode: 0644]
includes/specials/SpecialUserlogin.php [new file with mode: 0644]
includes/specials/SpecialUserlogout.php [new file with mode: 0644]
includes/specials/SpecialUserrights.php [new file with mode: 0644]
includes/specials/SpecialVersion.php [new file with mode: 0644]
includes/specials/SpecialWantedcategories.php [new file with mode: 0644]
includes/specials/SpecialWantedpages.php [new file with mode: 0644]
includes/specials/SpecialWatchlist.php [new file with mode: 0644]
includes/specials/SpecialWhatlinkshere.php [new file with mode: 0644]
includes/specials/SpecialWithoutinterwiki.php [new file with mode: 0644]
includes/specials/Specialpages.php [deleted file]
includes/specials/Statistics.php [deleted file]
includes/specials/Uncategorizedcategories.php [deleted file]
includes/specials/Uncategorizedimages.php [deleted file]
includes/specials/Uncategorizedpages.php [deleted file]
includes/specials/Uncategorizedtemplates.php [deleted file]
includes/specials/Undelete.php [deleted file]
includes/specials/Unlockdb.php [deleted file]
includes/specials/Unusedcategories.php [deleted file]
includes/specials/Unusedimages.php [deleted file]
includes/specials/Unusedtemplates.php [deleted file]
includes/specials/Unwatchedpages.php [deleted file]
includes/specials/Upload.php [deleted file]
includes/specials/UploadMogile.php [deleted file]
includes/specials/Userlogin.php [deleted file]
includes/specials/Userlogout.php [deleted file]
includes/specials/Userrights.php [deleted file]
includes/specials/Version.php [deleted file]
includes/specials/Wantedcategories.php [deleted file]
includes/specials/Wantedpages.php [deleted file]
includes/specials/Watchlist.php [deleted file]
includes/specials/Whatlinkshere.php [deleted file]
includes/specials/Withoutinterwiki.php [deleted file]

index 2f0726a..92ef753 100644 (file)
@@ -377,73 +377,73 @@ class AutoLoader {
                'StripState' => 'includes/parser/Parser.php',
 
                # includes/specials
-               'AncientPagesPage' => 'includes/specials/Ancientpages.php',
-               'BrokenRedirectsPage' => 'includes/specials/BrokenRedirects.php',
-               'ContribsPager' => 'includes/specials/Contributions.php',
-               'DBLockForm' => 'includes/specials/Lockdb.php',
-               'DBUnlockForm' => 'includes/specials/Unlockdb.php',
-               'DeadendPagesPage' => 'includes/specials/Deadendpages.php',
-               'DisambiguationsPage' => 'includes/specials/Disambiguations.php',
-               'DoubleRedirectsPage' => 'includes/specials/DoubleRedirects.php',
-               'EmailConfirmation' => 'includes/specials/Confirmemail.php',
-               'EmailInvalidation' => 'includes/specials/Confirmemail.php',
-               'EmailUserForm' => 'includes/specials/Emailuser.php',
-               'FewestrevisionsPage' => 'includes/specials/Fewestrevisions.php',
-               'FileDuplicateSearchPage' => 'includes/specials/FileDuplicateSearch.php',
-               'IPBlockForm' => 'includes/specials/Blockip.php',
-               'IPBlocklistPager' => 'includes/specials/Ipblocklist.php',
-               'IPUnblockForm' => 'includes/specials/Ipblocklist.php',
-               'ImportReporter' => 'includes/specials/Import.php',
-               'ImportStreamSource' => 'includes/specials/Import.php',
-               'ImportStringSource' => 'includes/specials/Import.php',
-               'ListredirectsPage' => 'includes/specials/Listredirects.php',
-               'LoginForm' => 'includes/specials/Userlogin.php',
-               'LonelyPagesPage' => 'includes/specials/Lonelypages.php',
-               'LongPagesPage' => 'includes/specials/Longpages.php',
-               'MIMEsearchPage' => 'includes/specials/MIMEsearch.php',
-               'MostcategoriesPage' => 'includes/specials/Mostcategories.php',
-               'MostimagesPage' => 'includes/specials/Mostimages.php',
-               'MostlinkedCategoriesPage' => 'includes/specials/Mostlinkedcategories.php',
-               'MostlinkedPage' => 'includes/specials/Mostlinked.php',
-               'MostrevisionsPage' => 'includes/specials/Mostrevisions.php',
-               'MovePageForm' => 'includes/specials/Movepage.php',
-               'SpecialNewpages' => 'includes/specials/Newpages.php',
-               'NewPagesPager' => 'includes/specials/Newpages.php',
-               'PageArchive' => 'includes/specials/Undelete.php',
-               'PasswordResetForm' => 'includes/specials/Resetpass.php',
-               'PopularPagesPage' => 'includes/specials/Popularpages.php',
-               'PreferencesForm' => 'includes/specials/Preferences.php',
-               'RandomPage' => 'includes/specials/Randompage.php',
-               'RevisionDeleteForm' => 'includes/specials/Revisiondelete.php',
-               'RevisionDeleter' => 'includes/specials/Revisiondelete.php',
-               'ShortPagesPage' => 'includes/specials/Shortpages.php',
-               'SpecialAllpages' => 'includes/specials/Allpages.php',
-               'SpecialBookSources' => 'includes/specials/Booksources.php',
-               'SpecialListGroupRights' => 'includes/specials/Listgrouprights.php',
-               'SpecialMostlinkedtemplates' => 'includes/specials/Mostlinkedtemplates.php',
-               'SpecialPrefixindex' => 'includes/specials/Prefixindex.php',
-               'SpecialRandomredirect' => 'includes/specials/Randomredirect.php',
-               'SpecialRecentChanges' => 'includes/specials/Recentchanges.php',
-               'SpecialSearch' => 'includes/specials/Search.php',
-               'SpecialVersion' => 'includes/specials/Version.php',
-               'UncategorizedCategoriesPage' => 'includes/specials/Uncategorizedcategories.php',
-               'UncategorizedPagesPage' => 'includes/specials/Uncategorizedpages.php',
-               'UncategorizedTemplatesPage' => 'includes/specials/Uncategorizedtemplates.php',
-               'UndeleteForm' => 'includes/specials/Undelete.php',
-               'UnusedCategoriesPage' => 'includes/specials/Unusedcategories.php',
-               'UnusedimagesPage' => 'includes/specials/Unusedimages.php',
-               'UnusedtemplatesPage' => 'includes/specials/Unusedtemplates.php',
-               'UnwatchedpagesPage' => 'includes/specials/Unwatchedpages.php',
-               'UploadForm' => 'includes/specials/Upload.php',
-               'UploadFormMogile' => 'includes/specials/UploadMogile.php',
-               'UserrightsPage' => 'includes/specials/Userrights.php',
-               'UsersPager' => 'includes/specials/Listusers.php',
-               'WantedCategoriesPage' => 'includes/specials/Wantedcategories.php',
-               'WantedPagesPage' => 'includes/specials/Wantedpages.php',
-               'WhatLinksHerePage' => 'includes/specials/Whatlinkshere.php',
-               'WikiImporter' => 'includes/specials/Import.php',
-               'WikiRevision' => 'includes/specials/Import.php',
-               'WithoutInterwikiPage' => 'includes/specials/Withoutinterwiki.php',
+               'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
+               'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
+               'ContribsPager' => 'includes/specials/SpecialContributions.php',
+               'DBLockForm' => 'includes/specials/SpecialLockdb.php',
+               'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
+               'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
+               'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php',
+               'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
+               'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
+               'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
+               'EmailUserForm' => 'includes/specials/SpecialEmailuser.php',
+               'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
+               'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
+               'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
+               'IPBlocklistPager' => 'includes/specials/SpecialIpblocklist.php',
+               'IPUnblockForm' => 'includes/specials/SpecialIpblocklist.php',
+               'ImportReporter' => 'includes/specials/SpecialImport.php',
+               'ImportStreamSource' => 'includes/specials/SpecialImport.php',
+               'ImportStringSource' => 'includes/specials/SpecialImport.php',
+               'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
+               'LoginForm' => 'includes/specials/SpecialUserlogin.php',
+               'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
+               'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
+               'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
+               'MostcategoriesPage' => 'includes/specials/SpecialMostcategories.php',
+               'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
+               'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php',
+               'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php',
+               'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
+               'MovePageForm' => 'includes/specials/SpecialMovepage.php',
+               'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
+               'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
+               'PageArchive' => 'includes/specials/SpecialUndelete.php',
+               'PasswordResetForm' => 'includes/specials/SpecialResetpass.php',
+               'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
+               'PreferencesForm' => 'includes/specials/SpecialPreferences.php',
+               'RandomPage' => 'includes/specials/SpecialRandompage.php',
+               'RevisionDeleteForm' => 'includes/specials/SpecialRevisiondelete.php',
+               'RevisionDeleter' => 'includes/specials/SpecialRevisiondelete.php',
+               'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
+               'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
+               'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
+               'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
+               'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
+               'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
+               'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
+               'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
+               'SpecialSearch' => 'includes/specials/SpecialSearch.php',
+               'SpecialVersion' => 'includes/specials/SpecialVersion.php',
+               'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
+               'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
+               'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
+               'UndeleteForm' => 'includes/specials/SpecialUndelete.php',
+               'UnusedCategoriesPage' => 'includes/specials/SpecialUnusedcategories.php',
+               'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php',
+               'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
+               'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
+               'UploadForm' => 'includes/specials/SpecialUpload.php',
+               'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
+               'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
+               'UsersPager' => 'includes/specials/SpecialListusers.php',
+               'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
+               'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
+               'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
+               'WikiImporter' => 'includes/specials/SpecialImport.php',
+               'WikiRevision' => 'includes/specials/SpecialImport.php',
+               'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
 
                # includes/templates
                'UsercreateTemplate' => 'includes/templates/Userlogin.php',
index f9211e9..e092980 100644 (file)
@@ -642,7 +642,7 @@ class SpecialPage
                        $this->mFunction = $function;
                }
                if ( $file === 'default' ) {
-                       $this->mFile = dirname(__FILE__) . "/specials/$name.php";
+                       $this->mFile = dirname(__FILE__) . "/specials/Special$name.php";
                } else {
                        $this->mFile = $file;
                }
diff --git a/includes/specials/Allmessages.php b/includes/specials/Allmessages.php
deleted file mode 100644 (file)
index c2a8de4..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-<?php
-/**
- * Use this special page to get a list of the MediaWiki system messages.
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor.
- */
-function wfSpecialAllmessages() {
-       global $wgOut, $wgRequest, $wgMessageCache, $wgTitle;
-       global $wgUseDatabaseMessages;
-
-       # The page isn't much use if the MediaWiki namespace is not being used
-       if( !$wgUseDatabaseMessages ) {
-               $wgOut->addWikiMsg( 'allmessagesnotsupportedDB' );
-               return;
-       }
-
-       wfProfileIn( __METHOD__ );
-
-       wfProfileIn( __METHOD__ . '-setup' );
-       $ot = $wgRequest->getText( 'ot' );
-
-       $navText = wfMsg( 'allmessagestext' );
-
-       # Make sure all extension messages are available
-
-       $wgMessageCache->loadAllMessages();
-
-       $sortedArray = array_merge( Language::getMessagesFor( 'en' ), $wgMessageCache->getExtensionMessagesFor( 'en' ) );
-       ksort( $sortedArray );
-       $messages = array();
-
-       foreach ( $sortedArray as $key => $value ) {
-               $messages[$key]['enmsg'] = $value;
-               $messages[$key]['statmsg'] = wfMsgReal( $key, array(), false, false, false ); // wfMsgNoDbNoTrans doesn't exist
-               $messages[$key]['msg'] = wfMsgNoTrans( $key );
-       }
-
-       wfProfileOut( __METHOD__ . '-setup' );
-
-       wfProfileIn( __METHOD__ . '-output' );
-       $wgOut->addScriptFile( 'allmessages.js' );
-       if ( $ot == 'php' ) {
-               $navText .= wfAllMessagesMakePhp( $messages );
-               $wgOut->addHTML( 'PHP | <a href="' . $wgTitle->escapeLocalUrl( 'ot=html' ) . '">HTML</a> | ' .
-                       '<a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' .
-                       '<pre>' . htmlspecialchars( $navText ) . '</pre>' );
-       } else if ( $ot == 'xml' ) {
-               $wgOut->disable();
-               header( 'Content-type: text/xml' );
-               echo wfAllMessagesMakeXml( $messages );
-       } else {
-               $wgOut->addHTML( '<a href="' . $wgTitle->escapeLocalUrl( 'ot=php' ) . '">PHP</a> | ' .
-                       'HTML |  <a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' );
-               $wgOut->addWikiText( $navText );
-               $wgOut->addHTML( wfAllMessagesMakeHTMLText( $messages ) );
-       }
-       wfProfileOut( __METHOD__ . '-output' );
-
-       wfProfileOut( __METHOD__ );
-}
-
-function wfAllMessagesMakeXml( $messages ) {
-       global $wgLang;
-       $lang = $wgLang->getCode();
-       $txt = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
-       $txt .= "<messages lang=\"$lang\">\n";
-       foreach( $messages as $key => $m ) {
-               $txt .= "\t" . Xml::element( 'message', array( 'name' => $key ), $m['msg'] ) . "\n";
-       }
-       $txt .= "</messages>";
-       return $txt;
-}
-
-/**
- * Create the messages array, formatted in PHP to copy to language files.
- * @param $messages Messages array.
- * @return The PHP messages array.
- * @todo Make suitable for language files.
- */
-function wfAllMessagesMakePhp( $messages ) {
-       global $wgLang;
-       $txt = "\n\n\$messages = array(\n";
-       foreach( $messages as $key => $m ) {
-               if( $wgLang->getCode() != 'en' && $m['msg'] == $m['enmsg'] ) {
-                       continue;
-               } else if ( wfEmptyMsg( $key, $m['msg'] ) ) {
-                       $m['msg'] = '';
-                       $comment = ' #empty';
-               } else {
-                       $comment = '';
-               }
-               $txt .= "'$key' => '" . preg_replace( '/(?<!\\\\)\'/', "\'", $m['msg']) . "',$comment\n";
-       }
-       $txt .= ');';
-       return $txt;
-}
-
-/**
- * Create a list of messages, formatted in HTML as a list of messages and values and showing differences between the default language file message and the message in MediaWiki: namespace.
- * @param $messages Messages array.
- * @return The HTML list of messages.
- */
-function wfAllMessagesMakeHTMLText( $messages ) {
-       global $wgLang, $wgContLang, $wgUser;
-       wfProfileIn( __METHOD__ );
-
-       $sk = $wgUser->getSkin();
-       $talk = wfMsg( 'talkpagelinktext' );
-
-       $input = Xml::element( 'input', array(
-               'type'    => 'text',
-               'id'      => 'allmessagesinput',
-               'onkeyup' => 'allmessagesfilter()'
-       ), '' );
-       $checkbox = Xml::element( 'input', array(
-               'type'    => 'button',
-               'value'   => wfMsgHtml( 'allmessagesmodified' ),
-               'id'      => 'allmessagescheckbox',
-               'onclick' => 'allmessagesmodified()'
-       ), '' );
-
-       $txt = '<span id="allmessagesfilter" style="display: none;">' . wfMsgHtml( 'allmessagesfilter' ) . " {$input}{$checkbox} " . '</span>';
-
-       $txt .= '
-<table border="1" cellspacing="0" width="100%" id="allmessagestable">
-       <tr>
-               <th rowspan="2">' . wfMsgHtml( 'allmessagesname' ) . '</th>
-               <th>' . wfMsgHtml( 'allmessagesdefault' ) . '</th>
-       </tr>
-       <tr>
-               <th>' . wfMsgHtml( 'allmessagescurrent' ) . '</th>
-       </tr>';
-
-       wfProfileIn( __METHOD__ . "-check" );
-
-       # This is a nasty hack to avoid doing independent existence checks
-       # without sending the links and table through the slow wiki parser.
-       $pageExists = array(
-               NS_MEDIAWIKI => array(),
-               NS_MEDIAWIKI_TALK => array()
-       );
-       $dbr = wfGetDB( DB_SLAVE );
-       $page = $dbr->tableName( 'page' );
-       $sql = "SELECT page_namespace,page_title FROM $page WHERE page_namespace IN (" . NS_MEDIAWIKI . ", " . NS_MEDIAWIKI_TALK . ")";
-       $res = $dbr->query( $sql );
-       while( $s = $dbr->fetchObject( $res ) ) {
-               $pageExists[$s->page_namespace][$s->page_title] = true;
-       }
-       $dbr->freeResult( $res );
-       wfProfileOut( __METHOD__ . "-check" );
-
-       wfProfileIn( __METHOD__ . "-output" );
-
-       $i = 0;
-
-       foreach( $messages as $key => $m ) {
-               $title = $wgLang->ucfirst( $key );
-               if( $wgLang->getCode() != $wgContLang->getCode() ) {
-                       $title .= '/' . $wgLang->getCode();
-               }
-
-               $titleObj =& Title::makeTitle( NS_MEDIAWIKI, $title );
-               $talkPage =& Title::makeTitle( NS_MEDIAWIKI_TALK, $title );
-
-               $changed = ( $m['statmsg'] != $m['msg'] );
-               $message = htmlspecialchars( $m['statmsg'] );
-               $mw = htmlspecialchars( $m['msg'] );
-
-               if( isset( $pageExists[NS_MEDIAWIKI][$title] ) ) {
-                       $pageLink = $sk->makeKnownLinkObj( $titleObj, "<span id=\"sp-allmessages-i-$i\">" .  htmlspecialchars( $key ) . '</span>' );
-               } else {
-                       $pageLink = $sk->makeBrokenLinkObj( $titleObj, "<span id=\"sp-allmessages-i-$i\">" .  htmlspecialchars( $key ) . '</span>' );
-               }
-               if( isset( $pageExists[NS_MEDIAWIKI_TALK][$title] ) ) {
-                       $talkLink = $sk->makeKnownLinkObj( $talkPage, htmlspecialchars( $talk ) );
-               } else {
-                       $talkLink = $sk->makeBrokenLinkObj( $talkPage, htmlspecialchars( $talk ) );
-               }
-
-               $anchor = 'msg_' . htmlspecialchars( strtolower( $title ) );
-               $anchor = "<a id=\"$anchor\" name=\"$anchor\"></a>";
-
-               if( $changed ) {
-                       $txt .= "
-       <tr class=\"orig\" id=\"sp-allmessages-r1-$i\">
-               <td rowspan=\"2\">
-                       $anchor$pageLink<br />$talkLink
-               </td><td>
-$message
-               </td>
-       </tr><tr class=\"new\" id=\"sp-allmessages-r2-$i\">
-               <td>
-$mw
-               </td>
-       </tr>";
-               } else {
-                       $txt .= "
-       <tr class=\"def\" id=\"sp-allmessages-r1-$i\">
-               <td>
-                       $anchor$pageLink<br />$talkLink
-               </td><td>
-$mw
-               </td>
-       </tr>";
-               }
-               $i++;
-       }
-       $txt .= '</table>';
-       wfProfileOut( __METHOD__ . '-output' );
-
-       wfProfileOut( __METHOD__ );
-       return $txt;
-}
diff --git a/includes/specials/Allpages.php b/includes/specials/Allpages.php
deleted file mode 100644 (file)
index 7223e31..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Entry point : initialise variables and call subfunctions.
- * @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL)
- * @param $specialPage See the SpecialPage object.
- */
-function wfSpecialAllpages( $par=NULL, $specialPage ) {
-       global $wgRequest, $wgOut, $wgContLang;
-
-       # GET values
-       $from = $wgRequest->getVal( 'from' );
-       $namespace = $wgRequest->getInt( 'namespace' );
-
-       $namespaces = $wgContLang->getNamespaces();
-
-       $indexPage = new SpecialAllpages();
-
-       $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) )  ?
-               wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
-               wfMsg( 'allarticles' )
-               );
-
-       if ( isset($par) ) {
-               $indexPage->showChunk( $namespace, $par, $specialPage->including() );
-       } elseif ( isset($from) ) {
-               $indexPage->showChunk( $namespace, $from, $specialPage->including() );
-       } else {
-               $indexPage->showToplevel ( $namespace, $specialPage->including() );
-       }
-}
-
-/**
- * Implements Special:Allpages
- * @ingroup SpecialPage
- */
-class SpecialAllpages {
-       /**
-        * Maximum number of pages to show on single subpage.
-        */
-       protected $maxPerPage = 960;
-
-       /**
-        * Name of this special page. Used to make title objects that reference back
-        * to this page.
-        */
-       protected $name = 'Allpages';
-
-       /**
-        * Determines, which message describes the input field 'nsfrom'.
-        */
-       protected $nsfromMsg = 'allpagesfrom';
-
-/**
- * HTML for the top form
- * @param integer $namespace A namespace constant (default NS_MAIN).
- * @param string $from Article name we are starting listing at.
- */
-function namespaceForm ( $namespace = NS_MAIN, $from = '' ) {
-       global $wgScript;
-       $t = SpecialPage::getTitleFor( $this->name );
-
-       $out  = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
-       $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
-       $out .= Xml::hidden( 'title', $t->getPrefixedText() );
-       $out .= Xml::openElement( 'fieldset' );
-       $out .= Xml::element( 'legend', null, wfMsg( 'allpages' ) );
-       $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
-       $out .= "<tr>
-                       <td class='mw-label'>" .
-                               Xml::label( wfMsg( $this->nsfromMsg ), 'nsfrom' ) .
-                       "</td>
-                       <td class='mw-input'>" .
-                               Xml::input( 'from', 20, $from, array( 'id' => 'nsfrom' ) ) .
-                       "</td>
-               </tr>
-               <tr>
-                       <td class='mw-label'>" .
-                               Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
-                       "</td>
-                       <td class='mw-input'>" .
-                               Xml::namespaceSelector( $namespace, null ) . ' ' .
-                               Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
-                       "</td>
-                       </tr>";
-       $out .= Xml::closeElement( 'table' );
-       $out .= Xml::closeElement( 'fieldset' );
-       $out .= Xml::closeElement( 'form' );
-       $out .= Xml::closeElement( 'div' );
-       return $out;
-}
-
-/**
- * @param integer $namespace (default NS_MAIN)
- */
-function showToplevel ( $namespace = NS_MAIN, $including = false ) {
-       global $wgOut, $wgContLang;
-       $align = $wgContLang->isRtl() ? 'left' : 'right';
-
-       # TODO: Either make this *much* faster or cache the title index points
-       # in the querycache table.
-
-       $dbr = wfGetDB( DB_SLAVE );
-       $out = "";
-       $where = array( 'page_namespace' => $namespace );
-
-       global $wgMemc;
-       $key = wfMemcKey( 'allpages', 'ns', $namespace );
-       $lines = $wgMemc->get( $key );
-
-       if( !is_array( $lines ) ) {
-               $options = array( 'LIMIT' => 1 );
-               if ( ! $dbr->implicitOrderby() ) {
-                       $options['ORDER BY'] = 'page_title';
-               }
-               $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
-               $lastTitle = $firstTitle;
-
-               # This array is going to hold the page_titles in order.
-               $lines = array( $firstTitle );
-
-               # If we are going to show n rows, we need n+1 queries to find the relevant titles.
-               $done = false;
-               for( $i = 0; !$done; ++$i ) {
-                       // Fetch the last title of this chunk and the first of the next
-                       $chunk = is_null( $lastTitle )
-                               ? ''
-                               : 'page_title >= ' . $dbr->addQuotes( $lastTitle );
-                       $res = $dbr->select(
-                               'page', /* FROM */
-                               'page_title', /* WHAT */
-                               $where + array($chunk),
-                               __METHOD__,
-                               array ('LIMIT' => 2, 'OFFSET' => $this->maxPerPage - 1, 'ORDER BY' => 'page_title') );
-
-                       if ( $s = $dbr->fetchObject( $res ) ) {
-                               array_push( $lines, $s->page_title );
-                       } else {
-                               // Final chunk, but ended prematurely. Go back and find the end.
-                               $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
-                                       array(
-                                               'page_namespace' => $namespace,
-                                               $chunk
-                                       ), __METHOD__ );
-                               array_push( $lines, $endTitle );
-                               $done = true;
-                       }
-                       if( $s = $dbr->fetchObject( $res ) ) {
-                               array_push( $lines, $s->page_title );
-                               $lastTitle = $s->page_title;
-                       } else {
-                               // This was a final chunk and ended exactly at the limit.
-                               // Rare but convenient!
-                               $done = true;
-                       }
-                       $dbr->freeResult( $res );
-               }
-               $wgMemc->add( $key, $lines, 3600 );
-       }
-
-       // If there are only two or less sections, don't even display them.
-       // Instead, display the first section directly.
-       if( count( $lines ) <= 2 ) {
-               $this->showChunk( $namespace, '', $including );
-               return;
-       }
-
-       # At this point, $lines should contain an even number of elements.
-       $out .= "<table class='allpageslist' style='background: inherit;'>";
-       while ( count ( $lines ) > 0 ) {
-               $inpoint = array_shift ( $lines );
-               $outpoint = array_shift ( $lines );
-               $out .= $this->showline ( $inpoint, $outpoint, $namespace, false );
-       }
-       $out .= '</table>';
-       $nsForm = $this->namespaceForm( $namespace, '', false );
-
-       # Is there more?
-       if ( $including ) {
-               $out2 = '';
-       } else {
-               $morelinks = '';
-               if ( $morelinks != '' ) {
-                       $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
-                       $out2 .= '<tr valign="top"><td>' . $nsForm;
-                       $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">';
-                       $out2 .= $morelinks . '</td></tr></table><hr />';
-               } else {
-                       $out2 = $nsForm . '<hr />';
-               }
-       }
-
-       $wgOut->addHtml( $out2 . $out );
-}
-
-/**
- * @todo Document
- * @param string $from
- * @param integer $namespace (Default NS_MAIN)
- */
-function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
-       global $wgContLang;
-       $align = $wgContLang->isRtl() ? 'left' : 'right';
-       $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
-       $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
-       $queryparams = ($namespace ? "namespace=$namespace" : '');
-       $special = SpecialPage::getTitleFor( $this->name, $inpoint );
-       $link = $special->escapeLocalUrl( $queryparams );
-
-       $out = wfMsgHtml(
-               'alphaindexline',
-               "<a href=\"$link\">$inpointf</a></td><td><a href=\"$link\">",
-               "</a></td><td><a href=\"$link\">$outpointf</a>"
-       );
-       return '<tr><td align="' . $align . '">'.$out.'</td></tr>';
-}
-
-/**
- * @param integer $namespace (Default NS_MAIN)
- * @param string $from list all pages from this name (default FALSE)
- */
-function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
-       global $wgOut, $wgUser, $wgContLang;
-
-       $sk = $wgUser->getSkin();
-
-       $fromList = $this->getNamespaceKeyAndText($namespace, $from);
-       $namespaces = $wgContLang->getNamespaces();
-       $align = $wgContLang->isRtl() ? 'left' : 'right';
-
-       $n = 0;
-
-       if ( !$fromList ) {
-               $out = wfMsgWikiHtml( 'allpagesbadtitle' );
-       } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
-               // Show errormessage and reset to NS_MAIN
-               $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
-               $namespace = NS_MAIN;
-       } else {
-               list( $namespace, $fromKey, $from ) = $fromList;
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'page',
-                       array( 'page_namespace', 'page_title', 'page_is_redirect' ),
-                       array(
-                               'page_namespace' => $namespace,
-                               'page_title >= ' . $dbr->addQuotes( $fromKey )
-                       ),
-                       __METHOD__,
-                       array(
-                               'ORDER BY'  => 'page_title',
-                               'LIMIT'     => $this->maxPerPage + 1,
-                               'USE INDEX' => 'name_title',
-                       )
-               );
-
-               if( $res->numRows() > 0 ) {
-                       $out = '<table style="background: inherit;" border="0" width="100%">';
-       
-                       while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
-                               $t = Title::makeTitle( $s->page_namespace, $s->page_title );
-                               if( $t ) {
-                                       $link = ($s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
-                                               $sk->makeKnownLinkObj( $t, htmlspecialchars( $t->getText() ), false, false ) .
-                                               ($s->page_is_redirect ? '</div>' : '' );
-                               } else {
-                                       $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
-                               }
-                               if( $n % 3 == 0 ) {
-                                       $out .= '<tr>';
-                               }
-                               $out .= "<td width=\"33%\">$link</td>";
-                               $n++;
-                               if( $n % 3 == 0 ) {
-                                       $out .= '</tr>';
-                               }
-                       }
-                       if( ($n % 3) != 0 ) {
-                               $out .= '</tr>';
-                       }
-                       $out .= '</table>';
-               } else {
-                       $out = '';
-               }
-       }
-
-       if ( $including ) {
-               $out2 = '';
-       } else {
-               if( $from == '' ) {
-                       // First chunk; no previous link.
-                       $prevTitle = null;
-               } else {
-                       # Get the last title from previous chunk
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $res_prev = $dbr->select(
-                               'page',
-                               'page_title',
-                               array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
-                               __METHOD__,
-                               array( 'ORDER BY' => 'page_title DESC', 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) )
-                       );
-
-                       # Get first title of previous complete chunk
-                       if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
-                               $pt = $dbr->fetchObject( $res_prev );
-                               $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
-                       } else {
-                               # The previous chunk is not complete, need to link to the very first title
-                               # available in the database
-                               $options = array( 'LIMIT' => 1 );
-                               if ( ! $dbr->implicitOrderby() ) {
-                                       $options['ORDER BY'] = 'page_title';
-                               }
-                               $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), __METHOD__, $options );
-                               # Show the previous link if it s not the current requested chunk
-                               if( $from != $reallyFirstPage_title ) {
-                                       $prevTitle =  Title::makeTitle( $namespace, $reallyFirstPage_title );
-                               } else {
-                                       $prevTitle = null;
-                               }
-                       }
-               }
-
-               $nsForm = $this->namespaceForm( $namespace, $from );
-               $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
-               $out2 .= '<tr valign="top"><td>' . $nsForm;
-               $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
-                               $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
-                                       wfMsgHtml ( 'allpages' ) );
-
-               $self = SpecialPage::getTitleFor( 'Allpages' );
-
-               # Do we put a previous link ?
-               if( isset( $prevTitle ) &&  $pt = $prevTitle->getText() ) {
-                       $q = 'from=' . $prevTitle->getPartialUrl()
-                               . ( $namespace ? '&namespace=' . $namespace : '' );
-                       $prevLink = $sk->makeKnownLinkObj( $self,
-                               wfMsgHTML( 'prevpage', htmlspecialchars( $pt ) ), $q );
-                       $out2 .= ' | ' . $prevLink;
-               }
-
-               if( $n == $this->maxPerPage && $s = $dbr->fetchObject($res) ) {
-                       # $s is the first link of the next chunk
-                       $t = Title::MakeTitle($namespace, $s->page_title);
-                       $q = 'from=' . $t->getPartialUrl()
-                               . ( $namespace ? '&namespace=' . $namespace : '' );
-                       $nextLink = $sk->makeKnownLinkObj( $self,
-                               wfMsgHtml( 'nextpage', htmlspecialchars( $t->getText() ) ), $q );
-                       $out2 .= ' | ' . $nextLink;
-               }
-               $out2 .= "</td></tr></table><hr />";
-       }
-
-       $wgOut->addHtml( $out2 . $out );
-       if( isset($prevLink) or isset($nextLink) ) {
-               $wgOut->addHtml( '<hr /><p style="font-size: smaller; float: ' . $align . '">' );
-               if( isset( $prevLink ) ) {
-                       $wgOut->addHTML( $prevLink );
-               }
-               if( isset( $prevLink ) && isset( $nextLink ) ) {
-                       $wgOut->addHTML( ' | ' );
-               }
-               if( isset( $nextLink ) ) {
-                       $wgOut->addHTML( $nextLink );
-               }
-               $wgOut->addHTML( '</p>' );
-
-       }
-
-}
-
-/**
- * @param int $ns the namespace of the article
- * @param string $text the name of the article
- * @return array( int namespace, string dbkey, string pagename ) or NULL on error
- * @static (sort of)
- * @access private
- */
-function getNamespaceKeyAndText ($ns, $text) {
-       if ( $text == '' )
-               return array( $ns, '', '' ); # shortcut for common case
-
-       $t = Title::makeTitleSafe($ns, $text);
-       if ( $t && $t->isLocal() ) {
-               return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
-       } else if ( $t ) {
-               return NULL;
-       }
-
-       # try again, in case the problem was an empty pagename
-       $text = preg_replace('/(#|$)/', 'X$1', $text);
-       $t = Title::makeTitleSafe($ns, $text);
-       if ( $t && $t->isLocal() ) {
-               return array( $t->getNamespace(), '', '' );
-       } else {
-               return NULL;
-       }
-}
-}
diff --git a/includes/specials/Ancientpages.php b/includes/specials/Ancientpages.php
deleted file mode 100644 (file)
index 724d34b..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Implements Special:Ancientpages
- * @ingroup SpecialPage
- */
-class AncientPagesPage extends QueryPage {
-
-       function getName() {
-               return "Ancientpages";
-       }
-
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               global $wgDBtype;
-               $db = wfGetDB( DB_SLAVE );
-               $page = $db->tableName( 'page' );
-               $revision = $db->tableName( 'revision' );
-               #$use_index = $db->useIndexClause( 'cur_timestamp' ); # FIXME! this is gone
-               $epoch = $wgDBtype == 'mysql' ? 'UNIX_TIMESTAMP(rev_timestamp)' :
-                       'EXTRACT(epoch FROM rev_timestamp)';
-               return
-                       "SELECT 'Ancientpages' as type,
-                                       page_namespace as namespace,
-                               page_title as title,
-                               $epoch as value
-                       FROM $page, $revision
-                       WHERE page_namespace=".NS_MAIN." AND page_is_redirect=0
-                         AND page_latest=rev_id";
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $result->value ), true );
-               $title = Title::makeTitle( $result->namespace, $result->title );
-               $link = $skin->makeKnownLinkObj( $title, htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) ) );
-               return wfSpecialList($link, $d);
-       }
-}
-
-function wfSpecialAncientpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $app = new AncientPagesPage();
-
-       $app->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Blockip.php b/includes/specials/Blockip.php
deleted file mode 100644 (file)
index 5ea25ca..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-<?php
-/**
- * Constructor for Special:Blockip page
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialBlockip( $par ) {
-       global $wgUser, $wgOut, $wgRequest;
-
-       # Can't block when the database is locked
-       if( wfReadOnly() ) {
-               $wgOut->readOnlyPage();
-               return;
-       }
-
-       # Permission check
-       if( !$wgUser->isAllowed( 'block' ) ) {
-               $wgOut->permissionRequired( 'block' );
-               return;
-       }
-
-       $ipb = new IPBlockForm( $par );
-
-       $action = $wgRequest->getVal( 'action' );
-       if ( 'success' == $action ) {
-               $ipb->showSuccess();
-       } else if ( $wgRequest->wasPosted() && 'submit' == $action &&
-               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               $ipb->doSubmit();
-       } else {
-               $ipb->showForm( '' );
-       }
-}
-
-/**
- * Form object for the Special:Blockip page.
- *
- * @ingroup SpecialPage
- */
-class IPBlockForm {
-       var $BlockAddress, $BlockExpiry, $BlockReason;
-#      var $BlockEmail;
-
-       function IPBlockForm( $par ) {
-               global $wgRequest, $wgUser;
-
-               $this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
-               $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
-               $this->BlockReason = $wgRequest->getText( 'wpBlockReason' );
-               $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' );
-               $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg('ipbotheroption') );
-               $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );
-
-               # Unchecked checkboxes are not included in the form data at all, so having one
-               # that is true by default is a bit tricky
-               $byDefault = !$wgRequest->wasPosted();
-               $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
-               $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
-               $this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
-               $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false );
-               $this->BlockWatchUser = $wgRequest->getBool( 'wpWatchUser', false );
-               # Re-check user's rights to hide names, very serious, defaults to 0
-               $this->BlockHideName = ( $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' ) ) ? 1 : 0;
-       }
-
-       function showForm( $err ) {
-               global $wgOut, $wgUser, $wgSysopUserBans;
-
-               $wgOut->setPagetitle( wfMsg( 'blockip' ) );
-               $wgOut->addWikiMsg( 'blockiptext' );
-
-               if($wgSysopUserBans) {
-                       $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' );
-               } else {
-                       $mIpaddress = Xml::label( wfMsg( 'ipaddress' ), 'mw-bi-target' );
-               }
-               $mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' );
-               $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' );
-               $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' );
-               $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );
-
-               $titleObj = SpecialPage::getTitleFor( 'Blockip' );
-
-               if ( "" != $err ) {
-                       $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
-                       $wgOut->addHTML( Xml::tags( 'p', array( 'class' => 'error' ), $err ) );
-               }
-
-               $scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );
-
-               $showblockoptions = $scBlockExpiryOptions != '-';
-               if (!$showblockoptions)
-                       $mIpbother = $mIpbexpiry;
-
-               $blockExpiryFormOptions = Xml::option( wfMsg( 'ipbotheroption' ), 'other' );
-               foreach (explode(',', $scBlockExpiryOptions) as $option) {
-                       if ( strpos($option, ":") === false ) $option = "$option:$option";
-                       list($show, $value) = explode(":", $option);
-                       $show = htmlspecialchars($show);
-                       $value = htmlspecialchars($value);
-                       $blockExpiryFormOptions .= Xml::option( $show, $value, $this->BlockExpiry === $value ? true : false ) . "\n";
-               }
-
-               $reasonDropDown = Xml::listDropDown( 'wpBlockReasonList',
-                       wfMsgForContent( 'ipbreason-dropdown' ),
-                       wfMsgForContent( 'ipbreasonotherlist' ), '', 'wpBlockDropDown', 4 );
-
-               global $wgStylePath, $wgStyleVersion;
-               $wgOut->addHTML(
-                       Xml::tags( 'script', array( 'type' => 'text/javascript', 'src' => "$wgStylePath/common/block.js?$wgStyleVersion" ), '' ) .
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( "action=submit" ), 'id' => 'blockip' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'blockip-legend' ) ) .
-                       Xml::openElement( 'table', array ( 'border' => '0', 'id' => 'mw-blockip-table' ) ) .
-                       "<tr>
-                               <td class='mw-label'>
-                                       {$mIpaddress}
-                               </td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'wpBlockAddress', 45, $this->BlockAddress,
-                                               array(
-                                                       'tabindex' => '1',
-                                                       'id' => 'mw-bi-target',
-                                                       'onchange' => 'updateBlockOptions()' ) ). "
-                               </td>
-                       </tr>
-                       <tr>"
-               );
-               if ( $showblockoptions ) {
-                       $wgOut->addHTML("
-                               <td class='mw-label'>
-                                       {$mIpbexpiry}
-                               </td>
-                               <td class='mw-input'>" .
-                                       Xml::tags( 'select',
-                                               array(
-                                                       'id' => 'wpBlockExpiry',
-                                                       'name' => 'wpBlockExpiry',
-                                                       'onchange' => 'considerChangingExpiryFocus()',
-                                                       'tabindex' => '2' ),
-                                               $blockExpiryFormOptions ) .
-                               "</td>"
-                       );
-               }
-               $wgOut->addHTML("
-                       </tr>
-                       <tr id='wpBlockOther'>
-                               <td class='mw-label'>
-                                       {$mIpbother}
-                               </td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'wpBlockOther', 45, $this->BlockOther,
-                                               array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "
-                               </td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>
-                                       {$mIpbreasonother}
-                               </td>
-                               <td class='mw-input'>
-                                       {$reasonDropDown}
-                               </td>
-                       </tr>
-                       <tr id=\"wpBlockReason\">
-                               <td class='mw-label'>
-                                       {$mIpbreason}
-                               </td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'wpBlockReason', 45, $this->BlockReason,
-                                               array( 'tabindex' => '5', 'id' => 'mw-bi-reason', 'maxlength'=> '200' ) ) . "
-                               </td>
-                       </tr>
-                       <tr id='wpAnonOnlyRow'>
-                               <td>&nbsp;</td>
-                               <td class='mw-input'>" .
-                               Xml::checkLabel( wfMsg( 'ipbanononly' ),
-                                               'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
-                                               array( 'tabindex' => '6' ) ) . "
-                               </td>
-                       </tr>
-                       <tr id='wpCreateAccountRow'>
-                               <td>&nbsp;</td>
-                               <td class='mw-input'>" .
-                                       Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
-                                               'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
-                                               array( 'tabindex' => '7' ) ) . "
-                               </td>
-                       </tr>
-                       <tr id='wpEnableAutoblockRow'>
-                               <td>&nbsp;</td>
-                               <td class='mw-input'>" .
-                                       Xml::checkLabel( wfMsg( 'ipbenableautoblock' ),
-                                               'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
-                                               array( 'tabindex' => '8' ) ) . "
-                               </td>
-                       </tr>"
-               );
-
-               global $wgSysopEmailBans;
-               if ( $wgSysopEmailBans && $wgUser->isAllowed( 'blockemail' ) ) {
-                       $wgOut->addHTML("
-                               <tr id='wpEnableEmailBan'>
-                                       <td>&nbsp;</td>
-                                       <td class='mw-input'>" .
-                                               Xml::checkLabel( wfMsg( 'ipbemailban' ),
-                                                       'wpEmailBan', 'wpEmailBan', $this->BlockEmail,
-                                                       array( 'tabindex' => '9' )) . "
-                                       </td>
-                               </tr>"
-                       );
-               }
-
-               // Allow some users to hide name from block log, blocklist and listusers
-               if ( $wgUser->isAllowed( 'hideuser' ) ) {
-                       $wgOut->addHTML("
-                               <tr id='wpEnableHideUser'>
-                                       <td>&nbsp;</td>
-                                       <td class='mw-input'>" .
-                                               Xml::checkLabel( wfMsg( 'ipbhidename' ),
-                                                       'wpHideName', 'wpHideName', $this->BlockHideName,
-                                                       array( 'tabindex' => '10' ) ) . "
-                                       </td>
-                               </tr>"
-                       );
-               }
-               
-               # Watchlist their user page?
-               $wgOut->addHTML("
-                       <tr id='wpEnableWatchUser'>
-                               <td>&nbsp;</td>
-                               <td class='mw-input'>" .
-                                       Xml::checkLabel( wfMsg( 'ipbwatchuser' ),
-                                               'wpWatchUser', 'wpWatchUser', $this->BlockWatchUser,
-                                               array( 'tabindex' => '11' ) ) . "
-                               </td>
-                       </tr>"
-               );
-
-               $wgOut->addHTML("
-                       <tr>
-                               <td style='padding-top: 1em'>&nbsp;</td>
-                               <td  class='mw-submit' style='padding-top: 1em'>" .
-                                       Xml::submitButton( wfMsg( 'ipbsubmit' ),
-                                               array( 'name' => 'wpBlock', 'tabindex' => '12' ) ) . "
-                               </td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) .
-                       Xml::tags( 'script', array( 'type' => 'text/javascript' ), 'updateBlockOptions()' ) . "\n"
-               );
-
-               $wgOut->addHtml( $this->getConvenienceLinks() );
-
-               $user = User::newFromName( $this->BlockAddress );
-               if( is_object( $user ) ) {
-                       $this->showLogFragment( $wgOut, $user->getUserPage() );
-               } elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
-                       $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
-               } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) {
-                       $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
-               }
-       }
-
-       /**
-        * Backend block code.
-        * $userID and $expiry will be filled accordingly
-        * @return array(message key, arguments) on failure, empty array on success
-        */
-       function doBlock(&$userId = null, &$expiry = null)
-       {
-               global $wgUser, $wgSysopUserBans, $wgSysopRangeBans;
-
-               $userId = 0;
-               # Expand valid IPv6 addresses, usernames are left as is
-               $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
-               # isIPv4() and IPv6() are used for final validation
-               $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
-               $rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}';
-               $rxIP = "($rxIP4|$rxIP6)";
-
-               # Check for invalid specifications
-               if ( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
-                       $matches = array();
-                       if ( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
-                               # IPv4
-                               if ( $wgSysopRangeBans ) {
-                                       if ( !IP::isIPv4( $this->BlockAddress ) || $matches[2] < 16 || $matches[2] > 32 ) {
-                                               return array('ip_range_invalid');
-                                       }
-                                       $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
-                               } else {
-                                       # Range block illegal
-                                       return array('range_block_disabled');
-                               }
-                       } else if ( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) {
-                               # IPv6
-                               if ( $wgSysopRangeBans ) {
-                                       if ( !IP::isIPv6( $this->BlockAddress ) || $matches[2] < 64 || $matches[2] > 128 ) {
-                                               return array('ip_range_invalid');
-                                       }
-                                       $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
-                               } else {
-                                       # Range block illegal
-                                       return array('range_block_disabled');
-                               }
-                       } else {
-                               # Username block
-                               if ( $wgSysopUserBans ) {
-                                       $user = User::newFromName( $this->BlockAddress );
-                                       if( !is_null( $user ) && $user->getId() ) {
-                                               # Use canonical name
-                                               $userId = $user->getId();
-                                               $this->BlockAddress = $user->getName();
-                                       } else {
-                                               return array('nosuchusershort', htmlspecialchars( $user ? $user->getName() : $this->BlockAddress ) );
-                                       }
-                               } else {
-                                       return array('badipaddress');
-                               }
-                       }
-               }
-
-               $reasonstr = $this->BlockReasonList;
-               if ( $reasonstr != 'other' && $this->BlockReason != '') {
-                       // Entry from drop down menu + additional comment
-                       $reasonstr .= ': ' . $this->BlockReason;
-               } elseif ( $reasonstr == 'other' ) {
-                       $reasonstr = $this->BlockReason;
-               }
-
-               $expirestr = $this->BlockExpiry;
-               if( $expirestr == 'other' )
-                       $expirestr = $this->BlockOther;
-
-               if (strlen($expirestr) == 0) {
-                       return array('ipb_expiry_invalid');
-               }
-               
-               if ( false === ($expiry = Block::parseExpiryInput( $expirestr )) ) {
-                       // Bad expiry.
-                       return array('ipb_expiry_invalid');
-               }
-               
-               if( $this->BlockHideName && $expiry != 'infinity' ) {
-                       // Bad expiry.
-                       return array('ipb_expiry_temp');
-               }
-
-               # Create block
-               # Note: for a user block, ipb_address is only for display purposes
-               $block = new Block( $this->BlockAddress, $userId, $wgUser->getId(),
-                       $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
-                       $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,
-                       $this->BlockEmail );
-
-               if ( wfRunHooks('BlockIp', array(&$block, &$wgUser)) ) {
-
-                       if ( !$block->insert() ) {
-                               return array('ipb_already_blocked', htmlspecialchars($this->BlockAddress));
-                       }
-
-                       wfRunHooks('BlockIpComplete', array($block, $wgUser));
-
-                       if ( $this->BlockWatchUser ) { 
-                               $wgUser->addWatch ( Title::makeTitle( NS_USER, $this->BlockAddress ) );
-                       }
-
-                       # Prepare log parameters
-                       $logParams = array();
-                       $logParams[] = $expirestr;
-                       $logParams[] = $this->blockLogFlags();
-
-                       # Make log entry, if the name is hidden, put it in the oversight log
-                       $log_type = ($this->BlockHideName) ? 'suppress' : 'block';
-                       $log = new LogPage( $log_type );
-                       $log->addEntry( 'block', Title::makeTitle( NS_USER, $this->BlockAddress ),
-                         $reasonstr, $logParams );
-
-                       # Report to the user
-                       return array();
-               }
-               else
-                       return array('hookaborted');
-       }
-
-       /**
-        * UI entry point for blocking
-        * Wraps around doBlock()
-        */
-       function doSubmit()
-       {
-               global $wgOut;
-               $retval = $this->doBlock();
-               if(empty($retval)) {
-                       $titleObj = SpecialPage::getTitleFor( 'Blockip' );
-                       $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip=' .
-                               urlencode( $this->BlockAddress ) ) );
-                       return;
-               }
-               $key = array_shift($retval);
-               $this->showForm(wfMsgReal($key, $retval));
-       }
-
-       function showSuccess() {
-               global $wgOut;
-
-               $wgOut->setPagetitle( wfMsg( 'blockip' ) );
-               $wgOut->setSubtitle( wfMsg( 'blockipsuccesssub' ) );
-               $text = wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->BlockAddress );
-               $wgOut->addHtml( $text );
-       }
-
-       function showLogFragment( $out, $title ) {
-               $out->addHtml( Xml::element( 'h2', NULL, LogPage::logName( 'block' ) ) );
-               LogEventsList::showLogExtract( $out, 'block', $title->getPrefixedText() );
-       }
-
-       /**
-        * Return a comma-delimited list of "flags" to be passed to the log
-        * reader for this block, to provide more information in the logs
-        *
-        * @return array
-        */
-       private function blockLogFlags() {
-               $flags = array();
-               if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) )
-                                       // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
-                       $flags[] = 'anononly';
-               if( $this->BlockCreateAccount )
-                       $flags[] = 'nocreate';
-               if( !$this->BlockEnableAutoblock )
-                       $flags[] = 'noautoblock';
-               if ( $this->BlockEmail )
-                       $flags[] = 'noemail';
-               return implode( ',', $flags );
-       }
-
-       /**
-        * Builds unblock and block list links
-        *
-        * @return string
-        */
-       private function getConvenienceLinks() {
-               global $wgUser;
-               $skin = $wgUser->getSkin();
-               $links[] = $skin->makeLink ( 'MediaWiki:Ipbreason-dropdown', wfMsgHtml( 'ipb-edit-dropdown' ) );
-               $links[] = $this->getUnblockLink( $skin );
-               $links[] = $this->getBlockListLink( $skin );
-               return '<p class="mw-ipb-conveniencelinks">' . implode( ' | ', $links ) . '</p>';
-       }
-
-       /**
-        * Build a convenient link to unblock the given username or IP
-        * address, if available; otherwise link to a blank unblock
-        * form
-        *
-        * @param $skin Skin to use
-        * @return string
-        */
-       private function getUnblockLink( $skin ) {
-               $list = SpecialPage::getTitleFor( 'Ipblocklist' );
-               if( $this->BlockAddress ) {
-                       $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
-                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock-addr', $addr ),
-                               'action=unblock&ip=' . urlencode( $this->BlockAddress ) );
-               } else {
-                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock' ),      'action=unblock' );
-               }
-       }
-
-       /**
-        * Build a convenience link to the block list
-        *
-        * @param $skin Skin to use
-        * @return string
-        */
-       private function getBlockListLink( $skin ) {
-               $list = SpecialPage::getTitleFor( 'Ipblocklist' );
-               if( $this->BlockAddress ) {
-                       $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
-                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist-addr', $addr ),
-                               'ip=' . urlencode( $this->BlockAddress ) );
-               } else {
-                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist' ) );
-               }
-       }
-}
diff --git a/includes/specials/Blockme.php b/includes/specials/Blockme.php
deleted file mode 100644 (file)
index f222e3c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- *
- */
-function wfSpecialBlockme() {
-       global $wgRequest, $wgBlockOpenProxies, $wgOut, $wgProxyKey;
-
-       $ip = wfGetIP();
-
-       if( !$wgBlockOpenProxies || $wgRequest->getText( 'ip' ) != md5( $ip . $wgProxyKey ) ) {
-               $wgOut->addWikiMsg( 'proxyblocker-disabled' );
-               return;
-       }
-
-       $blockerName = wfMsg( "proxyblocker" );
-       $reason = wfMsg( "proxyblockreason" );
-
-       $u = User::newFromName( $blockerName );
-       $id = $u->idForName();
-       if ( !$id ) {
-               $u = User::newFromName( $blockerName );
-               $u->addToDatabase();
-               $u->setPassword( bin2hex( mt_rand(0, 0x7fffffff ) ) );
-               $u->saveSettings();
-               $id = $u->getID();
-       }
-
-       $block = new Block( $ip, 0, $id, $reason, wfTimestampNow() );
-       $block->insert();
-
-       $wgOut->addWikiMsg( "proxyblocksuccess" );
-}
diff --git a/includes/specials/Booksources.php b/includes/specials/Booksources.php
deleted file mode 100644 (file)
index 0690c5c..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-/**
- * Special page outputs information on sourcing a book with a particular ISBN
- * The parser creates links to this page when dealing with ISBNs in wikitext
- *
- * @author Rob Church <robchur@gmail.com>
- * @todo Validate ISBNs using the standard check-digit method
- * @ingroup SpecialPages
- */
-class SpecialBookSources extends SpecialPage {
-
-       /**
-        * ISBN passed to the page, if any
-        */
-       private $isbn = '';
-
-       /**
-        * Constructor
-        */
-       public function __construct() {
-               parent::__construct( 'Booksources' );
-       }
-
-       /**
-        * Show the special page
-        *
-        * @param $isbn ISBN passed as a subpage parameter
-        */
-       public function execute( $isbn ) {
-               global $wgOut, $wgRequest;
-               $this->setHeaders();
-               $this->isbn = $this->cleanIsbn( $isbn ? $isbn : $wgRequest->getText( 'isbn' ) );
-               $wgOut->addWikiMsg( 'booksources-summary' );
-               $wgOut->addHtml( $this->makeForm() );
-               if( strlen( $this->isbn ) > 0 )
-                       $this->showList();
-       }
-
-       /**
-        * Trim ISBN and remove characters which aren't required
-        *
-        * @param $isbn Unclean ISBN
-        * @return string
-        */
-       private function cleanIsbn( $isbn ) {
-               return trim( preg_replace( '![^0-9X]!', '', $isbn ) );
-       }
-
-       /**
-        * Generate a form to allow users to enter an ISBN
-        *
-        * @return string
-        */
-       private function makeForm() {
-               global $wgScript;
-               $title = self::getTitleFor( 'Booksources' );
-               $form  = '<fieldset><legend>' . wfMsgHtml( 'booksources-search-legend' ) . '</legend>';
-               $form .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
-               $form .= Xml::hidden( 'title', $title->getPrefixedText() );
-               $form .= '<p>' . Xml::inputLabel( wfMsg( 'booksources-isbn' ), 'isbn', 'isbn', 20, $this->isbn );
-               $form .= '&nbsp;' . Xml::submitButton( wfMsg( 'booksources-go' ) ) . '</p>';
-               $form .= Xml::closeElement( 'form' );
-               $form .= '</fieldset>';
-               return $form;
-       }
-
-       /**
-        * Determine where to get the list of book sources from,
-        * format and output them
-        *
-        * @return string
-        */
-       private function showList() {
-               global $wgOut, $wgContLang;
-
-               # Hook to allow extensions to insert additional HTML,
-               # e.g. for API-interacting plugins and so on
-               wfRunHooks( 'BookInformation', array( $this->isbn, &$wgOut ) );
-
-               # Check for a local page such as Project:Book_sources and use that if available
-               $title = Title::makeTitleSafe( NS_PROJECT, wfMsgForContent( 'booksources' ) ); # Show list in content language
-               if( is_object( $title ) && $title->exists() ) {
-                       $rev = Revision::newFromTitle( $title );
-                       $wgOut->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $rev->getText() ) );
-                       return true;
-               }
-
-               # Fall back to the defaults given in the language file
-               $wgOut->addWikiMsg( 'booksources-text' );
-               $wgOut->addHtml( '<ul>' );
-               $items = $wgContLang->getBookstoreList();
-               foreach( $items as $label => $url )
-                       $wgOut->addHtml( $this->makeListItem( $label, $url ) );
-               $wgOut->addHtml( '</ul>' );
-               return true;
-       }
-
-       /**
-        * Format a book source list item
-        *
-        * @param $label Book source label
-        * @param $url Book source URL
-        * @return string
-        */
-       private function makeListItem( $label, $url ) {
-               $url = str_replace( '$1', $this->isbn, $url );
-               return '<li><a href="' . htmlspecialchars( $url ) . '">' . htmlspecialchars( $label ) . '</a></li>';
-       }
-}
diff --git a/includes/specials/BrokenRedirects.php b/includes/specials/BrokenRedirects.php
deleted file mode 100644 (file)
index 0a16e6d..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page listing redirects to non existent page. Those should be
- * fixed to point to an existing page.
- * @ingroup SpecialPage
- */
-class BrokenRedirectsPage extends PageQueryPage {
-       var $targets = array();
-
-       function getName() {
-               return 'BrokenRedirects';
-       }
-
-       function isExpensive( ) { return true; }
-       function isSyndicated() { return false; }
-
-       function getPageHeader( ) {
-               return wfMsgExt( 'brokenredirectstext', array( 'parse' ) );
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
-
-               $sql = "SELECT 'BrokenRedirects'  AS type,
-                               p1.page_namespace AS namespace,
-                               p1.page_title     AS title,
-                               rd_namespace,
-                               rd_title
-                          FROM $redirect AS rd
-                   JOIN $page p1 ON (rd.rd_from=p1.page_id)
-                     LEFT JOIN $page AS p2 ON (rd_namespace=p2.page_namespace AND rd_title=p2.page_title )
-                                 WHERE rd_namespace >= 0
-                                   AND p2.page_namespace IS NULL";
-               return $sql;
-       }
-
-       function getOrder() {
-               return '';
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgUser, $wgContLang;
-
-               $fromObj = Title::makeTitle( $result->namespace, $result->title );
-               if ( isset( $result->rd_title ) ) {
-                       $toObj = Title::makeTitle( $result->rd_namespace, $result->rd_title );
-               } else {
-                       $blinks = $fromObj->getBrokenLinksFrom(); # TODO: check for redirect, not for links
-                       if ( $blinks ) {
-                               $toObj = $blinks[0];
-                       } else {
-                               $toObj = false;
-                       }
-               }
-
-               // $toObj may very easily be false if the $result list is cached
-               if ( !is_object( $toObj ) ) {
-                       return '<s>' . $skin->makeLinkObj( $fromObj ) . '</s>';
-               }
-
-               $from = $skin->makeKnownLinkObj( $fromObj ,'', 'redirect=no' );
-               $edit = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-edit' ), 'action=edit' );
-               $to   = $skin->makeBrokenLinkObj( $toObj );
-               $arr = $wgContLang->getArrow();
-
-               $out = "{$from} {$edit}";
-
-               if( $wgUser->isAllowed( 'delete' ) ) {
-                       $delete = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-delete' ), 'action=delete' );
-                       $out .= " {$delete}";
-               }
-
-               $out .= " {$arr} {$to}";
-               return $out;
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialBrokenRedirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sbr = new BrokenRedirectsPage();
-
-       return $sbr->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Categories.php b/includes/specials/Categories.php
deleted file mode 100644 (file)
index 951c222..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-function wfSpecialCategories( $par=null ) {
-       global $wgOut, $wgRequest;
-
-       if( $par == '' ) {
-               $from = $wgRequest->getText( 'from' );
-       } else {
-               $from = $par;
-       }
-       $cap = new CategoryPager( $from );
-       $wgOut->addHTML(
-               wfMsgExt( 'categoriespagetext', array( 'parse' ) ) .
-               $cap->getStartForm( $from ) .
-               $cap->getNavigationBar() .
-               '<ul>' . $cap->getBody() . '</ul>' .
-               $cap->getNavigationBar()
-       );
-}
-
-/**
- * TODO: Allow sorting by count.  We need to have a unique index to do this
- * properly.
- *
- * @ingroup SpecialPage Pager
- */
-class CategoryPager extends AlphabeticPager {
-       function __construct( $from ) {
-               parent::__construct();
-               $from = str_replace( ' ', '_', $from );
-               if( $from !== '' ) {
-                       global $wgCapitalLinks, $wgContLang;
-                       if( $wgCapitalLinks ) {
-                               $from = $wgContLang->ucfirst( $from );
-                       }
-                       $this->mOffset = $from;
-               }
-       }
-       
-       function getQueryInfo() {
-               global $wgRequest;
-               return array(
-                       'tables' => array( 'category' ),
-                       'fields' => array( 'cat_title','cat_pages' ),
-                       'conds' => array( 'cat_pages > 0' ), 
-                       'options' => array( 'USE INDEX' => 'cat_title' ),
-               );
-       }
-
-       function getIndexField() {
-#              return array( 'abc' => 'cat_title', 'count' => 'cat_pages' );
-               return 'cat_title';
-       }
-
-       function getDefaultQuery() {
-               parent::getDefaultQuery();
-               unset( $this->mDefaultQuery['from'] );
-       }
-#      protected function getOrderTypeMessages() {
-#              return array( 'abc' => 'special-categories-sort-abc',
-#                      'count' => 'special-categories-sort-count' );
-#      }
-
-       protected function getDefaultDirections() {
-#              return array( 'abc' => false, 'count' => true );
-               return false;
-       }
-
-       /* Override getBody to apply LinksBatch on resultset before actually outputting anything. */
-       public function getBody() {
-               if (!$this->mQueryDone) {
-                       $this->doQuery();
-               }
-               $batch = new LinkBatch;
-
-               $this->mResult->rewind();
-
-               while ( $row = $this->mResult->fetchObject() ) {
-                       $batch->addObj( Title::makeTitleSafe( NS_CATEGORY, $row->cat_title ) );
-               }
-               $batch->execute();
-               $this->mResult->rewind();
-               return parent::getBody();
-       }
-
-       function formatRow($result) {
-               global $wgLang;
-               $title = Title::makeTitle( NS_CATEGORY, $result->cat_title );
-               $titleText = $this->getSkin()->makeLinkObj( $title, htmlspecialchars( $title->getText() ) );
-               $count = wfMsgExt( 'nmembers', array( 'parsemag', 'escape' ),
-                               $wgLang->formatNum( $result->cat_pages ) );
-               return Xml::tags('li', null, "$titleText ($count)" ) . "\n";
-       }
-       
-       public function getStartForm( $from ) {
-               global $wgScript;
-               $t = SpecialPage::getTitleFor( 'Categories' );
-       
-               return
-                       Xml::tags( 'form', array( 'method' => 'get', 'action' => $wgScript ),
-                               Xml::hidden( 'title', $t->getPrefixedText() ) .
-                               Xml::fieldset( wfMsg( 'categories' ),
-                                       Xml::inputLabel( wfMsg( 'categoriesfrom' ),
-                                               'from', 'from', 20, $from ) .
-                                       ' ' .
-                                       Xml::submitButton( wfMsg( 'allpagessubmit' ) ) ) );
-       }
-}
diff --git a/includes/specials/Confirmemail.php b/includes/specials/Confirmemail.php
deleted file mode 100644 (file)
index 9075fb9..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-/**
- * Special page allows users to request email confirmation message, and handles
- * processing of the confirmation code when the link in the email is followed
- *
- * @ingroup SpecialPage
- * @author Brion Vibber
- * @author Rob Church <robchur@gmail.com>
- */
-class EmailConfirmation extends UnlistedSpecialPage {
-
-       /**
-        * Constructor
-        */
-       public function __construct() {
-               parent::__construct( 'Confirmemail' );
-       }
-
-       /**
-        * Main execution point
-        *
-        * @param $code Confirmation code passed to the page
-        */
-       function execute( $code ) {
-               global $wgUser, $wgOut;
-               $this->setHeaders();
-               if( empty( $code ) ) {
-                       if( $wgUser->isLoggedIn() ) {
-                               if( User::isValidEmailAddr( $wgUser->getEmail() ) ) {
-                                       $this->showRequestForm();
-                               } else {
-                                       $wgOut->addWikiMsg( 'confirmemail_noemail' );
-                               }
-                       } else {
-                               $title = SpecialPage::getTitleFor( 'Userlogin' );
-                               $self = SpecialPage::getTitleFor( 'Confirmemail' );
-                               $skin = $wgUser->getSkin();
-                               $llink = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $self->getPrefixedUrl() );
-                               $wgOut->addHtml( wfMsgWikiHtml( 'confirmemail_needlogin', $llink ) );
-                       }
-               } else {
-                       $this->attemptConfirm( $code );
-               }
-       }
-
-       /**
-        * Show a nice form for the user to request a confirmation mail
-        */
-       function showRequestForm() {
-               global $wgOut, $wgUser, $wgLang, $wgRequest;
-               if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getText( 'token' ) ) ) {
-                       $ok = $wgUser->sendConfirmationMail();
-                       if ( WikiError::isError( $ok ) ) {
-                               $wgOut->addWikiMsg( 'confirmemail_sendfailed', $ok->toString() );
-                       } else {
-                               $wgOut->addWikiMsg( 'confirmemail_sent' );
-                       }
-               } else {
-                       if( $wgUser->isEmailConfirmed() ) {
-                               $time = $wgLang->timeAndDate( $wgUser->mEmailAuthenticated, true );
-                               $wgOut->addWikiMsg( 'emailauthenticated', $time );
-                       }
-                       if( $wgUser->isEmailConfirmationPending() ) {
-                               $wgOut->addWikiMsg( 'confirmemail_pending' );
-                       }
-                       $wgOut->addWikiMsg( 'confirmemail_text' );
-                       $self = SpecialPage::getTitleFor( 'Confirmemail' );
-                       $form  = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
-                       $form .= wfHidden( 'token', $wgUser->editToken() );
-                       $form .= wfSubmitButton( wfMsgHtml( 'confirmemail_send' ) );
-                       $form .= wfCloseElement( 'form' );
-                       $wgOut->addHtml( $form );
-               }
-       }
-
-       /**
-        * Attempt to confirm the user's email address and show success or failure
-        * as needed; if successful, take the user to log in
-        *
-        * @param $code Confirmation code
-        */
-       function attemptConfirm( $code ) {
-               global $wgUser, $wgOut;
-               $user = User::newFromConfirmationCode( $code );
-               if( is_object( $user ) ) {
-                       $user->confirmEmail();
-                       $user->saveSettings();
-                       $message = $wgUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success';
-                       $wgOut->addWikiMsg( $message );
-                       if( !$wgUser->isLoggedIn() ) {
-                               $title = SpecialPage::getTitleFor( 'Userlogin' );
-                               $wgOut->returnToMain( true, $title );
-                       }
-               } else {
-                       $wgOut->addWikiMsg( 'confirmemail_invalid' );
-               }
-       }
-
-}
-
-/**
- * Special page allows users to cancel an email confirmation using the e-mail
- * confirmation code
- *
- * @ingroup SpecialPage
- */
-class EmailInvalidation extends UnlistedSpecialPage {
-
-       public function __construct() {
-               parent::__construct( 'Invalidateemail' );
-       }
-
-       function execute( $code ) {
-               $this->setHeaders();
-               $this->attemptInvalidate( $code );
-       }
-
-       /**
-        * Attempt to invalidate the user's email address and show success or failure
-        * as needed; if successful, link to main page
-        *
-        * @param $code Confirmation code
-        */
-       function attemptInvalidate( $code ) {
-               global $wgUser, $wgOut;
-               $user = User::newFromConfirmationCode( $code );
-               if( is_object( $user ) ) {
-                       $user->invalidateEmail();
-                       $user->saveSettings();
-                       $wgOut->addWikiMsg( 'confirmemail_invalidated' );
-                       if( !$wgUser->isLoggedIn() ) {
-                               $wgOut->returnToMain();
-                       }
-               } else {
-                       $wgOut->addWikiMsg( 'confirmemail_invalid' );
-               }
-       }
-}
diff --git a/includes/specials/Contributions.php b/includes/specials/Contributions.php
deleted file mode 100644 (file)
index c9cbc18..0000000
+++ /dev/null
@@ -1,465 +0,0 @@
-<?php
-/**
- * Special:Contributions, show user contributions in a paged list
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Pager for Special:Contributions
- * @ingroup SpecialPage Pager
- */
-class ContribsPager extends ReverseChronologicalPager {
-       public $mDefaultDirection = true;
-       var $messages, $target;
-       var $namespace = '', $year = '', $month = '', $mDb;
-
-       function __construct( $target, $namespace = false, $year = false, $month = false ) {
-               parent::__construct();
-               foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist newpageletter minoreditletter' ) as $msg ) {
-                       $this->messages[$msg] = wfMsgExt( $msg, array( 'escape') );
-               }
-               $this->target = $target;
-               $this->namespace = $namespace;
-
-               $year = intval($year);
-               $month = intval($month);
-
-               $this->year = $year > 0 ? $year : false;
-               $this->month = ($month > 0 && $month < 13) ? $month : false;
-               $this->getDateCond();
-
-               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
-       }
-
-       function getDefaultQuery() {
-               $query = parent::getDefaultQuery();
-               $query['target'] = $this->target;
-               $query['month'] = $this->month;
-               $query['year'] = $this->year;
-               return $query;
-       }
-
-       function getQueryInfo() {
-               list( $index, $userCond ) = $this->getUserCond();
-               $conds = array_merge( array('page_id=rev_page'), $userCond, $this->getNamespaceCond() );
-               return array(
-                       'tables' => array( 'page', 'revision' ),
-                       'fields' => array(
-                               'page_namespace', 'page_title', 'page_is_new', 'page_latest', 'rev_id', 'rev_page',
-                               'rev_text_id', 'rev_timestamp', 'rev_comment', 'rev_minor_edit', 'rev_user',
-                               'rev_user_text', 'rev_parent_id', 'rev_deleted'
-                       ),
-                       'conds' => $conds,
-                       'options' => array( 'USE INDEX' => $index )
-               );
-       }
-
-       function getUserCond() {
-               $condition = array();
-
-               if ( $this->target == 'newbies' ) {
-                       $max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
-                       $condition[] = 'rev_user >' . (int)($max - $max / 100);
-                       $index = 'user_timestamp';
-               } else {
-                       $condition['rev_user_text'] = $this->target;
-                       $index = 'usertext_timestamp';
-               }
-               return array( $index, $condition );
-       }
-
-       function getNamespaceCond() {
-               if ( $this->namespace !== '' ) {
-                       return array( 'page_namespace' => (int)$this->namespace );
-               } else {
-                       return array();
-               }
-       }
-
-       function getDateCond() {
-               // Given an optional year and month, we need to generate a timestamp
-               // to use as "WHERE rev_timestamp <= result"
-               // Examples: year = 2006 equals < 20070101 (+000000)
-               // year=2005, month=1    equals < 20050201
-               // year=2005, month=12   equals < 20060101
-
-               if (!$this->year && !$this->month)
-                       return;
-
-               if ( $this->year ) {
-                       $year = $this->year;
-               }
-               else {
-                       // If no year given, assume the current one
-                       $year = gmdate( 'Y' );
-                       // If this month hasn't happened yet this year, go back to last year's month
-                       if( $this->month > gmdate( 'n' ) ) {
-                               $year--;
-                       }
-               }
-
-               if ( $this->month ) {
-                       $month = $this->month + 1;
-                       // For December, we want January 1 of the next year
-                       if ($month > 12) {
-                               $month = 1;
-                               $year++;
-                       }
-               }
-               else {
-                       // No month implies we want up to the end of the year in question
-                       $month = 1;
-                       $year++;
-               }
-
-               if ($year > 2032)
-                       $year = 2032;
-               $ymd = (int)sprintf( "%04d%02d01", $year, $month );
-
-               // Y2K38 bug
-               if ($ymd > 20320101)
-                       $ymd = 20320101;
-
-               $this->mOffset = $this->mDb->timestamp( "${ymd}000000" );
-       }
-
-       function getIndexField() {
-               return 'rev_timestamp';
-       }
-
-       function getStartBody() {
-               return "<ul>\n";
-       }
-
-       function getEndBody() {
-               return "</ul>\n";
-       }
-
-       /**
-        * Generates each row in the contributions list.
-        *
-        * Contributions which are marked "top" are currently on top of the history.
-        * For these contributions, a [rollback] link is shown for users with roll-
-        * back privileges. The rollback link restores the most recent version that
-        * was not written by the target user.
-        *
-        * @todo This would probably look a lot nicer in a table.
-        */
-       function formatRow( $row ) {
-               wfProfileIn( __METHOD__ );
-
-               global $wgLang, $wgUser, $wgContLang;
-
-               $sk = $this->getSkin();
-               $rev = new Revision( $row );
-
-               $page = Title::makeTitle( $row->page_namespace, $row->page_title );
-               $link = $sk->makeKnownLinkObj( $page );
-               $difftext = $topmarktext = '';
-               if( $row->rev_id == $row->page_latest ) {
-                       $topmarktext .= '<strong>' . $this->messages['uctop'] . '</strong>';
-                       if( !$row->page_is_new ) {
-                               $difftext .= '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=0' ) . ')';
-                       } else {
-                               $difftext .= $this->messages['newarticle'];
-                       }
-
-                       if( !$page->getUserPermissionsErrors( 'rollback', $wgUser )
-                       &&  !$page->getUserPermissionsErrors( 'edit', $wgUser ) ) {
-                               $topmarktext .= ' '.$sk->generateRollback( $rev );
-                       }
-
-               }
-               # Is there a visible previous revision?
-               if( $rev->userCan(Revision::DELETED_TEXT) ) {
-                       $difftext = '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
-               } else {
-                       $difftext = '(' . $this->messages['diff'] . ')';
-               }
-               $histlink='('.$sk->makeKnownLinkObj( $page, $this->messages['hist'], 'action=history' ) . ')';
-
-               $comment = $wgContLang->getDirMark() . $sk->revComment( $rev, false, true );
-               $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
-
-               if( $this->target == 'newbies' ) {
-                       $userlink = ' . . ' . $sk->userLink( $row->rev_user, $row->rev_user_text );
-                       $userlink .= ' (' . $sk->userTalkLink( $row->rev_user, $row->rev_user_text ) . ') ';
-               } else {
-                       $userlink = '';
-               }
-
-               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
-                       $d = '<span class="history-deleted">' . $d . '</span>';
-               }
-
-               if( $rev->getParentId() === 0 ) {
-                       $nflag = '<span class="newpage">' . $this->messages['newpageletter'] . '</span>';
-               } else {
-                       $nflag = '';
-               }
-
-               if( $row->rev_minor_edit ) {
-                       $mflag = '<span class="minor">' . $this->messages['minoreditletter'] . '</span> ';
-               } else {
-                       $mflag = '';
-               }
-
-               $ret = "{$d} {$histlink} {$difftext} {$nflag}{$mflag} {$link}{$userlink}{$comment} {$topmarktext}";
-               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
-                       $ret .= ' ' . wfMsgHtml( 'deletedrev' );
-               }
-               $ret = "<li>$ret</li>\n";
-               wfProfileOut( __METHOD__ );
-               return $ret;
-       }
-
-       /**
-        * Get the Database object in use
-        *
-        * @return Database
-        */
-       public function getDatabase() {
-               return $this->mDb;
-       }
-
-}
-
-/**
- * Special page "user contributions".
- * Shows a list of the contributions of a user.
- *
- * @return     none
- * @param      $par    String: (optional) user name of the user for which to show the contributions
- */
-function wfSpecialContributions( $par = null ) {
-       global $wgUser, $wgOut, $wgLang, $wgRequest;
-
-       $options = array();
-
-       if ( isset( $par ) && $par == 'newbies' ) {
-               $target = 'newbies';
-               $options['contribs'] = 'newbie';
-       } elseif ( isset( $par ) ) {
-               $target = $par;
-       } else {
-               $target = $wgRequest->getVal( 'target' );
-       }
-
-       // check for radiobox
-       if ( $wgRequest->getVal( 'contribs' ) == 'newbie' ) {
-               $target = 'newbies';
-               $options['contribs'] = 'newbie';
-       }
-
-       if ( !strlen( $target ) ) {
-               $wgOut->addHTML( contributionsForm( '' ) );
-               return;
-       }
-
-       $options['limit'] = $wgRequest->getInt( 'limit', 50 );
-       $options['target'] = $target;
-
-       $nt = Title::makeTitleSafe( NS_USER, $target );
-       if ( !$nt ) {
-               $wgOut->addHTML( contributionsForm( '' ) );
-               return;
-       }
-       $id = User::idFromName( $nt->getText() );
-
-       if ( $target != 'newbies' ) {
-               $target = $nt->getText();
-               $wgOut->setSubtitle( contributionsSub( $nt, $id ) );
-       } else {
-               $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') );
-       }
-
-       if ( ( $ns = $wgRequest->getVal( 'namespace', null ) ) !== null && $ns !== '' ) {
-               $options['namespace'] = intval( $ns );
-       } else {
-               $options['namespace'] = '';
-       }
-       if ( $wgUser->isAllowed( 'markbotedit' ) && $wgRequest->getBool( 'bot' ) ) {
-               $options['bot'] = '1';
-       }
-
-       $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
-       # Offset overrides year/month selection
-       if ( ( $month = $wgRequest->getIntOrNull( 'month' ) ) !== null && $month !== -1 ) {
-               $options['month'] = intval( $month );
-       } else {
-               $options['month'] = '';
-       }
-       if ( ( $year = $wgRequest->getIntOrNull( 'year' ) ) !== null ) {
-               $options['year'] = intval( $year );
-       } else if( $options['month'] ) {
-               $thisMonth = intval( gmdate( 'n' ) );
-               $thisYear = intval( gmdate( 'Y' ) );
-               if( intval( $options['month'] ) > $thisMonth ) {
-                       $thisYear--;
-               }
-               $options['year'] = $thisYear;
-       } else {
-               $options['year'] = '';
-       }
-
-       wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id );
-
-       if( $skip ) {
-               $options['year'] = '';
-               $options['month'] = '';
-       }
-
-       $wgOut->addHTML( contributionsForm( $options ) );
-
-       $pager = new ContribsPager( $target, $options['namespace'], $options['year'], $options['month'] );
-       if ( !$pager->getNumRows() ) {
-               $wgOut->addWikiMsg( 'nocontribs' );
-               return;
-       }
-
-       # Show a message about slave lag, if applicable
-       if( ( $lag = $pager->getDatabase()->getLag() ) > 0 )
-               $wgOut->showLagWarning( $lag );
-
-       $wgOut->addHTML(
-               '<p>' . $pager->getNavigationBar() . '</p>' .
-               $pager->getBody() .
-               '<p>' . $pager->getNavigationBar() . '</p>' );
-
-       # If there were contributions, and it was a valid user or IP, show
-       # the appropriate "footer" message - WHOIS tools, etc.
-       if( $target != 'newbies' ) {
-               $message = IP::isIPAddress( $target )
-                       ? 'sp-contributions-footer-anon'
-                       : 'sp-contributions-footer';
-
-
-               $text = wfMsgNoTrans( $message, $target );
-               if( !wfEmptyMsg( $message, $text ) && $text != '-' ) {
-                       $wgOut->addHtml( '<div class="mw-contributions-footer">' );
-                       $wgOut->addWikiText( $text );
-                       $wgOut->addHtml( '</div>' );
-               }
-       }
-}
-
-/**
- * Generates the subheading with links
- * @param Title $nt Title object for the target
- * @param integer $id User ID for the target
- * @return String: appropriately-escaped HTML to be output literally
- */
-function contributionsSub( $nt, $id ) {
-       global $wgSysopUserBans, $wgLang, $wgUser;
-
-       $sk = $wgUser->getSkin();
-
-       if ( 0 == $id ) {
-               $user = $nt->getText();
-       } else {
-               $user = $sk->makeLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
-       }
-       $talk = $nt->getTalkPage();
-       if( $talk ) {
-               # Talk page link
-               $tools[] = $sk->makeLinkObj( $talk, wfMsgHtml( 'talkpagelinktext' ) );
-               if( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $nt->getText() ) ) ) {
-                       # Block link
-                       if( $wgUser->isAllowed( 'block' ) )
-                               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $nt->getDBkey() ), wfMsgHtml( 'blocklink' ) );
-                       # Block log link
-                       $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'sp-contributions-blocklog' ), 'type=block&page=' . $nt->getPrefixedUrl() );
-               }
-               # Other logs link
-               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
-
-               wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) );
-
-               $links = implode( ' | ', $tools );
-       }
-
-       // Old message 'contribsub' had one parameter, but that doesn't work for
-       // languages that want to put the "for" bit right after $user but before
-       // $links.  If 'contribsub' is around, use it for reverse compatibility,
-       // otherwise use 'contribsub2'.
-       if( wfEmptyMsg( 'contribsub', wfMsg( 'contribsub' ) ) ) {
-               return wfMsgHtml( 'contribsub2', $user, $links );
-       } else {
-               return wfMsgHtml( 'contribsub', "$user ($links)" );
-       }
-}
-
-/**
- * Generates the namespace selector form with hidden attributes.
- * @param $options Array: the options to be included.
- */
-function contributionsForm( $options ) {
-       global $wgScript, $wgTitle, $wgRequest;
-
-       $options['title'] = $wgTitle->getPrefixedText();
-       if ( !isset( $options['target'] ) ) {
-               $options['target'] = '';
-       } else {
-               $options['target'] = str_replace( '_' , ' ' , $options['target'] );
-       }
-
-       if ( !isset( $options['namespace'] ) ) {
-               $options['namespace'] = '';
-       }
-
-       if ( !isset( $options['contribs'] ) ) {
-               $options['contribs'] = 'user';
-       }
-
-       if ( !isset( $options['year'] ) ) {
-               $options['year'] = '';
-       }
-
-       if ( !isset( $options['month'] ) ) {
-               $options['month'] = '';
-       }
-
-       if ( $options['contribs'] == 'newbie' ) {
-               $options['target'] = '';
-       }
-
-       $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
-
-       foreach ( $options as $name => $value ) {
-               if ( in_array( $name, array( 'namespace', 'target', 'contribs', 'year', 'month' ) ) ) {
-                       continue;
-               }
-               $f .= "\t" . Xml::hidden( $name, $value ) . "\n";
-       }
-
-       $f .= '<fieldset>' .
-               Xml::element( 'legend', array(), wfMsg( 'sp-contributions-search' ) ) .
-               Xml::radioLabel( wfMsgExt( 'sp-contributions-newbies', array( 'parseinline' ) ), 'contribs' , 'newbie' , 'newbie', $options['contribs'] == 'newbie' ? true : false ) . '<br />' .
-               Xml::radioLabel( wfMsgExt( 'sp-contributions-username', array( 'parseinline' ) ), 'contribs' , 'user', 'user', $options['contribs'] == 'user' ? true : false ) . ' ' .
-               Xml::input( 'target', 20, $options['target']) . ' '.
-               '<span style="white-space: nowrap">' .
-               Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' ' .
-               Xml::namespaceSelector( $options['namespace'], '' ) .
-               '</span>' .
-               Xml::openElement( 'p' ) .
-               '<span style="white-space: nowrap">' .
-               Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
-               Xml::input( 'year', 4, $options['year'], array('id' => 'year', 'maxlength' => 4) ) .
-               '</span>' .
-               ' '.
-               '<span style="white-space: nowrap">' .
-               Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
-               Xml::monthSelector( $options['month'], -1 ) . ' '.
-               '</span>' .
-               Xml::submitButton( wfMsg( 'sp-contributions-submit' ) ) .
-               Xml::closeElement( 'p' );
-
-       $explain = wfMsgExt( 'sp-contributions-explain', 'parseinline' );
-       if( !wfEmptyMsg( 'sp-contributions-explain', $explain ) )
-               $f .= "<p>{$explain}</p>";
-
-       $f .= '</fieldset>' .
-               Xml::closeElement( 'form' );
-       return $f;
-}
diff --git a/includes/specials/Deadendpages.php b/includes/specials/Deadendpages.php
deleted file mode 100644 (file)
index a8416c9..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @ingroup SpecialPage
- */
-class DeadendPagesPage extends PageQueryPage {
-
-       function getName( ) {
-               return "Deadendpages";
-       }
-
-       function getPageHeader() {
-               return wfMsgExt( 'deadendpagestext', array( 'parse' ) );
-       }
-
-       /**
-        * LEFT JOIN is expensive
-        *
-        * @return true
-        */
-       function isExpensive( ) {
-               return 1;
-       }
-
-       function isSyndicated() { return false; }
-
-       /**
-        * @return false
-        */
-       function sortDescending() {
-               return false;
-       }
-
-       /**
-        * @return string an sqlquery
-        */
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
-               return "SELECT 'Deadendpages' as type, page_namespace AS namespace, page_title as title, page_title AS value " .
-       "FROM $page LEFT JOIN $pagelinks ON page_id = pl_from " .
-       "WHERE pl_from IS NULL " .
-       "AND page_namespace = 0 " .
-       "AND page_is_redirect = 0";
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialDeadendpages() {
-
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $depp = new DeadendPagesPage();
-
-       return $depp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Disambiguations.php b/includes/specials/Disambiguations.php
deleted file mode 100644 (file)
index 3404566..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @ingroup SpecialPage
- */
-class DisambiguationsPage extends PageQueryPage {
-
-       function getName() {
-               return 'Disambiguations';
-       }
-
-       function isExpensive( ) { return true; }
-       function isSyndicated() { return false; }
-
-
-       function getPageHeader( ) {
-               return wfMsgExt( 'disambiguations-text', array( 'parse' ) );
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-
-               $dMsgText = wfMsgForContent('disambiguationspage');
-
-               $linkBatch = new LinkBatch;
-
-               # If the text can be treated as a title, use it verbatim.
-               # Otherwise, pull the titles from the links table
-               $dp = Title::newFromText($dMsgText);
-               if( $dp ) {
-                       if($dp->getNamespace() != NS_TEMPLATE) {
-                               # FIXME we assume the disambiguation message is a template but
-                               # the page can potentially be from another namespace :/
-                               wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
-                       }
-                       $linkBatch->addObj( $dp );
-               } else {
-                               # Get all the templates linked from the Mediawiki:Disambiguationspage
-                               $disPageObj = Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage' );
-                               $res = $dbr->select(
-                                       array('pagelinks', 'page'),
-                                       'pl_title',
-                                       array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
-                                               'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
-                                       __METHOD__ );
-
-                               while ( $row = $dbr->fetchObject( $res ) ) {
-                                       $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
-                               }
-
-                               $dbr->freeResult( $res );
-               }
-
-               $set = $linkBatch->constructSet( 'lb.tl', $dbr );
-               if( $set === false ) {
-                       # We must always return a valid sql query, but this way DB will always quicly return an empty result
-                       $set = 'FALSE';
-                       wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
-               }
-
-               list( $page, $pagelinks, $templatelinks) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
-
-               $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace,"
-                       ." pb.page_title AS title, la.pl_from AS value"
-                       ." FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa"
-                       ." WHERE $set"  # disambiguation template(s)
-                       .' AND pa.page_id = la.pl_from'
-                       .' AND pa.page_namespace = ' . NS_MAIN  # Limit to just articles in the main namespace
-                       .' AND pb.page_id = lb.tl_from'
-                       .' AND pb.page_namespace = la.pl_namespace'
-                       .' AND pb.page_title = la.pl_title'
-                       .' ORDER BY lb.tl_namespace, lb.tl_title';
-
-               return $sql;
-       }
-
-       function getOrder() {
-               return '';
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang;
-               $title = Title::newFromId( $result->value );
-               $dp = Title::makeTitle( $result->namespace, $result->title );
-
-               $from = $skin->makeKnownLinkObj( $title, '' );
-               $edit = $skin->makeKnownLinkObj( $title, "(".wfMsgHtml("qbedit").")" , 'redirect=no&action=edit' );
-               $arr  = $wgContLang->getArrow();
-               $to   = $skin->makeKnownLinkObj( $dp, '' );
-
-               return "$from $edit $arr $to";
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialDisambiguations() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sd = new DisambiguationsPage();
-
-       return $sd->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/DoubleRedirects.php b/includes/specials/DoubleRedirects.php
deleted file mode 100644 (file)
index b1bad0c..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page listing redirects to redirecting page.
- * The software will automatically not follow double redirects, to prevent loops.
- * @ingroup SpecialPage
- */
-class DoubleRedirectsPage extends PageQueryPage {
-
-       function getName() {
-               return 'DoubleRedirects';
-       }
-
-       function isExpensive( ) { return true; }
-       function isSyndicated() { return false; }
-
-       function getPageHeader( ) {
-               return wfMsgExt( 'doubleredirectstext', array( 'parse' ) );
-       }
-
-       function getSQLText( &$dbr, $namespace = null, $title = null ) {
-
-               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
-
-               $limitToTitle = !( $namespace === null && $title === null );
-               $sql = $limitToTitle ? "SELECT" : "SELECT 'DoubleRedirects' as type," ;
-               $sql .=
-                        " pa.page_namespace as namespace, pa.page_title as title," .
-                        " pb.page_namespace as nsb, pb.page_title as tb," .
-                        " pc.page_namespace as nsc, pc.page_title as tc" .
-                  " FROM $redirect AS ra, $redirect AS rb, $page AS pa, $page AS pb, $page AS pc" .
-                  " WHERE ra.rd_from=pa.page_id" .
-                        " AND ra.rd_namespace=pb.page_namespace" .
-                        " AND ra.rd_title=pb.page_title" .
-                        " AND rb.rd_from=pb.page_id" .
-                        " AND rb.rd_namespace=pc.page_namespace" .
-                        " AND rb.rd_title=pc.page_title";
-
-               if( $limitToTitle ) {
-                       $encTitle = $dbr->addQuotes( $title );
-                       $sql .= " AND pa.page_namespace=$namespace" .
-                                       " AND pa.page_title=$encTitle";
-               }
-
-               return $sql;
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               return $this->getSQLText( $dbr );
-       }
-
-       function getOrder() {
-               return '';
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang;
-
-               $fname = 'DoubleRedirectsPage::formatResult';
-               $titleA = Title::makeTitle( $result->namespace, $result->title );
-
-               if ( $result && !isset( $result->nsb ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $sql = $this->getSQLText( $dbr, $result->namespace, $result->title );
-                       $res = $dbr->query( $sql, $fname );
-                       if ( $res ) {
-                               $result = $dbr->fetchObject( $res );
-                               $dbr->freeResult( $res );
-                       }
-               }
-               if ( !$result ) {
-                       return '<s>' . $skin->makeLinkObj( $titleA, '', 'redirect=no' ) . '</s>';
-               }
-
-               $titleB = Title::makeTitle( $result->nsb, $result->tb );
-               $titleC = Title::makeTitle( $result->nsc, $result->tc );
-
-               $linkA = $skin->makeKnownLinkObj( $titleA, '', 'redirect=no' );
-               $edit = $skin->makeBrokenLinkObj( $titleA, "(".wfMsg("qbedit").")" , 'redirect=no');
-               $linkB = $skin->makeKnownLinkObj( $titleB, '', 'redirect=no' );
-               $linkC = $skin->makeKnownLinkObj( $titleC );
-               $arr = $wgContLang->getArrow() . $wgContLang->getDirMark();
-
-               return( "{$linkA} {$edit} {$arr} {$linkB} {$arr} {$linkC}" );
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialDoubleRedirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sdr = new DoubleRedirectsPage();
-
-       return $sdr->doQuery( $offset, $limit );
-
-}
diff --git a/includes/specials/Emailuser.php b/includes/specials/Emailuser.php
deleted file mode 100644 (file)
index 596f16b..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @todo document
- */
-function wfSpecialEmailuser( $par ) {
-       global $wgRequest, $wgUser, $wgOut;
-
-       $action = $wgRequest->getVal( 'action' );
-       $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
-       $targetUser = EmailUserForm::validateEmailTarget( $target );
-       
-       if ( !( $targetUser instanceof User ) ) {
-               $wgOut->showErrorPage( $targetUser[0], $targetUser[1] );
-               return;
-       }
-       
-       $form = new EmailUserForm( $targetUser,
-                       $wgRequest->getText( 'wpText' ),
-                       $wgRequest->getText( 'wpSubject' ),
-                       $wgRequest->getBool( 'wpCCMe' ) );
-       if ( $action == 'success' ) {
-               $form->showSuccess();
-               return;
-       }
-                                       
-       $error = EmailUserForm::getPermissionsError( $wgUser, $wgRequest->getVal( 'wpEditToken' ) );
-       if ( $error ) {
-               switch ( $error[0] ) {
-                       case 'blockedemailuser':
-                               $wgOut->blockedPage();
-                               return;
-                       case 'actionthrottledtext':
-                               $wgOut->rateLimited();
-                               return;
-                       case 'sessionfailure':
-                               $form->showForm();
-                               return;
-                       default:
-                               $wgOut->showErrorPage( $error[0], $error[1] );
-                               return;
-               }
-       }       
-               
-       
-       if ( "submit" == $action && $wgRequest->wasPosted() ) {
-               $result = $form->doSubmit();
-               
-               if ( !is_null( $result ) ) {
-                       $wgOut->addHTML( wfMsg( "usermailererror" ) .
-                                       ' ' . htmlspecialchars( $result->getMessage() ) );
-               } else {
-                       $titleObj = SpecialPage::getTitleFor( "Emailuser" );
-                       $encTarget = wfUrlencode( $form->getTarget()->getName() );
-                       $wgOut->redirect( $titleObj->getFullURL( "target={$encTarget}&action=success" ) );
-               }
-       } else {
-               $form->showForm();
-       }
-}
-
-/**
- * Implements the Special:Emailuser web interface, and invokes userMailer for sending the email message.
- * @ingroup SpecialPage
- */
-class EmailUserForm {
-
-       var $target;
-       var $text, $subject;
-       var $cc_me;     // Whether user requested to be sent a separate copy of their email.
-
-       /**
-        * @param User $target
-        */
-       function EmailUserForm( $target, $text, $subject, $cc_me ) {
-               $this->target = $target;
-               $this->text = $text;
-               $this->subject = $subject;
-               $this->cc_me = $cc_me;
-       }
-
-       function showForm() {
-               global $wgOut, $wgUser;
-               $skin = $wgUser->getSkin();
-
-               $wgOut->setPagetitle( wfMsg( "emailpage" ) );
-               $wgOut->addWikiMsg( "emailpagetext" );
-
-               if ( $this->subject === "" ) {
-                       $this->subject = wfMsgForContent( "defemailsubject" );
-               }
-
-               $emf = wfMsg( "emailfrom" );
-               $senderLink = $skin->makeLinkObj(
-                       $wgUser->getUserPage(), htmlspecialchars( $wgUser->getName() ) );
-               $emt = wfMsg( "emailto" );
-               $recipientLink = $skin->makeLinkObj(
-                       $this->target->getUserPage(), htmlspecialchars( $this->target->getName() ) );
-               $emr = wfMsg( "emailsubject" );
-               $emm = wfMsg( "emailmessage" );
-               $ems = wfMsg( "emailsend" );
-               $emc = wfMsg( "emailccme" );
-               $encSubject = htmlspecialchars( $this->subject );
-
-               $titleObj = SpecialPage::getTitleFor( "Emailuser" );
-               $action = $titleObj->escapeLocalURL( "target=" .
-                       urlencode( $this->target->getName() ) . "&action=submit" );
-               $token = htmlspecialchars( $wgUser->editToken() );
-
-               $wgOut->addHTML( "
-<form id=\"emailuser\" method=\"post\" action=\"{$action}\">
-<table border='0' id='mailheader'><tr>
-<td align='right'>{$emf}:</td>
-<td align='left'><strong>{$senderLink}</strong></td>
-</tr><tr>
-<td align='right'>{$emt}:</td>
-<td align='left'><strong>{$recipientLink}</strong></td>
-</tr><tr>
-<td align='right'>{$emr}:</td>
-<td align='left'>
-<input type='text' size='60' maxlength='200' name=\"wpSubject\" value=\"{$encSubject}\" />
-</td>
-</tr>
-</table>
-<span id='wpTextLabel'><label for=\"wpText\">{$emm}:</label><br /></span>
-<textarea id=\"wpText\" name=\"wpText\" rows='20' cols='80' style=\"width: 100%;\">" . htmlspecialchars( $this->text ) .
-"</textarea>
-" . wfCheckLabel( $emc, 'wpCCMe', 'wpCCMe', $wgUser->getBoolOption( 'ccmeonemails' ) ) . "<br />
-<input type='submit' name=\"wpSend\" value=\"{$ems}\" />
-<input type='hidden' name='wpEditToken' value=\"$token\" />
-</form>\n" );
-
-       }
-
-       /*
-        * Really send a mail. Permissions should have been checked using 
-        * EmailUserForm::getPermissionsError. It is probably also a good idea to
-        * check the edit token and ping limiter in advance.
-        */
-       function doSubmit() {
-               global $wgUser, $wgUserEmailUseReplyTo, $wgSiteName;
-
-               $to = new MailAddress( $this->target );
-               $from = new MailAddress( $wgUser );
-               $subject = $this->subject;
-
-               $prefsTitle = Title::newFromText( 'Preferences', NS_SPECIAL );
-               
-               // Add a standard footer
-               $footerArgs[0] = $from->name;
-               $footerArgs[1] = $to->name;
-               $footerArgs[2] = $prefsTitle->getFullURL();
-               $footerArgs[3] = wfMsg ('allowemail');
-               $this->text = $this->text . "\n" . wfMsgExt( 'emailuserfooter', 'parsemag', $footerArgs );
-               
-               if( wfRunHooks( 'EmailUser', array( &$to, &$from, &$subject, &$this->text ) ) ) {
-
-                       if( $wgUserEmailUseReplyTo ) {
-                               // Put the generic wiki autogenerated address in the From:
-                               // header and reserve the user for Reply-To.
-                               //
-                               // This is a bit ugly, but will serve to differentiate
-                               // wiki-borne mails from direct mails and protects against
-                               // SPF and bounce problems with some mailers (see below).
-                               global $wgPasswordSender;
-                               $mailFrom = new MailAddress( $wgPasswordSender );
-                               $replyTo = $from;
-                       } else {
-                               // Put the sending user's e-mail address in the From: header.
-                               //
-                               // This is clean-looking and convenient, but has issues.
-                               // One is that it doesn't as clearly differentiate the wiki mail
-                               // from "directly" sent mails.
-                               //
-                               // Another is that some mailers (like sSMTP) will use the From
-                               // address as the envelope sender as well. For open sites this
-                               // can cause mails to be flunked for SPF violations (since the
-                               // wiki server isn't an authorized sender for various users'
-                               // domains) as well as creating a privacy issue as bounces
-                               // containing the recipient's e-mail address may get sent to
-                               // the sending user.
-                               $mailFrom = $from;
-                               $replyTo = null;
-                       }
-                       
-                       $mailResult = UserMailer::send( $to, $mailFrom, $subject, $this->text, $replyTo );
-
-                       if( WikiError::isError( $mailResult ) ) {
-                               return $mailResult;
-                               
-                       } else {
-
-                               // if the user requested a copy of this mail, do this now,
-                               // unless they are emailing themselves, in which case one copy of the message is sufficient.
-                               if ($this->cc_me && $to != $from) {
-                                       $cc_subject = wfMsg('emailccsubject', $this->target->getName(), $subject);
-                                       if( wfRunHooks( 'EmailUser', array( &$from, &$from, &$cc_subject, &$this->text ) ) ) {
-                                               $ccResult = UserMailer::send( $from, $from, $cc_subject, $this->text );
-                                               if( WikiError::isError( $ccResult ) ) {
-                                                       // At this stage, the user's CC mail has failed, but their
-                                                       // original mail has succeeded. It's unlikely, but still, what to do?
-                                                       // We can either show them an error, or we can say everything was fine,
-                                                       // or we can say we sort of failed AND sort of succeeded. Of these options,
-                                                       // simply saying there was an error is probably best.
-                                                       return $ccResult;
-                                               }
-                                       }
-                               }
-
-                               wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $this->text ) );
-                               return;
-                       }
-               }
-       }
-
-       function showSuccess( &$user = null ) {
-               global $wgOut;
-               
-               if ( is_null($user) )
-                       $user = $this->target;
-
-               $wgOut->setPagetitle( wfMsg( "emailsent" ) );
-               $wgOut->addHTML( wfMsg( "emailsenttext" ) );
-
-               $wgOut->returnToMain( false, $user->getUserPage() );
-       }
-       
-       function getTarget() {
-               return $this->target;
-       }
-       
-       static function validateEmailTarget ( $target ) {
-               global $wgEnableEmail, $wgEnableUserEmail;
-
-               if( !( $wgEnableEmail && $wgEnableUserEmail ) ) 
-                       return array( "nosuchspecialpage", "nospecialpagetext" );
-               
-               if ( "" == $target ) {
-                       wfDebug( "Target is empty.\n" );
-                       return array( "notargettitle", "notargettext" );
-               }
-       
-               $nt = Title::newFromURL( $target );
-               if ( is_null( $nt ) ) {
-                       wfDebug( "Target is invalid title.\n" );
-                       return array( "notargettitle", "notargettext" );
-               }
-       
-               $nu = User::newFromName( $nt->getText() );
-               if( is_null( $nu ) || !$nu->canReceiveEmail() ) {
-                       wfDebug( "Target is invalid user or can't receive.\n" );
-                       return array( "noemailtitle", "noemailtext" );
-               }
-               
-               return $nu;
-       }
-       static function getPermissionsError ( $user, $editToken ) {
-               if( !$user->canSendEmail() ) {
-                       wfDebug( "User can't send.\n" );
-                       return array( "mailnologin", "mailnologintext" );
-               }
-               
-               if( $user->isBlockedFromEmailuser() ) {
-                       wfDebug( "User is blocked from sending e-mail.\n" );
-                       return array( "blockedemailuser", "" );
-               }
-               
-               if( $user->pingLimiter( 'emailuser' ) ) {
-                       wfDebug( "Ping limiter triggered.\n" ); 
-                       return array( 'actionthrottledtext', '' );
-               }
-               
-               if( !$user->matchEditToken( $editToken ) ) {
-                       wfDebug( "Matching edit token failed.\n" );
-                       return array( 'sessionfailure', '' );
-               }
-               
-               return;
-       }
-       
-       static function newFromURL( $target, $text, $subject, $cc_me )
-       {
-               $nt = Title::newFromURL( $target );
-               $nu = User::newFromName( $nt->getText() );
-               return new EmailUserForm( $nu, $text, $subject, $cc_me );
-       }
-}
diff --git a/includes/specials/Export.php b/includes/specials/Export.php
deleted file mode 100644 (file)
index 38bfc83..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-<?php
-# Copyright (C) 2003 Brion Vibber <brion@pobox.com>
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-function wfExportGetPagesFromCategory( $title ) {
-       global $wgContLang;
-
-       $name = $title->getDBkey();
-
-       $dbr = wfGetDB( DB_SLAVE );
-
-       list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
-       $sql = "SELECT page_namespace, page_title FROM $page " .
-               "JOIN $categorylinks ON cl_from = page_id " .
-               "WHERE cl_to = " . $dbr->addQuotes( $name );
-
-       $pages = array();
-       $res = $dbr->query( $sql, 'wfExportGetPagesFromCategory' );
-       while ( $row = $dbr->fetchObject( $res ) ) {
-               $n = $row->page_title;
-               if ($row->page_namespace) {
-                       $ns = $wgContLang->getNsText( $row->page_namespace );
-                       $n = $ns . ':' . $n;
-               }
-
-               $pages[] = $n;
-       }
-       $dbr->freeResult($res);
-
-       return $pages;
-}
-
-/**
- * Expand a list of pages to include templates used in those pages.
- * @param $inputPages array, list of titles to look up
- * @param $pageSet array, associative array indexed by titles for output
- * @return array associative array index by titles
- */
-function wfExportGetTemplates( $inputPages, $pageSet ) {
-       return wfExportGetLinks( $inputPages, $pageSet,
-               'templatelinks',
-               array( 'tl_namespace AS namespace', 'tl_title AS title' ),
-               array( 'page_id=tl_from' ) );
-}
-
-/**
- * Expand a list of pages to include images used in those pages.
- * @param $inputPages array, list of titles to look up
- * @param $pageSet array, associative array indexed by titles for output
- * @return array associative array index by titles
- */
-function wfExportGetImages( $inputPages, $pageSet ) {
-       return wfExportGetLinks( $inputPages, $pageSet,
-               'imagelinks',
-               array( NS_IMAGE . ' AS namespace', 'il_to AS title' ),
-               array( 'page_id=il_from' ) );
-}
-
-/**
- * Expand a list of pages to include items used in those pages.
- * @private
- */
-function wfExportGetLinks( $inputPages, $pageSet, $table, $fields, $join ) {
-       $dbr = wfGetDB( DB_SLAVE );
-       foreach( $inputPages as $page ) {
-               $title = Title::newFromText( $page );
-               if( $title ) {
-                       $pageSet[$title->getPrefixedText()] = true;
-                       /// @fixme May or may not be more efficient to batch these
-                       ///        by namespace when given multiple input pages.
-                       $result = $dbr->select(
-                               array( 'page', $table ),
-                               $fields,
-                               array_merge( $join,
-                                       array(
-                                               'page_namespace' => $title->getNamespace(),
-                                               'page_title' => $title->getDbKey() ) ),
-                               __METHOD__ );
-                       foreach( $result as $row ) {
-                               $template = Title::makeTitle( $row->namespace, $row->title );
-                               $pageSet[$template->getPrefixedText()] = true;
-                       }
-               }
-       }
-       return $pageSet;
-}
-
-/**
- * Callback function to remove empty strings from the pages array.
- */
-function wfFilterPage( $page ) {
-       return $page !== '' && $page !== null;
-}
-
-/**
- *
- */
-function wfSpecialExport( $page = '' ) {
-       global $wgOut, $wgRequest, $wgSitename, $wgExportAllowListContributors;
-       global $wgExportAllowHistory, $wgExportMaxHistory;
-
-       $curonly = true;
-       $doexport = false;
-
-       if ( $wgRequest->getCheck( 'addcat' ) ) {
-               $page = $wgRequest->getText( 'pages' );
-               $catname = $wgRequest->getText( 'catname' );
-
-               if ( $catname !== '' && $catname !== NULL && $catname !== false ) {
-                       $t = Title::makeTitleSafe( NS_CATEGORY, $catname );
-                       if ( $t ) {
-                               /**
-                                * @fixme This can lead to hitting memory limit for very large
-                                * categories. Ideally we would do the lookup synchronously
-                                * during the export in a single query.
-                                */
-                               $catpages = wfExportGetPagesFromCategory( $t );
-                               if ( $catpages ) $page .= "\n" . implode( "\n", $catpages );
-                       }
-               }
-       }
-       else if( $wgRequest->wasPosted() && $page == '' ) {
-               $page = $wgRequest->getText( 'pages' );
-               $curonly = $wgRequest->getCheck( 'curonly' );
-               $rawOffset = $wgRequest->getVal( 'offset' );
-               if( $rawOffset ) {
-                       $offset = wfTimestamp( TS_MW, $rawOffset );
-               } else {
-                       $offset = null;
-               }
-               $limit = $wgRequest->getInt( 'limit' );
-               $dir = $wgRequest->getVal( 'dir' );
-               $history = array(
-                       'dir' => 'asc',
-                       'offset' => false,
-                       'limit' => $wgExportMaxHistory,
-               );
-               $historyCheck = $wgRequest->getCheck( 'history' );
-               if ( $curonly ) {
-                       $history = WikiExporter::CURRENT;
-               } elseif ( !$historyCheck ) {
-                       if ( $limit > 0 && $limit < $wgExportMaxHistory ) {
-                               $history['limit'] = $limit;
-                       }
-                       if ( !is_null( $offset ) ) {
-                               $history['offset'] = $offset;
-                       }
-                       if ( strtolower( $dir ) == 'desc' ) {
-                               $history['dir'] = 'desc';
-                       }
-               }
-
-               if( $page != '' ) $doexport = true;
-       } else {
-               // Default to current-only for GET requests
-               $page = $wgRequest->getText( 'pages', $page );
-               $historyCheck = $wgRequest->getCheck( 'history' );
-               if( $historyCheck ) {
-                       $history = WikiExporter::FULL;
-               } else {
-                       $history = WikiExporter::CURRENT;
-               }
-
-               if( $page != '' ) $doexport = true;
-       }
-
-       if( !$wgExportAllowHistory ) {
-               // Override
-               $history = WikiExporter::CURRENT;
-       }
-
-       $list_authors = $wgRequest->getCheck( 'listauthors' );
-       if ( !$curonly || !$wgExportAllowListContributors ) $list_authors = false ;
-
-       if ( $doexport ) {
-               $wgOut->disable();
-
-               // Cancel output buffering and gzipping if set
-               // This should provide safer streaming for pages with history
-               wfResetOutputBuffers();
-               header( "Content-type: application/xml; charset=utf-8" );
-               if( $wgRequest->getCheck( 'wpDownload' ) ) {
-                       // Provide a sane filename suggestion
-                       $filename = urlencode( $wgSitename . '-' . wfTimestampNow() . '.xml' );
-                       $wgRequest->response()->header( "Content-disposition: attachment;filename={$filename}" );
-               }
-
-               /* Split up the input and look up linked pages */
-               $inputPages = array_filter( explode( "\n", $page ), 'wfFilterPage' );
-               $pageSet = array_flip( $inputPages );
-
-               if( $wgRequest->getCheck( 'templates' ) ) {
-                       $pageSet = wfExportGetTemplates( $inputPages, $pageSet );
-               }
-
-               /*
-               // Enable this when we can do something useful exporting/importing image information. :)
-               if( $wgRequest->getCheck( 'images' ) ) {
-                       $pageSet = wfExportGetImages( $inputPages, $pageSet );
-               }
-               */
-
-               $pages = array_keys( $pageSet );
-
-               /* Ok, let's get to it... */
-
-               $db = wfGetDB( DB_SLAVE );
-               $exporter = new WikiExporter( $db, $history );
-               $exporter->list_authors = $list_authors ;
-               $exporter->openStream();
-
-               foreach( $pages as $page ) {
-                       /*
-                       if( $wgExportMaxHistory && !$curonly ) {
-                               $title = Title::newFromText( $page );
-                               if( $title ) {
-                                       $count = Revision::countByTitle( $db, $title );
-                                       if( $count > $wgExportMaxHistory ) {
-                                               wfDebug( __FUNCTION__ .
-                                                       ": Skipped $page, $count revisions too big\n" );
-                                               continue;
-                                       }
-                               }
-                       }*/
-
-                       #Bug 8824: Only export pages the user can read
-                       $title = Title::newFromText( $page );
-                       if( is_null( $title ) ) continue; #TODO: perhaps output an <error> tag or something.
-                       if( !$title->userCanRead() ) continue; #TODO: perhaps output an <error> tag or something.
-
-                       $exporter->pageByTitle( $title );
-               }
-
-               $exporter->closeStream();
-               return;
-       }
-
-       $self = SpecialPage::getTitleFor( 'Export' );
-       $wgOut->addHtml( wfMsgExt( 'exporttext', 'parse' ) );
-
-       $form = Xml::openElement( 'form', array( 'method' => 'post',
-               'action' => $self->getLocalUrl( 'action=submit' ) ) );
-
-       $form .= Xml::inputLabel( wfMsg( 'export-addcattext' )  , 'catname', 'catname', 40 ) . '&nbsp;';
-       $form .= Xml::submitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
-
-       $form .= Xml::openElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) );
-       $form .= htmlspecialchars( $page );
-       $form .= Xml::closeElement( 'textarea' );
-       $form .= '<br />';
-
-       if( $wgExportAllowHistory ) {
-               $form .= Xml::checkLabel( wfMsg( 'exportcuronly' ), 'curonly', 'curonly', true ) . '<br />';
-       } else {
-               $wgOut->addHtml( wfMsgExt( 'exportnohistory', 'parse' ) );
-       }
-       $form .= Xml::checkLabel( wfMsg( 'export-templates' ), 'templates', 'wpExportTemplates', false ) . '<br />';
-       // Enable this when we can do something useful exporting/importing image information. :)
-       //$form .= Xml::checkLabel( wfMsg( 'export-images' ), 'images', 'wpExportImages', false ) . '<br />';
-       $form .= Xml::checkLabel( wfMsg( 'export-download' ), 'wpDownload', 'wpDownload', true ) . '<br />';
-
-       $form .= Xml::submitButton( wfMsg( 'export-submit' ) );
-       $form .= Xml::closeElement( 'form' );
-       $wgOut->addHtml( $form );
-}
diff --git a/includes/specials/Fewestrevisions.php b/includes/specials/Fewestrevisions.php
deleted file mode 100644 (file)
index 5ad8136..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Special page for listing the articles with the fewest revisions.
- *
- * @ingroup SpecialPage
- * @author Martin Drashkov
- */
-class FewestrevisionsPage extends QueryPage {
-
-       function getName() {
-               return 'Fewestrevisions';
-       }
-
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() {
-               return false;
-       }
-
-       function getSql() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
-
-               return "SELECT 'Fewestrevisions' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               COUNT(*) as value
-                       FROM $revision
-                       JOIN $page ON page_id = rev_page
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY 1,2,3
-                       HAVING COUNT(*) > 1";
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $nt = Title::makeTitleSafe( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getPrefixedText() );
-
-               $plink = $skin->makeKnownLinkObj( $nt, $text );
-
-               $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               $nlink = $skin->makeKnownLinkObj( $nt, $nl, 'action=history' );
-
-               return wfSpecialList( $plink, $nlink );
-       }
-}
-
-function wfSpecialFewestrevisions() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $frp = new FewestrevisionsPage();
-       $frp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/FileDuplicateSearch.php b/includes/specials/FileDuplicateSearch.php
deleted file mode 100644 (file)
index 5236ca2..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * A special page to search for files by hash value as defined in the
- * img_sha1 field in the image table
- *
- * @file
- * @ingroup SpecialPage
- *
- * @author Raimond Spekking, based on Special:MIMESearch by Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * Searches the database for files of the requested hash, comparing this with the
- * 'img_sha1' field in the image table.
- * @ingroup SpecialPage
- */
-class FileDuplicateSearchPage extends QueryPage {
-       var $hash, $filename;
-
-       function FileDuplicateSearchPage( $hash, $filename ) {
-               $this->hash = $hash;
-               $this->filename = $filename;
-       }
-
-       function getName() { return 'FileDuplicateSearch'; }
-       function isExpensive() { return false; }
-       function isSyndicated() { return false; }
-
-       function linkParameters() {
-               return array( 'filename' => $this->filename );
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $hash = $dbr->addQuotes( $this->hash );
-
-               return "SELECT 'FileDuplicateSearch' AS type,
-                               img_name AS title,
-                               img_sha1 AS value,
-                               img_user_text,
-                               img_timestamp
-                       FROM $image
-                       WHERE img_sha1 = $hash
-                       ";
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang, $wgLang;
-
-               $nt = Title::makeTitle( NS_IMAGE, $result->title );
-               $text = $wgContLang->convert( $nt->getText() );
-               $plink = $skin->makeLink( $nt->getPrefixedText(), $text );
-
-               $user = $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ), $result->img_user_text );
-               $time = $wgLang->timeanddate( $result->img_timestamp );
-
-               return "$plink . . $user . . $time";
-       }
-}
-
-/**
- * Output the HTML search form, and constructs the FileDuplicateSearch object.
- */
-function wfSpecialFileDuplicateSearch( $par = null ) {
-       global $wgRequest, $wgTitle, $wgOut, $wgLang, $wgContLang;
-
-       $hash = '';
-       $filename =  isset( $par ) ?  $par : $wgRequest->getText( 'filename' );
-
-       $title = Title::newFromText( $filename );
-       if( $title && $title->getText() != '' ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $encFilename = $dbr->addQuotes( htmlspecialchars( $title->getDbKey() ) );
-               $sql = "SELECT img_sha1 from $image where img_name = $encFilename";
-               $res = $dbr->query( $sql );
-               $row = $dbr->fetchRow( $res );
-               if( $row !== false ) {
-                       $hash = $row[0];
-               }
-               $dbr->freeResult( $res );
-       }
-
-       # Create the input form
-       $wgOut->addHTML(
-               Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgTitle->getLocalUrl() ) ) .
-               Xml::openElement( 'fieldset' ) .
-               Xml::element( 'legend', null, wfMsg( 'fileduplicatesearch-legend' ) ) .
-               Xml::inputLabel( wfMsg( 'fileduplicatesearch-filename' ), 'filename', 'filename', 50, $filename ) . ' ' .
-               Xml::submitButton( wfMsg( 'fileduplicatesearch-submit' ) ) .
-               Xml::closeElement( 'fieldset' ) .
-               Xml::closeElement( 'form' )
-       );
-
-       if( $hash != '' ) {
-               $align = $wgContLang->isRtl() ? 'left' : 'right';
-
-               # Show a thumbnail of the file
-               $img = wfFindFile( $title );
-               if ( $img ) {
-                       $thumb = $img->getThumbnail( 120, 120 );
-                       if( $thumb ) {
-                               $wgOut->addHTML( '<div style="float:' . $align . '" id="mw-fileduplicatesearch-icon">' .
-                                       $thumb->toHtml( array( 'desc-link' => false ) ) . '<br />' .
-                                       wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ),
-                                               $wgLang->formatNum( $img->getWidth() ),
-                                               $wgLang->formatNum( $img->getHeight() ),
-                                               $wgLang->formatSize( $img->getSize() ),
-                                               $img->getMimeType()
-                                       ) .
-                                       '</div>' );
-                       }
-               }
-
-               # Do the query
-               $wpp = new FileDuplicateSearchPage( $hash, $filename );
-               list( $limit, $offset ) = wfCheckLimits();
-               $count = $wpp->doQuery( $offset, $limit );
-
-               # Show a short summary
-               if( $count == 1 ) {
-                       $wgOut->addHTML( '<p class="mw-fileduplicatesearch-result-1">' .
-                               wfMsgHtml( 'fileduplicatesearch-result-1', $filename ) .
-                               '</p>'
-                       );
-               } elseif ( $count > 1 ) {
-                       $wgOut->addHTML( '<p class="mw-fileduplicatesearch-result-n">' .
-                               wfMsgExt( 'fileduplicatesearch-result-n', array( 'parseinline' ), $filename, $wgLang->formatNum( $count - 1 ) ) .
-                               '</p>'
-                       );
-               }
-       }
-}
diff --git a/includes/specials/Filepath.php b/includes/specials/Filepath.php
deleted file mode 100644 (file)
index a2ba3e5..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-function wfSpecialFilepath( $par ) {
-       global $wgRequest, $wgOut;
-
-       $file = isset( $par ) ? $par : $wgRequest->getText( 'file' );
-
-       $title = Title::newFromText( $file, NS_IMAGE );
-
-       if ( ! $title instanceof Title || $title->getNamespace() != NS_IMAGE ) {
-               $cform = new FilepathForm( $title );
-               $cform->execute();
-       } else {
-               $file = wfFindFile( $title );
-               if ( $file && $file->exists() ) {
-                       $wgOut->redirect( $file->getURL() );
-               } else {
-                       $wgOut->setStatusCode( 404 );
-                       $cform = new FilepathForm( $title );
-                       $cform->execute();
-               }
-       }
-}
-
-/**
- * @ingroup SpecialPage
- */
-class FilepathForm {
-       var $mTitle;
-
-       function FilepathForm( &$title ) {
-               $this->mTitle =& $title;
-       }
-
-       function execute() {
-               global $wgOut, $wgTitle, $wgScript;
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'specialfilepath' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'filepath' ) ) .
-                       Xml::hidden( 'title', $wgTitle->getPrefixedText() ) .
-                       Xml::inputLabel( wfMsg( 'filepath-page' ), 'file', 'file', 25, is_object( $this->mTitle ) ? $this->mTitle->getText() : '' ) . ' ' .
-                       Xml::submitButton( wfMsg( 'filepath-submit' ) ) . "\n" .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' )
-               );
-       }
-}
diff --git a/includes/specials/Imagelist.php b/includes/specials/Imagelist.php
deleted file mode 100644 (file)
index 3d449b5..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- *
- */
-function wfSpecialImagelist() {
-       global $wgOut;
-
-       $pager = new ImageListPager;
-
-       $limit = $pager->getForm();
-       $body = $pager->getBody();
-       $nav = $pager->getNavigationBar();
-       $wgOut->addHTML( "$limit<br />\n$body<br />\n$nav" );
-}
-
-/**
- * @ingroup SpecialPage Pager
- */
-class ImageListPager extends TablePager {
-       var $mFieldNames = null;
-       var $mQueryConds = array();
-
-       function __construct() {
-               global $wgRequest, $wgMiserMode;
-               if ( $wgRequest->getText( 'sort', 'img_date' ) == 'img_date' ) {
-                       $this->mDefaultDirection = true;
-               } else {
-                       $this->mDefaultDirection = false;
-               }
-               $search = $wgRequest->getText( 'ilsearch' );
-               if ( $search != '' && !$wgMiserMode ) {
-                       $nt = Title::newFromUrl( $search );
-                       if( $nt ) {
-                               $dbr = wfGetDB( DB_SLAVE );
-                               $m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
-                               $m = str_replace( "%", "\\%", $m );
-                               $m = str_replace( "_", "\\_", $m );
-                               $this->mQueryConds = array( "LOWER(img_name) LIKE '%{$m}%'" );
-                       }
-               }
-
-               parent::__construct();
-       }
-
-       function getFieldNames() {
-               if ( !$this->mFieldNames ) {
-                       $this->mFieldNames = array(
-                               'img_timestamp' => wfMsg( 'imagelist_date' ),
-                               'img_name' => wfMsg( 'imagelist_name' ),
-                               'img_user_text' => wfMsg( 'imagelist_user' ),
-                               'img_size' => wfMsg( 'imagelist_size' ),
-                               'img_description' => wfMsg( 'imagelist_description' ),
-                       );
-               }
-               return $this->mFieldNames;
-       }
-
-       function isFieldSortable( $field ) {
-               static $sortable = array( 'img_timestamp', 'img_name', 'img_size' );
-               return in_array( $field, $sortable );
-       }
-
-       function getQueryInfo() {
-               $fields = $this->getFieldNames();
-               $fields = array_keys( $fields );
-               $fields[] = 'img_user';
-               return array(
-                       'tables' => 'image',
-                       'fields' => $fields,
-                       'conds' => $this->mQueryConds
-               );
-       }
-
-       function getDefaultSort() {
-               return 'img_timestamp';
-       }
-
-       function getStartBody() {
-               # Do a link batch query for user pages
-               if ( $this->mResult->numRows() ) {
-                       $lb = new LinkBatch;
-                       $this->mResult->seek( 0 );
-                       while ( $row = $this->mResult->fetchObject() ) {
-                               if ( $row->img_user ) {
-                                       $lb->add( NS_USER, str_replace( ' ', '_', $row->img_user_text ) );
-                               }
-                       }
-                       $lb->execute();
-               }
-
-               return parent::getStartBody();
-       }
-
-       function formatValue( $field, $value ) {
-               global $wgLang;
-               switch ( $field ) {
-                       case 'img_timestamp':
-                               return $wgLang->timeanddate( $value, true );
-                       case 'img_name':
-                               static $imgfile = null;
-                               if ( $imgfile === null ) $imgfile = wfMsg( 'imgfile' );
-
-                               $name = $this->mCurrentRow->img_name;
-                               $link = $this->getSkin()->makeKnownLinkObj( Title::makeTitle( NS_IMAGE, $name ), $value );
-                               $image = wfLocalFile( $value );
-                               $url = $image->getURL();
-                               $download = Xml::element('a', array( 'href' => $url ), $imgfile );
-                               return "$link ($download)";
-                       case 'img_user_text':
-                               if ( $this->mCurrentRow->img_user ) {
-                                       $link = $this->getSkin()->makeLinkObj( Title::makeTitle( NS_USER, $value ),
-                                               htmlspecialchars( $value ) );
-                               } else {
-                                       $link = htmlspecialchars( $value );
-                               }
-                               return $link;
-                       case 'img_size':
-                               return $this->getSkin()->formatSize( $value );
-                       case 'img_description':
-                               return $this->getSkin()->commentBlock( $value );
-               }
-       }
-
-       function getForm() {
-               global $wgRequest, $wgMiserMode;
-               $search = $wgRequest->getText( 'ilsearch' );
-
-               $s = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $this->getTitle()->getLocalURL(), 'id' => 'mw-imagelist-form' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'imagelist' ) ) .
-                       Xml::tags( 'label', null, wfMsgHtml( 'table_pager_limit', $this->getLimitSelect() ) );
-
-               if ( !$wgMiserMode ) {
-                       $s .= "<br />\n" .
-                               Xml::inputLabel( wfMsg( 'imagelist_search_for' ), 'ilsearch', 'mw-ilsearch', 20, $search );
-               }
-               $s .= ' ' .
-                       Xml::submitButton( wfMsg( 'table_pager_limit_submit' ) ) ."\n" .
-                       $this->getHiddenFields( array( 'limit', 'ilsearch' ) ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n";
-               return $s;
-       }
-
-       function getTableClass() {
-               return 'imagelist ' . parent::getTableClass();
-       }
-
-       function getNavClass() {
-               return 'imagelist_nav ' . parent::getNavClass();
-       }
-
-       function getSortHeaderClass() {
-               return 'imagelist_sort ' . parent::getSortHeaderClass();
-       }
-}
diff --git a/includes/specials/Import.php b/includes/specials/Import.php
deleted file mode 100644 (file)
index 4c37f1f..0000000
+++ /dev/null
@@ -1,1154 +0,0 @@
-<?php
-/**
- * MediaWiki page data importer
- * Copyright (C) 2003,2005 Brion Vibber <brion@pobox.com>
- * http://www.mediawiki.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialImport( $page = '' ) {
-       global $wgUser, $wgOut, $wgRequest, $wgTitle, $wgImportSources;
-       global $wgImportTargetNamespace;
-
-       $interwiki = false;
-       $namespace = $wgImportTargetNamespace;
-       $frompage = '';
-       $history = true;
-
-       if ( wfReadOnly() ) {
-               $wgOut->readOnlyPage();
-               return;
-       }
-
-       if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') {
-               $isUpload = false;
-               $namespace = $wgRequest->getIntOrNull( 'namespace' );
-
-               switch( $wgRequest->getVal( "source" ) ) {
-               case "upload":
-                       $isUpload = true;
-                       if( $wgUser->isAllowed( 'importupload' ) ) {
-                               $source = ImportStreamSource::newFromUpload( "xmlimport" );
-                       } else {
-                               return $wgOut->permissionRequired( 'importupload' );
-                       }
-                       break;
-               case "interwiki":
-                       $interwiki = $wgRequest->getVal( 'interwiki' );
-                       $history = $wgRequest->getCheck( 'interwikiHistory' );
-                       $frompage = $wgRequest->getText( "frompage" );
-                       $source = ImportStreamSource::newFromInterwiki(
-                               $interwiki,
-                               $frompage,
-                               $history );
-                       break;
-               default:
-                       $source = new WikiErrorMsg( "importunknownsource" );
-               }
-
-               if( WikiError::isError( $source ) ) {
-                       $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $source->getMessage() ) );
-               } else {
-                       $wgOut->addWikiMsg( "importstart" );
-
-                       $importer = new WikiImporter( $source );
-                       if( !is_null( $namespace ) ) {
-                               $importer->setTargetNamespace( $namespace );
-                       }
-                       $reporter = new ImportReporter( $importer, $isUpload, $interwiki );
-
-                       $reporter->open();
-                       $result = $importer->doImport();
-                       $resultCount = $reporter->close();
-
-                       if( WikiError::isError( $result ) ) {
-                               # No source or XML parse error
-                               $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $result->getMessage() ) );
-                       } elseif( WikiError::isError( $resultCount ) ) {
-                               # Zero revisions
-                               $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $resultCount->getMessage() ) );
-                       } else {
-                               # Success!
-                               $wgOut->addWikiMsg( 'importsuccess' );
-                       }
-                       $wgOut->addWikiText( '<hr />' );
-               }
-       }
-
-       $action = $wgTitle->getLocalUrl( 'action=submit' );
-
-       if( $wgUser->isAllowed( 'importupload' ) ) {
-               $wgOut->addWikiMsg( "importtext" );
-               $wgOut->addHTML(
-                       Xml::openElement( 'fieldset' ).
-                       Xml::element( 'legend', null, wfMsg( 'import-upload' ) ) .
-                       Xml::openElement( 'form', array( 'enctype' => 'multipart/form-data', 'method' => 'post', 'action' => $action ) ) .
-                       Xml::hidden( 'action', 'submit' ) .
-                       Xml::hidden( 'source', 'upload' ) .
-                       Xml::input( 'xmlimport', 50, '', array( 'type' => 'file' ) ) . ' ' .
-                       Xml::submitButton( wfMsg( 'uploadbtn' ) ) .
-                       Xml::closeElement( 'form' ) .
-                       Xml::closeElement( 'fieldset' )
-               );
-       } else {
-               if( empty( $wgImportSources ) ) {
-                       $wgOut->addWikiMsg( 'importnosources' );
-               }
-       }
-
-       if( !empty( $wgImportSources ) ) {
-               $wgOut->addHTML(
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'importinterwiki' ) ) .
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action ) ) .
-                       wfMsgExt( 'import-interwiki-text', array( 'parse' ) ) .
-                       Xml::hidden( 'action', 'submit' ) .
-                       Xml::hidden( 'source', 'interwiki' ) .
-                       Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
-                       "<tr>
-                               <td>" .
-                                       Xml::openElement( 'select', array( 'name' => 'interwiki' ) )
-               );
-               foreach( $wgImportSources as $prefix ) {
-                       $selected = ( $interwiki === $prefix ) ? ' selected="selected"' : '';
-                       $wgOut->addHTML( Xml::option( $prefix, $prefix, $selected ) );
-               }
-               $wgOut->addHTML(
-                                       Xml::closeElement( 'select' ) .
-                               "</td>
-                               <td>" .
-                                       Xml::input( 'frompage', 50, $frompage ) .
-                               "</td>
-                       </tr>
-                       <tr>
-                               <td>
-                               </td>
-                               <td>" .
-                                       Xml::checkLabel( wfMsg( 'import-interwiki-history' ), 'interwikiHistory', 'interwikiHistory', $history ) .
-                               "</td>
-                       </tr>
-                       <tr>
-                               <td>
-                               </td>
-                               <td>" .
-                                       Xml::label( wfMsg( 'import-interwiki-namespace' ), 'namespace' ) .
-                                       Xml::namespaceSelector( $namespace, '' ) .
-                               "</td>
-                       </tr>
-                       <tr>
-                               <td>
-                               </td>
-                               <td>" .
-                                       Xml::submitButton( wfMsg( 'import-interwiki-submit' ) ) .
-                               "</td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ).
-                       Xml::closeElement( 'form' ) .
-                       Xml::closeElement( 'fieldset' )
-               );
-       }
-}
-
-/**
- * Reporting callback
- * @ingroup SpecialPage
- */
-class ImportReporter {
-       function __construct( $importer, $upload, $interwiki ) {
-               $importer->setPageOutCallback( array( $this, 'reportPage' ) );
-               $this->mPageCount = 0;
-               $this->mIsUpload = $upload;
-               $this->mInterwiki = $interwiki;
-       }
-
-       function open() {
-               global $wgOut;
-               $wgOut->addHtml( "<ul>\n" );
-       }
-
-       function reportPage( $title, $origTitle, $revisionCount, $successCount ) {
-               global $wgOut, $wgUser, $wgLang, $wgContLang;
-
-               $skin = $wgUser->getSkin();
-
-               $this->mPageCount++;
-
-               $localCount = $wgLang->formatNum( $successCount );
-               $contentCount = $wgContLang->formatNum( $successCount );
-
-               if( $successCount > 0 ) {
-                       $wgOut->addHtml( "<li>" . $skin->makeKnownLinkObj( $title ) . " " .
-                               wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
-                               "</li>\n"
-                       );
-
-                       $log = new LogPage( 'import' );
-                       if( $this->mIsUpload ) {
-                               $detail = wfMsgExt( 'import-logentry-upload-detail', array( 'content', 'parsemag' ),
-                                       $contentCount );
-                               $log->addEntry( 'upload', $title, $detail );
-                       } else {
-                               $interwiki = '[[:' . $this->mInterwiki . ':' .
-                                       $origTitle->getPrefixedText() . ']]';
-                               $detail = wfMsgExt( 'import-logentry-interwiki-detail', array( 'content', 'parsemag' ),
-                                       $contentCount, $interwiki );
-                               $log->addEntry( 'interwiki', $title, $detail );
-                       }
-
-                       $comment = $detail; // quick
-                       $dbw = wfGetDB( DB_MASTER );
-                       $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleId(), $comment, true );
-                       $nullRevision->insertOn( $dbw );
-                       $article = new Article( $title );
-                       # Update page record
-                       $article->updateRevisionOn( $dbw, $nullRevision );
-                       wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, false) );
-               } else {
-                       $wgOut->addHtml( '<li>' . wfMsgHtml( 'import-nonewrevisions' ) . '</li>' );
-               }
-       }
-
-       function close() {
-               global $wgOut;
-               if( $this->mPageCount == 0 ) {
-                       $wgOut->addHtml( "</ul>\n" );
-                       return new WikiErrorMsg( "importnopages" );
-               }
-               $wgOut->addHtml( "</ul>\n" );
-
-               return $this->mPageCount;
-       }
-}
-
-/**
- *
- * @ingroup SpecialPage
- */
-class WikiRevision {
-       var $title = null;
-       var $id = 0;
-       var $timestamp = "20010115000000";
-       var $user = 0;
-       var $user_text = "";
-       var $text = "";
-       var $comment = "";
-       var $minor = false;
-
-       function setTitle( $title ) {
-               if( is_object( $title ) ) {
-                       $this->title = $title;
-               } elseif( is_null( $title ) ) {
-                       throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
-               } else {
-                       throw new MWException( "WikiRevision given non-object title in import." );
-               }
-       }
-
-       function setID( $id ) {
-               $this->id = $id;
-       }
-
-       function setTimestamp( $ts ) {
-               # 2003-08-05T18:30:02Z
-               $this->timestamp = wfTimestamp( TS_MW, $ts );
-       }
-
-       function setUsername( $user ) {
-               $this->user_text = $user;
-       }
-
-       function setUserIP( $ip ) {
-               $this->user_text = $ip;
-       }
-
-       function setText( $text ) {
-               $this->text = $text;
-       }
-
-       function setComment( $text ) {
-               $this->comment = $text;
-       }
-
-       function setMinor( $minor ) {
-               $this->minor = (bool)$minor;
-       }
-
-       function setSrc( $src ) {
-               $this->src = $src;
-       }
-
-       function setFilename( $filename ) {
-               $this->filename = $filename;
-       }
-
-       function setSize( $size ) {
-               $this->size = intval( $size );
-       }
-
-       function getTitle() {
-               return $this->title;
-       }
-
-       function getID() {
-               return $this->id;
-       }
-
-       function getTimestamp() {
-               return $this->timestamp;
-       }
-
-       function getUser() {
-               return $this->user_text;
-       }
-
-       function getText() {
-               return $this->text;
-       }
-
-       function getComment() {
-               return $this->comment;
-       }
-
-       function getMinor() {
-               return $this->minor;
-       }
-
-       function getSrc() {
-               return $this->src;
-       }
-
-       function getFilename() {
-               return $this->filename;
-       }
-
-       function getSize() {
-               return $this->size;
-       }
-
-       function importOldRevision() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               # Sneak a single revision into place
-               $user = User::newFromName( $this->getUser() );
-               if( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-               }
-
-               // avoid memory leak...?
-               $linkCache = LinkCache::singleton();
-               $linkCache->clear();
-
-               $article = new Article( $this->title );
-               $pageId = $article->getId();
-               if( $pageId == 0 ) {
-                       # must create the page...
-                       $pageId = $article->insertOn( $dbw );
-                       $created = true;
-               } else {
-                       $created = false;
-
-                       $prior = Revision::loadFromTimestamp( $dbw, $this->title, $this->timestamp );
-                       if( !is_null( $prior ) ) {
-                               // FIXME: this could fail slightly for multiple matches :P
-                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
-                                       $this->title->getPrefixedText() . "]], timestamp " .
-                                       $this->timestamp . "\n" );
-                               return false;
-                       }
-               }
-
-               # FIXME: Use original rev_id optionally
-               # FIXME: blah blah blah
-
-               #if( $numrows > 0 ) {
-               #       return wfMsg( "importhistoryconflict" );
-               #}
-
-               # Insert the row
-               $revision = new Revision( array(
-                       'page'       => $pageId,
-                       'text'       => $this->getText(),
-                       'comment'    => $this->getComment(),
-                       'user'       => $userId,
-                       'user_text'  => $userText,
-                       'timestamp'  => $this->timestamp,
-                       'minor_edit' => $this->minor,
-                       ) );
-               $revId = $revision->insertOn( $dbw );
-               $changed = $article->updateIfNewerOn( $dbw, $revision );
-
-               if( $created ) {
-                       wfDebug( __METHOD__ . ": running onArticleCreate\n" );
-                       Article::onArticleCreate( $this->title );
-
-                       wfDebug( __METHOD__ . ": running create updates\n" );
-                       $article->createUpdates( $revision );
-
-               } elseif( $changed ) {
-                       wfDebug( __METHOD__ . ": running onArticleEdit\n" );
-                       Article::onArticleEdit( $this->title );
-
-                       wfDebug( __METHOD__ . ": running edit updates\n" );
-                       $article->editUpdates(
-                               $this->getText(),
-                               $this->getComment(),
-                               $this->minor,
-                               $this->timestamp,
-                               $revId );
-               }
-
-               return true;
-       }
-
-       function importUpload() {
-               wfDebug( __METHOD__ . ": STUB\n" );
-
-               /**
-                       // from file revert...
-                       $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
-                       $comment = $wgRequest->getText( 'wpComment' );
-                       // TODO: Preserve file properties from database instead of reloading from file
-                       $status = $this->file->upload( $source, $comment, $comment );
-                       if( $status->isGood() ) {
-               */
-
-               /**
-                       // from file upload...
-               $this->mLocalFile = wfLocalFile( $nt );
-               $this->mDestName = $this->mLocalFile->getName();
-               //....
-                       $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
-                       File::DELETE_SOURCE, $this->mFileProps );
-                       if ( !$status->isGood() ) {
-                               $resultDetails = array( 'internal' => $status->getWikiText() );
-               */
-
-               // @fixme upload() uses $wgUser, which is wrong here
-               // it may also create a page without our desire, also wrong potentially.
-               // and, it will record a *current* upload, but we might want an archive version here
-
-               $file = wfLocalFile( $this->getTitle() );
-               if( !$file ) {
-                       var_dump( $file );
-                       wfDebug( "IMPORT: Bad file. :(\n" );
-                       return false;
-               }
-
-               $source = $this->downloadSource();
-               if( !$source ) {
-                       wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
-                       return false;
-               }
-
-               $status = $file->upload( $source,
-                       $this->getComment(),
-                       $this->getComment(), // Initial page, if none present...
-                       File::DELETE_SOURCE,
-                       false, // props...
-                       $this->getTimestamp() );
-
-               if( $status->isGood() ) {
-                       // yay?
-                       wfDebug( "IMPORT: is ok?\n" );
-                       return true;
-               }
-
-               wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
-               return false;
-
-       }
-
-       function downloadSource() {
-               global $wgEnableUploads;
-               if( !$wgEnableUploads ) {
-                       return false;
-               }
-
-               $tempo = tempnam( wfTempDir(), 'download' );
-               $f = fopen( $tempo, 'wb' );
-               if( !$f ) {
-                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
-                       return false;
-               }
-
-               // @fixme!
-               $src = $this->getSrc();
-               $data = Http::get( $src );
-               if( !$data ) {
-                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
-                       fclose( $f );
-                       unlink( $tempo );
-                       return false;
-               }
-
-               fwrite( $f, $data );
-               fclose( $f );
-
-               return $tempo;
-       }
-
-}
-
-/**
- * implements Special:Import
- * @ingroup SpecialPage
- */
-class WikiImporter {
-       var $mDebug = false;
-       var $mSource = null;
-       var $mPageCallback = null;
-       var $mPageOutCallback = null;
-       var $mRevisionCallback = null;
-       var $mUploadCallback = null;
-       var $mTargetNamespace = null;
-       var $lastfield;
-       var $tagStack = array();
-
-       function __construct( $source ) {
-               $this->setRevisionCallback( array( $this, "importRevision" ) );
-               $this->setUploadCallback( array( $this, "importUpload" ) );
-               $this->mSource = $source;
-       }
-
-       function throwXmlError( $err ) {
-               $this->debug( "FAILURE: $err" );
-               wfDebug( "WikiImporter XML error: $err\n" );
-       }
-
-       # --------------
-
-       function doImport() {
-               if( empty( $this->mSource ) ) {
-                       return new WikiErrorMsg( "importnotext" );
-               }
-
-               $parser = xml_parser_create( "UTF-8" );
-
-               # case folding violates XML standard, turn it off
-               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
-               xml_set_object( $parser, $this );
-               xml_set_element_handler( $parser, "in_start", "" );
-
-               $offset = 0; // for context extraction on error reporting
-               do {
-                       $chunk = $this->mSource->readChunk();
-                       if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
-                               wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
-                               return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
-                       }
-                       $offset += strlen( $chunk );
-               } while( $chunk !== false && !$this->mSource->atEnd() );
-               xml_parser_free( $parser );
-
-               return true;
-       }
-
-       function debug( $data ) {
-               if( $this->mDebug ) {
-                       wfDebug( "IMPORT: $data\n" );
-               }
-       }
-
-       function notice( $data ) {
-               global $wgCommandLineMode;
-               if( $wgCommandLineMode ) {
-                       print "$data\n";
-               } else {
-                       global $wgOut;
-                       $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
-               }
-       }
-
-       /**
-        * Set debug mode...
-        */
-       function setDebug( $debug ) {
-               $this->mDebug = $debug;
-       }
-
-       /**
-        * Sets the action to perform as each new page in the stream is reached.
-        * @param $callback callback
-        * @return callback
-        */
-       function setPageCallback( $callback ) {
-               $previous = $this->mPageCallback;
-               $this->mPageCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page in the stream is completed.
-        * Callback accepts the page title (as a Title object), a second object
-        * with the original title form (in case it's been overridden into a
-        * local namespace), and a count of revisions.
-        *
-        * @param $callback callback
-        * @return callback
-        */
-       function setPageOutCallback( $callback ) {
-               $previous = $this->mPageOutCallback;
-               $this->mPageOutCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page revision is reached.
-        * @param $callback callback
-        * @return callback
-        */
-       function setRevisionCallback( $callback ) {
-               $previous = $this->mRevisionCallback;
-               $this->mRevisionCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each file upload version is reached.
-        * @param $callback callback
-        * @return callback
-        */
-       function setUploadCallback( $callback ) {
-               $previous = $this->mUploadCallback;
-               $this->mUploadCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Set a target namespace to override the defaults
-        */
-       function setTargetNamespace( $namespace ) {
-               if( is_null( $namespace ) ) {
-                       // Don't override namespaces
-                       $this->mTargetNamespace = null;
-               } elseif( $namespace >= 0 ) {
-                       // FIXME: Check for validity
-                       $this->mTargetNamespace = intval( $namespace );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Default per-revision callback, performs the import.
-        * @param $revision WikiRevision
-        * @private
-        */
-       function importRevision( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
-       }
-
-       /**
-        * Dummy for now...
-        */
-       function importUpload( $revision ) {
-               //$dbw = wfGetDB( DB_MASTER );
-               //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
-               return false;
-       }
-
-       /**
-        * Alternate per-revision callback, for debugging.
-        * @param $revision WikiRevision
-        * @private
-        */
-       function debugRevisionHandler( &$revision ) {
-               $this->debug( "Got revision:" );
-               if( is_object( $revision->title ) ) {
-                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
-               } else {
-                       $this->debug( "-- Title: <invalid>" );
-               }
-               $this->debug( "-- User: " . $revision->user_text );
-               $this->debug( "-- Timestamp: " . $revision->timestamp );
-               $this->debug( "-- Comment: " . $revision->comment );
-               $this->debug( "-- Text: " . $revision->text );
-       }
-
-       /**
-        * Notify the callback function when a new <page> is reached.
-        * @param $title Title
-        * @private
-        */
-       function pageCallback( $title ) {
-               if( is_callable( $this->mPageCallback ) ) {
-                       call_user_func( $this->mPageCallback, $title );
-               }
-       }
-
-       /**
-        * Notify the callback function when a </page> is closed.
-        * @param $title Title
-        * @param $origTitle Title
-        * @param $revisionCount int
-        * @param $successCount Int: number of revisions for which callback returned true
-        * @private
-        */
-       function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
-               if( is_callable( $this->mPageOutCallback ) ) {
-                       call_user_func( $this->mPageOutCallback, $title, $origTitle,
-                               $revisionCount, $successCount );
-               }
-       }
-
-
-       # XML parser callbacks from here out -- beware!
-       function donothing( $parser, $x, $y="" ) {
-               #$this->debug( "donothing" );
-       }
-
-       function in_start( $parser, $name, $attribs ) {
-               $this->debug( "in_start $name" );
-               if( $name != "mediawiki" ) {
-                       return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
-               }
-               xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
-       }
-
-       function in_mediawiki( $parser, $name, $attribs ) {
-               $this->debug( "in_mediawiki $name" );
-               if( $name == 'siteinfo' ) {
-                       xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
-               } elseif( $name == 'page' ) {
-                       $this->push( $name );
-                       $this->workRevisionCount = 0;
-                       $this->workSuccessCount = 0;
-                       $this->uploadCount = 0;
-                       $this->uploadSuccessCount = 0;
-                       xml_set_element_handler( $parser, "in_page", "out_page" );
-               } else {
-                       return $this->throwXMLerror( "Expected <page>, got <$name>" );
-               }
-       }
-       function out_mediawiki( $parser, $name ) {
-               $this->debug( "out_mediawiki $name" );
-               if( $name != "mediawiki" ) {
-                       return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
-               }
-               xml_set_element_handler( $parser, "donothing", "donothing" );
-       }
-
-
-       function in_siteinfo( $parser, $name, $attribs ) {
-               // no-ops for now
-               $this->debug( "in_siteinfo $name" );
-               switch( $name ) {
-               case "sitename":
-               case "base":
-               case "generator":
-               case "case":
-               case "namespaces":
-               case "namespace":
-                       break;
-               default:
-                       return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
-               }
-       }
-
-       function out_siteinfo( $parser, $name ) {
-               if( $name == "siteinfo" ) {
-                       xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
-               }
-       }
-
-
-       function in_page( $parser, $name, $attribs ) {
-               $this->debug( "in_page $name" );
-               switch( $name ) {
-               case "id":
-               case "title":
-               case "restrictions":
-                       $this->appendfield = $name;
-                       $this->appenddata = "";
-                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
-                       xml_set_character_data_handler( $parser, "char_append" );
-                       break;
-               case "revision":
-                       $this->push( "revision" );
-                       if( is_object( $this->pageTitle ) ) {
-                               $this->workRevision = new WikiRevision;
-                               $this->workRevision->setTitle( $this->pageTitle );
-                               $this->workRevisionCount++;
-                       } else {
-                               // Skipping items due to invalid page title
-                               $this->workRevision = null;
-                       }
-                       xml_set_element_handler( $parser, "in_revision", "out_revision" );
-                       break;
-               case "upload":
-                       $this->push( "upload" );
-                       if( is_object( $this->pageTitle ) ) {
-                               $this->workRevision = new WikiRevision;
-                               $this->workRevision->setTitle( $this->pageTitle );
-                               $this->uploadCount++;
-                       } else {
-                               // Skipping items due to invalid page title
-                               $this->workRevision = null;
-                       }
-                       xml_set_element_handler( $parser, "in_upload", "out_upload" );
-                       break;
-               default:
-                       return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
-               }
-       }
-
-       function out_page( $parser, $name ) {
-               $this->debug( "out_page $name" );
-               $this->pop();
-               if( $name != "page" ) {
-                       return $this->throwXMLerror( "Expected </page>, got </$name>" );
-               }
-               xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
-
-               $this->pageOutCallback( $this->pageTitle, $this->origTitle,
-                       $this->workRevisionCount, $this->workSuccessCount );
-
-               $this->workTitle = null;
-               $this->workRevision = null;
-               $this->workRevisionCount = 0;
-               $this->workSuccessCount = 0;
-               $this->pageTitle = null;
-               $this->origTitle = null;
-       }
-
-       function in_nothing( $parser, $name, $attribs ) {
-               $this->debug( "in_nothing $name" );
-               return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
-       }
-       function char_append( $parser, $data ) {
-               $this->debug( "char_append '$data'" );
-               $this->appenddata .= $data;
-       }
-       function out_append( $parser, $name ) {
-               $this->debug( "out_append $name" );
-               if( $name != $this->appendfield ) {
-                       return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
-               }
-
-               switch( $this->appendfield ) {
-               case "title":
-                       $this->workTitle = $this->appenddata;
-                       $this->origTitle = Title::newFromText( $this->workTitle );
-                       if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
-                               $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
-                                       $this->origTitle->getDBkey() );
-                       } else {
-                               $this->pageTitle = Title::newFromText( $this->workTitle );
-                       }
-                       if( is_null( $this->pageTitle ) ) {
-                               // Invalid page title? Ignore the page
-                               $this->notice( "Skipping invalid page title '$this->workTitle'" );
-                       } else {
-                               $this->pageCallback( $this->workTitle );
-                       }
-                       break;
-               case "id":
-                       if ( $this->parentTag() == 'revision' ) {
-                               if( $this->workRevision )
-                                       $this->workRevision->setID( $this->appenddata );
-                       }
-                       break;
-               case "text":
-                       if( $this->workRevision )
-                               $this->workRevision->setText( $this->appenddata );
-                       break;
-               case "username":
-                       if( $this->workRevision )
-                               $this->workRevision->setUsername( $this->appenddata );
-                       break;
-               case "ip":
-                       if( $this->workRevision )
-                               $this->workRevision->setUserIP( $this->appenddata );
-                       break;
-               case "timestamp":
-                       if( $this->workRevision )
-                               $this->workRevision->setTimestamp( $this->appenddata );
-                       break;
-               case "comment":
-                       if( $this->workRevision )
-                               $this->workRevision->setComment( $this->appenddata );
-                       break;
-               case "minor":
-                       if( $this->workRevision )
-                               $this->workRevision->setMinor( true );
-                       break;
-               case "filename":
-                       if( $this->workRevision )
-                               $this->workRevision->setFilename( $this->appenddata );
-                       break;
-               case "src":
-                       if( $this->workRevision )
-                               $this->workRevision->setSrc( $this->appenddata );
-                       break;
-               case "size":
-                       if( $this->workRevision )
-                               $this->workRevision->setSize( intval( $this->appenddata ) );
-                       break;
-               default:
-                       $this->debug( "Bad append: {$this->appendfield}" );
-               }
-               $this->appendfield = "";
-               $this->appenddata = "";
-
-               $parent = $this->parentTag();
-               xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
-               xml_set_character_data_handler( $parser, "donothing" );
-       }
-
-       function in_revision( $parser, $name, $attribs ) {
-               $this->debug( "in_revision $name" );
-               switch( $name ) {
-               case "id":
-               case "timestamp":
-               case "comment":
-               case "minor":
-               case "text":
-                       $this->appendfield = $name;
-                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
-                       xml_set_character_data_handler( $parser, "char_append" );
-                       break;
-               case "contributor":
-                       $this->push( "contributor" );
-                       xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
-                       break;
-               default:
-                       return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
-               }
-       }
-
-       function out_revision( $parser, $name ) {
-               $this->debug( "out_revision $name" );
-               $this->pop();
-               if( $name != "revision" ) {
-                       return $this->throwXMLerror( "Expected </revision>, got </$name>" );
-               }
-               xml_set_element_handler( $parser, "in_page", "out_page" );
-
-               if( $this->workRevision ) {
-                       $ok = call_user_func_array( $this->mRevisionCallback,
-                               array( $this->workRevision, $this ) );
-                       if( $ok ) {
-                               $this->workSuccessCount++;
-                       }
-               }
-       }
-
-       function in_upload( $parser, $name, $attribs ) {
-               $this->debug( "in_upload $name" );
-               switch( $name ) {
-               case "timestamp":
-               case "comment":
-               case "text":
-               case "filename":
-               case "src":
-               case "size":
-                       $this->appendfield = $name;
-                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
-                       xml_set_character_data_handler( $parser, "char_append" );
-                       break;
-               case "contributor":
-                       $this->push( "contributor" );
-                       xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
-                       break;
-               default:
-                       return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
-               }
-       }
-
-       function out_upload( $parser, $name ) {
-               $this->debug( "out_revision $name" );
-               $this->pop();
-               if( $name != "upload" ) {
-                       return $this->throwXMLerror( "Expected </upload>, got </$name>" );
-               }
-               xml_set_element_handler( $parser, "in_page", "out_page" );
-
-               if( $this->workRevision ) {
-                       $ok = call_user_func_array( $this->mUploadCallback,
-                               array( $this->workRevision, $this ) );
-                       if( $ok ) {
-                               $this->workUploadSuccessCount++;
-                       }
-               }
-       }
-
-       function in_contributor( $parser, $name, $attribs ) {
-               $this->debug( "in_contributor $name" );
-               switch( $name ) {
-               case "username":
-               case "ip":
-               case "id":
-                       $this->appendfield = $name;
-                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
-                       xml_set_character_data_handler( $parser, "char_append" );
-                       break;
-               default:
-                       $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
-               }
-       }
-
-       function out_contributor( $parser, $name ) {
-               $this->debug( "out_contributor $name" );
-               $this->pop();
-               if( $name != "contributor" ) {
-                       return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
-               }
-               $parent = $this->parentTag();
-               xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
-       }
-
-       private function push( $name ) {
-               array_push( $this->tagStack, $name );
-               $this->debug( "PUSH $name" );
-       }
-
-       private function pop() {
-               $name = array_pop( $this->tagStack );
-               $this->debug( "POP $name" );
-               return $name;
-       }
-
-       private function parentTag() {
-               $name = $this->tagStack[count( $this->tagStack ) - 1];
-               $this->debug( "PARENT $name" );
-               return $name;
-       }
-
-}
-
-/**
- * @todo document (e.g. one-sentence class description).
- * @ingroup SpecialPage
- */
-class ImportStringSource {
-       function __construct( $string ) {
-               $this->mString = $string;
-               $this->mRead = false;
-       }
-
-       function atEnd() {
-               return $this->mRead;
-       }
-
-       function readChunk() {
-               if( $this->atEnd() ) {
-                       return false;
-               } else {
-                       $this->mRead = true;
-                       return $this->mString;
-               }
-       }
-}
-
-/**
- * @todo document (e.g. one-sentence class description).
- * @ingroup SpecialPage
- */
-class ImportStreamSource {
-       function __construct( $handle ) {
-               $this->mHandle = $handle;
-       }
-
-       function atEnd() {
-               return feof( $this->mHandle );
-       }
-
-       function readChunk() {
-               return fread( $this->mHandle, 32768 );
-       }
-
-       static function newFromFile( $filename ) {
-               $file = @fopen( $filename, 'rt' );
-               if( !$file ) {
-                       return new WikiErrorMsg( "importcantopen" );
-               }
-               return new ImportStreamSource( $file );
-       }
-
-       static function newFromUpload( $fieldname = "xmlimport" ) {
-               $upload =& $_FILES[$fieldname];
-
-               if( !isset( $upload ) || !$upload['name'] ) {
-                       return new WikiErrorMsg( 'importnofile' );
-               }
-               if( !empty( $upload['error'] ) ) {
-                       switch($upload['error']){
-                               case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
-                                       return new WikiErrorMsg( 'importuploaderrorsize' );
-                               case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
-                                       return new WikiErrorMsg( 'importuploaderrorsize' );
-                               case 3: # The uploaded file was only partially uploaded
-                                       return new WikiErrorMsg( 'importuploaderrorpartial' );
-                           case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
-                               return new WikiErrorMsg( 'importuploaderrortemp' );
-                           # case else: # Currently impossible
-                       }
-
-               }
-               $fname = $upload['tmp_name'];
-               if( is_uploaded_file( $fname ) ) {
-                       return ImportStreamSource::newFromFile( $fname );
-               } else {
-                       return new WikiErrorMsg( 'importnofile' );
-               }
-       }
-
-       static function newFromURL( $url, $method = 'GET' ) {
-               wfDebug( __METHOD__ . ": opening $url\n" );
-               # Use the standard HTTP fetch function; it times out
-               # quicker and sorts out user-agent problems which might
-               # otherwise prevent importing from large sites, such
-               # as the Wikimedia cluster, etc.
-               $data = Http::request( $method, $url );
-               if( $data !== false ) {
-                       $file = tmpfile();
-                       fwrite( $file, $data );
-                       fflush( $file );
-                       fseek( $file, 0 );
-                       return new ImportStreamSource( $file );
-               } else {
-                       return new WikiErrorMsg( 'importcantopen' );
-               }
-       }
-
-       public static function newFromInterwiki( $interwiki, $page, $history=false ) {
-               if( $page == '' ) {
-                       return new WikiErrorMsg( 'import-noarticle' );
-               }
-               $link = Title::newFromText( "$interwiki:Special:Export/$page" );
-               if( is_null( $link ) || $link->getInterwiki() == '' ) {
-                       return new WikiErrorMsg( 'importbadinterwiki' );
-               } else {
-                       $params = $history ? 'history=1' : '';
-                       $url = $link->getFullUrl( $params );
-                       # For interwikis, use POST to avoid redirects.
-                       return ImportStreamSource::newFromURL( $url, "POST" );
-               }
-       }
-}
diff --git a/includes/specials/Ipblocklist.php b/includes/specials/Ipblocklist.php
deleted file mode 100644 (file)
index 696c7ef..0000000
+++ /dev/null
@@ -1,427 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @todo document
- */
-function wfSpecialIpblocklist() {
-       global $wgUser, $wgOut, $wgRequest;
-
-       $ip = $wgRequest->getVal( 'wpUnblockAddress', $wgRequest->getVal( 'ip' ) );
-       $id = $wgRequest->getVal( 'id' );
-       $reason = $wgRequest->getText( 'wpUnblockReason' );
-       $action = $wgRequest->getText( 'action' );
-       $successip = $wgRequest->getVal( 'successip' );
-
-       $ipu = new IPUnblockForm( $ip, $id, $reason );
-
-       if( $action == 'unblock' ) {
-               # Check permissions
-               if( !$wgUser->isAllowed( 'block' ) ) {
-                       $wgOut->permissionRequired( 'block' );
-                       return;
-               }
-               # Check for database lock
-               if( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-               # Show unblock form
-               $ipu->showForm( '' );
-       } elseif( $action == 'submit' && $wgRequest->wasPosted()
-               && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               # Check permissions
-               if( !$wgUser->isAllowed( 'block' ) ) {
-                       $wgOut->permissionRequired( 'block' );
-                       return;
-               }
-               # Check for database lock
-               if( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-               # Remove blocks and redirect user to success page
-               $ipu->doSubmit();
-       } elseif( $action == 'success' ) {
-               # Inform the user of a successful unblock
-               # (No need to check permissions or locks here,
-               # if something was done, then it's too late!)
-               if ( substr( $successip, 0, 1) == '#' ) {
-                       // A block ID was unblocked
-                       $ipu->showList( $wgOut->parse( wfMsg( 'unblocked-id', $successip ) ) );
-               } else {
-                       // A username/IP was unblocked
-                       $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) );
-               }
-       } else {
-               # Just show the block list
-               $ipu->showList( '' );
-       }
-
-}
-
-/**
- * implements Special:ipblocklist GUI
- * @ingroup SpecialPage
- */
-class IPUnblockForm {
-       var $ip, $reason, $id;
-
-       function IPUnblockForm( $ip, $id, $reason ) {
-               $this->ip = strtr( $ip, '_', ' ' );
-               $this->id = $id;
-               $this->reason = $reason;
-       }
-
-       /**
-        * Generates the unblock form
-        * @param $err string: error message
-        * @return $out string: HTML form
-        */
-       function showForm( $err ) {
-               global $wgOut, $wgUser, $wgSysopUserBans;
-
-               $wgOut->setPagetitle( wfMsg( 'unblockip' ) );
-               $wgOut->addWikiMsg( 'unblockiptext' );
-
-               $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
-               $action = $titleObj->getLocalURL( "action=submit" );
-
-               if ( "" != $err ) {
-                       $wgOut->setSubtitle( wfMsg( "formerror" ) );
-                       $wgOut->addWikiText( Xml::tags( 'span', array( 'class' => 'error' ), $err ) . "\n" );
-               }
-
-               $addressPart = false;
-               if ( $this->id ) {
-                       $block = Block::newFromID( $this->id );
-                       if ( $block ) {
-                               $encName = htmlspecialchars( $block->getRedactedName() );
-                               $encId = $this->id;
-                               $addressPart = $encName . Xml::hidden( 'id', $encId );
-                               $ipa = wfMsgHtml( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' );
-                       }
-               }
-               if ( !$addressPart ) {
-                       $addressPart = Xml::input( 'wpUnblockAddress', 40, $this->ip, array( 'type' => 'text', 'tabindex' => '1' ) );
-                       $ipa = Xml::label( wfMsg( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' ), 'wpUnblockAddress' );
-               }
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'unblockip' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'ipb-unblock' ) ) .
-                       Xml::openElement( 'table', array( 'id' => 'mw-unblock-table' ) ).
-                       "<tr>
-                               <td class='mw-label'>
-                                       {$ipa}
-                               </td>
-                               <td class='mw-input'>
-                                       {$addressPart}
-                               </td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>" .
-                                       Xml::label( wfMsg( 'ipbreason' ), 'wpUnblockReason' ) .
-                               "</td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'wpUnblockReason', 40, $this->reason, array( 'type' => 'text', 'tabindex' => '2' ) ) .
-                               "</td>
-                       </tr>
-                       <tr>
-                               <td>&nbsp;</td>
-                               <td class='mw-submit'>" .
-                                       Xml::submitButton( wfMsg( 'ipusubmit' ), array( 'name' => 'wpBlock', 'tabindex' => '3' ) ) .
-                               "</td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-
-       }
-
-       const UNBLOCK_SUCCESS = 0; // Success
-       const UNBLOCK_NO_SUCH_ID = 1; // No such block ID
-       const UNBLOCK_USER_NOT_BLOCKED = 2; // IP wasn't blocked
-       const UNBLOCK_BLOCKED_AS_RANGE = 3; // IP is part of a range block
-       const UNBLOCK_UNKNOWNERR = 4; // Unknown error
-
-       /**
-        * Backend code for unblocking. doSubmit() wraps around this.
-        * $range is only used when UNBLOCK_BLOCKED_AS_RANGE is returned, in which
-        * case it contains the range $ip is part of.
-        * @return array array(message key, parameters) on failure, empty array on success
-        */
-
-       static function doUnblock(&$id, &$ip, &$reason, &$range = null)
-       {
-               if ( $id ) {
-                       $block = Block::newFromID( $id );
-                       if ( !$block ) {
-                               return array('ipb_cant_unblock', htmlspecialchars($id));
-                       }
-                       $ip = $block->getRedactedName();
-               } else {
-                       $block = new Block();
-                       $ip = trim( $ip );
-                       if ( substr( $ip, 0, 1 ) == "#" ) {
-                               $id = substr( $ip, 1 );
-                               $block = Block::newFromID( $id );
-                               if( !$block ) {
-                                       return array('ipb_cant_unblock', htmlspecialchars($id));
-                               }
-                               $ip = $block->getRedactedName();
-                       } else {
-                               $block = Block::newFromDB( $ip );
-                               if ( !$block ) {
-                                       return array('ipb_cant_unblock', htmlspecialchars($id));
-                               }
-                               if( $block->mRangeStart != $block->mRangeEnd
-                                               && !strstr( $ip, "/" ) ) {
-                                       /* If the specified IP is a single address, and the block is
-                                        * a range block, don't unblock the range. */
-                                        $range = $block->mAddress;
-                                        return array('ipb_blocked_as_range', $ip, $range);
-                               }
-                       }
-               }
-               // Yes, this is really necessary
-               $id = $block->mId;
-
-               # Delete block
-               if ( !$block->delete() ) {
-                       return array('ipb_cant_unblock', htmlspecialchars($id));
-               }
-
-               # Make log entry
-               $log = new LogPage( 'block' );
-               $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $ip ), $reason );
-               return array();
-       }
-
-       function doSubmit() {
-               global $wgOut;
-               $retval = self::doUnblock($this->id, $this->ip, $this->reason, $range);
-               if(!empty($retval))
-               {
-                       $key = array_shift($retval);
-                       $this->showForm(wfMsgReal($key, $retval));
-                       return;
-               }
-               # Report to the user
-               $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
-               $success = $titleObj->getFullURL( "action=success&successip=" . urlencode( $this->ip ) );
-               $wgOut->redirect( $success );
-       }
-
-       function showList( $msg ) {
-               global $wgOut, $wgUser;
-
-               $wgOut->setPagetitle( wfMsg( "ipblocklist" ) );
-               if ( "" != $msg ) {
-                       $wgOut->setSubtitle( $msg );
-               }
-
-               // Purge expired entries on one in every 10 queries
-               if ( !mt_rand( 0, 10 ) ) {
-                       Block::purgeExpired();
-               }
-
-               $conds = array();
-               $matches = array();
-               // Is user allowed to see all the blocks?
-               if ( !$wgUser->isAllowed( 'suppress' ) )
-                       $conds['ipb_deleted'] = 0;
-               if ( $this->ip == '' ) {
-                       // No extra conditions
-               } elseif ( substr( $this->ip, 0, 1 ) == '#' ) {
-                       $conds['ipb_id'] = substr( $this->ip, 1 );
-               } elseif ( IP::toUnsigned( $this->ip ) !== false ) {
-                       $conds['ipb_address'] = $this->ip;
-                       $conds['ipb_auto'] = 0;
-               } elseif( preg_match( '/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\\/(\\d{1,2})$/', $this->ip, $matches ) ) {
-                       $conds['ipb_address'] = Block::normaliseRange( $this->ip );
-                       $conds['ipb_auto'] = 0;
-               } else {
-                       $user = User::newFromName( $this->ip );
-                       if ( $user && ( $id = $user->getId() ) != 0 ) {
-                               $conds['ipb_user'] = $id;
-                       } else {
-                               // Uh...?
-                               $conds['ipb_address'] = $this->ip;
-                               $conds['ipb_auto'] = 0;
-                       }
-               }
-
-               $pager = new IPBlocklistPager( $this, $conds );
-               if ( $pager->getNumRows() ) {
-                       $wgOut->addHTML(
-                               $this->searchForm() .
-                               $pager->getNavigationBar() .
-                               Xml::tags( 'ul', null, $pager->getBody() ) .
-                               $pager->getNavigationBar()
-                       );
-               } elseif ( $this->ip != '') {
-                       $wgOut->addHTML( $this->searchForm() );
-                       $wgOut->addWikiMsg( 'ipblocklist-no-results' );
-               } else {
-                       $wgOut->addWikiMsg( 'ipblocklist-empty' );
-               }
-       }
-
-       function searchForm() {
-               global $wgTitle, $wgScript, $wgRequest;
-               return
-                       Xml::tags( 'form', array( 'action' => $wgScript ),
-                               Xml::hidden( 'title', $wgTitle->getPrefixedDbKey() ) .
-                               Xml::openElement( 'fieldset' ) .
-                               Xml::element( 'legend', null, wfMsg( 'ipblocklist-legend' ) ) .
-                               Xml::inputLabel( wfMsg( 'ipblocklist-username' ), 'ip', 'ip', /* size */ false, $this->ip ) .
-                               '&nbsp;' .
-                               Xml::submitButton( wfMsg( 'ipblocklist-submit' ) ) .
-                               Xml::closeElement( 'fieldset' )
-                       );
-       }
-
-       /**
-        * Callback function to output a block
-        */
-       function formatRow( $block ) {
-               global $wgUser, $wgLang;
-
-               wfProfileIn( __METHOD__ );
-
-               static $sk=null, $msg=null;
-
-               if( is_null( $sk ) )
-                       $sk = $wgUser->getSkin();
-               if( is_null( $msg ) ) {
-                       $msg = array();
-                       $keys = array( 'infiniteblock', 'expiringblock', 'unblocklink',
-                               'anononlyblock', 'createaccountblock', 'noautoblockblock', 'emailblock' );
-                       foreach( $keys as $key ) {
-                               $msg[$key] = wfMsgHtml( $key );
-                       }
-                       $msg['blocklistline'] = wfMsg( 'blocklistline' );
-               }
-
-               # Prepare links to the blocker's user and talk pages
-               $blocker_id = $block->getBy();
-               $blocker_name = $block->getByName();
-               $blocker = $sk->userLink( $blocker_id, $blocker_name );
-               $blocker .= $sk->userToolLinks( $blocker_id, $blocker_name );
-
-               # Prepare links to the block target's user and contribs. pages (as applicable, don't do it for autoblocks)
-               if( $block->mAuto ) {
-                       $target = $block->getRedactedName(); # Hide the IP addresses of auto-blocks; privacy
-               } else {
-                       $target = $sk->userLink( $block->mUser, $block->mAddress )
-                               . $sk->userToolLinks( $block->mUser, $block->mAddress, false, Linker::TOOL_LINKS_NOBLOCK );
-               }
-
-               $formattedTime = $wgLang->timeanddate( $block->mTimestamp, true );
-
-               $properties = array();
-               $properties[] = Block::formatExpiry( $block->mExpiry );
-               if ( $block->mAnonOnly ) {
-                       $properties[] = $msg['anononlyblock'];
-               }
-               if ( $block->mCreateAccount ) {
-                       $properties[] = $msg['createaccountblock'];
-               }
-               if (!$block->mEnableAutoblock && $block->mUser ) {
-                       $properties[] = $msg['noautoblockblock'];
-               }
-
-               if ( $block->mBlockEmail && $block->mUser ) {
-                       $properties[] = $msg['emailblock'];
-               }
-
-               $properties = implode( ', ', $properties );
-
-               $line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) );
-
-               $unblocklink = '';
-               if ( $wgUser->isAllowed('block') ) {
-                       $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
-                       $unblocklink = ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')';
-               }
-
-               $comment = $sk->commentBlock( $block->mReason );
-
-               $s = "{$line} $comment";
-               if ( $block->mHideName )
-                       $s = '<span class="history-deleted">' . $s . '</span>';
-
-               wfProfileOut( __METHOD__ );
-               return "<li>$s $unblocklink</li>\n";
-       }
-}
-
-/**
- * @todo document
- * @ingroup Pager
- */
-class IPBlocklistPager extends ReverseChronologicalPager {
-       public $mForm, $mConds;
-
-       function __construct( $form, $conds = array() ) {
-               $this->mForm = $form;
-               $this->mConds = $conds;
-               parent::__construct();
-       }
-
-       function getStartBody() {
-               wfProfileIn( __METHOD__ );
-               # Do a link batch query
-               $this->mResult->seek( 0 );
-               $lb = new LinkBatch;
-
-               /*
-               while ( $row = $this->mResult->fetchObject() ) {
-                       $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
-                       $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) );
-                       $lb->addObj( Title::makeTitleSafe( NS_USER, $row->ipb_address ) );
-                       $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ipb_address ) );
-               }*/
-               # Faster way
-               # Usernames and titles are in fact related by a simple substitution of space -> underscore
-               # The last few lines of Title::secureAndSplit() tell the story.
-               while ( $row = $this->mResult->fetchObject() ) {
-                       $name = str_replace( ' ', '_', $row->ipb_by_text );
-                       $lb->add( NS_USER, $name );
-                       $lb->add( NS_USER_TALK, $name );
-                       $name = str_replace( ' ', '_', $row->ipb_address );
-                       $lb->add( NS_USER, $name );
-                       $lb->add( NS_USER_TALK, $name );
-               }
-               $lb->execute();
-               wfProfileOut( __METHOD__ );
-               return '';
-       }
-
-       function formatRow( $row ) {
-               $block = new Block;
-               $block->initFromRow( $row );
-               return $this->mForm->formatRow( $block );
-       }
-
-       function getQueryInfo() {
-               $conds = $this->mConds;
-               $conds[] = 'ipb_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
-               return array(
-                       'tables' => 'ipblocks',
-                       'fields' => '*',
-                       'conds' => $conds,
-               );
-       }
-
-       function getIndexField() {
-               return 'ipb_timestamp';
-       }
-}
diff --git a/includes/specials/Listgrouprights.php b/includes/specials/Listgrouprights.php
deleted file mode 100644 (file)
index 131c060..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * This special page lists all defined user groups and the associated rights.
- * See also @ref $wgGroupPermissions.
- *
- * @ingroup SpecialPage
- * @author Petr Kadlec <mormegil@centrum.cz>
- */
-class SpecialListGroupRights extends SpecialPage {
-
-       var $skin;
-
-       /**
-        * Constructor
-        */
-       function __construct() {
-               global $wgUser;
-               parent::__construct( 'Listgrouprights' );
-               $this->skin = $wgUser->getSkin();
-       }
-
-       /**
-        * Show the special page
-        */
-       public function execute( $par ) {
-               global $wgOut, $wgGroupPermissions, $wgImplicitGroups, $wgMessageCache;
-               $wgMessageCache->loadAllMessages();
-
-               $this->setHeaders();
-               $this->outputHeader();
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'table', array( 'class' => 'mw-listgrouprights-table' ) ) .
-                               '<tr>' .
-                                       Xml::element( 'th', null, wfMsg( 'listgrouprights-group' ) ) .
-                                       Xml::element( 'th', null, wfMsg( 'listgrouprights-rights' ) ) .
-                               '</tr>'
-               );
-
-               foreach( $wgGroupPermissions as $group => $permissions ) {
-                       $groupname = ( $group == '*' ) ? 'all' : htmlspecialchars( $group ); // Replace * with a more descriptive groupname
-
-                       $msg = wfMsg( 'group-' . $groupname );
-                       if ( wfEmptyMsg( 'group-' . $groupname, $msg ) || $msg == '' ) {
-                               $groupnameLocalized = $groupname;
-                       } else {
-                               $groupnameLocalized = $msg;
-                       }
-
-                       $msg = wfMsgForContent( 'grouppage-' . $groupname );
-                       if ( wfEmptyMsg( 'grouppage-' . $groupname, $msg ) || $msg == '' ) {
-                               $grouppageLocalized = MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname;
-                       } else {
-                               $grouppageLocalized = $msg;
-                       }
-
-                       if( $group == '*' ) {
-                               // Do not make a link for the generic * group
-                               $grouppage = $groupnameLocalized;
-                       } else {
-                               $grouppage = $this->skin->makeLink( $grouppageLocalized, $groupnameLocalized );
-                       }
-
-                       if ( !in_array( $group, $wgImplicitGroups ) ) {
-                               $grouplink = '<br />' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Listusers' ), wfMsgHtml( 'listgrouprights-members' ), 'group=' . $group );
-                       } else {
-                               // No link to Special:listusers for implicit groups as they are unlistable
-                               $grouplink = '';
-                       }
-
-                       $wgOut->addHTML(
-                               '<tr>
-                                       <td>' .
-                                               $grouppage . $grouplink .
-                                       '</td>
-                                       <td>' .
-                                               self::formatPermissions( $permissions ) .
-                                       '</td>
-                               </tr>'
-                       );
-               }
-               $wgOut->addHTML(
-                       Xml::closeElement( 'table' ) . "\n"
-               );
-       }
-
-       /**
-        * Create a user-readable list of permissions from the given array.
-        *
-        * @param $permissions Array of permission => bool (from $wgGroupPermissions items)
-        * @return string List of all granted permissions, separated by comma separator
-        */
-        private static function formatPermissions( $permissions ) {
-               $r = array();
-               foreach( $permissions as $permission => $granted ) {
-                       if ( $granted ) {
-                               $description = wfMsgHTML( 'listgrouprights-right-display',
-                                       User::getRightDescription($permission),
-                                       $permission
-                               );
-                               $r[] = $description;
-                       }
-               }
-               sort( $r );
-               if( empty( $r ) ) {
-                       return '';
-               } else {
-                       return '<ul><li>' . implode( "</li>\n<li>", $r ) . '</li></ul>';
-               }
-       }
-}
diff --git a/includes/specials/Listredirects.php b/includes/specials/Listredirects.php
deleted file mode 100644 (file)
index 808aab1..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- *
- * @author Rob Church <robchur@gmail.com>
- * @copyright Â© 2006 Rob Church
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * Special:Listredirects - Lists all the redirects on the wiki.
- * @ingroup SpecialPage
- */
-class ListredirectsPage extends QueryPage {
-
-       function getName() { return( 'Listredirects' ); }
-       function isExpensive() { return( true ); }
-       function isSyndicated() { return( false ); }
-       function sortDescending() { return( false ); }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-               $sql = "SELECT 'Listredirects' AS type, page_title AS title, page_namespace AS namespace, 0 AS value FROM $page WHERE page_is_redirect = 1";
-               return( $sql );
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang;
-
-               # Make a link to the redirect itself
-               $rd_title = Title::makeTitle( $result->namespace, $result->title );
-               $rd_link = $skin->makeLinkObj( $rd_title, '', 'redirect=no' );
-
-               # Find out where the redirect leads
-               $revision = Revision::newFromTitle( $rd_title );
-               if( $revision ) {
-                       # Make a link to the destination page
-                       $target = Title::newFromRedirect( $revision->getText() );
-                       if( $target ) {
-                               $arr = $wgContLang->getArrow() . $wgContLang->getDirMark();
-                               $targetLink = $skin->makeLinkObj( $target );
-                               return "$rd_link $arr $targetLink";
-                       } else {
-                               return "<s>$rd_link</s>";
-                       }
-               } else {
-                       return "<s>$rd_link</s>";
-               }
-       }
-}
-
-function wfSpecialListredirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $lrp = new ListredirectsPage();
-       $lrp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Listusers.php b/includes/specials/Listusers.php
deleted file mode 100644 (file)
index 7dba44e..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-
-# Copyright (C) 2004 Brion Vibber, lcrocker, Tim Starling,
-# Domas Mituzas, Ashar Voultoiz, Jens Frank, Zhengzhu.
-#
-# Â© 2006 Rob Church <robchur@gmail.com>
-#
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * This class is used to get a list of user. The ones with specials
- * rights (sysop, bureaucrat, developer) will have them displayed
- * next to their names.
- *
- * @ingroup SpecialPage
- */
-class UsersPager extends AlphabeticPager {
-
-       function __construct($group=null) {
-               global $wgRequest;
-               $this->requestedGroup = $group != "" ? $group : $wgRequest->getVal( 'group' );
-               $un = $wgRequest->getText( 'username' );
-               $this->requestedUser = '';
-               if ( $un != '' ) {
-                       $username = Title::makeTitleSafe( NS_USER, $un );
-                       if( ! is_null( $username ) ) {
-                               $this->requestedUser = $username->getText();
-                       }
-               }
-               parent::__construct();
-       }
-
-
-       function getIndexField() {
-               return 'user_name';
-       }
-
-       function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $conds=array();
-               // don't show hidden names
-               $conds[]='ipb_deleted IS NULL OR ipb_deleted = 0';
-               if ($this->requestedGroup != "") {
-                       $conds['ug_group'] = $this->requestedGroup;
-                       $useIndex = '';
-               } else {
-                       $useIndex = $dbr->useIndexClause('user_name');
-               }
-               if ($this->requestedUser != "") {
-                       $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser );
-               }
-
-               list ($user,$user_groups,$ipblocks) = $dbr->tableNamesN('user','user_groups','ipblocks');
-
-               $query = array(
-                       'tables' => " $user $useIndex LEFT JOIN $user_groups ON user_id=ug_user
-                               LEFT JOIN $ipblocks ON user_id=ipb_user AND ipb_auto=0 ",
-                       'fields' => array('user_name',
-                               'MAX(user_id) AS user_id',
-                               'COUNT(ug_group) AS numgroups',
-                               'MAX(ug_group) AS singlegroup'),
-                       'options' => array('GROUP BY' => 'user_name'),
-                       'conds' => $conds
-               );
-
-               wfRunHooks( 'SpecialListusersQueryInfo', array( $this, &$query ) );
-               return $query;
-       }
-
-       function formatRow( $row ) {
-               $userPage = Title::makeTitle( NS_USER, $row->user_name );
-               $name = $this->getSkin()->makeLinkObj( $userPage, htmlspecialchars( $userPage->getText() ) );
-
-               if( $row->numgroups > 1 || ( $this->requestedGroup && $row->numgroups == 1 ) ) {
-                       $list = array();
-                       foreach( self::getGroups( $row->user_id ) as $group )
-                               $list[] = self::buildGroupLink( $group );
-                       $groups = implode( ', ', $list );
-               } elseif( $row->numgroups == 1 ) {
-                       $groups = self::buildGroupLink( $row->singlegroup );
-               } else {
-                       $groups = '';
-               }
-
-               $item = wfSpecialList( $name, $groups );
-               wfRunHooks( 'SpecialListusersFormatRow', array( &$item, $row ) );
-               return "<li>{$item}</li>";
-       }
-
-       function getBody() {
-               if (!$this->mQueryDone) {
-                       $this->doQuery();
-               }
-               $batch = new LinkBatch;
-
-               $this->mResult->rewind();
-
-               while ( $row = $this->mResult->fetchObject() ) {
-                       $batch->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
-               }
-               $batch->execute();
-               $this->mResult->rewind();
-               return parent::getBody();
-       }
-
-       function getPageHeader( ) {
-               global $wgScript, $wgRequest;
-               $self = $this->getTitle();
-
-               # Form tag
-               $out  = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
-                       '<fieldset>' .
-                       Xml::element( 'legend', array(), wfMsg( 'listusers' ) );
-               $out .= Xml::hidden( 'title', $self->getPrefixedDbKey() );
-
-               # Username field
-               $out .= Xml::label( wfMsg( 'listusersfrom' ), 'offset' ) . ' ' .
-                       Xml::input( 'username', 20, $this->requestedUser, array( 'id' => 'offset' ) ) . ' ';
-
-               # Group drop-down list
-               $out .= Xml::label( wfMsg( 'group' ), 'group' ) . ' ' .
-                       Xml::openElement('select',  array( 'name' => 'group', 'id' => 'group' ) ) .
-                       Xml::option( wfMsg( 'group-all' ), '' );
-               foreach( $this->getAllGroups() as $group => $groupText )
-                       $out .= Xml::option( $groupText, $group, $group == $this->requestedGroup );
-               $out .= Xml::closeElement( 'select' ) . ' ';
-
-               wfRunHooks( 'SpecialListusersHeaderForm', array( $this, &$out ) );
-
-               # Submit button and form bottom
-               if( $this->mLimit )
-                       $out .= Xml::hidden( 'limit', $this->mLimit );
-               $out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
-               wfRunHooks( 'SpecialListusersHeader', array( $this, &$out ) );
-               $out .= '</fieldset>' .
-                       Xml::closeElement( 'form' );
-
-               return $out;
-       }
-
-       function getAllGroups() {
-               $result = array();
-               foreach( User::getAllGroups() as $group ) {
-                       $result[$group] = User::getGroupName( $group );
-               }
-               return $result;
-       }
-
-       /**
-        * Preserve group and username offset parameters when paging
-        * @return array
-        */
-       function getDefaultQuery() {
-               $query = parent::getDefaultQuery();
-               if( $this->requestedGroup != '' )
-                       $query['group'] = $this->requestedGroup;
-               if( $this->requestedUser != '' )
-                       $query['username'] = $this->requestedUser;
-               wfRunHooks( 'SpecialListusersDefaultQuery', array( $this, &$query ) );
-               return $query;
-       }
-
-       /**
-        * Get a list of groups the specified user belongs to
-        *
-        * @param int $uid
-        * @return array
-        */
-       protected static function getGroups( $uid ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $groups = array();
-               $res = $dbr->select( 'user_groups', 'ug_group', array( 'ug_user' => $uid ), __METHOD__ );
-               if( $res && $dbr->numRows( $res ) > 0 ) {
-                       while( $row = $dbr->fetchObject( $res ) )
-                               $groups[] = $row->ug_group;
-                       $dbr->freeResult( $res );
-               }
-               return $groups;
-       }
-
-       /**
-        * Format a link to a group description page
-        *
-        * @param string $group
-        * @return string
-        */
-       protected static function buildGroupLink( $group ) {
-               static $cache = array();
-               if( !isset( $cache[$group] ) )
-                       $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupMember( $group ) );
-               return $cache[$group];
-       }
-}
-
-/**
- * constructor
- * $par string (optional) A group to list users from
- */
-function wfSpecialListusers( $par = null ) {
-       global $wgRequest, $wgOut;
-
-       $up = new UsersPager($par);
-
-       # getBody() first to check, if empty
-       $usersbody = $up->getBody();
-       $s = $up->getPageHeader();
-       if( $usersbody ) {
-               $s .=   $up->getNavigationBar();
-               $s .=   '<ul>' . $usersbody . '</ul>';
-               $s .=   $up->getNavigationBar() ;
-       } else {
-               $s .=   '<p>' . wfMsgHTML('listusers-noresult') . '</p>';
-       };
-
-       $wgOut->addHTML( $s );
-}
diff --git a/includes/specials/Lockdb.php b/includes/specials/Lockdb.php
deleted file mode 100644 (file)
index 0401922..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialLockdb() {
-       global $wgUser, $wgOut, $wgRequest;
-
-       if( !$wgUser->isAllowed( 'siteadmin' ) ) {
-               $wgOut->permissionRequired( 'siteadmin' );
-               return;
-       }
-
-       # If the lock file isn't writable, we can do sweet bugger all
-       global $wgReadOnlyFile;
-       if( !is_writable( dirname( $wgReadOnlyFile ) ) ) {
-               DBLockForm::notWritable();
-               return;
-       }
-
-       $action = $wgRequest->getVal( 'action' );
-       $f = new DBLockForm();
-
-       if ( 'success' == $action ) {
-               $f->showSuccess();
-       } else if ( 'submit' == $action && $wgRequest->wasPosted() &&
-               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               $f->doSubmit();
-       } else {
-               $f->showForm( '' );
-       }
-}
-
-/**
- * A form to make the database readonly (eg for maintenance purposes).
- * @ingroup SpecialPage
- */
-class DBLockForm {
-       var $reason = '';
-
-       function DBLockForm() {
-               global $wgRequest;
-               $this->reason = $wgRequest->getText( 'wpLockReason' );
-       }
-
-       function showForm( $err ) {
-               global $wgOut, $wgUser;
-
-               $wgOut->setPagetitle( wfMsg( 'lockdb' ) );
-               $wgOut->addWikiMsg( 'lockdbtext' );
-
-               if ( "" != $err ) {
-                       $wgOut->setSubtitle( wfMsg( 'formerror' ) );
-                       $wgOut->addHTML( '<p class="error">' . htmlspecialchars( $err ) . "</p>\n" );
-               }
-               $lc = htmlspecialchars( wfMsg( 'lockconfirm' ) );
-               $lb = htmlspecialchars( wfMsg( 'lockbtn' ) );
-               $elr = htmlspecialchars( wfMsg( 'enterlockreason' ) );
-               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
-               $action = $titleObj->escapeLocalURL( 'action=submit' );
-               $reason = htmlspecialchars( $this->reason );
-               $token = htmlspecialchars( $wgUser->editToken() );
-
-               $wgOut->addHTML( <<<END
-<form id="lockdb" method="post" action="{$action}">
-{$elr}:
-<textarea name="wpLockReason" rows="10" cols="60" wrap="virtual">{$reason}</textarea>
-<table border="0">
-       <tr>
-               <td align="right">
-                       <input type="checkbox" name="wpLockConfirm" />
-               </td>
-               <td align="left">{$lc}</td>
-       </tr>
-       <tr>
-               <td>&nbsp;</td>
-               <td align="left">
-                       <input type="submit" name="wpLock" value="{$lb}" />
-               </td>
-       </tr>
-</table>
-<input type="hidden" name="wpEditToken" value="{$token}" />
-</form>
-END
-);
-
-       }
-
-       function doSubmit() {
-               global $wgOut, $wgUser, $wgLang, $wgRequest;
-               global $wgReadOnlyFile;
-
-               if ( ! $wgRequest->getCheck( 'wpLockConfirm' ) ) {
-                       $this->showForm( wfMsg( 'locknoconfirm' ) );
-                       return;
-               }
-               $fp = @fopen( $wgReadOnlyFile, 'w' );
-
-               if ( false === $fp ) {
-                       # This used to show a file not found error, but the likeliest reason for fopen()
-                       # to fail at this point is insufficient permission to write to the file...good old
-                       # is_writable() is plain wrong in some cases, it seems...
-                       self::notWritable();
-                       return;
-               }
-               fwrite( $fp, $this->reason );
-               fwrite( $fp, "\n<p>(by " . $wgUser->getName() . " at " .
-                 $wgLang->timeanddate( wfTimestampNow() ) . ")\n" );
-               fclose( $fp );
-
-               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
-               $wgOut->redirect( $titleObj->getFullURL( 'action=success' ) );
-       }
-
-       function showSuccess() {
-               global $wgOut;
-
-               $wgOut->setPagetitle( wfMsg( 'lockdb' ) );
-               $wgOut->setSubtitle( wfMsg( 'lockdbsuccesssub' ) );
-               $wgOut->addWikiMsg( 'lockdbsuccesstext' );
-       }
-
-       public static function notWritable() {
-               global $wgOut;
-               $wgOut->showErrorPage( 'lockdb', 'lockfilenotwritable' );
-       }
-}
diff --git a/includes/specials/Log.php b/includes/specials/Log.php
deleted file mode 100644 (file)
index 3154ed1..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-# Copyright (C) 2008 Aaron Schulz
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * constructor
- */
-function wfSpecialLog( $par = '' ) {
-       global $wgRequest, $wgOut, $wgUser;
-       # Get parameters
-       $type = $wgRequest->getVal( 'type', $par );
-       $user = $wgRequest->getText( 'user' );
-       $title = $wgRequest->getText( 'page' );
-       $pattern = $wgRequest->getBool( 'pattern' );
-       $y = $wgRequest->getIntOrNull( 'year' );
-       $m = $wgRequest->getIntOrNull( 'month' );
-       # Don't let the user get stuck with a certain date
-       $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
-       if( $skip ) {
-               $y = '';
-               $m = '';
-       }
-       # Create a LogPager item to get the results and a LogEventsList
-       # item to format them...
-       $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
-       $pager = new LogPager( $loglist, $type, $user, $title, $pattern, array(), $y, $m );
-       # Set title and add header
-       $loglist->showHeader( $pager->getType() );
-       # Show form options
-       $loglist->showOptions( $pager->getType(), $pager->getUser(), $pager->getPage(), $pager->getPattern(),
-               $pager->getYear(), $pager->getMonth() );
-       # Insert list
-       $logBody = $pager->getBody();
-       if( $logBody ) {
-               $wgOut->addHTML(
-                       $pager->getNavigationBar() .
-                       $loglist->beginLogEventsList() .
-                       $logBody .
-                       $loglist->endLogEventsList() .
-                       $pager->getNavigationBar()
-               );
-       } else {
-               $wgOut->addWikiMsg( 'logempty' );
-       }
-}
diff --git a/includes/specials/Lonelypages.php b/includes/specials/Lonelypages.php
deleted file mode 100644 (file)
index 5aafac7..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page looking for articles with no article linking to them,
- * thus being lonely.
- * @ingroup SpecialPage
- */
-class LonelyPagesPage extends PageQueryPage {
-
-       function getName() {
-               return "Lonelypages";
-       }
-       function getPageHeader() {
-               return wfMsgExt( 'lonelypagestext', array( 'parse' ) );
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function isExpensive() {
-               return true;
-       }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
-
-               return
-                 "SELECT 'Lonelypages'  AS type,
-                         page_namespace AS namespace,
-                         page_title     AS title,
-                         page_title     AS value
-                    FROM $page
-               LEFT JOIN $pagelinks
-                      ON page_namespace=pl_namespace AND page_title=pl_title
-                   WHERE pl_namespace IS NULL
-                     AND page_namespace=".NS_MAIN."
-                     AND page_is_redirect=0";
-
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialLonelypages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new LonelyPagesPage();
-
-       return $lpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Longpages.php b/includes/specials/Longpages.php
deleted file mode 100644 (file)
index be16a02..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- *
- * @ingroup SpecialPage
- */
-class LongPagesPage extends ShortPagesPage {
-
-       function getName() {
-               return "Longpages";
-       }
-
-       function sortDescending() {
-               return true;
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialLongpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new LongPagesPage();
-
-       $lpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/MIMEsearch.php b/includes/specials/MIMEsearch.php
deleted file mode 100644 (file)
index 82ee4be..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-/**
- * A special page to search for files by MIME type as defined in the
- * img_major_mime and img_minor_mime fields in the image table
- *
- * @file
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * Searches the database for files of the requested MIME type, comparing this with the
- * 'img_major_mime' and 'img_minor_mime' fields in the image table.
- * @ingroup SpecialPage
- */
-class MIMEsearchPage extends QueryPage {
-       var $major, $minor;
-
-       function MIMEsearchPage( $major, $minor ) {
-               $this->major = $major;
-               $this->minor = $minor;
-       }
-
-       function getName() { return 'MIMEsearch'; }
-
-       /**
-        * Due to this page relying upon extra fields being passed in the SELECT it
-        * will fail if it's set as expensive and misermode is on
-        */
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function linkParameters() {
-               $arr = array( $this->major, $this->minor );
-               $mime = implode( '/', $arr );
-               return array( 'mime' => $mime );
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $major = $dbr->addQuotes( $this->major );
-               $minor = $dbr->addQuotes( $this->minor );
-
-               return
-                       "SELECT 'MIMEsearch' AS type,
-                               " . NS_IMAGE . " AS namespace,
-                               img_name AS title,
-                               img_major_mime AS value,
-
-                               img_size,
-                               img_width,
-                               img_height,
-                               img_user_text,
-                               img_timestamp
-                       FROM $image
-                       WHERE img_major_mime = $major AND img_minor_mime = $minor
-                       ";
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang, $wgLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getText() );
-               $plink = $skin->makeLink( $nt->getPrefixedText(), $text );
-
-               $download = $skin->makeMediaLinkObj( $nt, wfMsgHtml( 'download' ) );
-               $bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->img_size ) );
-               $dimensions = wfMsgHtml( 'widthheight', $wgLang->formatNum( $result->img_width ),
-                       $wgLang->formatNum( $result->img_height ) );
-               $user = $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ), $result->img_user_text );
-               $time = $wgLang->timeanddate( $result->img_timestamp );
-
-               return "($download) $plink . . $dimensions . . $bytes . . $user . . $time";
-       }
-}
-
-/**
- * Output the HTML search form, and constructs the MIMEsearchPage object.
- */
-function wfSpecialMIMEsearch( $par = null ) {
-       global $wgRequest, $wgTitle, $wgOut;
-
-       $mime = isset( $par ) ? $par : $wgRequest->getText( 'mime' );
-
-       $wgOut->addHTML(
-               Xml::openElement( 'form', array( 'id' => 'specialmimesearch', 'method' => 'get', 'action' => $wgTitle->getLocalUrl() ) ) .
-               Xml::openElement( 'fieldset' ) .
-               Xml::element( 'legend', null, wfMsg( 'mimesearch' ) ) .
-               Xml::inputLabel( wfMsg( 'mimetype' ), 'mime', 'mime', 20, $mime ) . ' ' .
-               Xml::submitButton( wfMsg( 'ilsubmit' ) ) .
-               Xml::closeElement( 'fieldset' ) .
-               Xml::closeElement( 'form' )
-       );
-
-       list( $major, $minor ) = wfSpecialMIMEsearchParse( $mime );
-       if ( $major == '' or $minor == '' or !wfSpecialMIMEsearchValidType( $major ) )
-               return;
-       $wpp = new MIMEsearchPage( $major, $minor );
-
-       list( $limit, $offset ) = wfCheckLimits();
-       $wpp->doQuery( $offset, $limit );
-}
-
-function wfSpecialMIMEsearchParse( $str ) {
-       // searched for an invalid MIME type.
-       if( strpos( $str, '/' ) === false) {
-               return array ('', '');
-       }
-
-       list( $major, $minor ) = explode( '/', $str, 2 );
-
-       return array(
-               ltrim( $major, ' ' ),
-               rtrim( $minor, ' ' )
-       );
-}
-
-function wfSpecialMIMEsearchValidType( $type ) {
-       // From maintenance/tables.sql => img_major_mime
-       $types = array(
-               'unknown',
-               'application',
-               'audio',
-               'image',
-               'text',
-               'video',
-               'message',
-               'model',
-               'multipart'
-       );
-
-       return in_array( $type, $types );
-}
diff --git a/includes/specials/MergeHistory.php b/includes/specials/MergeHistory.php
deleted file mode 100644 (file)
index 6183374..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-<?php
-/**
- * Special page allowing users with the appropriate permissions to
- * merge article histories, with some restrictions
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialMergehistory( $par ) {
-       global $wgRequest;
-
-       $form = new MergehistoryForm( $wgRequest, $par );
-       $form->execute();
-}
-
-/**
- * The HTML form for Special:MergeHistory, which allows users with the appropriate
- * permissions to view and restore deleted content.
- * @ingroup SpecialPage
- */
-class MergehistoryForm {
-       var $mAction, $mTarget, $mDest, $mTimestamp, $mTargetID, $mDestID, $mComment;
-       var $mTargetObj, $mDestObj;
-
-       function MergehistoryForm( $request, $par = "" ) {
-               global $wgUser;
-
-               $this->mAction = $request->getVal( 'action' );
-               $this->mTarget = $request->getVal( 'target' );
-               $this->mDest = $request->getVal( 'dest' );
-               $this->mSubmitted = $request->getBool( 'submitted' );
-
-               $this->mTargetID = intval( $request->getVal( 'targetID' ) );
-               $this->mDestID = intval( $request->getVal( 'destID' ) );
-               $this->mTimestamp = $request->getVal( 'mergepoint' );
-               if( !preg_match("/[0-9]{14}/",$this->mTimestamp) ) {
-                       $this->mTimestamp = '';
-               }
-               $this->mComment = $request->getText( 'wpComment' );
-
-               $this->mMerge = $request->wasPosted() && $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
-               // target page
-               if( $this->mSubmitted ) {
-                       $this->mTargetObj = Title::newFromURL( $this->mTarget );
-                       $this->mDestObj = Title::newFromURL( $this->mDest );
-               } else {
-                       $this->mTargetObj = null;
-                       $this->mDestObj = null;
-               }
-
-               $this->preCacheMessages();
-       }
-
-       /**
-        * As we use the same small set of messages in various methods and that
-        * they are called often, we call them once and save them in $this->message
-        */
-       function preCacheMessages() {
-               // Precache various messages
-               if( !isset( $this->message ) ) {
-                       $this->message['last'] = wfMsgExt( 'last', array( 'escape') );
-               }
-       }
-
-       function execute() {
-               global $wgOut, $wgUser;
-
-               $wgOut->setPagetitle( wfMsgHtml( "mergehistory" ) );
-
-               if( $this->mTargetID && $this->mDestID && $this->mAction=="submit" && $this->mMerge ) {
-                       return $this->merge();
-               }
-
-               if ( !$this->mSubmitted ) {
-                       $this->showMergeForm();
-                       return;
-               }
-
-               $errors = array();
-               if ( !$this->mTargetObj instanceof Title ) {
-                       $errors[] = wfMsgExt( 'mergehistory-invalid-source', array( 'parse' ) );
-               } elseif( !$this->mTargetObj->exists() ) {
-                       $errors[] = wfMsgExt( 'mergehistory-no-source', array( 'parse' ),
-                               wfEscapeWikiText( $this->mTargetObj->getPrefixedText() )
-                       );
-               }
-
-               if ( !$this->mDestObj instanceof Title) {
-                       $errors[] = wfMsgExt( 'mergehistory-invalid-destination', array( 'parse' ) );
-               } elseif( !$this->mDestObj->exists() ) {
-                       $errors[] = wfMsgExt( 'mergehistory-no-destination', array( 'parse' ),
-                               wfEscapeWikiText( $this->mDestObj->getPrefixedText() )
-                       );
-               }
-
-               // TODO: warn about target = dest?
-
-               if ( count( $errors ) ) {
-                       $this->showMergeForm();
-                       $wgOut->addHTML( implode( "\n", $errors ) );
-               } else {
-                       $this->showHistory();
-               }
-
-       }
-
-       function showMergeForm() {
-               global $wgOut, $wgScript;
-
-               $wgOut->addWikiMsg( 'mergehistory-header' );
-
-               $wgOut->addHtml(
-                       Xml::openElement( 'form', array(
-                               'method' => 'get',
-                               'action' => $wgScript ) ) .
-                       '<fieldset>' .
-                       Xml::element( 'legend', array(),
-                               wfMsg( 'mergehistory-box' ) ) .
-                       Xml::hidden( 'title',
-                               SpecialPage::getTitleFor( 'Mergehistory' )->getPrefixedDbKey() ) .
-                       Xml::hidden( 'submitted', '1' ) .
-                       Xml::hidden( 'mergepoint', $this->mTimestamp ) .
-                       Xml::openElement( 'table' ) .
-                       "<tr>
-                               <td>".Xml::label( wfMsg( 'mergehistory-from' ), 'target' )."</td>
-                               <td>".Xml::input( 'target', 30, $this->mTarget, array('id'=>'target') )."</td>
-                       </tr><tr>
-                               <td>".Xml::label( wfMsg( 'mergehistory-into' ), 'dest' )."</td>
-                               <td>".Xml::input( 'dest', 30, $this->mDest, array('id'=>'dest') )."</td>
-                       </tr><tr><td>" .
-                       Xml::submitButton( wfMsg( 'mergehistory-go' ) ) .
-                       "</td></tr>" .
-                       Xml::closeElement( 'table' ) .
-                       '</fieldset>' .
-                       '</form>' );
-       }
-
-       private function showHistory() {
-               global $wgLang, $wgContLang, $wgUser, $wgOut;
-
-               $this->sk = $wgUser->getSkin();
-
-               $wgOut->setPagetitle( wfMsg( "mergehistory" ) );
-
-               $this->showMergeForm();
-
-               # List all stored revisions
-               $revisions = new MergeHistoryPager( $this, array(), $this->mTargetObj, $this->mDestObj );
-               $haveRevisions = $revisions && $revisions->getNumRows() > 0;
-
-               $titleObj = SpecialPage::getTitleFor( "Mergehistory" );
-               $action = $titleObj->getLocalURL( "action=submit" );
-               # Start the form here
-               $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'merge' ) );
-               $wgOut->addHtml( $top );
-
-               if( $haveRevisions ) {
-                       # Format the user-visible controls (comment field, submission button)
-                       # in a nice little table
-                       $align = $wgContLang->isRtl() ? 'left' : 'right';
-                       $table =
-                               Xml::openElement( 'fieldset' ) .
-                               Xml::openElement( 'table' ) .
-                                       "<tr>
-                                               <td colspan='2'>" .
-                                                       wfMsgExt( 'mergehistory-merge', array('parseinline'),
-                                                               $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText() ) .
-                                               "</td>
-                                       </tr>
-                                       <tr>
-                                               <td align='$align'>" .
-                                                       Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) .
-                                               "</td>
-                                               <td>" .
-                                                       Xml::input( 'wpComment', 50, $this->mComment ) .
-                                               "</td>
-                                       </tr>
-                                       <tr>
-                                               <td>&nbsp;</td>
-                                               <td>" .
-                                                       Xml::submitButton( wfMsg( 'mergehistory-submit' ), array( 'name' => 'merge', 'id' => 'mw-merge-submit' ) ) .
-                                               "</td>
-                                       </tr>" .
-                               Xml::closeElement( 'table' ) .
-                               Xml::closeElement( 'fieldset' );
-
-                       $wgOut->addHtml( $table );
-               }
-
-               $wgOut->addHTML( "<h2 id=\"mw-mergehistory\">" . wfMsgHtml( "mergehistory-list" ) . "</h2>\n" );
-
-               if( $haveRevisions ) {
-                       $wgOut->addHTML( $revisions->getNavigationBar() );
-                       $wgOut->addHTML( "<ul>" );
-                       $wgOut->addHTML( $revisions->getBody() );
-                       $wgOut->addHTML( "</ul>" );
-                       $wgOut->addHTML( $revisions->getNavigationBar() );
-               } else {
-                       $wgOut->addWikiMsg( "mergehistory-empty" );
-               }
-
-               # Show relevant lines from the deletion log:
-               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'merge' ) ) . "</h2>\n" );
-               LogEventsList::showLogExtract( $wgOut, 'merge', $this->mTargetObj->getPrefixedText() );
-
-               # When we submit, go by page ID to avoid some nasty but unlikely collisions.
-               # Such would happen if a page was renamed after the form loaded, but before submit
-               $misc = Xml::hidden( 'targetID', $this->mTargetObj->getArticleID() );
-               $misc .= Xml::hidden( 'destID', $this->mDestObj->getArticleID() );
-               $misc .= Xml::hidden( 'target', $this->mTarget );
-               $misc .= Xml::hidden( 'dest', $this->mDest );
-               $misc .= Xml::hidden( 'wpEditToken', $wgUser->editToken() );
-               $misc .= Xml::closeElement( 'form' );
-               $wgOut->addHtml( $misc );
-
-               return true;
-       }
-
-       function formatRevisionRow( $row ) {
-               global $wgUser, $wgLang;
-
-               $rev = new Revision( $row );
-
-               $stxt = '';
-               $last = $this->message['last'];
-
-               $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
-               $checkBox = wfRadio( "mergepoint", $ts, false );
-
-               $pageLink = $this->sk->makeKnownLinkObj( $rev->getTitle(),
-                       htmlspecialchars( $wgLang->timeanddate( $ts ) ), 'oldid=' . $rev->getId() );
-               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
-                       $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
-               }
-
-               # Last link
-               if( !$rev->userCan( Revision::DELETED_TEXT ) )
-                       $last = $this->message['last'];
-               else if( isset($this->prevId[$row->rev_id]) )
-                       $last = $this->sk->makeKnownLinkObj( $rev->getTitle(), $this->message['last'],
-                               "diff=" . $row->rev_id . "&oldid=" . $this->prevId[$row->rev_id] );
-
-               $userLink = $this->sk->revUserTools( $rev );
-
-               if(!is_null($size = $row->rev_len)) {
-                       if($size == 0)
-                               $stxt = wfMsgHtml('historyempty');
-                       else
-                               $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
-               }
-               $comment = $this->sk->revComment( $rev );
-
-               return "<li>$checkBox ($last) $pageLink . . $userLink $stxt $comment</li>";
-       }
-
-       /**
-        * Fetch revision text link if it's available to all users
-        * @return string
-        */
-       function getPageLink( $row, $titleObj, $ts, $target ) {
-               global $wgLang;
-
-               if( !$this->userCan($row, Revision::DELETED_TEXT) ) {
-                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
-               } else {
-                       $link = $this->sk->makeKnownLinkObj( $titleObj,
-                               $wgLang->timeanddate( $ts, true ), "target=$target&timestamp=$ts" );
-                       if( $this->isDeleted($row, Revision::DELETED_TEXT) )
-                               $link = '<span class="history-deleted">' . $link . '</span>';
-                       return $link;
-               }
-       }
-
-       function merge() {
-               global $wgOut, $wgUser;
-               # Get the titles directly from the IDs, in case the target page params
-               # were spoofed. The queries are done based on the IDs, so it's best to
-               # keep it consistent...
-               $targetTitle = Title::newFromID( $this->mTargetID );
-               $destTitle = Title::newFromID( $this->mDestID );
-               if( is_null($targetTitle) || is_null($destTitle) )
-                       return false; // validate these
-               if( $targetTitle->getArticleId() == $destTitle->getArticleId() )
-                       return false;
-               # Verify that this timestamp is valid
-               # Must be older than the destination page
-               $dbw = wfGetDB( DB_MASTER );
-               # Get timestamp into DB format
-               $this->mTimestamp = $this->mTimestamp ? $dbw->timestamp($this->mTimestamp) : '';
-               # Max timestamp should be min of destination page
-               $maxtimestamp = $dbw->selectField( 'revision', 'MIN(rev_timestamp)',
-                       array('rev_page' => $this->mDestID ),
-                       __METHOD__ );
-               # Destination page must exist with revisions
-               if( !$maxtimestamp ) {
-                       $wgOut->addWikiMsg('mergehistory-fail');
-                       return false;
-               }
-               # Get the latest timestamp of the source
-               $lasttimestamp = $dbw->selectField( array('page','revision'),
-                       'rev_timestamp',
-                       array('page_id' => $this->mTargetID, 'page_latest = rev_id' ),
-                       __METHOD__ );
-               # $this->mTimestamp must be older than $maxtimestamp
-               if( $this->mTimestamp >= $maxtimestamp ) {
-                       $wgOut->addWikiMsg('mergehistory-fail');
-                       return false;
-               }
-               # Update the revisions
-               if( $this->mTimestamp ) {
-                       $timewhere = "rev_timestamp <= {$this->mTimestamp}";
-                       $TimestampLimit = wfTimestamp(TS_MW,$this->mTimestamp);
-               } else {
-                       $timewhere = "rev_timestamp <= {$maxtimestamp}";
-                       $TimestampLimit = wfTimestamp(TS_MW,$lasttimestamp);
-               }
-               # Do the moving...
-               $dbw->update( 'revision',
-                       array( 'rev_page' => $this->mDestID ),
-                       array( 'rev_page' => $this->mTargetID,
-                               $timewhere ),
-                       __METHOD__ );
-
-               $count = $dbw->affectedRows();
-               # Make the source page a redirect if no revisions are left
-               $haveRevisions = $dbw->selectField( 'revision',
-                       'rev_timestamp',
-                       array( 'rev_page' => $this->mTargetID  ),
-                       __METHOD__,
-                       array( 'FOR UPDATE' ) );
-               if( !$haveRevisions ) {
-                       if( $this->mComment ) {
-                               $comment = wfMsgForContent( 'mergehistory-comment', $targetTitle->getPrefixedText(),
-                                       $destTitle->getPrefixedText(), $this->mComment );
-                       } else {
-                               $comment = wfMsgForContent( 'mergehistory-autocomment', $targetTitle->getPrefixedText(),
-                                       $destTitle->getPrefixedText() );
-                       }
-                       $mwRedir = MagicWord::get( 'redirect' );
-                       $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $destTitle->getPrefixedText() . "]]\n";
-                       $redirectArticle = new Article( $targetTitle );
-                       $redirectRevision = new Revision( array(
-                               'page'    => $this->mTargetID,
-                               'comment' => $comment,
-                               'text'    => $redirectText ) );
-                       $redirectRevision->insertOn( $dbw );
-                       $redirectArticle->updateRevisionOn( $dbw, $redirectRevision );
-
-                       # Now, we record the link from the redirect to the new title.
-                       # It should have no other outgoing links...
-                       $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mDestID ), __METHOD__ );
-                       $dbw->insert( 'pagelinks',
-                               array(
-                                       'pl_from'      => $this->mDestID,
-                                       'pl_namespace' => $destTitle->getNamespace(),
-                                       'pl_title'     => $destTitle->getDBkey() ),
-                               __METHOD__ );
-               } else {
-                       $targetTitle->invalidateCache(); // update histories
-               }
-               $destTitle->invalidateCache(); // update histories
-               # Check if this did anything
-               if( !$count ) {
-                       $wgOut->addWikiMsg('mergehistory-fail');
-                       return false;
-               }
-               # Update our logs
-               $log = new LogPage( 'merge' );
-               $log->addEntry( 'merge', $targetTitle, $this->mComment,
-                       array($destTitle->getPrefixedText(),$TimestampLimit) );
-
-               $wgOut->addHtml( wfMsgExt( 'mergehistory-success', array('parseinline'),
-                       $targetTitle->getPrefixedText(), $destTitle->getPrefixedText(), $count ) );
-
-               wfRunHooks( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) );
-
-               return true;
-       }
-}
-
-class MergeHistoryPager extends ReverseChronologicalPager {
-       public $mForm, $mConds;
-
-       function __construct( $form, $conds = array(), $source, $dest ) {
-               $this->mForm = $form;
-               $this->mConds = $conds;
-               $this->title = $source;
-               $this->articleID = $source->getArticleID();
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $maxtimestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)',
-                       array('rev_page' => $dest->getArticleID() ),
-                       __METHOD__ );
-               $this->maxTimestamp = $maxtimestamp;
-
-               parent::__construct();
-       }
-
-       function getStartBody() {
-               wfProfileIn( __METHOD__ );
-               # Do a link batch query
-               $this->mResult->seek( 0 );
-               $batch = new LinkBatch();
-               # Give some pointers to make (last) links
-               $this->mForm->prevId = array();
-               while( $row = $this->mResult->fetchObject() ) {
-                       $batch->addObj( Title::makeTitleSafe( NS_USER, $row->rev_user_text ) );
-                       $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->rev_user_text ) );
-
-                       $rev_id = isset($rev_id) ? $rev_id : $row->rev_id;
-                       if( $rev_id > $row->rev_id )
-                               $this->mForm->prevId[$rev_id] = $row->rev_id;
-                       else if( $rev_id < $row->rev_id )
-                               $this->mForm->prevId[$row->rev_id] = $rev_id;
-
-                       $rev_id = $row->rev_id;
-               }
-
-               $batch->execute();
-               $this->mResult->seek( 0 );
-
-               wfProfileOut( __METHOD__ );
-               return '';
-       }
-
-       function formatRow( $row ) {
-               $block = new Block;
-               return $this->mForm->formatRevisionRow( $row );
-       }
-
-       function getQueryInfo() {
-               $conds = $this->mConds;
-               $conds['rev_page'] = $this->articleID;
-               $conds[] = "rev_timestamp < {$this->maxTimestamp}";
-
-               return array(
-                       'tables' => array('revision'),
-                       'fields' => array( 'rev_minor_edit', 'rev_timestamp', 'rev_user', 'rev_user_text', 'rev_comment',
-                                'rev_id', 'rev_page', 'rev_text_id', 'rev_len', 'rev_deleted' ),
-                       'conds' => $conds
-               );
-       }
-
-       function getIndexField() {
-               return 'rev_timestamp';
-       }
-}
diff --git a/includes/specials/Mostcategories.php b/includes/specials/Mostcategories.php
deleted file mode 100644 (file)
index 5df9c86..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * implements Special:Mostcategories
- * @ingroup SpecialPage
- */
-class MostcategoriesPage extends QueryPage {
-
-       function getName() { return 'Mostcategories'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               return
-                       "
-                       SELECT
-                               'Mostcategories' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       LEFT JOIN $page ON cl_from = page_id
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY 1,2,3
-                       HAVING COUNT(*) > 1
-                       ";
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang;
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               if ( !$title instanceof Title ) { throw new MWException('Invalid title in database'); }
-               $count = wfMsgExt( 'ncategories', array( 'parsemag', 'escape' ), $wgLang->formatNum( $result->value ) );
-               $link = $skin->makeKnownLinkObj( $title, $title->getText() );
-               return wfSpecialList( $link, $count );
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialMostcategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostcategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Mostimages.php b/includes/specials/Mostimages.php
deleted file mode 100644 (file)
index 2fed0bd..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * implements Special:Mostimages
- * @ingroup SpecialPage
- */
-class MostimagesPage extends ImageQueryPage {
-
-       function getName() { return 'Mostimages'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $imagelinks = $dbr->tableName( 'imagelinks' );
-               return
-                       "
-                       SELECT
-                               'Mostimages' as type,
-                               " . NS_IMAGE . " as namespace,
-                               il_to as title,
-                               COUNT(*) as value
-                       FROM $imagelinks
-                       GROUP BY 1,2,3
-                       HAVING COUNT(*) > 1
-                       ";
-       }
-
-       function getCellHtml( $row ) {
-               global $wgLang;
-               return wfMsgExt( 'nlinks',  array( 'parsemag', 'escape' ),
-                       $wgLang->formatNum( $row->value ) ) . '<br />';
-       }
-
-}
-
-/**
- * Constructor
- */
-function wfSpecialMostimages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostimagesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Mostlinked.php b/includes/specials/Mostlinked.php
deleted file mode 100644 (file)
index a56ac26..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page to show pages ordered by the number of pages linking to them.
- * Implements Special:Mostlinked
- *
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @author Rob Church <robchur@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @copyright Â© 2006 Rob Church
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-class MostlinkedPage extends QueryPage {
-
-       function getName() { return 'Mostlinked'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       /**
-        * Note: Getting page_namespace only works if $this->isCached() is false
-        */
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $pagelinks, $page ) = $dbr->tableNamesN( 'pagelinks', 'page' );
-               return
-                       "SELECT 'Mostlinked' AS type,
-                               pl_namespace AS namespace,
-                               pl_title AS title,
-                               COUNT(*) AS value,
-                               page_namespace
-                       FROM $pagelinks
-                       LEFT JOIN $page ON pl_namespace=page_namespace AND pl_title=page_title
-                       GROUP BY 1,2,3,5
-                       HAVING COUNT(*) > 1";
-       }
-
-       /**
-        * Pre-fill the link cache
-        */
-       function preprocessResults( $db, $res ) {
-               if( $db->numRows( $res ) > 0 ) {
-                       $linkBatch = new LinkBatch();
-                       while( $row = $db->fetchObject( $res ) )
-                               $linkBatch->add( $row->namespace, $row->title );
-                       $db->dataSeek( $res, 0 );
-                       $linkBatch->execute();
-               }
-       }
-
-       /**
-        * Make a link to "what links here" for the specified title
-        *
-        * @param $title Title being queried
-        * @param $skin Skin to use
-        * @return string
-        */
-       function makeWlhLink( &$title, $caption, &$skin ) {
-               $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedDBkey() );
-               return $skin->makeKnownLinkObj( $wlh, $caption );
-       }
-
-       /**
-        * Make links to the page corresponding to the item, and the "what links here" page for it
-        *
-        * @param $skin Skin to be used
-        * @param $result Result row
-        * @return string
-        */
-       function formatResult( $skin, $result ) {
-               global $wgLang;
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               $link = $skin->makeLinkObj( $title );
-               $wlh = $this->makeWlhLink( $title,
-                       wfMsgExt( 'nlinks', array( 'parsemag', 'escape'),
-                               $wgLang->formatNum( $result->value ) ), $skin );
-               return wfSpecialList( $link, $wlh );
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialMostlinked() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostlinkedPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Mostlinkedcategories.php b/includes/specials/Mostlinkedcategories.php
deleted file mode 100644 (file)
index 1b66d48..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A querypage to show categories ordered in descending order by the pages  in them
- *
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-class MostlinkedCategoriesPage extends QueryPage {
-
-       function getName() { return 'Mostlinkedcategories'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $categorylinks = $dbr->tableName( 'categorylinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               " . NS_CATEGORY . " as namespace,
-                               cl_to as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       GROUP BY 1,2,3
-                       ";
-       }
-
-       function sortDescending() { return true; }
-
-       /**
-        * Fetch user page links and cache their existence
-        */
-       function preprocessResults( $db, $res ) {
-               $batch = new LinkBatch;
-               while ( $row = $db->fetchObject( $res ) )
-                       $batch->add( $row->namespace, $row->title );
-               $batch->execute();
-
-               // Back to start for display
-               if ( $db->numRows( $res ) > 0 )
-                       // If there are no rows we get an error seeking.
-                       $db->dataSeek( $res, 0 );
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getText() );
-
-               $plink = $skin->makeLinkObj( $nt, htmlspecialchars( $text ) );
-
-               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               return wfSpecialList($plink, $nlinks);
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialMostlinkedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostlinkedCategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Mostlinkedtemplates.php b/includes/specials/Mostlinkedtemplates.php
deleted file mode 100644 (file)
index b8d47e6..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-/**
- * Special page lists templates with a large number of
- * transclusion links, i.e. "most used" templates
- *
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>
- */
-class SpecialMostlinkedtemplates extends QueryPage {
-
-       /**
-        * Name of the report
-        *
-        * @return string
-        */
-       public function getName() {
-               return 'Mostlinkedtemplates';
-       }
-
-       /**
-        * Is this report expensive, i.e should it be cached?
-        *
-        * @return bool
-        */
-       public function isExpensive() {
-               return true;
-       }
-
-       /**
-        * Is there a feed available?
-        *
-        * @return bool
-        */
-       public function isSyndicated() {
-               return false;
-       }
-
-       /**
-        * Sort the results in descending order?
-        *
-        * @return bool
-        */
-       public function sortDescending() {
-               return true;
-       }
-
-       /**
-        * Generate SQL for the report
-        *
-        * @return string
-        */
-       public function getSql() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $templatelinks = $dbr->tableName( 'templatelinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return "SELECT {$name} AS type,
-                       " . NS_TEMPLATE . " AS namespace,
-                       tl_title AS title,
-                       COUNT(*) AS value
-                       FROM {$templatelinks}
-                       WHERE tl_namespace = " . NS_TEMPLATE . "
-                       GROUP BY 1, 2, 3";
-       }
-
-       /**
-        * Pre-cache page existence to speed up link generation
-        *
-        * @param Database $dbr Database connection
-        * @param int $res Result pointer
-        */
-       public function preprocessResults( $db, $res ) {
-               $batch = new LinkBatch();
-               while( $row = $db->fetchObject( $res ) ) {
-                       $batch->add( $row->namespace, $row->title );
-               }
-               $batch->execute();
-               if( $db->numRows( $res ) > 0 )
-                       $db->dataSeek( $res, 0 );
-       }
-
-       /**
-        * Format a result row
-        *
-        * @param Skin $skin Skin to use for UI elements
-        * @param object $result Result row
-        * @return string
-        */
-       public function formatResult( $skin, $result ) {
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               if( $title instanceof Title ) {
-                       return wfSpecialList(
-                               $skin->makeLinkObj( $title ),
-                               $this->makeWlhLink( $title, $skin, $result )
-                       );
-               } else {
-                       $tsafe = htmlspecialchars( $result->title );
-                       return "Invalid title in result set; {$tsafe}";
-               }
-       }
-
-       /**
-        * Make a "what links here" link for a given title
-        *
-        * @param Title $title Title to make the link for
-        * @param Skin $skin Skin to use
-        * @param object $result Result row
-        * @return string
-        */
-       private function makeWlhLink( $title, $skin, $result ) {
-               global $wgLang;
-               $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
-               $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
-                       $wgLang->formatNum( $result->value ) );
-               return $skin->makeKnownLinkObj( $wlh, $label, 'target=' . $title->getPrefixedUrl() );
-       }
-}
-
-/**
- * Execution function
- *
- * @param mixed $par Parameters passed to the page
- */
-function wfSpecialMostlinkedtemplates( $par = false ) {
-       list( $limit, $offset ) = wfCheckLimits();
-       $mlt = new SpecialMostlinkedtemplates();
-       $mlt->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Mostrevisions.php b/includes/specials/Mostrevisions.php
deleted file mode 100644 (file)
index 001a08b..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-/**
- * A special page to show pages in the
- *
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * @ingroup SpecialPage
- */
-class MostrevisionsPage extends QueryPage {
-
-       function getName() { return 'Mostrevisions'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
-               return
-                       "
-                       SELECT
-                               'Mostrevisions' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               COUNT(*) as value
-                       FROM $revision
-                       JOIN $page ON page_id = rev_page
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY 1,2,3
-                       HAVING COUNT(*) > 1
-                       ";
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getPrefixedText() );
-
-               $plink = $skin->makeKnownLinkObj( $nt, $text );
-
-               $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               $nlink = $skin->makeKnownLinkObj( $nt, $nl, 'action=history' );
-
-               return wfSpecialList($plink, $nlink);
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialMostrevisions() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostrevisionsPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Movepage.php b/includes/specials/Movepage.php
deleted file mode 100644 (file)
index d08fb66..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialMovepage( $par = null ) {
-       global $wgUser, $wgOut, $wgRequest, $action;
-
-       # Check for database lock
-       if ( wfReadOnly() ) {
-               $wgOut->readOnlyPage();
-               return;
-       }
-
-       $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
-       $oldTitle = $wgRequest->getText( 'wpOldTitle', $target );
-       $newTitle = $wgRequest->getText( 'wpNewTitle' );
-
-       # Variables beginning with 'o' for old article 'n' for new article
-       $ot = Title::newFromText( $oldTitle );
-       $nt = Title::newFromText( $newTitle );
-
-       if( is_null( $ot ) ) {
-               $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
-               return;
-       }
-       if( !$ot->exists() ) {
-               $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' );
-               return;
-       }
-
-       # Check rights
-       $permErrors = $ot->getUserPermissionsErrors( 'move', $wgUser );
-       if( !empty( $permErrors ) ) {
-               $wgOut->showPermissionsErrorPage( $permErrors );
-               return;
-       }
-
-       $f = new MovePageForm( $ot, $nt );
-
-       if ( 'submit' == $action && $wgRequest->wasPosted()
-               && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               $f->doSubmit();
-       } else {
-               $f->showForm( '' );
-       }
-}
-
-/**
- * HTML form for Special:Movepage
- * @ingroup SpecialPage
- */
-class MovePageForm {
-       var $oldTitle, $newTitle, $reason; # Text input
-       var $moveTalk, $deleteAndMove, $moveSubpages;
-
-       private $watch = false;
-
-       function MovePageForm( $oldTitle, $newTitle ) {
-               global $wgRequest;
-               $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
-               $this->oldTitle = $oldTitle;
-               $this->newTitle = $newTitle;
-               $this->reason = $wgRequest->getText( 'wpReason' );
-               if ( $wgRequest->wasPosted() ) {
-                       $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', false );
-               } else {
-                       $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', true );
-               }
-               $this->moveSubpages = $wgRequest->getBool( 'wpMovesubpages', false );
-               $this->deleteAndMove = $wgRequest->getBool( 'wpDeleteAndMove' ) && $wgRequest->getBool( 'wpConfirm' );
-               $this->watch = $wgRequest->getCheck( 'wpWatch' );
-       }
-
-       function showForm( $err, $hookErr = '' ) {
-               global $wgOut, $wgUser;
-
-               $ot = $this->oldTitle;
-               $sk = $wgUser->getSkin();
-
-               $oldTitleLink = $sk->makeLinkObj( $ot );
-               $oldTitle = $ot->getPrefixedText();
-
-               $wgOut->setPagetitle( wfMsg( 'move-page', $oldTitle ) );
-               $wgOut->setSubtitle( wfMsg( 'move-page-backlink', $oldTitleLink ) );
-
-               if( $this->newTitle == '' ) {
-                       # Show the current title as a default
-                       # when the form is first opened.
-                       $newTitle = $oldTitle;
-               } else {
-                       if( $err == '' ) {
-                               $nt = Title::newFromURL( $this->newTitle );
-                               if( $nt ) {
-                                       # If a title was supplied, probably from the move log revert
-                                       # link, check for validity. We can then show some diagnostic
-                                       # information and save a click.
-                                       $newerr = $ot->isValidMoveOperation( $nt );
-                                       if( is_string( $newerr ) ) {
-                                               $err = $newerr;
-                                       }
-                               }
-                       }
-                       $newTitle = $this->newTitle;
-               }
-
-               if ( $err == 'articleexists' && $wgUser->isAllowed( 'delete' ) ) {
-                       $wgOut->addWikiMsg( 'delete_and_move_text', $newTitle );
-                       $movepagebtn = wfMsg( 'delete_and_move' );
-                       $submitVar = 'wpDeleteAndMove';
-                       $confirm = "
-                               <tr>
-                                       <td></td>
-                                       <td class='mw-input'>" .
-                                               Xml::checkLabel( wfMsg( 'delete_and_move_confirm' ), 'wpConfirm', 'wpConfirm' ) .
-                                       "</td>
-                               </tr>";
-                       $err = '';
-               } else {
-                       $wgOut->addWikiMsg( 'movepagetext' );
-                       $movepagebtn = wfMsg( 'movepagebtn' );
-                       $submitVar = 'wpMove';
-                       $confirm = false;
-               }
-
-               $oldTalk = $ot->getTalkPage();
-               $considerTalk = ( !$ot->isTalkPage() && $oldTalk->exists() );
-
-               if ( $considerTalk ) {
-                       $wgOut->addWikiMsg( 'movepagetalktext' );
-               }
-
-               $titleObj = SpecialPage::getTitleFor( 'Movepage' );
-               $token = htmlspecialchars( $wgUser->editToken() );
-
-               if ( $err != '' ) {
-                       $wgOut->setSubtitle( wfMsg( 'formerror' ) );
-                       $errMsg = "";
-                       if( $err == 'hookaborted' ) {
-                               $errMsg = "<p><strong class=\"error\">$hookErr</strong></p>\n";
-                       } else if (is_array($err)) {
-                               $errMsg = '<p><strong class="error">' . call_user_func_array( 'wfMsgWikiHtml', $err ) . "</strong></p>\n";
-                       } else {
-                               $errMsg = '<p><strong class="error">' . wfMsgWikiHtml( $err ) . "</strong></p>\n";
-                       }
-                       $wgOut->addHTML( $errMsg );
-               }
-
-               $wgOut->addHTML(
-                        Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) .
-                        Xml::openElement( 'fieldset' ) .
-                        Xml::element( 'legend', null, wfMsg( 'move-page-legend' ) ) .
-                        Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-movepage-table' ) ) .
-                        "<tr>
-                               <td class='mw-label'>" .
-                                       wfMsgHtml( 'movearticle' ) .
-                               "</td>
-                               <td class='mw-input'>
-                                       <strong>{$oldTitleLink}</strong>
-                               </td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>" .
-                                       Xml::label( wfMsg( 'newtitle' ), 'wpNewTitle' ) .
-                               "</td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'wpNewTitle', 40, $newTitle, array( 'type' => 'text', 'id' => 'wpNewTitle' ) ) .
-                                       Xml::hidden( 'wpOldTitle', $oldTitle ) .
-                               "</td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>" .
-                                       Xml::label( wfMsg( 'movereason' ), 'wpReason' ) .
-                               "</td>
-                               <td class='mw-input'>" .
-                                       Xml::tags( 'textarea', array( 'name' => 'wpReason', 'id' => 'wpReason', 'cols' => 60, 'rows' => 2 ), htmlspecialchars( $this->reason ) ) .
-                               "</td>
-                       </tr>"
-               );
-
-               if( $considerTalk ) {
-                       $wgOut->addHTML( "
-                               <tr>
-                                       <td></td>
-                                       <td class='mw-input'>" .
-                                               Xml::checkLabel( wfMsg( 'movetalk' ), 'wpMovetalk', 'wpMovetalk', $this->moveTalk ) .
-                                       "</td>
-                               </tr>"
-                       );
-               }
-
-               if( ($ot->hasSubpages() || $ot->getTalkPage()->hasSubpages())
-               && $ot->userCan( 'move-subpages' ) ) {
-                       $wgOut->addHTML( "
-                               <tr>
-                                       <td></td>
-                                       <td class=\"mw-input\">" .
-                               Xml::checkLabel( wfMsgHtml(
-                                               $ot->hasSubpages()
-                                               ? 'move-subpages'
-                                               : 'move-talk-subpages'
-                                       ),
-                                       'wpMovesubpages', 'wpMovesubpages',
-                                       # Don't check the box if we only have talk subpages to
-                                       # move and we aren't moving the talk page.
-                                       $this->moveSubpages && ($ot->hasSubpages() || $this->moveTalk)
-                               ) .
-                                       "</td>
-                               </tr>"
-                       );
-               }
-
-               $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) || $ot->userIsWatching();
-               $wgOut->addHTML( "
-                       <tr>
-                               <td></td>
-                               <td class='mw-input'>" .
-                                       Xml::checkLabel( wfMsg( 'move-watch' ), 'wpWatch', 'watch', $watchChecked ) .
-                               "</td>
-                       </tr>
-                               {$confirm}
-                       <tr>
-                               <td>&nbsp;</td>
-                               <td class='mw-submit'>" .
-                                       Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
-                               "</td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::hidden( 'wpEditToken', $token ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) .
-                       "\n"
-               );
-
-               $this->showLogFragment( $ot, $wgOut );
-
-       }
-
-       function doSubmit() {
-               global $wgOut, $wgUser, $wgRequest, $wgMaximumMovedPages, $wgLang;
-
-               if ( $wgUser->pingLimiter( 'move' ) ) {
-                       $wgOut->rateLimited();
-                       return;
-               }
-
-               $ot = $this->oldTitle;
-               $nt = $this->newTitle;
-
-               # Delete to make way if requested
-               if ( $wgUser->isAllowed( 'delete' ) && $this->deleteAndMove ) {
-                       $article = new Article( $nt );
-
-                       # Disallow deletions of big articles
-                       $bigHistory = $article->isBigDeletion();
-                       if( $bigHistory && !$nt->userCan( 'bigdelete' ) ) {
-                               global $wgLang, $wgDeleteRevisionsLimit;
-                               $this->showForm( array('delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
-                               return;
-                       }
-
-                       // This may output an error message and exit
-                       $article->doDelete( wfMsgForContent( 'delete_and_move_reason' ) );
-               }
-
-               # don't allow moving to pages with # in
-               if ( !$nt || $nt->getFragment() != '' ) {
-                       $this->showForm( 'badtitletext' );
-                       return;
-               }
-
-               $error = $ot->moveTo( $nt, true, $this->reason );
-               if ( $error !== true ) {
-                       # FIXME: showForm() should handle multiple errors
-                       call_user_func_array(array($this, 'showForm'), $error[0]);
-                       return;
-               }
-
-               wfRunHooks( 'SpecialMovepageAfterMove', array( &$this , &$ot , &$nt ) ) ;
-
-               $wgOut->setPagetitle( wfMsg( 'pagemovedsub' ) );
-
-               $oldUrl = $ot->getFullUrl( 'redirect=no' );
-               $newUrl = $nt->getFullUrl();
-               $oldText = $ot->getPrefixedText();
-               $newText = $nt->getPrefixedText();
-               $oldLink = "<span class='plainlinks'>[$oldUrl $oldText]</span>";
-               $newLink = "<span class='plainlinks'>[$newUrl $newText]</span>";
-
-               $wgOut->addWikiMsg( 'movepage-moved', $oldLink, $newLink, $oldText, $newText );
-
-               # Now we move extra pages we've been asked to move: subpages and talk
-               # pages.  First, if the old page or the new page is a talk page, we
-               # can't move any talk pages: cancel that.
-               if( $ot->isTalkPage() || $nt->isTalkPage() ) {
-                       $this->moveTalk = false;
-               }
-
-               if( !$ot->userCan( 'move-subpages' ) ) {
-                       $this->moveSubpages = false;
-               }
-
-               # Next make a list of id's.  This might be marginally less efficient
-               # than a more direct method, but this is not a highly performance-cri-
-               # tical code path and readable code is more important here.
-               #
-               # Note: this query works nicely on MySQL 5, but the optimizer in MySQL
-               # 4 might get confused.  If so, consider rewriting as a UNION.
-               #
-               # If the target namespace doesn't allow subpages, moving with subpages
-               # would mean that you couldn't move them back in one operation, which
-               # is bad.  FIXME: A specific error message should be given in this
-               # case.
-               $dbr = wfGetDB( DB_MASTER );
-               if( $this->moveSubpages && (
-                       MWNamespace::hasSubpages( $nt->getNamespace() ) || (
-                               $this->moveTalk &&
-                               MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() )
-                       )
-               ) ) {
-                       $conds = array(
-                               'page_title LIKE '.$dbr->addQuotes( $dbr->escapeLike( $ot->getDBkey() ) . '/%' )
-                                       .' OR page_title = ' . $dbr->addQuotes( $ot->getDBkey() )
-                       );
-                       $conds['page_namespace'] = array();
-                       if( MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
-                               $conds['page_namespace'] []= $ot->getNamespace();
-                       }
-                       if( $this->moveTalk && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) ) {
-                               $conds['page_namespace'] []= $ot->getTalkPage()->getNamespace();
-                       }
-               } elseif( $this->moveTalk ) {
-                       $conds = array(
-                               'page_namespace' => $ot->getTalkPage()->getNamespace(),
-                               'page_title' => $ot->getDBKey()
-                       );
-               } else {
-                       # Skip the query
-                       $conds = null;
-               }
-
-               $extrapages = array();
-               if( !is_null( $conds ) ) {
-                       $extrapages = $dbr->select( 'page',
-                               array( 'page_id', 'page_namespace', 'page_title' ),
-                               $conds,
-                               __METHOD__
-                       );
-               }
-
-               $extraOutput = array();
-               $skin = $wgUser->getSkin();
-               $count = 1;
-               foreach( $extrapages as $row ) {
-                       if( $row->page_id == $ot->getArticleId() ) {
-                               # Already did this one.
-                               continue;
-                       }
-
-                       $oldPage = Title::newFromRow( $row );
-                       $newPageName = preg_replace(
-                               '#^'.preg_quote( $ot->getDBKey(), '#' ).'#',
-                               $nt->getDBKey(),
-                               $oldPage->getDBKey()
-                       );
-                       if( $oldPage->isTalkPage() ) {
-                               $newNs = $nt->getTalkPage()->getNamespace();
-                       } else {
-                               $newNs = $nt->getSubjectPage()->getNamespace();
-                       }
-                       # Bug 14385: we need makeTitleSafe because the new page names may
-                       # be longer than 255 characters.
-                       $newPage = Title::makeTitleSafe( $newNs, $newPageName );
-                       if( !$newPage ) {
-                               $oldLink = $skin->makeKnownLinkObj( $oldPage );
-                               $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink,
-                                       htmlspecialchars(Title::makeName( $newNs, $newPageName )));
-                               continue;
-                       }
-
-                       # This was copy-pasted from Renameuser, bleh.
-                       if ( $newPage->exists() && !$oldPage->isValidMoveTarget( $newPage ) ) {
-                               $link = $skin->makeKnownLinkObj( $newPage );
-                               $extraOutput []= wfMsgHtml( 'movepage-page-exists', $link );
-                       } else {
-                               $success = $oldPage->moveTo( $newPage, true, $this->reason );
-                               if( $success === true ) {
-                                       $oldLink = $skin->makeKnownLinkObj( $oldPage, '', 'redirect=no' );
-                                       $newLink = $skin->makeKnownLinkObj( $newPage );
-                                       $extraOutput []= wfMsgHtml( 'movepage-page-moved', $oldLink, $newLink );
-                               } else {
-                                       $oldLink = $skin->makeKnownLinkObj( $oldPage );
-                                       $newLink = $skin->makeLinkObj( $newPage );
-                                       $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink, $newLink );
-                               }
-                       }
-
-                       ++$count;
-                       if( $count >= $wgMaximumMovedPages ) {
-                               $extraOutput []= wfMsgExt( 'movepage-max-pages', array( 'parsemag', 'escape' ), $wgLang->formatNum( $wgMaximumMovedPages ) );
-                               break;
-                       }
-               }
-
-               if( $extraOutput !== array() ) {
-                       $wgOut->addHTML( "<ul>\n<li>" . implode( "</li>\n<li>", $extraOutput ) . "</li>\n</ul>" );
-               }
-
-               # Deal with watches (we don't watch subpages)
-               if( $this->watch ) {
-                       $wgUser->addWatch( $ot );
-                       $wgUser->addWatch( $nt );
-               } else {
-                       $wgUser->removeWatch( $ot );
-                       $wgUser->removeWatch( $nt );
-               }
-       }
-
-       function showLogFragment( $title, &$out ) {
-               $out->addHTML( Xml::element( 'h2', NULL, LogPage::logName( 'move' ) ) );
-               LogEventsList::showLogExtract( $out, 'move', $title->getPrefixedText() );
-       }
-
-}
diff --git a/includes/specials/Newimages.php b/includes/specials/Newimages.php
deleted file mode 100644 (file)
index 5fd37e8..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- * FIXME: this code is crap, should use Pager and Database::select().
- */
-
-/**
- *
- */
-function wfSpecialNewimages( $par, $specialPage ) {
-       global $wgUser, $wgOut, $wgLang, $wgRequest, $wgGroupPermissions, $wgMiserMode;
-
-       $wpIlMatch = $wgRequest->getText( 'wpIlMatch' );
-       $dbr = wfGetDB( DB_SLAVE );
-       $sk = $wgUser->getSkin();
-       $shownav = !$specialPage->including();
-       $hidebots = $wgRequest->getBool('hidebots',1);
-
-       $hidebotsql = '';
-       if ($hidebots) {
-
-               /** Make a list of group names which have the 'bot' flag
-                   set.
-               */
-               $botconds=array();
-               foreach ($wgGroupPermissions as $groupname=>$perms) {
-                       if(array_key_exists('bot',$perms) && $perms['bot']) {
-                               $botconds[]="ug_group='$groupname'";
-                       }
-               }
-
-               /* If not bot groups, do not set $hidebotsql */
-               if ($botconds) {
-                       $isbotmember=$dbr->makeList($botconds, LIST_OR);
-
-                       /** This join, in conjunction with WHERE ug_group
-                           IS NULL, returns only those rows from IMAGE
-                       where the uploading user is not a member of
-                       a group which has the 'bot' permission set.
-                       */
-                       $ug = $dbr->tableName('user_groups');
-                       $hidebotsql = " LEFT OUTER JOIN $ug ON img_user=ug_user AND ($isbotmember)";
-               }
-       }
-
-       $image = $dbr->tableName('image');
-
-       $sql="SELECT img_timestamp from $image";
-       if ($hidebotsql) {
-               $sql .= "$hidebotsql WHERE ug_group IS NULL";
-       }
-       $sql.=' ORDER BY img_timestamp DESC LIMIT 1';
-       $res = $dbr->query($sql, 'wfSpecialNewImages');
-       $row = $dbr->fetchRow($res);
-       if($row!==false) {
-               $ts=$row[0];
-       } else {
-               $ts=false;
-       }
-       $dbr->freeResult($res);
-       $sql='';
-
-       /** If we were clever, we'd use this to cache. */
-       $latestTimestamp = wfTimestamp( TS_MW, $ts);
-
-       /** Hardcode this for now. */
-       $limit = 48;
-
-       if ( $parval = intval( $par ) ) {
-               if ( $parval <= $limit && $parval > 0 ) {
-                       $limit = $parval;
-               }
-       }
-
-       $where = array();
-       $searchpar = '';
-       if ( $wpIlMatch != '' && !$wgMiserMode) {
-               $nt = Title::newFromUrl( $wpIlMatch );
-               if($nt ) {
-                       $m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
-                       $m = str_replace( '%', "\\%", $m );
-                       $m = str_replace( '_', "\\_", $m );
-                       $where[] = "LOWER(img_name) LIKE '%{$m}%'";
-                       $searchpar = '&wpIlMatch=' . urlencode( $wpIlMatch );
-               }
-       }
-
-       $invertSort = false;
-       if( $until = $wgRequest->getVal( 'until' ) ) {
-               $where[] = "img_timestamp < '" . $dbr->timestamp( $until ) . "'";
-       }
-       if( $from = $wgRequest->getVal( 'from' ) ) {
-               $where[] = "img_timestamp >= '" . $dbr->timestamp( $from ) . "'";
-               $invertSort = true;
-       }
-       $sql='SELECT img_size, img_name, img_user, img_user_text,'.
-            "img_description,img_timestamp FROM $image";
-
-       if($hidebotsql) {
-               $sql .= $hidebotsql;
-               $where[]='ug_group IS NULL';
-       }
-       if(count($where)) {
-               $sql.=' WHERE '.$dbr->makeList($where, LIST_AND);
-       }
-       $sql.=' ORDER BY img_timestamp '. ( $invertSort ? '' : ' DESC' );
-       $sql.=' LIMIT '.($limit+1);
-       $res = $dbr->query($sql, 'wfSpecialNewImages');
-
-       /**
-        * We have to flip things around to get the last N after a certain date
-        */
-       $images = array();
-       while ( $s = $dbr->fetchObject( $res ) ) {
-               if( $invertSort ) {
-                       array_unshift( $images, $s );
-               } else {
-                       array_push( $images, $s );
-               }
-       }
-       $dbr->freeResult( $res );
-
-       $gallery = new ImageGallery();
-       $firstTimestamp = null;
-       $lastTimestamp = null;
-       $shownImages = 0;
-       foreach( $images as $s ) {
-               if( ++$shownImages > $limit ) {
-                       # One extra just to test for whether to show a page link;
-                       # don't actually show it.
-                       break;
-               }
-
-               $name = $s->img_name;
-               $ut = $s->img_user_text;
-
-               $nt = Title::newFromText( $name, NS_IMAGE );
-               $ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut );
-
-               $gallery->add( $nt, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
-
-               $timestamp = wfTimestamp( TS_MW, $s->img_timestamp );
-               if( empty( $firstTimestamp ) ) {
-                       $firstTimestamp = $timestamp;
-               }
-               $lastTimestamp = $timestamp;
-       }
-
-       $bydate = wfMsg( 'bydate' );
-       $lt = $wgLang->formatNum( min( $shownImages, $limit ) );
-       if ($shownav) {
-               $text = wfMsgExt( 'imagelisttext', array('parse'), $lt, $bydate );
-               $wgOut->addHTML( $text . "\n" );
-       }
-
-       $sub = wfMsg( 'ilsubmit' );
-       $titleObj = SpecialPage::getTitleFor( 'Newimages' );
-       $action = $titleObj->escapeLocalURL( $hidebots ? '' : 'hidebots=0' );
-       if ($shownav && !$wgMiserMode) {
-               $wgOut->addHTML( "<form id=\"imagesearch\" method=\"post\" action=\"" .
-                 "{$action}\">" .
-                       Xml::input( 'wpIlMatch', 20, $wpIlMatch ) . ' ' .
-                 Xml::submitButton( $sub, array( 'name' => 'wpIlSubmit' ) ) .
-                 "</form>" );
-       }
-
-       /**
-        * Paging controls...
-        */
-
-       # If we change bot visibility, this needs to be carried along.
-       if(!$hidebots) {
-               $botpar='&hidebots=0';
-       } else {
-               $botpar='';
-       }
-       $now = wfTimestampNow();
-       $d = $wgLang->date( $now, true );
-       $t = $wgLang->time( $now, true );
-       $dateLink = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'sp-newimages-showfrom', $d, $t ), 
-               'from='.$now.$botpar.$searchpar );
-
-       $botLink = $sk->makeKnownLinkObj($titleObj, wfMsgHtml( 'showhidebots', 
-               ($hidebots ? wfMsgHtml('show') : wfMsgHtml('hide'))),'hidebots='.($hidebots ? '0' : '1').$searchpar);
-
-       $prevLink = wfMsgHtml( 'prevn', $wgLang->formatNum( $limit ) );
-       if( $firstTimestamp && $firstTimestamp != $latestTimestamp ) {
-               $prevLink = $sk->makeKnownLinkObj( $titleObj, $prevLink, 'from=' . $firstTimestamp . $botpar . $searchpar );
-       }
-
-       $nextLink = wfMsgHtml( 'nextn', $wgLang->formatNum( $limit ) );
-       if( $shownImages > $limit && $lastTimestamp ) {
-               $nextLink = $sk->makeKnownLinkObj( $titleObj, $nextLink, 'until=' . $lastTimestamp.$botpar.$searchpar );
-       }
-
-       $prevnext = '<p>' . $botLink . ' '. wfMsgHtml( 'viewprevnext', $prevLink, $nextLink, $dateLink ) .'</p>';
-
-       if ($shownav)
-               $wgOut->addHTML( $prevnext );
-
-       if( count( $images ) ) {
-               $wgOut->addHTML( $gallery->toHTML() );
-               if ($shownav)
-                       $wgOut->addHTML( $prevnext );
-       } else {
-               $wgOut->addWikiMsg( 'noimages' );
-       }
-}
diff --git a/includes/specials/Newpages.php b/includes/specials/Newpages.php
deleted file mode 100644 (file)
index 72a0183..0000000
+++ /dev/null
@@ -1,437 +0,0 @@
-<?php
-
-/**
- * implements Special:Newpages
- * @ingroup SpecialPage
- */
-class SpecialNewpages extends SpecialPage {
-
-       // Stored objects
-       protected $opts, $skin;
-
-       // Some internal settings
-       protected $showNavigation = false;
-
-       public function __construct(){
-               parent::__construct( 'Newpages' );
-               $this->includable( true );      
-       }
-
-       protected function setup( $par ) {
-               global $wgRequest, $wgUser, $wgEnableNewpagesUserFilter;
-
-               // Options
-               $opts = new FormOptions();
-               $this->opts = $opts; // bind
-               $opts->add( 'hideliu', false );
-               $opts->add( 'hidepatrolled', false );
-               $opts->add( 'hidebots', false );
-               $opts->add( 'limit', 50 );
-               $opts->add( 'offset', '' );
-               $opts->add( 'namespace', '0' );
-               $opts->add( 'username', '' );
-               $opts->add( 'feed', '' );
-
-               // Set values
-               $opts->fetchValuesFromRequest( $wgRequest );
-               if ( $par ) $this->parseParams( $par );
-
-               // Validate
-               $opts->validateIntBounds( 'limit', 0, 5000 );
-               if( !$wgEnableNewpagesUserFilter ) {
-                       $opts->setValue( 'username', '' );
-               }
-
-               // Store some objects
-               $this->skin = $wgUser->getSkin();
-       }
-
-       protected function parseParams( $par ) {
-               global $wgLang;
-               $bits = preg_split( '/\s*,\s*/', trim( $par ) );
-               foreach ( $bits as $bit ) {
-                       if ( 'shownav' == $bit )
-                               $this->showNavigation = true;
-                       if ( 'hideliu' === $bit )
-                               $this->opts->setValue( 'hideliu', true );
-                       if ( 'hidepatrolled' == $bit )
-                               $this->opts->setValue( 'hidepatrolled', true );
-                       if ( 'hidebots' == $bit )
-                               $this->opts->setValue( 'hidebots', true );
-                       if ( is_numeric( $bit ) )
-                               $this->opts->setValue( 'limit', intval( $bit ) );
-
-                       $m = array();
-                       if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) )
-                               $this->opts->setValue( 'limit', intval($m[1]) );
-                       // PG offsets not just digits!
-                       if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) )
-                               $this->opts->setValue( 'offset',  intval($m[1]) );
-                       if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
-                               $ns = $wgLang->getNsIndex( $m[1] );
-                               if( $ns !== false ) {
-                                       $this->opts->setValue( 'namespace',  $ns );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Show a form for filtering namespace and username
-        *
-        * @param string $par
-        * @return string
-        */
-       public function execute( $par ) {
-               global $wgLang, $wgGroupPermissions, $wgUser, $wgOut;
-
-               $this->setHeaders();
-               $this->outputHeader();
-
-               $this->showNavigation = !$this->including(); // Maybe changed in setup
-               $this->setup( $par );
-
-               if( !$this->including() ) {
-                       // Settings
-                       $this->form();
-
-                       $this->setSyndicated();
-                       $feedType = $this->opts->getValue( 'feed' );
-                       if( $feedType ) {
-                               return $this->feed( $feedType );
-                       }
-               }
-
-               $pager = new NewPagesPager( $this, $this->opts );
-               $pager->mLimit = $this->opts->getValue( 'limit' );
-               $pager->mOffset = $this->opts->getValue( 'offset' );
-
-               if( $pager->getNumRows() ) {
-                       $navigation = '';
-                       if ( $this->showNavigation ) $navigation = $pager->getNavigationBar();
-                       $wgOut->addHTML( $navigation . $pager->getBody() . $navigation );
-               } else {
-                       $wgOut->addWikiMsg( 'specialpage-empty' );
-               }
-       }
-
-       protected function filterLinks() {
-               global $wgGroupPermissions, $wgUser;
-
-               // show/hide links
-               $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
-
-               // Option value -> message mapping
-               $filters = array(
-                       'hideliu' => 'rcshowhideliu',
-                       'hidepatrolled' => 'rcshowhidepatr',
-                       'hidebots' => 'rcshowhidebots'
-               );
-
-               // Disable some if needed
-               if ( $wgGroupPermissions['*']['createpage'] !== true )
-                       unset($filters['hideliu']);
-
-               if ( !$wgUser->useNPPatrol() )
-                       unset($filters['hidepatrolled']);
-
-               $links = array();
-               $changed = $this->opts->getChangedValues();
-               unset($changed['offset']); // Reset offset if query type changes
-
-               $self = $this->getTitle();
-               foreach ( $filters as $key => $msg ) {
-                       $onoff = 1 - $this->opts->getValue($key);
-                       $link = $this->skin->makeKnownLinkObj( $self, $showhide[$onoff],
-                               wfArrayToCGI( array( $key => $onoff ), $changed )
-                       );
-                       $links[$key] = wfMsgHtml( $msg, $link );
-               }
-
-               return implode( ' | ', $links );
-       }
-
-       protected function form() {
-               global $wgOut, $wgEnableNewpagesUserFilter, $wgScript;
-
-               // Consume values
-               $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
-               $namespace = $this->opts->consumeValue( 'namespace' );
-               $username = $this->opts->consumeValue( 'username' );
-
-               // Check username input validity
-               $ut = Title::makeTitleSafe( NS_USER, $username );
-               $userText = $ut ? $ut->getText() : '';
-
-               // Store query values in hidden fields so that form submission doesn't lose them
-               $hidden = array();
-               foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
-                       $hidden[] = Xml::hidden( $key, $value );
-               }
-               $hidden = implode( "\n", $hidden );
-
-               $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
-                       Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
-                       Xml::fieldset( wfMsg( 'newpages' ) ) .
-                       Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
-                       "<tr>
-                               <td class='mw-label'>" .
-                                       Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
-                               "</td>
-                               <td class='mw-input'>" .
-                                       Xml::namespaceSelector( $namespace, 'all' ) .
-                               "</td>
-                       </tr>" .
-                       ($wgEnableNewpagesUserFilter ?
-                       "<tr>
-                               <td class='mw-label'>" .
-                                       Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
-                               "</td>
-                               <td class='mw-input'>" .
-                                       Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
-                               "</td>
-                       </tr>" : "" ) .
-                       "<tr> <td></td>
-                               <td class='mw-submit'>" .
-                                       Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
-                               "</td>
-                       </tr>" .
-                       "<tr>
-                               <td></td>
-                               <td class='mw-input'>" .
-                                       $this->filterLinks() .
-                               "</td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       $hidden .
-                       Xml::closeElement( 'form' );
-
-               $wgOut->addHTML( $form );
-       }
-
-       protected function setSyndicated() {
-               global $wgOut;
-               $queryParams = array(
-                       'namespace' => $this->opts->getValue( 'namespace' ),
-                       'username' => $this->opts->getValue( 'username' )
-               );
-               $wgOut->setSyndicated( true );
-               $wgOut->setFeedAppendQuery( wfArrayToCGI( $queryParams ) );
-       }
-
-       /**
-        * Format a row, providing the timestamp, links to the page/history, size, user links, and a comment
-        *
-        * @param $skin Skin to use
-        * @param $result Result row
-        * @return string
-        */
-       public function formatRow( $result ) {
-               global $wgLang, $wgContLang, $wgUser;
-               $dm = $wgContLang->getDirMark();
-
-               $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
-               $time = $wgLang->timeAndDate( $result->rc_timestamp, true );
-               $plink = $this->skin->makeKnownLinkObj( $title, '', $this->patrollable( $result ) ? 'rcid=' . $result->rc_id : '' );
-               $hist = $this->skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
-               $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
-                       $wgLang->formatNum( $result->length ) );
-               $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
-                       $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
-               $comment = $this->skin->commentBlock( $result->rc_comment );
-               $css = $this->patrollable( $result ) ? " class='not-patrolled'" : '';
-
-               return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment}</li>\n";
-       }
-
-       /**
-        * Should a specific result row provide "patrollable" links?
-        *
-        * @param $result Result row
-        * @return bool
-        */
-       protected function patrollable( $result ) {
-               global $wgUser;
-               return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
-       }
-
-       /**
-        * Output a subscription feed listing recent edits to this page.
-        * @param string $type
-        */
-       protected function feed( $type ) {
-               global $wgFeed, $wgFeedClasses;
-
-               if ( !$wgFeed ) {
-                       global $wgOut;
-                       $wgOut->addWikiMsg( 'feed-unavailable' );
-                       return;
-               }
-
-               if( !isset( $wgFeedClasses[$type] ) ) {
-                       global $wgOut;
-                       $wgOut->addWikiMsg( 'feed-invalid' );
-                       return;
-               }
-
-               $feed = new $wgFeedClasses[$type](
-                       $this->feedTitle(),
-                       wfMsg( 'tagline' ),
-                       $this->getTitle()->getFullUrl() );
-
-               $pager = new NewPagesPager( $this, $this->opts );
-               $limit = $this->opts->getValue( 'limit' );
-               global $wgFeedLimit;
-               if( $limit > $wgFeedLimit ) {
-                       $limit = $wgFeedLimit;
-               }
-               $pager->mLimit = $limit;
-
-               $feed->outHeader();
-               if( $pager->getNumRows() > 0 ) {
-                       while( $row = $pager->mResult->fetchObject() ) {
-                               $feed->outItem( $this->feedItem( $row ) );
-                       }
-               }
-               $feed->outFooter();
-       }
-
-       protected function feedTitle() {
-               global $wgContLanguageCode, $wgSitename;
-               $page = SpecialPage::getPage( 'Newpages' );
-               $desc = $page->getDescription();
-               return "$wgSitename - $desc [$wgContLanguageCode]";
-       }
-
-       protected function feedItem( $row ) {
-               $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
-               if( $title ) {
-                       $date = $row->rc_timestamp;
-                       $comments = $title->getTalkPage()->getFullURL();
-
-                       return new FeedItem(
-                               $title->getPrefixedText(),
-                               $this->feedItemDesc( $row ),
-                               $title->getFullURL(),
-                               $date,
-                               $this->feedItemAuthor( $row ),
-                               $comments);
-               } else {
-                       return NULL;
-               }
-       }
-
-       /**
-        * Quickie hack... strip out wikilinks to more legible form from the comment.
-        */
-       protected function stripComment( $text ) {
-               return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
-       }
-
-       protected function feedItemAuthor( $row ) {
-               return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
-       }
-
-       protected function feedItemDesc( $row ) {
-               $revision = Revision::newFromId( $row->rev_id );
-               if( $revision ) {
-                       return '<p>' . htmlspecialchars( $revision->getUserText() ) . ': ' .
-                               htmlspecialchars( $revision->getComment() ) . 
-                               "</p>\n<hr />\n<div>" .
-                               nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
-               }
-               return '';
-       }
-}
-
-/**
- * @ingroup SpecialPage Pager
- */
-class NewPagesPager extends ReverseChronologicalPager {
-       // Stored opts
-       protected $opts, $mForm;
-
-       private $hideliu, $hidepatrolled, $hidebots, $namespace, $user, $spTitle;
-
-       function __construct( $form, FormOptions $opts ) {
-               parent::__construct();
-               $this->mForm = $form;
-               $this->opts = $opts;
-       }
-
-       function getTitle(){
-               static $title = null;
-               if ( $title === null )
-                       $title = $this->mForm->getTitle();
-               return $title;
-       }
-
-       function getQueryInfo() {
-               global $wgEnableNewpagesUserFilter, $wgGroupPermissions, $wgUser;
-               $conds = array();
-               $conds['rc_new'] = 1;
-
-               $namespace = $this->opts->getValue( 'namespace' );
-               $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
-
-               $username = $this->opts->getValue( 'username' );
-               $user = Title::makeTitleSafe( NS_USER, $username );
-
-               if( $namespace !== false ) {
-                       $conds['rc_namespace'] = $namespace;
-                       $rcIndexes = array( 'new_name_timestamp' );
-               } else {
-                       $rcIndexes = array( 'rc_timestamp' );
-               }
-               $conds[] = 'page_id = rc_cur_id';
-               $conds['page_is_redirect'] = 0;
-               # $wgEnableNewpagesUserFilter - temp WMF hack
-               if( $wgEnableNewpagesUserFilter && $user ) {
-                       $conds['rc_user_text'] = $user->getText();
-                       $rcIndexes = 'rc_user_text';
-               # If anons cannot make new pages, don't "exclude logged in users"!
-               } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
-                       $conds['rc_user'] = 0;
-               }
-               # If this user cannot see patrolled edits or they are off, don't do dumb queries!
-               if( $this->opts->getValue( 'hidepatrolled' ) && $wgUser->useNPPatrol() ) {
-                       $conds['rc_patrolled'] = 0;
-               }
-               if( $this->opts->getValue( 'hidebots' ) ) {
-                       $conds['rc_bot'] = 0;
-               }
-
-               return array(
-                       'tables' => array( 'recentchanges', 'page' ),
-                       'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
-                               rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id',
-                       'conds' => $conds,
-                       'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) )
-               );
-       }
-
-       function getIndexField() {
-               return 'rc_timestamp';
-       }
-
-       function formatRow( $row ) {
-               return $this->mForm->formatRow( $row );
-       }
-
-       function getStartBody() {
-               # Do a batch existence check on pages
-               $linkBatch = new LinkBatch();
-               while( $row = $this->mResult->fetchObject() ) {
-                       $linkBatch->add( NS_USER, $row->rc_user_text );
-                       $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
-                       $linkBatch->add( $row->rc_namespace, $row->rc_title );
-               }
-               $linkBatch->execute();
-               return "<ul>";
-       }
-
-       function getEndBody() {
-               return "</ul>";
-       }
-}
diff --git a/includes/specials/Popularpages.php b/includes/specials/Popularpages.php
deleted file mode 100644 (file)
index eb57273..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * implements Special:Popularpages
- * @ingroup SpecialPage
- */
-class PopularPagesPage extends QueryPage {
-
-       function getName() {
-               return "Popularpages";
-       }
-
-       function isExpensive() {
-               # page_counter is not indexed
-               return true;
-       }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-
-               $query =
-                       "SELECT 'Popularpages' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_counter as value
-                       FROM $page ";
-               $where =
-                       "WHERE page_is_redirect=0 AND page_namespace";
-
-               global $wgContentNamespaces;
-               if( empty( $wgContentNamespaces ) ) {
-                       $where .= '='.NS_MAIN;
-               } else if( count( $wgContentNamespaces ) > 1 ) {
-                       $where .= ' in (' . implode( ', ', $wgContentNamespaces ) . ')';
-               } else {
-                       $where .= '='.$wgContentNamespaces[0];
-               }
-
-               return $query . $where;
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-               $title = Title::makeTitle( $result->namespace, $result->title );
-               $link = $skin->makeKnownLinkObj( $title, htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) ) );
-               $nv = wfMsgExt( 'nviews', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               return wfSpecialList($link, $nv);
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialPopularpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $ppp = new PopularPagesPage();
-
-       return $ppp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Preferences.php b/includes/specials/Preferences.php
deleted file mode 100644 (file)
index 6a16f1c..0000000
+++ /dev/null
@@ -1,1122 +0,0 @@
-<?php
-/**
- * Hold things related to displaying and saving user preferences.
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Entry point that create the "Preferences" object
- */
-function wfSpecialPreferences() {
-       global $wgRequest;
-
-       $form = new PreferencesForm( $wgRequest );
-       $form->execute();
-}
-
-/**
- * Preferences form handling
- * This object will show the preferences form and can save it as well.
- * @ingroup SpecialPage
- */
-class PreferencesForm {
-       var $mQuickbar, $mOldpass, $mNewpass, $mRetypePass, $mStubs;
-       var $mRows, $mCols, $mSkin, $mMath, $mDate, $mUserEmail, $mEmailFlag, $mNick;
-       var $mUserLanguage, $mUserVariant;
-       var $mSearch, $mRecent, $mRecentDays, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
-       var $mReset, $mPosted, $mToggles, $mUseAjaxSearch, $mSearchNs, $mRealName, $mImageSize;
-       var $mUnderline, $mWatchlistEdits;
-
-       /**
-        * Constructor
-        * Load some values
-        */
-       function PreferencesForm( &$request ) {
-               global $wgContLang, $wgUser, $wgAllowRealName;
-
-               $this->mQuickbar = $request->getVal( 'wpQuickbar' );
-               $this->mOldpass = $request->getVal( 'wpOldpass' );
-               $this->mNewpass = $request->getVal( 'wpNewpass' );
-               $this->mRetypePass =$request->getVal( 'wpRetypePass' );
-               $this->mStubs = $request->getVal( 'wpStubs' );
-               $this->mRows = $request->getVal( 'wpRows' );
-               $this->mCols = $request->getVal( 'wpCols' );
-               $this->mSkin = $request->getVal( 'wpSkin' );
-               $this->mMath = $request->getVal( 'wpMath' );
-               $this->mDate = $request->getVal( 'wpDate' );
-               $this->mUserEmail = $request->getVal( 'wpUserEmail' );
-               $this->mRealName = $wgAllowRealName ? $request->getVal( 'wpRealName' ) : '';
-               $this->mEmailFlag = $request->getCheck( 'wpEmailFlag' ) ? 0 : 1;
-               $this->mNick = $request->getVal( 'wpNick' );
-               $this->mUserLanguage = $request->getVal( 'wpUserLanguage' );
-               $this->mUserVariant = $request->getVal( 'wpUserVariant' );
-               $this->mSearch = $request->getVal( 'wpSearch' );
-               $this->mRecent = $request->getVal( 'wpRecent' );
-               $this->mRecentDays = $request->getVal( 'wpRecentDays' );
-               $this->mHourDiff = $request->getVal( 'wpHourDiff' );
-               $this->mSearchLines = $request->getVal( 'wpSearchLines' );
-               $this->mSearchChars = $request->getVal( 'wpSearchChars' );
-               $this->mImageSize = $request->getVal( 'wpImageSize' );
-               $this->mThumbSize = $request->getInt( 'wpThumbSize' );
-               $this->mUnderline = $request->getInt( 'wpOpunderline' );
-               $this->mAction = $request->getVal( 'action' );
-               $this->mReset = $request->getCheck( 'wpReset' );
-               $this->mPosted = $request->wasPosted();
-               $this->mSuccess = $request->getCheck( 'success' );
-               $this->mWatchlistDays = $request->getVal( 'wpWatchlistDays' );
-               $this->mWatchlistEdits = $request->getVal( 'wpWatchlistEdits' );
-               $this->mUseAjaxSearch = $request->getCheck( 'wpUseAjaxSearch' );
-               $this->mDisableMWSuggest = $request->getCheck( 'wpDisableMWSuggest' );
-
-               $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) &&
-                       $this->mPosted &&
-                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
-
-               # User toggles  (the big ugly unsorted list of checkboxes)
-               $this->mToggles = array();
-               if ( $this->mPosted ) {
-                       $togs = User::getToggles();
-                       foreach ( $togs as $tname ) {
-                               $this->mToggles[$tname] = $request->getCheck( "wpOp$tname" ) ? 1 : 0;
-                       }
-               }
-
-               $this->mUsedToggles = array();
-
-               # Search namespace options
-               # Note: namespaces don't necessarily have consecutive keys
-               $this->mSearchNs = array();
-               if ( $this->mPosted ) {
-                       $namespaces = $wgContLang->getNamespaces();
-                       foreach ( $namespaces as $i => $namespace ) {
-                               if ( $i >= 0 ) {
-                                       $this->mSearchNs[$i] = $request->getCheck( "wpNs$i" ) ? 1 : 0;
-                               }
-                       }
-               }
-
-               # Validate language
-               if ( !preg_match( '/^[a-z\-]*$/', $this->mUserLanguage ) ) {
-                       $this->mUserLanguage = 'nolanguage';
-               }
-
-               wfRunHooks( 'InitPreferencesForm', array( $this, $request ) );
-       }
-
-       function execute() {
-               global $wgUser, $wgOut;
-
-               if ( $wgUser->isAnon() ) {
-                       $wgOut->showErrorPage( 'prefsnologin', 'prefsnologintext' );
-                       return;
-               }
-               if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-               if ( $this->mReset ) {
-                       $this->resetPrefs();
-                       $this->mainPrefsForm( 'reset', wfMsg( 'prefsreset' ) );
-               } else if ( $this->mSaveprefs ) {
-                       $this->savePreferences();
-               } else {
-                       $this->resetPrefs();
-                       $this->mainPrefsForm( '' );
-               }
-       }
-       /**
-        * @access private
-        */
-       function validateInt( &$val, $min=0, $max=0x7fffffff ) {
-               $val = intval($val);
-               $val = min($val, $max);
-               $val = max($val, $min);
-               return $val;
-       }
-
-       /**
-        * @access private
-        */
-       function validateFloat( &$val, $min, $max=0x7fffffff ) {
-               $val = floatval( $val );
-               $val = min( $val, $max );
-               $val = max( $val, $min );
-               return( $val );
-       }
-
-       /**
-        * @access private
-        */
-       function validateIntOrNull( &$val, $min=0, $max=0x7fffffff ) {
-               $val = trim($val);
-               if($val === '') {
-                       return null;
-               } else {
-                       return $this->validateInt( $val, $min, $max );
-               }
-       }
-
-       /**
-        * @access private
-        */
-       function validateDate( $val ) {
-               global $wgLang, $wgContLang;
-               if ( $val !== false && (
-                       in_array( $val, (array)$wgLang->getDatePreferences() ) ||
-                       in_array( $val, (array)$wgContLang->getDatePreferences() ) ) )
-               {
-                       return $val;
-               } else {
-                       return $wgLang->getDefaultDateFormat();
-               }
-       }
-
-       /**
-        * Used to validate the user inputed timezone before saving it as
-        * 'timecorrection', will return '00:00' if fed bogus data.
-        * Note: It's not a 100% correct implementation timezone-wise, it will
-        * accept stuff like '14:30',
-        * @access private
-        * @param string $s the user input
-        * @return string
-        */
-       function validateTimeZone( $s ) {
-               if ( $s !== '' ) {
-                       if ( strpos( $s, ':' ) ) {
-                               # HH:MM
-                               $array = explode( ':' , $s );
-                               $hour = intval( $array[0] );
-                               $minute = intval( $array[1] );
-                       } else {
-                               $minute = intval( $s * 60 );
-                               $hour = intval( $minute / 60 );
-                               $minute = abs( $minute ) % 60;
-                       }
-                       # Max is +14:00 and min is -12:00, see:
-                       # http://en.wikipedia.org/wiki/Timezone
-                       $hour = min( $hour, 14 );
-                       $hour = max( $hour, -12 );
-                       $minute = min( $minute, 59 );
-                       $minute = max( $minute, 0 );
-                       $s = sprintf( "%02d:%02d", $hour, $minute );
-               }
-               return $s;
-       }
-
-       /**
-        * @access private
-        */
-       function savePreferences() {
-               global $wgUser, $wgOut, $wgParser;
-               global $wgEnableUserEmail, $wgEnableEmail;
-               global $wgEmailAuthentication, $wgRCMaxAge;
-               global $wgAuth, $wgEmailConfirmToEdit;
-
-
-               if ( '' != $this->mNewpass && $wgAuth->allowPasswordChange() ) {
-                       if ( $this->mNewpass != $this->mRetypePass ) {
-                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'badretype' ) );
-                               $this->mainPrefsForm( 'error', wfMsg( 'badretype' ) );
-                               return;
-                       }
-
-                       if (!$wgUser->checkPassword( $this->mOldpass )) {
-                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'wrongpassword' ) );
-                               $this->mainPrefsForm( 'error', wfMsg( 'wrongpassword' ) );
-                               return;
-                       }
-
-                       try {
-                               $wgUser->setPassword( $this->mNewpass );
-                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'success' ) );
-                               $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
-                       } catch( PasswordError $e ) {
-                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'error' ) );
-                               $this->mainPrefsForm( 'error', $e->getMessage() );
-                               return;
-                       }
-               }
-               $wgUser->setRealName( $this->mRealName );
-               $oldOptions = $wgUser->mOptions;
-
-               if( $wgUser->getOption( 'language' ) !== $this->mUserLanguage ) {
-                       $needRedirect = true;
-               } else {
-                       $needRedirect = false;
-               }
-
-               # Validate the signature and clean it up as needed
-               global $wgMaxSigChars;
-               if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
-                       global $wgLang;
-                       $this->mainPrefsForm( 'error',
-                               wfMsgExt( 'badsiglength', 'parsemag', $wgLang->formatNum( $wgMaxSigChars ) ) );
-                       return;
-               } elseif( $this->mToggles['fancysig'] ) {
-                       if( $wgParser->validateSig( $this->mNick ) !== false ) {
-                               $this->mNick = $wgParser->cleanSig( $this->mNick );
-                       } else {
-                               $this->mainPrefsForm( 'error', wfMsg( 'badsig' ) );
-                               return;
-                       }
-               } else {
-                       // When no fancy sig used, make sure ~{3,5} get removed.
-                       $this->mNick = $wgParser->cleanSigInSig( $this->mNick );
-               }
-
-               $wgUser->setOption( 'language', $this->mUserLanguage );
-               $wgUser->setOption( 'variant', $this->mUserVariant );
-               $wgUser->setOption( 'nickname', $this->mNick );
-               $wgUser->setOption( 'quickbar', $this->mQuickbar );
-               $wgUser->setOption( 'skin', $this->mSkin );
-               global $wgUseTeX;
-               if( $wgUseTeX ) {
-                       $wgUser->setOption( 'math', $this->mMath );
-               }
-               $wgUser->setOption( 'date', $this->validateDate( $this->mDate ) );
-               $wgUser->setOption( 'searchlimit', $this->validateIntOrNull( $this->mSearch ) );
-               $wgUser->setOption( 'contextlines', $this->validateIntOrNull( $this->mSearchLines ) );
-               $wgUser->setOption( 'contextchars', $this->validateIntOrNull( $this->mSearchChars ) );
-               $wgUser->setOption( 'rclimit', $this->validateIntOrNull( $this->mRecent ) );
-               $wgUser->setOption( 'rcdays', $this->validateInt($this->mRecentDays, 1, ceil($wgRCMaxAge / (3600*24))));
-               $wgUser->setOption( 'wllimit', $this->validateIntOrNull( $this->mWatchlistEdits, 0, 1000 ) );
-               $wgUser->setOption( 'rows', $this->validateInt( $this->mRows, 4, 1000 ) );
-               $wgUser->setOption( 'cols', $this->validateInt( $this->mCols, 4, 1000 ) );
-               $wgUser->setOption( 'stubthreshold', $this->validateIntOrNull( $this->mStubs ) );
-               $wgUser->setOption( 'timecorrection', $this->validateTimeZone( $this->mHourDiff, -12, 14 ) );
-               $wgUser->setOption( 'imagesize', $this->mImageSize );
-               $wgUser->setOption( 'thumbsize', $this->mThumbSize );
-               $wgUser->setOption( 'underline', $this->validateInt($this->mUnderline, 0, 2) );
-               $wgUser->setOption( 'watchlistdays', $this->validateFloat( $this->mWatchlistDays, 0, 7 ) );
-               $wgUser->setOption( 'ajaxsearch', $this->mUseAjaxSearch );
-               $wgUser->setOption( 'disablesuggest', $this->mDisableMWSuggest );
-
-               # Set search namespace options
-               foreach( $this->mSearchNs as $i => $value ) {
-                       $wgUser->setOption( "searchNs{$i}", $value );
-               }
-
-               if( $wgEnableEmail && $wgEnableUserEmail ) {
-                       $wgUser->setOption( 'disablemail', $this->mEmailFlag );
-               }
-
-               # Set user toggles
-               foreach ( $this->mToggles as $tname => $tvalue ) {
-                       $wgUser->setOption( $tname, $tvalue );
-               }
-
-               $error = false;
-               if( $wgEnableEmail ) {
-                       $newadr = $this->mUserEmail;
-                       $oldadr = $wgUser->getEmail();
-                       if( ($newadr != '') && ($newadr != $oldadr) ) {
-                               # the user has supplied a new email address on the login page
-                               if( $wgUser->isValidEmailAddr( $newadr ) ) {
-                                       # new behaviour: set this new emailaddr from login-page into user database record
-                                       $wgUser->setEmail( $newadr );
-                                       # but flag as "dirty" = unauthenticated
-                                       $wgUser->invalidateEmail();
-                                       if ($wgEmailAuthentication) {
-                                               # Mail a temporary password to the dirty address.
-                                               # User can come back through the confirmation URL to re-enable email.
-                                               $result = $wgUser->sendConfirmationMail();
-                                               if( WikiError::isError( $result ) ) {
-                                                       $error = wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) );
-                                               } else {
-                                                       $error = wfMsg( 'eauthentsent', $wgUser->getName() );
-                                               }
-                                       }
-                               } else {
-                                       $error = wfMsg( 'invalidemailaddress' );
-                               }
-                       } else {
-                               if( $wgEmailConfirmToEdit && empty( $newadr ) ) {
-                                       $this->mainPrefsForm( 'error', wfMsg( 'noemailtitle' ) );
-                                       return;
-                               }
-                               $wgUser->setEmail( $this->mUserEmail );
-                       }
-                       if( $oldadr != $newadr ) {
-                               wfRunHooks( 'PrefsEmailAudit', array( $wgUser, $oldadr, $newadr ) );
-                       }
-               }
-
-               if( !$wgAuth->updateExternalDB( $wgUser ) ){
-                       $this->mainPrefsForm( 'error', wfMsg( 'externaldberror' ) );
-                       return;
-               }
-
-               $msg = '';
-               if ( !wfRunHooks( 'SavePreferences', array( $this, $wgUser, &$msg, $oldOptions ) ) ) {
-                       $this->mainPrefsForm( 'error', $msg );
-                       return;
-               }
-
-               $wgUser->setCookies();
-               $wgUser->saveSettings();
-
-               if( $needRedirect && $error === false ) {
-                       $title = SpecialPage::getTitleFor( 'Preferences' );
-                       $wgOut->redirect( $title->getFullURL( 'success' ) );
-                       return;
-               }
-
-               $wgOut->parserOptions( ParserOptions::newFromUser( $wgUser ) );
-               $this->mainPrefsForm( $error === false ? 'success' : 'error', $error);
-       }
-
-       /**
-        * @access private
-        */
-       function resetPrefs() {
-               global $wgUser, $wgLang, $wgContLang, $wgContLanguageCode, $wgAllowRealName;
-
-               $this->mOldpass = $this->mNewpass = $this->mRetypePass = '';
-               $this->mUserEmail = $wgUser->getEmail();
-               $this->mUserEmailAuthenticationtimestamp = $wgUser->getEmailAuthenticationtimestamp();
-               $this->mRealName = ($wgAllowRealName) ? $wgUser->getRealName() : '';
-
-               # language value might be blank, default to content language
-               $this->mUserLanguage = $wgUser->getOption( 'language', $wgContLanguageCode );
-
-               $this->mUserVariant = $wgUser->getOption( 'variant');
-               $this->mEmailFlag = $wgUser->getOption( 'disablemail' ) == 1 ? 1 : 0;
-               $this->mNick = $wgUser->getOption( 'nickname' );
-
-               $this->mQuickbar = $wgUser->getOption( 'quickbar' );
-               $this->mSkin = Skin::normalizeKey( $wgUser->getOption( 'skin' ) );
-               $this->mMath = $wgUser->getOption( 'math' );
-               $this->mDate = $wgUser->getDatePreference();
-               $this->mRows = $wgUser->getOption( 'rows' );
-               $this->mCols = $wgUser->getOption( 'cols' );
-               $this->mStubs = $wgUser->getOption( 'stubthreshold' );
-               $this->mHourDiff = $wgUser->getOption( 'timecorrection' );
-               $this->mSearch = $wgUser->getOption( 'searchlimit' );
-               $this->mSearchLines = $wgUser->getOption( 'contextlines' );
-               $this->mSearchChars = $wgUser->getOption( 'contextchars' );
-               $this->mImageSize = $wgUser->getOption( 'imagesize' );
-               $this->mThumbSize = $wgUser->getOption( 'thumbsize' );
-               $this->mRecent = $wgUser->getOption( 'rclimit' );
-               $this->mRecentDays = $wgUser->getOption( 'rcdays' );
-               $this->mWatchlistEdits = $wgUser->getOption( 'wllimit' );
-               $this->mUnderline = $wgUser->getOption( 'underline' );
-               $this->mWatchlistDays = $wgUser->getOption( 'watchlistdays' );
-               $this->mUseAjaxSearch = $wgUser->getBoolOption( 'ajaxsearch' );
-               $this->mDisableMWSuggest = $wgUser->getBoolOption( 'disablesuggest' );
-
-               $togs = User::getToggles();
-               foreach ( $togs as $tname ) {
-                       $this->mToggles[$tname] = $wgUser->getOption( $tname );
-               }
-
-               $namespaces = $wgContLang->getNamespaces();
-               foreach ( $namespaces as $i => $namespace ) {
-                       if ( $i >= NS_MAIN ) {
-                               $this->mSearchNs[$i] = $wgUser->getOption( 'searchNs'.$i );
-                       }
-               }
-
-               wfRunHooks( 'ResetPreferences', array( $this, $wgUser ) );
-       }
-
-       /**
-        * @access private
-        */
-       function namespacesCheckboxes() {
-               global $wgContLang;
-
-               # Determine namespace checkboxes
-               $namespaces = $wgContLang->getNamespaces();
-               $r1 = null;
-
-               foreach ( $namespaces as $i => $name ) {
-                       if ($i < 0)
-                               continue;
-                       $checked = $this->mSearchNs[$i] ? "checked='checked'" : '';
-                       $name = str_replace( '_', ' ', $namespaces[$i] );
-
-                       if ( empty($name) )
-                               $name = wfMsg( 'blanknamespace' );
-
-                       $r1 .= "<input type='checkbox' value='1' name='wpNs$i' id='wpNs$i' {$checked}/> <label for='wpNs$i'>{$name}</label><br />\n";
-               }
-               return $r1;
-       }
-
-
-       function getToggle( $tname, $trailer = false, $disabled = false ) {
-               global $wgUser, $wgLang;
-
-               $this->mUsedToggles[$tname] = true;
-               $ttext = $wgLang->getUserToggle( $tname );
-
-               $checked = $wgUser->getOption( $tname ) == 1 ? ' checked="checked"' : '';
-               $disabled = $disabled ? ' disabled="disabled"' : '';
-               $trailer = $trailer ? $trailer : '';
-               return "<div class='toggle'><input type='checkbox' value='1' id=\"$tname\" name=\"wpOp$tname\"$checked$disabled />" .
-                       " <span class='toggletext'><label for=\"$tname\">$ttext</label>$trailer</span></div>\n";
-       }
-
-       function getToggles( $items ) {
-               $out = "";
-               foreach( $items as $item ) {
-                       if( $item === false )
-                               continue;
-                       if( is_array( $item ) ) {
-                               list( $key, $trailer ) = $item;
-                       } else {
-                               $key = $item;
-                               $trailer = false;
-                       }
-                       $out .= $this->getToggle( $key, $trailer );
-               }
-               return $out;
-       }
-
-       function addRow($td1, $td2) {
-               return "<tr><td class='mw-label'>$td1</td><td class='mw-input'>$td2</td></tr>";
-       }
-
-       /**
-        * Helper function for user information panel
-        * @param $td1 label for an item
-        * @param $td2 item or null
-        * @param $td3 optional help or null
-        * @return xhtml block
-        */
-       function tableRow( $td1, $td2 = null, $td3 = null ) {
-
-               if ( is_null( $td3 ) ) {
-                       $td3 = '';
-               } else {
-                       $td3 = Xml::tags( 'tr', null,
-                               Xml::tags( 'td', array( 'class' => 'pref-label', 'colspan' => '2' ), $td3 )
-                       );
-               }
-
-               if ( is_null( $td2 ) ) {
-                       $td1 = Xml::tags( 'td', array( 'class' => 'pref-label', 'colspan' => '2' ), $td1 );
-                       $td2 = '';
-               } else {
-                       $td1 = Xml::tags( 'td', array( 'class' => 'pref-label' ), $td1 );
-                       $td2 = Xml::tags( 'td', array( 'class' => 'pref-input' ), $td2 );
-               }
-
-               return Xml::tags( 'tr', null, $td1 . $td2 ). $td3 . "\n";
-
-       }
-
-       /**
-        * @access private
-        */
-       function mainPrefsForm( $status , $message = '' ) {
-               global $wgUser, $wgOut, $wgLang, $wgContLang;
-               global $wgAllowRealName, $wgImageLimits, $wgThumbLimits;
-               global $wgDisableLangConversion;
-               global $wgEnotifWatchlist, $wgEnotifUserTalk,$wgEnotifMinorEdits;
-               global $wgRCShowWatchingUsers, $wgEnotifRevealEditorAddress;
-               global $wgEnableEmail, $wgEnableUserEmail, $wgEmailAuthentication;
-               global $wgContLanguageCode, $wgDefaultSkin, $wgSkipSkins, $wgAuth;
-               global $wgEmailConfirmToEdit, $wgAjaxSearch, $wgEnableMWSuggest;
-
-               $wgOut->setPageTitle( wfMsg( 'preferences' ) );
-               $wgOut->setArticleRelated( false );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->addScriptFile( 'prefs.js' );
-
-               $wgOut->disallowUserJs();  # Prevent hijacked user scripts from sniffing passwords etc.
-
-               if ( $this->mSuccess || 'success' == $status ) {
-                       $wgOut->wrapWikiMsg( '<div class="successbox"><strong>$1</strong></div>', 'savedprefs' );
-               } else  if ( 'error' == $status ) {
-                       $wgOut->addWikiText( '<div class="errorbox"><strong>' . $message  . '</strong></div>' );
-               } else if ( '' != $status ) {
-                       $wgOut->addWikiText( $message . "\n----" );
-               }
-
-               $qbs = $wgLang->getQuickbarSettings();
-               $skinNames = $wgLang->getSkinNames();
-               $mathopts = $wgLang->getMathNames();
-               $dateopts = $wgLang->getDatePreferences();
-               $togs = User::getToggles();
-
-               $titleObj = SpecialPage::getTitleFor( 'Preferences' );
-               $action = $titleObj->escapeLocalURL();
-
-               # Pre-expire some toggles so they won't show if disabled
-               $this->mUsedToggles[ 'shownumberswatching' ] = true;
-               $this->mUsedToggles[ 'showupdated' ] = true;
-               $this->mUsedToggles[ 'enotifwatchlistpages' ] = true;
-               $this->mUsedToggles[ 'enotifusertalkpages' ] = true;
-               $this->mUsedToggles[ 'enotifminoredits' ] = true;
-               $this->mUsedToggles[ 'enotifrevealaddr' ] = true;
-               $this->mUsedToggles[ 'ccmeonemails' ] = true;
-               $this->mUsedToggles[ 'uselivepreview' ] = true;
-
-
-               if ( !$this->mEmailFlag ) { $emfc = 'checked="checked"'; }
-               else { $emfc = ''; }
-
-
-               if ($wgEmailAuthentication && ($this->mUserEmail != '') ) {
-                       if( $wgUser->getEmailAuthenticationTimestamp() ) {
-                               $emailauthenticated = wfMsg('emailauthenticated',$wgLang->timeanddate($wgUser->getEmailAuthenticationTimestamp(), true ) ).'<br />';
-                               $disableEmailPrefs = false;
-                       } else {
-                               $disableEmailPrefs = true;
-                               $skin = $wgUser->getSkin();
-                               $emailauthenticated = wfMsg('emailnotauthenticated').'<br />' .
-                                       $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Confirmemail' ),
-                                               wfMsg( 'emailconfirmlink' ) ) . '<br />';
-                       }
-               } else {
-                       $emailauthenticated = '';
-                       $disableEmailPrefs = false;
-               }
-
-               if ($this->mUserEmail == '') {
-                       $emailauthenticated = wfMsg( 'noemailprefs' ) . '<br />';
-               }
-
-               $ps = $this->namespacesCheckboxes();
-
-               $enotifwatchlistpages = ($wgEnotifWatchlist) ? $this->getToggle( 'enotifwatchlistpages', false, $disableEmailPrefs ) : '';
-               $enotifusertalkpages = ($wgEnotifUserTalk) ? $this->getToggle( 'enotifusertalkpages', false, $disableEmailPrefs ) : '';
-               $enotifminoredits = ($wgEnotifWatchlist && $wgEnotifMinorEdits) ? $this->getToggle( 'enotifminoredits', false, $disableEmailPrefs ) : '';
-               $enotifrevealaddr = (($wgEnotifWatchlist || $wgEnotifUserTalk) && $wgEnotifRevealEditorAddress) ? $this->getToggle( 'enotifrevealaddr', false, $disableEmailPrefs ) : '';
-
-               # </FIXME>
-
-               $wgOut->addHTML( "<form action=\"$action\" method='post'>" );
-               $wgOut->addHTML( "<div id='preferences'>" );
-
-               # User data
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'fieldset ' ) .
-                       Xml::element( 'legend', null, wfMsg('prefs-personal') ) .
-                       Xml::openElement( 'table' ) .
-                       $this->tableRow( Xml::element( 'h2', null, wfMsg( 'prefs-personal' ) ) )
-               );
-
-               # Get groups to which the user belongs
-               $userEffectiveGroups = $wgUser->getEffectiveGroups();
-               $userEffectiveGroupsArray = array();
-               foreach( $userEffectiveGroups as $ueg ) {
-                       if( $ueg == '*' ) {
-                               // Skip the default * group, seems useless here
-                               continue;
-                       }
-                       $userEffectiveGroupsArray[] = User::makeGroupLinkHTML( $ueg );
-               }
-               asort( $userEffectiveGroupsArray );
-
-               $sk = $wgUser->getSkin();
-               $toolLinks = array();
-               $toolLinks[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'ListGroupRights' ), wfMsg( 'listgrouprights' ) );
-               # At the moment one tool link only but be prepared for the future...
-               # FIXME: Add a link to Special:Userrights for users who are allowed to use it. 
-               # $wgUser->isAllowed( 'userrights' ) seems to strict in some cases
-
-               $userInformationHtml =
-                       $this->tableRow( wfMsgHtml( 'username' ), htmlspecialchars( $wgUser->getName() ) ) .
-                       $this->tableRow( wfMsgHtml( 'uid' ), htmlspecialchars( $wgUser->getId() ) ) .
-
-                       $this->tableRow(
-                               wfMsgExt( 'prefs-memberingroups', array( 'parseinline' ), count( $userEffectiveGroupsArray ) ),
-                               implode( wfMsg( 'comma-separator' ), $userEffectiveGroupsArray ) . 
-                               '<br />(' . implode( ' | ', $toolLinks ) . ')'
-                       ) .
-
-                       $this->tableRow(
-                               wfMsgHtml( 'prefs-edits' ),
-                               $wgLang->formatNum( User::edits( $wgUser->getId() ) )
-                       );
-
-               if( wfRunHooks( 'PreferencesUserInformationPanel', array( $this, &$userInformationHtml ) ) ) {
-                       $wgOut->addHtml( $userInformationHtml );
-               }
-
-               if ( $wgAllowRealName ) {
-                       $wgOut->addHTML(
-                               $this->tableRow(
-                                       Xml::label( wfMsg('yourrealname'), 'wpRealName' ),
-                                       Xml::input( 'wpRealName', 25, $this->mRealName, array( 'id' => 'wpRealName' ) ),
-                                       Xml::tags('div', array( 'class' => 'prefsectiontip' ),
-                                               wfMsgExt( 'prefs-help-realname', 'parseinline' )
-                                       )
-                               )
-                       );
-               }
-               if ( $wgEnableEmail ) {
-                       $wgOut->addHTML(
-                               $this->tableRow(
-                                       Xml::label( wfMsg('youremail'), 'wpUserEmail' ),
-                                       Xml::input( 'wpUserEmail', 25, $this->mUserEmail, array( 'id' => 'wpUserEmail' ) ),
-                                       Xml::tags('div', array( 'class' => 'prefsectiontip' ),
-                                               wfMsgExt( $wgEmailConfirmToEdit ? 'prefs-help-email-required' : 'prefs-help-email', 'parseinline' )
-                                       )
-                               )
-                       );
-               }
-
-               global $wgParser, $wgMaxSigChars;
-               if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
-                       $invalidSig = $this->tableRow(
-                               '&nbsp;',
-                               Xml::element( 'span', array( 'class' => 'error' ),
-                                       wfMsgExt( 'badsiglength', 'parsemag', $wgLang->formatNum( $wgMaxSigChars ) ) )
-                       );
-               } elseif( !empty( $this->mToggles['fancysig'] ) &&
-                       false === $wgParser->validateSig( $this->mNick ) ) {
-                       $invalidSig = $this->tableRow(
-                               '&nbsp;',
-                               Xml::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) )
-                       );
-               } else {
-                       $invalidSig = '';
-               }
-
-               $wgOut->addHTML(
-                       $this->tableRow(
-                               Xml::label( wfMsg( 'yournick' ), 'wpNick' ),
-                               Xml::input( 'wpNick', 25, $this->mNick,
-                                       array(
-                                               'id' => 'wpNick',
-                                               // Note: $wgMaxSigChars is enforced in Unicode characters,
-                                               // both on the backend and now in the browser.
-                                               // Badly-behaved requests may still try to submit
-                                               // an overlong string, however.
-                                               'maxlength' => $wgMaxSigChars ) )
-                       ) .
-                       $invalidSig .
-                       $this->tableRow( '&nbsp;', $this->getToggle( 'fancysig' ) )
-               );
-
-               list( $lsLabel, $lsSelect) = Xml::languageSelector( $this->mUserLanguage );
-               $wgOut->addHTML(
-                       $this->tableRow( $lsLabel, $lsSelect )
-               );
-
-               /* see if there are multiple language variants to choose from*/
-               if(!$wgDisableLangConversion) {
-                       $variants = $wgContLang->getVariants();
-                       $variantArray = array();
-
-                       $languages = Language::getLanguageNames( true );
-                       foreach($variants as $v) {
-                               $v = str_replace( '_', '-', strtolower($v));
-                               if( array_key_exists( $v, $languages ) ) {
-                                       // If it doesn't have a name, we'll pretend it doesn't exist
-                                       $variantArray[$v] = $languages[$v];
-                               }
-                       }
-
-                       $options = "\n";
-                       foreach( $variantArray as $code => $name ) {
-                               $selected = ($code == $this->mUserVariant);
-                               $options .= Xml::option( "$code - $name", $code, $selected ) . "\n";
-                       }
-
-                       if(count($variantArray) > 1) {
-                               $wgOut->addHtml(
-                                       $this->tableRow(
-                                               Xml::label( wfMsg( 'yourvariant' ), 'wpUserVariant' ),
-                                               Xml::tags( 'select',
-                                                       array( 'name' => 'wpUserVariant', 'id' => 'wpUserVariant' ),
-                                                       $options
-                                               )
-                                       )
-                               );
-                       }
-               }
-
-               # Password
-               if( $wgAuth->allowPasswordChange() ) {
-                       $wgOut->addHTML(
-                               $this->tableRow( Xml::element( 'h2', null, wfMsg( 'changepassword' ) ) ) .
-                               $this->tableRow(
-                                       Xml::label( wfMsg( 'oldpassword' ), 'wpOldpass' ),
-                                       Xml::password( 'wpOldpass', 25, $this->mOldpass, array( 'id' => 'wpOldpass' ) )
-                               ) .
-                               $this->tableRow(
-                                       Xml::label( wfMsg( 'newpassword' ), 'wpNewpass' ),
-                                       Xml::password( 'wpNewpass', 25, $this->mNewpass, array( 'id' => 'wpNewpass' ) )
-                               ) .
-                               $this->tableRow(
-                                       Xml::label( wfMsg( 'retypenew' ), 'wpRetypePass' ),
-                                       Xml::password( 'wpRetypePass', 25, $this->mRetypePass, array( 'id' => 'wpRetypePass' ) )
-                               ) .
-                               Xml::tags( 'tr', null,
-                                       Xml::tags( 'td', array( 'colspan' => '2' ),
-                                               $this->getToggle( "rememberpassword" )
-                                       )
-                               )
-                       );
-               }
-
-               # <FIXME>
-               # Enotif
-               if ( $wgEnableEmail ) {
-
-                       $moreEmail = '';
-                       if ($wgEnableUserEmail) {
-                               // fixme -- the "allowemail" pseudotoggle is a hacked-together
-                               // inversion for the "disableemail" preference.
-                               $emf = wfMsg( 'allowemail' );
-                               $disabled = $disableEmailPrefs ? ' disabled="disabled"' : '';
-                               $moreEmail =
-                                       "<input type='checkbox' $emfc $disabled value='1' name='wpEmailFlag' id='wpEmailFlag' /> <label for='wpEmailFlag'>$emf</label>" .
-                                       $this->getToggle( 'ccmeonemails', '', $disableEmailPrefs );
-                       }
-
-
-                       $wgOut->addHTML(
-                               $this->tableRow( Xml::element( 'h2', null, wfMsg( 'email' ) ) ) .
-                               $this->tableRow(
-                                       $emailauthenticated.
-                                       $enotifrevealaddr.
-                                       $enotifwatchlistpages.
-                                       $enotifusertalkpages.
-                                       $enotifminoredits.
-                                       $moreEmail
-                               )
-                       );
-               }
-               # </FIXME>
-
-               $wgOut->addHTML(
-                       Xml::closeElement( 'table' ) .
-                       Xml::closeElement( 'fieldset' )
-               );
-
-
-               # Quickbar
-               #
-               if ($this->mSkin == 'cologneblue' || $this->mSkin == 'standard') {
-                       $wgOut->addHtml( "<fieldset>\n<legend>" . wfMsg( 'qbsettings' ) . "</legend>\n" );
-                       for ( $i = 0; $i < count( $qbs ); ++$i ) {
-                               if ( $i == $this->mQuickbar ) { $checked = ' checked="checked"'; }
-                               else { $checked = ""; }
-                               $wgOut->addHTML( "<div><label><input type='radio' name='wpQuickbar' value=\"$i\"$checked />{$qbs[$i]}</label></div>\n" );
-                       }
-                       $wgOut->addHtml( "</fieldset>\n\n" );
-               } else {
-                       # Need to output a hidden option even if the relevant skin is not in use,
-                       # otherwise the preference will get reset to 0 on submit
-                       $wgOut->addHtml( wfHidden( 'wpQuickbar', $this->mQuickbar ) );
-               }
-
-               # Skin
-               #
-               $wgOut->addHTML( "<fieldset>\n<legend>\n" . wfMsg('skin') . "</legend>\n" );
-               $mptitle = Title::newMainPage();
-               $previewtext = wfMsg('skinpreview');
-               # Only show members of Skin::getSkinNames() rather than
-               # $skinNames (skins is all skin names from Language.php)
-               $validSkinNames = Skin::getSkinNames();
-               # Sort by UI skin name. First though need to update validSkinNames as sometimes
-               # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI).
-               foreach ($validSkinNames as $skinkey => & $skinname ) {
-                       if ( isset( $skinNames[$skinkey] ) )  {
-                               $skinname = $skinNames[$skinkey];
-                       }
-               }
-               asort($validSkinNames);
-               foreach ($validSkinNames as $skinkey => $sn ) {
-                       if ( in_array( $skinkey, $wgSkipSkins ) ) {
-                               continue;
-                       }
-                       $checked = $skinkey == $this->mSkin ? ' checked="checked"' : '';
-
-                       $mplink = htmlspecialchars($mptitle->getLocalURL("useskin=$skinkey"));
-                       $previewlink = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
-                       if( $skinkey == $wgDefaultSkin )
-                               $sn .= ' (' . wfMsg( 'default' ) . ')';
-                       $wgOut->addHTML( "<input type='radio' name='wpSkin' id=\"wpSkin$skinkey\" value=\"$skinkey\"$checked /> <label for=\"wpSkin$skinkey\">{$sn}</label> $previewlink<br />\n" );
-               }
-               $wgOut->addHTML( "</fieldset>\n\n" );
-
-               # Math
-               #
-               global $wgUseTeX;
-               if( $wgUseTeX ) {
-                       $wgOut->addHTML( "<fieldset>\n<legend>" . wfMsg('math') . '</legend>' );
-                       foreach ( $mathopts as $k => $v ) {
-                               $checked = ($k == $this->mMath);
-                               $wgOut->addHTML(
-                                       Xml::openElement( 'div' ) .
-                                       Xml::radioLabel( wfMsg( $v ), 'wpMath', $k, "mw-sp-math-$k", $checked ) .
-                                       Xml::closeElement( 'div' ) . "\n"
-                               );
-                       }
-                       $wgOut->addHTML( "</fieldset>\n\n" );
-               }
-
-               # Files
-               #
-               $wgOut->addHTML(
-                       "<fieldset>\n" . Xml::element( 'legend', null, wfMsg( 'files' ) ) . "\n"
-               );
-
-               $imageLimitOptions = null;
-               foreach ( $wgImageLimits as $index => $limits ) {
-                       $selected = ($index == $this->mImageSize);
-                       $imageLimitOptions .= Xml::option( "{$limits[0]}×{$limits[1]}" .
-                               wfMsg('unit-pixel'), $index, $selected );
-               }
-
-               $imageSizeId = 'wpImageSize';
-               $wgOut->addHTML(
-                       "<div>" . Xml::label( wfMsg('imagemaxsize'), $imageSizeId ) . " " .
-                       Xml::openElement( 'select', array( 'name' => $imageSizeId, 'id' => $imageSizeId ) ) .
-                               $imageLimitOptions .
-                       Xml::closeElement( 'select' ) . "</div>\n"
-               );
-
-               $imageThumbOptions = null;
-               foreach ( $wgThumbLimits as $index => $size ) {
-                       $selected = ($index == $this->mThumbSize);
-                       $imageThumbOptions .= Xml::option($size . wfMsg('unit-pixel'), $index,
-                               $selected);
-               }
-
-               $thumbSizeId = 'wpThumbSize';
-               $wgOut->addHTML(
-                       "<div>" . Xml::label( wfMsg('thumbsize'), $thumbSizeId ) . " " .
-                       Xml::openElement( 'select', array( 'name' => $thumbSizeId, 'id' => $thumbSizeId ) ) .
-                               $imageThumbOptions .
-                       Xml::closeElement( 'select' ) . "</div>\n"
-               );
-
-               $wgOut->addHTML( "</fieldset>\n\n" );
-
-               # Date format
-               #
-               # Date/Time
-               #
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'datetime' ) ) . "\n"
-               );
-
-               if ($dateopts) {
-                       $wgOut->addHTML(
-                               Xml::openElement( 'fieldset' ) .
-                               Xml::element( 'legend', null, wfMsg( 'dateformat' ) ) . "\n"
-                       );
-                       $idCnt = 0;
-                       $epoch = '20010115161234'; # Wikipedia day
-                       foreach( $dateopts as $key ) {
-                               if( $key == 'default' ) {
-                                       $formatted = wfMsg( 'datedefault' );
-                               } else {
-                                       $formatted = $wgLang->timeanddate( $epoch, false, $key );
-                               }
-                               $wgOut->addHTML(
-                                       Xml::tags( 'div', null,
-                                               Xml::radioLabel( $formatted, 'wpDate', $key, "wpDate$idCnt", $key == $this->mDate )
-                                       ) . "\n"
-                               );
-                               $idCnt++;
-                       }
-                       $wgOut->addHTML( Xml::closeElement( 'fieldset' ) . "\n" );
-               }
-
-               $nowlocal = $wgLang->time( $now = wfTimestampNow(), true );
-               $nowserver = $wgLang->time( $now, false );
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'timezonelegend' ) ) .
-                       Xml::openElement( 'table' ) .
-                       $this->addRow( wfMsg( 'servertime' ), $nowserver ) .
-                       $this->addRow( wfMsg( 'localtime' ), $nowlocal ) .
-                       $this->addRow(
-                               Xml::label( wfMsg( 'timezoneoffset' ), 'wpHourDiff'  ),
-                               Xml::input( 'wpHourDiff', 6, $this->mHourDiff, array( 'id' => 'wpHourDiff' ) ) ) .
-                       "<tr>
-                               <td></td>
-                               <td class='mw-submit'>" .
-                                       Xml::element( 'input',
-                                               array( 'type' => 'button',
-                                                       'value' => wfMsg( 'guesstimezone' ),
-                                                       'onclick' => 'javascript:guessTimezone()',
-                                                       'id' => 'guesstimezonebutton',
-                                                       'style' => 'display:none;' ) ) .
-                               "</td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::tags( 'div', array( 'class' => 'prefsectiontip' ), wfMsgExt( 'timezonetext', 'parseinline' ) ).
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'fieldset' ) . "\n\n"
-               );
-
-               # Editing
-               #
-               global $wgLivePreview;
-               $wgOut->addHTML( '<fieldset><legend>' . wfMsg( 'textboxsize' ) . '</legend>
-                       <div>' .
-                               wfInputLabel( wfMsg( 'rows' ), 'wpRows', 'wpRows', 3, $this->mRows ) .
-                               ' ' .
-                               wfInputLabel( wfMsg( 'columns' ), 'wpCols', 'wpCols', 3, $this->mCols ) .
-                       "</div>" .
-                       $this->getToggles( array(
-                               'editsection',
-                               'editsectiononrightclick',
-                               'editondblclick',
-                               'editwidth',
-                               'showtoolbar',
-                               'previewonfirst',
-                               'previewontop',
-                               'minordefault',
-                               'externaleditor',
-                               'externaldiff',
-                               $wgLivePreview ? 'uselivepreview' : false,
-                               'forceeditsummary',
-                       ) ) . '</fieldset>'
-               );
-
-               # Recent changes
-               $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-rc' ) . '</legend>' );
-
-               $rc  = '<table><tr>';
-               $rc .= '<td>' . Xml::label( wfMsg( 'recentchangesdays' ), 'wpRecentDays' ) . '</td>';
-               $rc .= '<td>' . Xml::input( 'wpRecentDays', 3, $this->mRecentDays, array( 'id' => 'wpRecentDays' ) ) . '</td>';
-               $rc .= '</tr><tr>';
-               $rc .= '<td>' . Xml::label( wfMsg( 'recentchangescount' ), 'wpRecent' ) . '</td>';
-               $rc .= '<td>' . Xml::input( 'wpRecent', 3, $this->mRecent, array( 'id' => 'wpRecent' ) ) . '</td>';
-               $rc .= '</tr></table>';
-               $wgOut->addHtml( $rc );
-
-               $wgOut->addHtml( '<br />' );
-
-               $toggles[] = 'hideminor';
-               if( $wgRCShowWatchingUsers )
-                       $toggles[] = 'shownumberswatching';
-               $toggles[] = 'usenewrc';
-               $wgOut->addHtml( $this->getToggles( $toggles ) );
-
-               $wgOut->addHtml( '</fieldset>' );
-
-               # Watchlist
-               $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-watchlist' ) . '</legend>' );
-
-               $wgOut->addHtml( wfInputLabel( wfMsg( 'prefs-watchlist-days' ), 'wpWatchlistDays', 'wpWatchlistDays', 3, $this->mWatchlistDays ) );
-               $wgOut->addHtml( '<br /><br />' );
-
-               $wgOut->addHtml( $this->getToggle( 'extendwatchlist' ) );
-               $wgOut->addHtml( wfInputLabel( wfMsg( 'prefs-watchlist-edits' ), 'wpWatchlistEdits', 'wpWatchlistEdits', 3, $this->mWatchlistEdits ) );
-               $wgOut->addHtml( '<br /><br />' );
-
-               $wgOut->addHtml( $this->getToggles( array( 'watchlisthideown', 'watchlisthidebots', 'watchlisthideminor' ) ) );
-
-               if( $wgUser->isAllowed( 'createpage' ) || $wgUser->isAllowed( 'createtalk' ) )
-                       $wgOut->addHtml( $this->getToggle( 'watchcreations' ) );
-               foreach( array( 'edit' => 'watchdefault', 'move' => 'watchmoves', 'delete' => 'watchdeletion' ) as $action => $toggle ) {
-                       if( $wgUser->isAllowed( $action ) )
-                               $wgOut->addHtml( $this->getToggle( $toggle ) );
-               }
-               $this->mUsedToggles['watchcreations'] = true;
-               $this->mUsedToggles['watchdefault'] = true;
-               $this->mUsedToggles['watchmoves'] = true;
-               $this->mUsedToggles['watchdeletion'] = true;
-
-               $wgOut->addHtml( '</fieldset>' );
-
-               # Search
-               $ajaxsearch = $wgAjaxSearch ?
-                       $this->addRow(
-                               Xml::label( wfMsg( 'useajaxsearch' ), 'wpUseAjaxSearch' ),
-                               Xml::check( 'wpUseAjaxSearch', $this->mUseAjaxSearch, array( 'id' => 'wpUseAjaxSearch' ) )
-                       ) : '';
-               $mwsuggest = $wgEnableMWSuggest ?
-                       $this->addRow(
-                               Xml::label( wfMsg( 'mwsuggest-disable' ), 'wpDisableMWSuggest' ),
-                               Xml::check( 'wpDisableMWSuggest', $this->mDisableMWSuggest, array( 'id' => 'wpDisableMWSuggest' ) )
-                       ) : '';
-               $wgOut->addHTML(
-                       // Elements for the search tab itself
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'searchresultshead' ) ) .
-                       // Elements for the search options in the search tab
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'prefs-searchoptions' ) ) .
-                       Xml::openElement( 'table' ) .
-                       $ajaxsearch .
-                       $this->addRow(
-                               Xml::label( wfMsg( 'resultsperpage' ), 'wpSearch' ),
-                               Xml::input( 'wpSearch', 4, $this->mSearch, array( 'id' => 'wpSearch' ) )
-                       ) .
-                       $this->addRow(
-                               Xml::label( wfMsg( 'contextlines' ), 'wpSearchLines' ),
-                               Xml::input( 'wpSearchLines', 4, $this->mSearchLines, array( 'id' => 'wpSearchLines' ) )
-                       ) .
-                       $this->addRow(
-                               Xml::label( wfMsg( 'contextchars' ), 'wpSearchChars' ),
-                               Xml::input( 'wpSearchChars', 4, $this->mSearchChars, array( 'id' => 'wpSearchChars' ) )
-                       ) .
-                       $mwsuggest .
-                       Xml::closeElement( 'table' ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       // Elements for the namespace options in the search tab
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'prefs-namespaces' ) ) .
-                       wfMsgExt( 'defaultns', array( 'parse' ) ) .
-                       $ps .
-                       Xml::closeElement( 'fieldset' ) .
-                       // End of the search tab
-                       Xml::closeElement( 'fieldset' )
-               );
-
-               # Misc
-               #
-               $wgOut->addHTML('<fieldset><legend>' . wfMsg('prefs-misc') . '</legend>');
-               $wgOut->addHtml( '<label for="wpStubs">' . wfMsg( 'stub-threshold' ) . '</label>&nbsp;' );
-               $wgOut->addHtml( Xml::input( 'wpStubs', 6, $this->mStubs, array( 'id' => 'wpStubs' ) ) );
-               $msgUnderline = htmlspecialchars( wfMsg ( 'tog-underline' ) );
-               $msgUnderlinenever = htmlspecialchars( wfMsg ( 'underline-never' ) );
-               $msgUnderlinealways = htmlspecialchars( wfMsg ( 'underline-always' ) );
-               $msgUnderlinedefault = htmlspecialchars( wfMsg ( 'underline-default' ) );
-               $uopt = $wgUser->getOption("underline");
-               $s0 = $uopt == 0 ? ' selected="selected"' : '';
-               $s1 = $uopt == 1 ? ' selected="selected"' : '';
-               $s2 = $uopt == 2 ? ' selected="selected"' : '';
-               $wgOut->addHTML("
-<div class='toggle'><p><label for='wpOpunderline'>$msgUnderline</label>
-<select name='wpOpunderline' id='wpOpunderline'>
-<option value=\"0\"$s0>$msgUnderlinenever</option>
-<option value=\"1\"$s1>$msgUnderlinealways</option>
-<option value=\"2\"$s2>$msgUnderlinedefault</option>
-</select></p></div>");
-
-               foreach ( $togs as $tname ) {
-                       if( !array_key_exists( $tname, $this->mUsedToggles ) ) {
-                               $wgOut->addHTML( $this->getToggle( $tname ) );
-                       }
-               }
-               $wgOut->addHTML( '</fieldset>' );
-
-               wfRunHooks( 'RenderPreferencesForm', array( $this, $wgOut ) );
-
-               $token = htmlspecialchars( $wgUser->editToken() );
-               $skin = $wgUser->getSkin();
-               $wgOut->addHTML( "
-       <div id='prefsubmit'>
-       <div>
-               <input type='submit' name='wpSaveprefs' class='btnSavePrefs' value=\"" . wfMsgHtml( 'saveprefs' ) . '"'.$skin->tooltipAndAccesskey('save')." />
-               <input type='submit' name='wpReset' value=\"" . wfMsgHtml( 'resetprefs' ) . "\" />
-       </div>
-
-       </div>
-
-       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
-       </div></form>\n" );
-
-               $wgOut->addHtml( Xml::tags( 'div', array( 'class' => "prefcache" ),
-                       wfMsgExt( 'clearyourcache', 'parseinline' ) )
-               );
-       }
-}
diff --git a/includes/specials/Prefixindex.php b/includes/specials/Prefixindex.php
deleted file mode 100644 (file)
index 1819d4e..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Entry point : initialise variables and call subfunctions.
- * @param $par String: becomes "FOO" when called like Special:Prefixindex/FOO (default NULL)
- * @param $specialPage SpecialPage object.
- */
-function wfSpecialPrefixIndex( $par=NULL, $specialPage ) {
-       global $wgRequest, $wgOut, $wgContLang;
-
-       # GET values
-       $from = $wgRequest->getVal( 'from' );
-       $prefix = $wgRequest->getVal( 'prefix' );
-       $namespace = $wgRequest->getInt( 'namespace' );
-       $namespaces = $wgContLang->getNamespaces();
-
-       $indexPage = new SpecialPrefixIndex();
-
-       $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces ) ) )
-               ? wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) )
-               : wfMsg( 'allarticles' )
-       );
-
-       if ( isset($par) ) {
-               $indexPage->showChunk( $namespace, $par, $specialPage->including(), $from );
-       } elseif ( isset($prefix) ) {
-               $indexPage->showChunk( $namespace, $prefix, $specialPage->including(), $from );
-       } elseif ( isset($from) ) {
-               $indexPage->showChunk( $namespace, $from, $specialPage->including(), $from );
-       } else {
-               $wgOut->addHtml($indexPage->namespaceForm ( $namespace, null ));
-       }
-}
-
-/**
- * implements Special:Prefixindex
- * @ingroup SpecialPage
- */
-class SpecialPrefixindex extends SpecialAllpages {
-       // Inherit $maxPerPage
-
-       // Define other properties
-       protected $name = 'Prefixindex';
-       protected $nsfromMsg = 'allpagesprefix';
-
-       /**
-        * @param integer $namespace (Default NS_MAIN)
-        * @param string $from list all pages from this name (default FALSE)
-        */
-       function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = null ) {
-               global $wgOut, $wgUser, $wgContLang;
-
-               $fname = 'indexShowChunk';
-
-               $sk = $wgUser->getSkin();
-
-               if (!isset($from)) $from = $prefix;
-
-               $fromList = $this->getNamespaceKeyAndText($namespace, $from);
-               $prefixList = $this->getNamespaceKeyAndText($namespace, $prefix);
-               $namespaces = $wgContLang->getNamespaces();
-               $align = $wgContLang->isRtl() ? 'left' : 'right';
-
-               if ( !$prefixList || !$fromList ) {
-                       $out = wfMsgWikiHtml( 'allpagesbadtitle' );
-               } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
-                       // Show errormessage and reset to NS_MAIN
-                       $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
-                       $namespace = NS_MAIN;
-               } else {
-                       list( $namespace, $prefixKey, $prefix ) = $prefixList;
-                       list( /* $fromNs */, $fromKey, $from ) = $fromList;
-
-                       ### FIXME: should complain if $fromNs != $namespace
-
-                       $dbr = wfGetDB( DB_SLAVE );
-
-                       $res = $dbr->select( 'page',
-                               array( 'page_namespace', 'page_title', 'page_is_redirect' ),
-                               array(
-                                       'page_namespace' => $namespace,
-                                       'page_title LIKE \'' . $dbr->escapeLike( $prefixKey ) .'%\'',
-                                       'page_title >= ' . $dbr->addQuotes( $fromKey ),
-                               ),
-                               $fname,
-                               array(
-                                       'ORDER BY'  => 'page_title',
-                                       'LIMIT'     => $this->maxPerPage + 1,
-                                       'USE INDEX' => 'name_title',
-                               )
-                       );
-
-                       ### FIXME: side link to previous
-
-                       $n = 0;
-                       if( $res->numRows() > 0 ) {
-                               $out = '<table style="background: inherit;" border="0" width="100%">';
-       
-                               while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
-                                       $t = Title::makeTitle( $s->page_namespace, $s->page_title );
-                                       if( $t ) {
-                                               $link = ($s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
-                                                       $sk->makeKnownLinkObj( $t, htmlspecialchars( $t->getText() ), false, false ) .
-                                                       ($s->page_is_redirect ? '</div>' : '' );
-                                       } else {
-                                               $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
-                                       }
-                                       if( $n % 3 == 0 ) {
-                                               $out .= '<tr>';
-                                       }
-                                       $out .= "<td>$link</td>";
-                                       $n++;
-                                       if( $n % 3 == 0 ) {
-                                               $out .= '</tr>';
-                                       }
-                               }
-                               if( ($n % 3) != 0 ) {
-                                       $out .= '</tr>';
-                               }
-                               $out .= '</table>';
-                       } else {
-                               $out = '';
-                       }
-               }
-
-               if ( $including ) {
-                       $out2 = '';
-               } else {
-                       $nsForm = $this->namespaceForm ( $namespace, $prefix );
-                       $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
-                       $out2 .= '<tr valign="top"><td>' . $nsForm;
-                       $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
-                                       $sk->makeKnownLink( $wgContLang->specialPage( $this->name ),
-                                               wfMsg ( 'allpages' ) );
-                       if ( isset($dbr) && $dbr && ($n == $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
-                               $namespaceparam = $namespace ? "&namespace=$namespace" : "";
-                               $out2 .= " | " . $sk->makeKnownLink(
-                                       $wgContLang->specialPage( $this->name ),
-                                       wfMsg ( 'nextpage', $s->page_title ),
-                                       "from=" . wfUrlEncode ( $s->page_title ) .
-                                       "&prefix=" . wfUrlEncode ( $prefix ) . $namespaceparam );
-                       }
-                       $out2 .= "</td></tr></table><hr />";
-               }
-
-               $wgOut->addHtml( $out2 . $out );
-       }
-}
diff --git a/includes/specials/Protectedpages.php b/includes/specials/Protectedpages.php
deleted file mode 100644 (file)
index e168902..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @todo document
- * @ingroup SpecialPage
- */
-class ProtectedPagesForm {
-
-       protected $IdLevel = 'level';
-       protected $IdType  = 'type';
-
-       public function showList( $msg = '' ) {
-               global $wgOut, $wgRequest;
-
-               $wgOut->setPagetitle( wfMsg( "protectedpages" ) );
-               if ( "" != $msg ) {
-                       $wgOut->setSubtitle( $msg );
-               }
-
-               // Purge expired entries on one in every 10 queries
-               if ( !mt_rand( 0, 10 ) ) {
-                       Title::purgeExpiredRestrictions();
-               }
-
-               $type = $wgRequest->getVal( $this->IdType );
-               $level = $wgRequest->getVal( $this->IdLevel );
-               $sizetype = $wgRequest->getVal( 'sizetype' );
-               $size = $wgRequest->getIntOrNull( 'size' );
-               $NS = $wgRequest->getIntOrNull( 'namespace' );
-               $indefOnly = $wgRequest->getBool( 'indefonly' ) ? 1 : 0;
-
-               $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $sizetype, $size, $indefOnly );
-
-               $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size, $indefOnly ) );
-
-               if ( $pager->getNumRows() ) {
-                       $s = $pager->getNavigationBar();
-                       $s .= "<ul>" .
-                               $pager->getBody() .
-                               "</ul>";
-                       $s .= $pager->getNavigationBar();
-               } else {
-                       $s = '<p>' . wfMsgHtml( 'protectedpagesempty' ) . '</p>';
-               }
-               $wgOut->addHTML( $s );
-       }
-
-       /**
-        * Callback function to output a restriction
-        * @param $row object Protected title
-        * @return string Formatted <li> element
-        */
-       public function formatRow( $row ) {
-               global $wgUser, $wgLang, $wgContLang;
-
-               wfProfileIn( __METHOD__ );
-
-               static $skin=null;
-
-               if( is_null( $skin ) )
-                       $skin = $wgUser->getSkin();
-
-               $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
-               $link = $skin->makeLinkObj( $title );
-
-               $description_items = array ();
-
-               $protType = wfMsgHtml( 'restriction-level-' . $row->pr_level );
-
-               $description_items[] = $protType;
-
-               if ( $row->pr_cascade ) {
-                       $description_items[] = wfMsg( 'protect-summary-cascade' );
-               }
-
-               $expiry_description = '';
-               $stxt = '';
-
-               if ( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) {
-                       $expiry = Block::decodeExpiry( $row->pr_expiry );
-
-                       $expiry_description = wfMsgForContent( 'protect-expiring', $wgLang->timeanddate( $expiry ) );
-
-                       $description_items[] = $expiry_description;
-               }
-
-               if (!is_null($size = $row->page_len)) {
-                       if ($size == 0)
-                               $stxt = ' <small>' . wfMsgHtml('historyempty') . '</small>';
-                       else
-                               $stxt = ' <small>' . wfMsgHtml('historysize', $wgLang->formatNum( $size ) ) . '</small>';
-                       $stxt = $wgContLang->getDirMark() . $stxt;
-               }
-
-               # Show a link to the change protection form for allowed users otherwise a link to the protection log
-               if( $wgUser->isAllowed( 'protect' ) ) {
-                       $changeProtection = ' (' . $skin->makeKnownLinkObj( $title, wfMsgHtml( 'protect_change' ), 'action=unprotect' ) . ')';
-               } else {
-                       $ltitle = SpecialPage::getTitleFor( 'Log' );
-                       $changeProtection = ' (' . $skin->makeKnownLinkObj( $ltitle, wfMsgHtml( 'protectlogpage' ), 'type=protect&page=' . $title->getPrefixedUrl() ) . ')';
-               }
-
-               wfProfileOut( __METHOD__ );
-
-               return '<li>' . wfSpecialList( $link . $stxt, implode( $description_items, ', ' ) ) . $changeProtection . "</li>\n";
-       }
-
-       /**
-        * @param $namespace int
-        * @param $type string
-        * @param $level string
-        * @param $minsize int
-        * @param $indefOnly bool
-        * @return string Input form
-        * @private
-        */
-       protected function showOptions( $namespace, $type='edit', $level, $sizetype, $size, $indefOnly ) {
-               global $wgScript;
-               $title = SpecialPage::getTitleFor( 'ProtectedPages' );
-               return Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', array(), wfMsg( 'protectedpages' ) ) .
-                       Xml::hidden( 'title', $title->getPrefixedDBkey() ) . "&nbsp;\n" .
-                       $this->getNamespaceMenu( $namespace ) . "&nbsp;\n" .
-                       $this->getTypeMenu( $type ) . "&nbsp;\n" .
-                       $this->getLevelMenu( $level ) . "&nbsp;\n" .
-                       "<br /><span style='white-space: nowrap'>&nbsp;&nbsp;" .
-                       $this->getExpiryCheck( $indefOnly ) . "&nbsp;\n" .
-                       $this->getSizeLimit( $sizetype, $size ) . "&nbsp;\n" .
-                       "</span>" .
-                       "&nbsp;" . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' );
-       }
-
-       /**
-        * Prepare the namespace filter drop-down; standard namespace
-        * selector, sans the MediaWiki namespace
-        *
-        * @param mixed $namespace Pre-select namespace
-        * @return string
-        */
-       protected function getNamespaceMenu( $namespace = null ) {
-               return "<span style='white-space: nowrap'>" .
-                       Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;'
-                       . Xml::namespaceSelector( $namespace, '' ) . "</span>";
-       }
-
-       /**
-        * @return string Formatted HTML
-        */
-       protected function getExpiryCheck( $indefOnly ) {
-               return
-                       Xml::checkLabel( wfMsg('protectedpages-indef'), 'indefonly', 'indefonly', $indefOnly ) . "\n";
-       }
-
-       /**
-        * @return string Formatted HTML
-        */
-       protected function getSizeLimit( $sizetype, $size ) {
-               $max = $sizetype === 'max';
-
-               return
-                       Xml::radioLabel( wfMsg('minimum-size'), 'sizetype', 'min', 'wpmin', !$max ) .
-                       '&nbsp;' .
-                       Xml::radioLabel( wfMsg('maximum-size'), 'sizetype', 'max', 'wpmax', $max ) .
-                       '&nbsp;' .
-                       Xml::input( 'size', 9, $size, array( 'id' => 'wpsize' ) ) .
-                       '&nbsp;' .
-                       Xml::label( wfMsg('pagesize'), 'wpsize' );
-       }
-
-       /**
-        * @return string Formatted HTML
-        */
-       protected function getTypeMenu( $pr_type ) {
-               global $wgRestrictionTypes;
-
-               $m = array(); // Temporary array
-               $options = array();
-
-               // First pass to load the log names
-               foreach( $wgRestrictionTypes as $type ) {
-                       $text = wfMsg("restriction-$type");
-                       $m[$text] = $type;
-               }
-
-               // Third pass generates sorted XHTML content
-               foreach( $m as $text => $type ) {
-                       $selected = ($type == $pr_type );
-                       $options[] = Xml::option( $text, $type, $selected ) . "\n";
-               }
-
-               return "<span style='white-space: nowrap'>" .
-                       Xml::label( wfMsg('restriction-type') , $this->IdType ) . '&nbsp;' .
-                       Xml::tags( 'select',
-                               array( 'id' => $this->IdType, 'name' => $this->IdType ),
-                               implode( "\n", $options ) ) . "</span>";
-       }
-
-       /**
-        * @return string Formatted HTML
-        */
-       protected function getLevelMenu( $pr_level ) {
-               global $wgRestrictionLevels;
-
-               $m = array( wfMsg('restriction-level-all') => 0 ); // Temporary array
-               $options = array();
-
-               // First pass to load the log names
-               foreach( $wgRestrictionLevels as $type ) {
-                       if ( $type !='' && $type !='*') {
-                               $text = wfMsg("restriction-level-$type");
-                               $m[$text] = $type;
-                       }
-               }
-
-               // Third pass generates sorted XHTML content
-               foreach( $m as $text => $type ) {
-                       $selected = ($type == $pr_level );
-                       $options[] = Xml::option( $text, $type, $selected );
-               }
-
-               return
-                       Xml::label( wfMsg('restriction-level') , $this->IdLevel ) . '&nbsp;' .
-                       Xml::tags( 'select',
-                               array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ),
-                               implode( "\n", $options ) );
-       }
-}
-
-/**
- * @todo document
- * @ingroup Pager
- */
-class ProtectedPagesPager extends AlphabeticPager {
-       public $mForm, $mConds;
-       private $type, $level, $namespace, $sizetype, $size, $indefonly;
-
-       function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0, $indefonly=false ) {
-               $this->mForm = $form;
-               $this->mConds = $conds;
-               $this->type = ( $type ) ? $type : 'edit';
-               $this->level = $level;
-               $this->namespace = $namespace;
-               $this->sizetype = $sizetype;
-               $this->size = intval($size);
-               $this->indefonly = (bool)$indefonly;
-               parent::__construct();
-       }
-
-       function getStartBody() {
-               wfProfileIn( __METHOD__ );
-               # Do a link batch query
-               $lb = new LinkBatch;
-               while( $row = $this->mResult->fetchObject() ) {
-                       $lb->add( $row->page_namespace, $row->page_title );
-               }
-               $lb->execute();
-
-               wfProfileOut( __METHOD__ );
-               return '';
-       }
-
-       function formatRow( $row ) {
-               return $this->mForm->formatRow( $row );
-       }
-
-       function getQueryInfo() {
-               $conds = $this->mConds;
-               $conds[] = 'pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
-               $conds[] = 'page_id=pr_page';
-               $conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type );
-
-               if( $this->sizetype=='min' ) {
-                       $conds[] = 'page_len>=' . $this->size;
-               } else if( $this->sizetype=='max' ) {
-                       $conds[] = 'page_len<=' . $this->size;
-               }
-
-               if( $this->indefonly ) {
-                       $conds[] = "pr_expiry = 'infinity' OR pr_expiry IS NULL";
-               }
-
-               if( $this->level )
-                       $conds[] = 'pr_level=' . $this->mDb->addQuotes( $this->level );
-               if( !is_null($this->namespace) )
-                       $conds[] = 'page_namespace=' . $this->mDb->addQuotes( $this->namespace );
-               return array(
-                       'tables' => array( 'page_restrictions', 'page' ),
-                       'fields' => 'pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry,pr_cascade',
-                       'conds' => $conds
-               );
-       }
-
-       function getIndexField() {
-               return 'pr_id';
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialProtectedpages() {
-
-       $ppForm = new ProtectedPagesForm();
-
-       $ppForm->showList();
-}
diff --git a/includes/specials/Protectedtitles.php b/includes/specials/Protectedtitles.php
deleted file mode 100644 (file)
index 2ec68a6..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @todo document
- * @ingroup SpecialPage
- */
-class ProtectedTitlesForm {
-
-       protected $IdLevel = 'level';
-       protected $IdType  = 'type';
-
-       function showList( $msg = '' ) {
-               global $wgOut, $wgRequest;
-
-               $wgOut->setPagetitle( wfMsg( "protectedtitles" ) );
-               if ( "" != $msg ) {
-                       $wgOut->setSubtitle( $msg );
-               }
-
-               // Purge expired entries on one in every 10 queries
-               if ( !mt_rand( 0, 10 ) ) {
-                       Title::purgeExpiredRestrictions();
-               }
-
-               $type = $wgRequest->getVal( $this->IdType );
-               $level = $wgRequest->getVal( $this->IdLevel );
-               $sizetype = $wgRequest->getVal( 'sizetype' );
-               $size = $wgRequest->getIntOrNull( 'size' );
-               $NS = $wgRequest->getIntOrNull( 'namespace' );
-
-               $pager = new ProtectedTitlesPager( $this, array(), $type, $level, $NS, $sizetype, $size );
-
-               $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size ) );
-
-               if ( $pager->getNumRows() ) {
-                       $s = $pager->getNavigationBar();
-                       $s .= "<ul>" .
-                               $pager->getBody() .
-                               "</ul>";
-                       $s .= $pager->getNavigationBar();
-               } else {
-                       $s = '<p>' . wfMsgHtml( 'protectedtitlesempty' ) . '</p>';
-               }
-               $wgOut->addHTML( $s );
-       }
-
-       /**
-        * Callback function to output a restriction
-        */
-       function formatRow( $row ) {
-               global $wgUser, $wgLang, $wgContLang;
-
-               wfProfileIn( __METHOD__ );
-
-               static $skin=null;
-
-               if( is_null( $skin ) )
-                       $skin = $wgUser->getSkin();
-
-               $title = Title::makeTitleSafe( $row->pt_namespace, $row->pt_title );
-               $link = $skin->makeLinkObj( $title );
-
-               $description_items = array ();
-
-               $protType = wfMsgHtml( 'restriction-level-' . $row->pt_create_perm );
-
-               $description_items[] = $protType;
-
-               $expiry_description = ''; $stxt = '';
-
-               if ( $row->pt_expiry != 'infinity' && strlen($row->pt_expiry) ) {
-                       $expiry = Block::decodeExpiry( $row->pt_expiry );
-
-                       $expiry_description = wfMsgForContent( 'protect-expiring', $wgLang->timeanddate( $expiry ) );
-
-                       $description_items[] = $expiry_description;
-               }
-
-               wfProfileOut( __METHOD__ );
-
-               return '<li>' . wfSpecialList( $link . $stxt, implode( $description_items, ', ' ) ) . "</li>\n";
-       }
-
-       /**
-        * @param $namespace int
-        * @param $type string
-        * @param $level string
-        * @param $minsize int
-        * @private
-        */
-       function showOptions( $namespace, $type='edit', $level, $sizetype, $size ) {
-               global $wgScript;
-               $action = htmlspecialchars( $wgScript );
-               $title = SpecialPage::getTitleFor( 'ProtectedTitles' );
-               $special = htmlspecialchars( $title->getPrefixedDBkey() );
-               return "<form action=\"$action\" method=\"get\">\n" .
-                       '<fieldset>' .
-                       Xml::element( 'legend', array(), wfMsg( 'protectedtitles' ) ) .
-                       Xml::hidden( 'title', $special ) . "&nbsp;\n" .
-                       $this->getNamespaceMenu( $namespace ) . "&nbsp;\n" .
-                       // $this->getLevelMenu( $level ) . "<br/>\n" .
-                       "&nbsp;" . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
-                       "</fieldset></form>";
-       }
-
-       /**
-        * Prepare the namespace filter drop-down; standard namespace
-        * selector, sans the MediaWiki namespace
-        *
-        * @param mixed $namespace Pre-select namespace
-        * @return string
-        */
-       function getNamespaceMenu( $namespace = null ) {
-               return Xml::label( wfMsg( 'namespace' ), 'namespace' )
-                       . '&nbsp;'
-                       . Xml::namespaceSelector( $namespace, '' );
-       }
-
-       /**
-        * @return string Formatted HTML
-        * @private
-        */
-       function getLevelMenu( $pr_level ) {
-               global $wgRestrictionLevels;
-
-               $m = array( wfMsg('restriction-level-all') => 0 ); // Temporary array
-               $options = array();
-
-               // First pass to load the log names
-               foreach( $wgRestrictionLevels as $type ) {
-                       if ( $type !='' && $type !='*') {
-                               $text = wfMsg("restriction-level-$type");
-                               $m[$text] = $type;
-                       }
-               }
-
-               // Third pass generates sorted XHTML content
-               foreach( $m as $text => $type ) {
-                       $selected = ($type == $pr_level );
-                       $options[] = Xml::option( $text, $type, $selected );
-               }
-
-               return
-                       Xml::label( wfMsg('restriction-level') , $this->IdLevel ) . '&nbsp;' .
-                       Xml::tags( 'select',
-                               array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ),
-                               implode( "\n", $options ) );
-       }
-}
-
-/**
- * @todo document
- * @ingroup Pager
- */
-class ProtectedtitlesPager extends AlphabeticPager {
-       public $mForm, $mConds;
-
-       function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0 ) {
-               $this->mForm = $form;
-               $this->mConds = $conds;
-               $this->level = $level;
-               $this->namespace = $namespace;
-               $this->size = intval($size);
-               parent::__construct();
-       }
-
-       function getStartBody() {
-               wfProfileIn( __METHOD__ );
-               # Do a link batch query
-               $this->mResult->seek( 0 );
-               $lb = new LinkBatch;
-
-               while ( $row = $this->mResult->fetchObject() ) {
-                       $lb->add( $row->pt_namespace, $row->pt_title );
-               }
-
-               $lb->execute();
-               wfProfileOut( __METHOD__ );
-               return '';
-       }
-
-       function formatRow( $row ) {
-               return $this->mForm->formatRow( $row );
-       }
-
-       function getQueryInfo() {
-               $conds = $this->mConds;
-               $conds[] = 'pt_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
-
-               if( !is_null($this->namespace) )
-                       $conds[] = 'pt_namespace=' . $this->mDb->addQuotes( $this->namespace );
-               return array(
-                       'tables' => 'protected_titles',
-                       'fields' => 'pt_namespace,pt_title,pt_create_perm,pt_expiry,pt_timestamp',
-                       'conds' => $conds
-               );
-       }
-
-       function getIndexField() {
-               return 'pt_timestamp';
-       }
-}
-
-/**
- * Constructor
- */
-function wfSpecialProtectedtitles() {
-
-       $ppForm = new ProtectedTitlesForm();
-
-       $ppForm->showList();
-}
diff --git a/includes/specials/Randompage.php b/includes/specials/Randompage.php
deleted file mode 100644 (file)
index 0e7ada1..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-
-/**
- * Special page to direct the user to a random page
- *
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
- * @license GNU General Public Licence 2.0 or later
- */
-class RandomPage extends SpecialPage {
-       private $namespace = NS_MAIN;  // namespace to select pages from
-
-       function __construct( $name = 'Randompage' ){
-               parent::__construct( $name );
-       }
-
-       public function getNamespace() {
-               return $this->namespace;
-       }
-
-       public function setNamespace ( $ns ) {
-               if( $ns < NS_MAIN ) $ns = NS_MAIN;
-               $this->namespace = $ns;
-       }
-
-       // select redirects instead of normal pages?
-       // Overriden by SpecialRandomredirect
-       public function isRedirect(){
-               return false;
-       }
-
-       public function execute( $par ) {
-               global $wgOut, $wgContLang;
-
-               if ($par)
-                       $this->setNamespace( $wgContLang->getNsIndex( $par ) );
-
-               $title = $this->getRandomTitle();
-
-               if( is_null( $title ) ) {
-                       $this->setHeaders();
-                       $wgOut->addWikiMsg( strtolower( $this->mName ) . '-nopages' );
-                       return;
-               }
-
-               $query = $this->isRedirect() ? 'redirect=no' : '';
-               $wgOut->redirect( $title->getFullUrl( $query ) );
-       }
-
-
-       /**
-        * Choose a random title.
-        * @return Title object (or null if nothing to choose from)
-        */
-       public function getRandomTitle() {
-               $randstr = wfRandom();
-               $row = $this->selectRandomPageFromDB( $randstr );
-
-               /* If we picked a value that was higher than any in
-                * the DB, wrap around and select the page with the
-                * lowest value instead!  One might think this would
-                * skew the distribution, but in fact it won't cause
-                * any more bias than what the page_random scheme
-                * causes anyway.  Trust me, I'm a mathematician. :)
-                */
-               if( !$row )
-                       $row = $this->selectRandomPageFromDB( "0" );
-
-               if( $row )
-                       return Title::makeTitleSafe( $this->namespace, $row->page_title );
-               else
-                       return null;
-       }
-
-       private function selectRandomPageFromDB( $randstr ) {
-               global $wgExtraRandompageSQL;
-               $fname = 'RandomPage::selectRandomPageFromDB';
-
-               $dbr = wfGetDB( DB_SLAVE );
-
-               $use_index = $dbr->useIndexClause( 'page_random' );
-               $page = $dbr->tableName( 'page' );
-
-               $ns = (int) $this->namespace;
-               $redirect = $this->isRedirect() ? 1 : 0;
-
-               $extra = $wgExtraRandompageSQL ? "AND ($wgExtraRandompageSQL)" : "";
-               $sql = "SELECT page_title
-                       FROM $page $use_index
-                       WHERE page_namespace = $ns
-                       AND page_is_redirect = $redirect
-                       AND page_random >= $randstr
-                       $extra
-                       ORDER BY page_random";
-
-               $sql = $dbr->limitResult( $sql, 1, 0 );
-               $res = $dbr->query( $sql, $fname );
-               return $dbr->fetchObject( $res );
-       }
-}
diff --git a/includes/specials/Randomredirect.php b/includes/specials/Randomredirect.php
deleted file mode 100644 (file)
index 629d5b3..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-/**
- * Special page to direct the user to a random redirect page (minus the second redirect)
- *
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
- * @license GNU General Public Licence 2.0 or later
- */
-class SpecialRandomredirect extends RandomPage {
-       function __construct(){
-               parent::__construct( 'Randomredirect' );
-       }
-
-       // Override parent::isRedirect()
-       public function isRedirect(){
-               return true;
-       }
-}
diff --git a/includes/specials/Recentchanges.php b/includes/specials/Recentchanges.php
deleted file mode 100644 (file)
index 16904b9..0000000
+++ /dev/null
@@ -1,613 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-class SpecialRecentChanges extends SpecialPage {
-       public function __construct() {
-       SpecialPage::SpecialPage( 'Recentchanges' );
-               $this->includable( true );
-       }
-
-       public function getDefaultOptions() {
-               $opts = new FormOptions();
-
-               $opts->add( 'days',  (int)User::getDefaultOption( 'rcdays' ) );
-               $opts->add( 'limit', (int)User::getDefaultOption( 'rclimit' ) );
-               $opts->add( 'from', '' );
-
-               $opts->add( 'hideminor',     false );
-               $opts->add( 'hidebots',      true  );
-               $opts->add( 'hideanons',     false );
-               $opts->add( 'hideliu',       false );
-               $opts->add( 'hidepatrolled', false );
-               $opts->add( 'hidemyself',    false );
-
-               $opts->add( 'namespace', '', FormOptions::INTNULL );
-               $opts->add( 'invert', false );
-
-               $opts->add( 'categories', '' );
-               $opts->add( 'categories_any', false );
-
-               return $opts;
-}
-
-       public function setup( $parameters ) {
-               global $wgUser, $wgRequest;
-
-               $opts = $this->getDefaultOptions();
-               $opts['days'] = (int)$wgUser->getOption( 'rcdays', $opts['days'] );
-               $opts['limit'] = (int)$wgUser->getOption( 'rclimit', $opts['limit'] );
-               $opts['hideminor'] = $wgUser->getOption( 'hideminor', $opts['hideminor'] );
-               $opts->fetchValuesFromRequest( $wgRequest );
-
-               // Give precedence to subpage syntax
-               if ( $parameters !== null ) {
-                       $this->parseParameters( $parameters, $opts );
-               }
-
-               $opts->validateIntBounds( 'limit', 0, 5000 );
-               return $opts;
-       }
-
-       public function feedSetup() {
-               global $wgFeedLimit, $wgRequest;
-               $opts = $this->getDefaultOptions();
-               $opts->fetchValuesFromRequest( $wgRequest, array( 'days', 'limit', 'hideminor' ) );
-               $opts->validateIntBounds( 'limit', 0, $wgFeedLimit );
-               return $opts;
-       }
-
-       public function execute( $parameters ) {
-               global $wgRequest, $wgOut;
-               $feedFormat = $wgRequest->getVal( 'feed' );
-
-               # 10 seconds server-side caching max
-               $wgOut->setSquidMaxage( 10 );
-
-               $lastmod = $this->checkLastModified( $feedFormat );
-               if( $lastmod === false ){
-                       return;
-               }
-
-               $opts = $feedFormat ? $this->feedSetup() : $this->setup( $parameters );
-               $this->setHeaders();
-
-               // Fetch results, prepare a batch link existence check query
-               $rows = array();
-               $batch = new LinkBatch;
-               $conds = $this->buildMainQueryConds( $opts );
-               $res = $this->doMainQuery( $conds, $opts );
-               $dbr = wfGetDB( DB_SLAVE );
-               while( $row = $dbr->fetchObject( $res ) ){
-                       $rows[] = $row;
-                       if ( !$feedFormat ) {
-                               // User page and talk links
-                               $batch->add( NS_USER, $row->rc_user_text  );
-                               $batch->add( NS_USER_TALK, $row->rc_user_text  );
-                       }
-
-               }
-               $dbr->freeResult( $res );
-
-               if ( $feedFormat ) {
-                       $feed = new ChangesFeed( $feedFormat, 'rcfeed' );
-                       $feedObj = $feed->getFeedObject(
-                               wfMsgForContent( 'recentchanges' ),
-                               wfMsgForContent( 'recentchanges-feed-description' )
-                       );
-                       $feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod );
-               } else {
-                       $batch->execute();
-                       $this->webOutput( $rows, $opts );
-               }
-       
-       }
-
-       public function parseParameters( $par, FormOptions $opts ) {
-               $bits = preg_split( '/\s*,\s*/', trim( $par ) );
-               foreach ( $bits as $bit ) {
-                       if ( 'hidebots' === $bit ) $opts['hidebots'] = true;
-                       if ( 'bots' === $bit ) $opts['hidebots'] = false;
-                       if ( 'hideminor' === $bit ) $opts['hideminor'] = true;
-                       if ( 'minor' === $bit ) $opts['hideminor'] = false;
-                       if ( 'hideliu' === $bit ) $opts['hideliu'] = true;
-                       if ( 'hidepatrolled' === $bit ) $opts['hidepatrolled'] = true;
-                       if ( 'hideanons' === $bit ) $opts['hideanons'] = true;
-                       if ( 'hidemyself' === $bit ) $opts['hidemyself'] = true;
-
-                       if ( is_numeric( $bit ) ) $opts['limit'] =  $bit;
-
-                       $m = array();
-                       if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) $opts['limit'] = $m[1];
-                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) $opts['days'] = $m[1];
-               }
-       }
-
-       # Get last modified date, for client caching
-       # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp
-       public function checkLastModified( $feedFormat ) {
-               global $wgUseRCPatrol, $wgOut;
-               $dbr = wfGetDB( DB_SLAVE );
-               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __FUNCTION__ );
-               if ( $feedFormat || !$wgUseRCPatrol ) {
-                       if( $lastmod && $wgOut->checkLastModified( $lastmod ) ){
-                               # Client cache fresh and headers sent, nothing more to do.
-                               return false;
-                       }
-               }
-               return $lastmod;
-       }
-
-       public function buildMainQueryConds( FormOptions $opts ) {
-               global $wgUser;
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $conds = array();
-
-               # It makes no sense to hide both anons and logged-in users
-               # Where this occurs, force anons to be shown
-               $forcebot = false;
-               if( $opts['hideanons'] && $opts['hideliu'] ){
-                       # Check if the user wants to show bots only
-                       if( $opts['hidebots'] ){
-                               $opts['hideanons'] = false;
-                       } else {
-                               $forcebot = true;
-                               $opts['hidebots'] = false;
-                       }
-               }
-
-               // Calculate cutoff
-               $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
-               $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400);
-               $cutoff = $dbr->timestamp( $cutoff_unixtime );
-
-               $fromValid = preg_match('/^[0-9]{14}$/', $opts['from']);
-               if( $fromValid && $opts['from'] > wfTimestamp(TS_MW,$cutoff) ) {
-                       $cutoff = $dbr->timestamp($opts['from']);
-               } else {
-                       $opts->reset( 'from' );
-               }
-
-               $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff );
-
-
-               $hidePatrol = $wgUser->useRCPatrol() && $opts['hidepatrolled'];
-               $hideLoggedInUsers = $opts['hideliu'] && !$forcebot;
-               $hideAnonymousUsers = $opts['hideanons'] && !$forcebot;
-
-               if ( $opts['hideminor'] )  $conds['rc_minor'] = 0;
-               if ( $opts['hidebots'] )   $conds['rc_bot'] = 0;
-               if ( $hidePatrol )         $conds['rc_patrolled'] = 0;
-               if ( $forcebot )           $conds['rc_bot'] = 1;
-               if ( $hideLoggedInUsers )  $conds[] = 'rc_user = 0';
-               if ( $hideAnonymousUsers ) $conds[] = 'rc_user != 0';
-
-               if( $opts['hidemyself'] ) {
-                       if( $wgUser->getId() ) {
-                               $conds[] = 'rc_user != ' . $dbr->addQuotes( $wgUser->getId() );
-                       } else {
-                               $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $wgUser->getName() );
-                       }
-               }
-               
-               # Namespace filtering
-               if ( $opts['namespace'] !== '' ) {
-                       if ( !$opts['invert'] ) {
-                               $conds[] = 'rc_namespace = ' . $dbr->addQuotes( $opts['namespace'] );
-                       } else {
-                               $conds[] = 'rc_namespace != ' . $dbr->addQuotes( $opts['namespace'] );
-                       }
-               }
-
-               return $conds;
-       }
-
-       public function doMainQuery( $conds, $opts ) {
-               global $wgUser;
-
-               $tables = array( 'recentchanges' );
-               $join_conds = array();
-
-               $uid = $wgUser->getId();
-               $dbr = wfGetDB( DB_SLAVE );
-               $limit = $opts['limit'];
-               $namespace = $opts['namespace'];
-               $invert = $opts['invert'];
-
-               // JOIN on watchlist for users
-               if( $wgUser->getId() ) {
-                       $tables[] = 'watchlist';
-                       $join_conds = array( 'watchlist' => array('LEFT JOIN',"wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") );
-               }
-
-               wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) );
-
-               // Is there either one namespace selected or excluded?
-               // Also, if this is "all" or main namespace, just use timestamp index.
-               if( is_null($namespace) || $invert || $namespace == NS_MAIN ) {
-                       $res = $dbr->select( $tables, '*', $conds, __METHOD__,
-                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
-                                       'USE INDEX' => array('recentchanges' => 'rc_timestamp') ),
-                               $join_conds );
-               // We have a new_namespace_time index! UNION over new=(0,1) and sort result set!
-               } else {
-                       // New pages
-                       $sqlNew = $dbr->selectSQLText( $tables, '*',
-                               array( 'rc_new' => 1 ) + $conds,
-                               __METHOD__,
-                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
-                                       'USE INDEX' =>  array('recentchanges' => 'new_name_timestamp') ),
-                               $join_conds );
-                       // Old pages
-                       $sqlOld = $dbr->selectSQLText( $tables, '*',
-                               array( 'rc_new' => 0 ) + $conds,
-                               __METHOD__,
-                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
-                                       'USE INDEX' =>  array('recentchanges' => 'new_name_timestamp') ),
-                               $join_conds );
-                       # Join the two fast queries, and sort the result set
-                       $sql = "($sqlNew) UNION ($sqlOld) ORDER BY rc_timestamp DESC LIMIT $limit";
-                       $res = $dbr->query( $sql, __METHOD__ );
-               }
-
-               return $res;
-       }
-
-       public function webOutput( $rows, $opts ) {
-               global $wgOut, $wgUser, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
-               global $wgAllowCategorizedRecentChanges;
-
-               $limit = $opts['limit'];
-
-               if ( !$this->including() ) {
-                       // Output options box
-                       $this->doHeader( $opts );
-               }
-
-               // And now for the content
-               $wgOut->setSyndicated( true );
-
-               $list = ChangesList::newFromUser( $wgUser );
-
-               if ( $wgAllowCategorizedRecentChanges ) {
-                       rcFilterByCategories( $rows, $opts );
-               }
-
-               $s = $list->beginRecentChangesList();
-               $counter = 1;
-
-               $showWatcherCount = $wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' );
-               $watcherCache = array();
-
-               $dbr = wfGetDB( DB_SLAVE );
-
-               foreach( $rows as $obj ){
-                       if( $limit == 0) {
-                               break;
-                       }
-
-                       if ( ! ( $opts['hideminor']     && $obj->rc_minor     ) &&
-                            ! ( $opts['hidepatrolled'] && $obj->rc_patrolled ) ) {
-                               $rc = RecentChange::newFromRow( $obj );
-                               $rc->counter = $counter++;
-
-                               if ($wgShowUpdatedMarker
-                                       && !empty( $obj->wl_notificationtimestamp )
-                                       && ($obj->rc_timestamp >= $obj->wl_notificationtimestamp)) {
-                                               $rc->notificationtimestamp = true;
-                               } else {
-                                       $rc->notificationtimestamp = false;
-                               }
-
-                               $rc->numberofWatchingusers = 0; // Default
-                               if ($showWatcherCount && $obj->rc_namespace >= 0) {
-                                       if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
-                                               $watcherCache[$obj->rc_namespace][$obj->rc_title] =
-                                                       $dbr->selectField( 'watchlist',
-                                                               'COUNT(*)',
-                                                               array(
-                                                                       'wl_namespace' => $obj->rc_namespace,
-                                                                       'wl_title' => $obj->rc_title,
-                                                               ),
-                                                               __METHOD__ . '-watchers' );
-                                       }
-                                       $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
-                               }
-                               $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ) );
-                               --$limit;
-                       }
-               }
-               $s .= $list->endRecentChangesList();
-               $wgOut->addHTML( $s );
-       }
-
-       public function doHeader( $opts ) {
-               global $wgScript, $wgOut;
-               $wgOut->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) );
-
-               $defaults = $opts->getAllValues();
-               $nondefaults = $opts->getChangedValues();
-               $opts->consumeValues( array( 'namespace', 'invert' ) );
-
-               $panel = array();
-               $panel[] = rcOptionsPanel( $defaults, $nondefaults );
-               $panel[] = '<hr />';
-
-               $extraOpts = array();
-               $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
-
-               global $wgAllowCategorizedRecentChanges;
-               if ( $wgAllowCategorizedRecentChanges ) {
-                       $extraOpts['category'] = $this->categoryFilterForm( $opts );
-               }
-
-               wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
-               $extraOpts['submit'] = Xml::submitbutton( wfMsg('allpagessubmit') );
-
-               $out = Xml::openElement( 'table' );
-               foreach ( $extraOpts as $optionRow ) {
-                       $out .= Xml::openElement( 'tr' );
-                       if ( is_array($optionRow) ) {
-                               $out .= Xml::tags( 'td', null, $optionRow[0] );
-                               $out .= Xml::tags( 'td', null, $optionRow[1] );
-                       } else {
-                               $out .= Xml::tags( 'td', array( 'colspan' => 2 ), $optionRow );
-                       }
-                       $out .= Xml::closeElement( 'tr' );
-               }
-               $out .= Xml::closeElement( 'table' );
-
-               $unconsumed = $opts->getUnconsumedValues();
-               foreach ( $unconsumed as $key => $value ) {
-                       $out .= Xml::hidden( $key, $value );
-               }
-
-               $t = $this->getTitle();
-               $out .= Xml::hidden( 'title', $t->getPrefixedText() );
-               $form = Xml::tags( 'form', array( 'action' => $wgScript ), $out );
-               $panel[] = $form;
-               $panelString = implode( "\n", $panel );
-
-               $wgOut->addHTML(
-                       Xml::fieldset( wfMsg( 'recentchanges' ), $panelString, array( 'class' => 'rcoptions' ) )
-               );
-       }
-
-       /**
-       * Creates the choose namespace selection
-       *
-       * @return string
-       */
-       protected function namespaceFilterForm( FormOptions $opts ) {
-               $nsSelect = HTMLnamespaceselector( $opts['namespace'], '' );
-               $nsLabel = Xml::label( wfMsg('namespace'), 'namespace' );
-               $invert = Xml::checkLabel( wfMsg('invert'), 'invert', 'nsinvert', $opts['invert'] );
-               return array( $nsLabel, "$nsSelect $invert" );
-       }
-
-       protected function categoryFilterForm( FormOptions $opts ) {
-               list( $label, $input ) = Xml::inputLabelSep( wfMsg('rc_categories'),
-                       'categories', 'mw-categories', false, $opts['categories'] );
-
-               $input .= ' ' . Xml::checkLabel( wfMsg('rc_categories_any'),
-                       'categories_any', 'mw-categories_any', $opts['categories_any'] );
-
-               return array( $label, $input );
-       }
-
-}
-
-function rcFilterByCategories ( &$rows, FormOptions $opts ) {
-       $categories = array_map( 'trim', explode( "|" , $categories ) );
-
-       if( empty($categories) ) {
-               return;
-       }
-
-       # Filter categories
-       $cats = array();
-       foreach ( $opts['categories'] AS $cat ) {
-               $cat = trim( $cat );
-               if ( $cat == "" ) continue;
-               $cats[] = $cat;
-       }
-
-       # Filter articles
-       $articles = array();
-       $a2r = array();
-       foreach ( $rows AS $k => $r ) {
-               $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
-               $id = $nt->getArticleID();
-               if ( $id == 0 ) continue; # Page might have been deleted...
-               if ( !in_array($id, $articles) ) {
-                       $articles[] = $id;
-               }
-               if ( !isset($a2r[$id]) ) {
-                       $a2r[$id] = array();
-               }
-               $a2r[$id][] = $k;
-       }
-
-       # Shortcut?
-       if ( !count($articles) || !count($cats) )
-               return ;
-
-       # Look up
-       $c = new Categoryfinder ;
-       $c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ;
-       $match = $c->run();
-
-       # Filter
-       $newrows = array();
-       foreach ( $match AS $id ) {
-               foreach ( $a2r[$id] AS $rev ) {
-                       $k = $rev;
-                       $newrows[$k] = $rows[$k];
-               }
-       }
-       $rows = $newrows;
-}
-
-/**
- *
- */
-function rcCountLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) {
-       global $wgUser, $wgLang, $wgContLang;
-       $sk = $wgUser->getSkin();
-       $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ),
-         ($lim ? $wgLang->formatNum( "{$lim}" ) : wfMsg( 'recentchangesall' ) ), "{$more}" .
-         ($d ? "days={$d}&" : '') . 'limit='.$lim, '', '',
-         $active ? 'style="font-weight: bold;"' : '' );
-       return $s;
-}
-
-/**
- *
- */
-function rcDaysLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) {
-       global $wgUser, $wgLang, $wgContLang;
-       $sk = $wgUser->getSkin();
-       $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ),
-         ($d ? $wgLang->formatNum( "{$d}" ) : wfMsg( 'recentchangesall' ) ), $more.'days='.$d .
-         ($lim ? '&limit='.$lim : ''), '', '',
-         $active ? 'style="font-weight: bold;"' : '' );
-       return $s;
-}
-
-/**
- * Used by Recentchangeslinked
- */
-function rcDayLimitLinks( $days, $limit, $page='Recentchanges', $more='', $doall = false, $minorLink = '',
-       $botLink = '', $liuLink = '', $patrLink = '', $myselfLink = '' ) {
-       global $wgRCLinkLimits, $wgRCLinkDays;
-       if ($more != '') $more .= '&';
-       
-       # Sort data for display and make sure it's unique after we've added user data.
-       # FIXME: why does this piss around with globals like this? Why is $limit added on globally?
-       $wgRCLinkLimits[] = $limit;
-       $wgRCLinkDays[] = $days;
-       sort($wgRCLinkLimits);
-       sort($wgRCLinkDays);
-       $wgRCLinkLimits = array_unique($wgRCLinkLimits);
-       $wgRCLinkDays = array_unique($wgRCLinkDays);
-       
-       $cl = array();
-       foreach( $wgRCLinkLimits as $countLink ) {
-               $cl[] = rcCountLink( $countLink, $days, $page, $more, $countLink == $limit );
-       }
-       if( $doall ) $cl[] = rcCountLink( 0, $days, $page, $more );
-       $cl = implode( ' | ', $cl);
-       
-       $dl = array();
-       foreach( $wgRCLinkDays as $daysLink ) {
-               $dl[] = rcDaysLink( $limit, $daysLink, $page, $more, $daysLink == $days );
-       }
-       if( $doall ) $dl[] = rcDaysLink( $limit, 0, $page, $more );
-       $dl = implode( ' | ', $dl);
-       
-       $linkParts = array( 'minorLink' => 'minor', 'botLink' => 'bots', 'liuLink' => 'liu', 'patrLink' => 'patr', 'myselfLink' => 'mine' );
-       foreach( $linkParts as $linkVar => $linkMsg ) {
-               if( $$linkVar != '' )
-                       $links[] = wfMsgHtml( 'rcshowhide' . $linkMsg, $$linkVar );
-       }
-
-       $shm = implode( ' | ', $links );
-       $note = wfMsg( 'rclinks', $cl, $dl, $shm );
-       return $note;
-}
-
-
-/**
- * Makes change an option link which carries all the other options
- * @param $title see Title
- * @param $override
- * @param $options
- */
-function makeOptionsLink( $title, $override, $options, $active = false ) {
-       global $wgUser, $wgContLang;
-       $sk = $wgUser->getSkin();
-       return $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchanges' ),
-               htmlspecialchars( $title ), wfArrayToCGI( $override, $options ), '', '',
-               $active ? 'style="font-weight: bold;"' : '' );
-}
-
-/**
- * Creates the options panel.
- * @param $defaults
- * @param $nondefaults
- */
-function rcOptionsPanel( $defaults, $nondefaults ) {
-       global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays;
-
-       $options = $nondefaults + $defaults;
-
-       if( $options['from'] )
-               $note = wfMsgExt( 'rcnotefrom', array( 'parseinline' ),
-                       $wgLang->formatNum( $options['limit'] ),
-                       $wgLang->timeanddate( $options['from'], true ) );
-       else
-               $note = wfMsgExt( 'rcnote', array( 'parseinline' ),
-                       $wgLang->formatNum( $options['limit'] ),
-                       $wgLang->formatNum( $options['days'] ),
-                       $wgLang->timeAndDate( wfTimestampNow(), true ) );
-
-       # Sort data for display and make sure it's unique after we've added user data.
-       $wgRCLinkLimits[] = $options['limit'];
-       $wgRCLinkDays[] = $options['days'];
-       sort($wgRCLinkLimits);
-       sort($wgRCLinkDays);
-       $wgRCLinkLimits = array_unique($wgRCLinkLimits);
-       $wgRCLinkDays = array_unique($wgRCLinkDays);
-       
-       // limit links
-       foreach( $wgRCLinkLimits as $value ) {
-               $cl[] = makeOptionsLink( $wgLang->formatNum( $value ),
-                       array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ;
-       }
-       $cl = implode( ' | ', $cl);
-
-       // day links, reset 'from' to none
-       foreach( $wgRCLinkDays as $value ) {
-               $dl[] = makeOptionsLink( $wgLang->formatNum( $value ),
-                       array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ;
-       }
-       $dl = implode( ' | ', $dl);
-
-
-       // show/hide links
-       $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' ));
-       $minorLink = makeOptionsLink( $showhide[1-$options['hideminor']],
-               array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults);
-       $botLink = makeOptionsLink( $showhide[1-$options['hidebots']],
-               array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults);
-       $anonsLink = makeOptionsLink( $showhide[ 1 - $options['hideanons'] ],
-               array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults );
-       $liuLink   = makeOptionsLink( $showhide[1-$options['hideliu']],
-               array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults);
-       $patrLink  = makeOptionsLink( $showhide[1-$options['hidepatrolled']],
-               array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults);
-       $myselfLink = makeOptionsLink( $showhide[1-$options['hidemyself']],
-               array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults);
-
-       $links[] = wfMsgHtml( 'rcshowhideminor', $minorLink );
-       $links[] = wfMsgHtml( 'rcshowhidebots', $botLink );
-       $links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink );
-       $links[] = wfMsgHtml( 'rcshowhideliu', $liuLink );
-       if( $wgUser->useRCPatrol() )
-               $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink );
-       $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink );
-       $hl = implode( ' | ', $links );
-
-       // show from this onward link
-       $now = $wgLang->timeanddate( wfTimestampNow(), true );
-       $tl =  makeOptionsLink( $now, array( 'from' => wfTimestampNow()), $nondefaults );
-
-       $rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter'),
-               $cl, $dl, $hl );
-       $rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter'), $tl );
-       return "$note<br />$rclinks<br />$rclistfrom";
-
-}
\ No newline at end of file
diff --git a/includes/specials/Recentchangeslinked.php b/includes/specials/Recentchangeslinked.php
deleted file mode 100644 (file)
index 73d46f8..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-/**
- * This is to display changes made to all articles linked in an article.
- * @file
- * @ingroup SpecialPage
- */
-
-require_once( 'Recentchanges.php' );
-
-/**
- * Entrypoint
- * @param string $par parent page we will look at
- */
-function wfSpecialRecentchangeslinked( $par = NULL ) {
-       global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest, $wgTitle, $wgScript;
-
-       $days = $wgRequest->getInt( 'days' );
-       $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
-       $hideminor = $wgRequest->getBool( 'hideminor' ) ? 1 : 0;
-       $showlinkedto = $wgRequest->getBool( 'showlinkedto' ) ? 1 : 0;
-
-       $title = Title::newFromURL( $target );
-       $target = $title ? $title->getPrefixedText() : '';
-
-       $wgOut->setPagetitle( wfMsg( 'recentchangeslinked' ) );
-       $sk = $wgUser->getSkin();
-
-       $wgOut->addHTML(
-               Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
-               Xml::openElement( 'fieldset' ) .
-               Xml::element( 'legend', array(), wfMsg( 'recentchangeslinked' ) ) . "\n" .
-               Xml::inputLabel( wfMsg( 'recentchangeslinked-page' ), 'target', 'recentchangeslinked-target', 40, $target ) .
-               "&nbsp;&nbsp;&nbsp;<span style='white-space: nowrap'>" .
-               Xml::check( 'showlinkedto', $showlinkedto, array('id' => 'showlinkedto') ) . ' ' .
-               Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) .
-               "</span><br/>\n" .
-               Xml::hidden( 'title', $wgTitle->getPrefixedText() ). "\n" .
-               Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
-               Xml::closeElement( 'fieldset' ) .
-               Xml::closeElement( 'form' ) . "\n"
-       );
-
-       if ( !$target ) {
-               return;
-       }
-       $nt = Title::newFromURL( $target );
-       if( !$nt ) {
-               $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
-               return;
-       }
-       $id = $nt->getArticleId();
-
-       $wgOut->setPageTitle( wfMsg( 'recentchangeslinked-title', $nt->getPrefixedText() ) );
-       $wgOut->setSyndicated();
-       $wgOut->setFeedAppendQuery( "target=" . urlencode( $target ) );
-
-       if ( !$days ) {
-               $days = (int)$wgUser->getOption( 'rcdays', 7 );
-       }
-       list( $limit, /* offset */ ) = wfCheckLimits( 100, 'rclimit' );
-
-       $dbr = wfGetDB( DB_SLAVE,'recentchangeslinked' );
-       $cutoff = $dbr->timestamp( time() - ( $days * 86400 ) );
-
-       $hideminor = ($hideminor ? 1 : 0);
-       if ( $hideminor ) {
-               $mlink = $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchangeslinked' ),
-                 wfMsg( 'show' ), 'target=' . htmlspecialchars( $nt->getPrefixedURL() ) .
-                 "&days={$days}&limit={$limit}&hideminor=0&showlinkedto={$showlinkedto}" );
-       } else {
-               $mlink = $sk->makeKnownLink( $wgContLang->specialPage( "Recentchangeslinked" ),
-                 wfMsg( "hide" ), "target=" . htmlspecialchars( $nt->getPrefixedURL() ) .
-                 "&days={$days}&limit={$limit}&hideminor=1&showlinkedto={$showlinkedto}" );
-       }
-       if ( $hideminor ) {
-               $cmq = 'AND rc_minor=0';
-       } else { $cmq = ''; }
-
-       list($recentchanges, $categorylinks, $pagelinks, $watchlist) =
-           $dbr->tableNamesN( 'recentchanges', 'categorylinks', 'pagelinks', "watchlist" );
-
-       $uid = $wgUser->getId();
-       // The fields we are selecting
-       $fields = "rc_cur_id,rc_namespace,rc_title,
-               rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_log_type,rc_log_action,rc_params,rc_deleted,
-               rc_new, rc_id, rc_this_oldid, rc_last_oldid, rc_bot, rc_patrolled, rc_type, rc_old_len, rc_new_len";
-       $fields .= $uid ? ",wl_user" : "";
-
-       // Check if this should be a feed
-
-       $feed = false;
-       global $wgFeedLimit;
-       $feedFormat = $wgRequest->getVal( 'feed' );
-       if( $feedFormat ) {
-               $feed = new ChangesFeed( $feedFormat, false );
-               # Sanity check
-               if( $limit > $wgFeedLimit ) {
-                       $limit = $wgFeedLimit;
-               }
-       }
-
-       // If target is a Category, use categorylinks and invert from and to
-       if( $nt->getNamespace() == NS_CATEGORY ) {
-               $catkey = $dbr->addQuotes( $nt->getDBkey() );
-               # The table clauses
-               $tables = "$categorylinks, $recentchanges";
-               $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "";
-
-               $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables 
-                       WHERE rc_timestamp > '{$cutoff}' {$cmq} 
-                       AND cl_from=rc_cur_id 
-                       AND cl_to=$catkey 
-                       GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently
-       } else {
-               if( $showlinkedto ) {
-                       $ns = $dbr->addQuotes( $nt->getNamespace() );
-                       $dbkey = $dbr->addQuotes( $nt->getDBkey() );
-                       $joinConds = "AND pl_namespace={$ns} AND pl_title={$dbkey} AND pl_from=rc_cur_id";
-               } else {
-                       $joinConds = "AND pl_namespace=rc_namespace AND pl_title=rc_title AND pl_from=$id";
-               }
-               # The table clauses
-               $tables = "$pagelinks, $recentchanges";
-               $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "";
-
-               $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables
-                       WHERE rc_timestamp > '{$cutoff}' {$cmq}
-                       {$joinConds}
-                       GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently
-       }
-       # Actually do the query
-       $res = $dbr->query( $sql, __METHOD__ );
-       $count = $dbr->numRows( $res );
-       $rchanges = array();
-       # Output feeds now and be done with it!
-       if( $feed ) {
-               if( $count ) {
-                       $counter = 1;
-                       while ( $limit ) {
-                               if ( 0 == $count ) { break; }
-                               $obj = $dbr->fetchObject( $res );
-                               --$count;
-                               $rc = RecentChange::newFromRow( $obj );
-                               $rc->counter = $counter++;
-                               --$limit;
-                               $rchanges[] = $obj;
-                       }
-               }
-               $wgOut->disable();
-
-               $feedObj = $feed->getFeedObject(
-                       wfMsgForContent( 'recentchangeslinked-title', $nt->getPrefixedText() ),
-                       wfMsgForContent( 'recentchangeslinked' )
-               );
-               ChangesFeed::generateFeed( $rchanges, $feedObj );
-               return;
-       }
-       
-       # Otherwise, carry on with regular output...
-       $wgOut->addHTML("&lt; ".$sk->makeLinkObj($nt, "", "redirect=no" )."<br />\n");
-       $note = wfMsgExt( "rcnote", array ( 'parseinline' ), $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) );
-       $wgOut->addHTML( "<hr />\n{$note}\n<br />" );
-
-       $note = rcDayLimitlinks( $days, $limit, "Recentchangeslinked",
-                                "target=" . $nt->getPrefixedURL() . "&hideminor={$hideminor}&showlinkedto={$showlinkedto}",
-                                false, $mlink );
-
-       $wgOut->addHTML( $note."\n" );
-
-       $list = ChangesList::newFromUser( $wgUser );
-       $s = $list->beginRecentChangesList();
-
-       if ( $count ) {
-               $counter = 1;
-               while ( $limit ) {
-                       if ( 0 == $count ) { break; }
-                       $obj = $dbr->fetchObject( $res );
-                       --$count;
-                       $rc = RecentChange::newFromRow( $obj );
-                       $rc->counter = $counter++;
-                       $s .= $list->recentChangesLine( $rc , !empty( $obj->wl_user) );
-                       --$limit;
-               }
-       } else {
-               $wgOut->addWikiMsg('recentchangeslinked-noresult');
-       }
-       $s .= $list->endRecentChangesList();
-
-       $dbr->freeResult( $res );
-       $wgOut->addHTML( $s );
-}
diff --git a/includes/specials/Resetpass.php b/includes/specials/Resetpass.php
deleted file mode 100644 (file)
index 707b941..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/** Constructor */
-function wfSpecialResetpass( $par ) {
-       $form = new PasswordResetForm();
-       $form->execute( $par );
-}
-
-/**
- * Let users recover their password.
- * @ingroup SpecialPage
- */
-class PasswordResetForm extends SpecialPage {
-       function __construct( $name=null, $reset=null ) {
-               if( $name !== null ) {
-                       $this->mName = $name;
-                       $this->mTemporaryPassword = $reset;
-               } else {
-                       global $wgRequest;
-                       $this->mName = $wgRequest->getVal( 'wpName' );
-                       $this->mTemporaryPassword = $wgRequest->getVal( 'wpPassword' );
-               }
-       }
-
-       /**
-        * Main execution point
-        */
-       function execute( $par ) {
-               global $wgUser, $wgAuth, $wgOut, $wgRequest;
-
-               if( !$wgAuth->allowPasswordChange() ) {
-                       $this->error( wfMsg( 'resetpass_forbidden' ) );
-                       return;
-               }
-
-               if( $this->mName === null && !$wgRequest->wasPosted() ) {
-                       $this->error( wfMsg( 'resetpass_missing' ) );
-                       return;
-               }
-
-               if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'token' ) ) ) {
-                       $newpass = $wgRequest->getVal( 'wpNewPassword' );
-                       $retype = $wgRequest->getVal( 'wpRetype' );
-                       try {
-                               $this->attemptReset( $newpass, $retype );
-                               $wgOut->addWikiMsg( 'resetpass_success' );
-
-                               $data = array(
-                                       'action' => 'submitlogin',
-                                       'wpName' => $this->mName,
-                                       'wpPassword' => $newpass,
-                                       'returnto' => $wgRequest->getVal( 'returnto' ),
-                               );
-                               if( $wgRequest->getCheck( 'wpRemember' ) ) {
-                                       $data['wpRemember'] = 1;
-                               }
-                               $login = new LoginForm( new FauxRequest( $data, true ) );
-                               $login->execute();
-
-                               return;
-                       } catch( PasswordError $e ) {
-                               $this->error( $e->getMessage() );
-                       }
-               }
-               $this->showForm();
-       }
-
-       function error( $msg ) {
-               global $wgOut;
-               $wgOut->addHtml( '<div class="errorbox">' .
-                       htmlspecialchars( $msg ) .
-                       '</div>' );
-       }
-
-       function showForm() {
-               global $wgOut, $wgUser, $wgRequest;
-
-               $wgOut->disallowUserJs();
-
-               $self = SpecialPage::getTitleFor( 'Resetpass' );
-               $form  =
-                       '<div id="userloginForm">' .
-                       wfOpenElement( 'form',
-                               array(
-                                       'method' => 'post',
-                                       'action' => $self->getLocalUrl() ) ) .
-                       '<h2>' . wfMsgHtml( 'resetpass_header' ) . '</h2>' .
-                       '<div id="userloginprompt">' .
-                       wfMsgExt( 'resetpass_text', array( 'parse' ) ) .
-                       '</div>' .
-                       '<table>' .
-                       wfHidden( 'token', $wgUser->editToken() ) .
-                       wfHidden( 'wpName', $this->mName ) .
-                       wfHidden( 'wpPassword', $this->mTemporaryPassword ) .
-                       wfHidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) .
-                       $this->pretty( array(
-                               array( 'wpName', 'username', 'text', $this->mName ),
-                               array( 'wpNewPassword', 'newpassword', 'password', '' ),
-                               array( 'wpRetype', 'yourpasswordagain', 'password', '' ),
-                       ) ) .
-                       '<tr>' .
-                               '<td></td>' .
-                               '<td>' .
-                                       Xml::checkLabel( wfMsg( 'remembermypassword' ),
-                                               'wpRemember', 'wpRemember',
-                                               $wgRequest->getCheck( 'wpRemember' ) ) .
-                               '</td>' .
-                       '</tr>' .
-                       '<tr>' .
-                               '<td></td>' .
-                               '<td>' .
-                                       wfSubmitButton( wfMsgHtml( 'resetpass_submit' ) ) .
-                               '</td>' .
-                       '</tr>' .
-                       '</table>' .
-                       wfCloseElement( 'form' ) .
-                       '</div>';
-               $wgOut->addHtml( $form );
-       }
-
-       function pretty( $fields ) {
-               $out = '';
-               foreach( $fields as $list ) {
-                       list( $name, $label, $type, $value ) = $list;
-                       if( $type == 'text' ) {
-                               $field = '<tt>' . htmlspecialchars( $value ) . '</tt>';
-                       } else {
-                               $field = Xml::input( $name, 20, $value,
-                                       array( 'id' => $name, 'type' => $type ) );
-                       }
-                       $out .= '<tr>';
-                       $out .= '<td align="right">';
-                       $out .= Xml::label( wfMsg( $label ), $name );
-                       $out .= '</td>';
-                       $out .= '<td>';
-                       $out .= $field;
-                       $out .= '</td>';
-                       $out .= '</tr>';
-               }
-               return $out;
-       }
-
-       /**
-        * @throws PasswordError when cannot set the new password because requirements not met.
-        */
-       function attemptReset( $newpass, $retype ) {
-               $user = User::newFromName( $this->mName );
-               if( $user->isAnon() ) {
-                       throw new PasswordError( 'no such user' );
-               }
-
-               if( !$user->checkTemporaryPassword( $this->mTemporaryPassword ) ) {
-                       throw new PasswordError( wfMsg( 'resetpass_bad_temporary' ) );
-               }
-
-               if( $newpass !== $retype ) {
-                       throw new PasswordError( wfMsg( 'badretype' ) );
-               }
-
-               $user->setPassword( $newpass );
-               $user->saveSettings();
-       }
-}
diff --git a/includes/specials/Revisiondelete.php b/includes/specials/Revisiondelete.php
deleted file mode 100644 (file)
index a3d4b7e..0000000
+++ /dev/null
@@ -1,1470 +0,0 @@
-<?php
-/**
- * Special page allowing users with the appropriate permissions to view
- * and hide revisions. Log items can also be hidden.
- *
- * @file
- * @ingroup SpecialPage
- */
-
-function wfSpecialRevisiondelete( $par = null ) {
-       global $wgOut, $wgRequest, $wgUser;
-       # Handle our many different possible input types
-       $target = $wgRequest->getText( 'target' );
-       $oldid = $wgRequest->getArray( 'oldid' );
-       $artimestamp = $wgRequest->getArray( 'artimestamp' );
-       $logid = $wgRequest->getArray( 'logid' );
-       $img = $wgRequest->getArray( 'oldimage' );
-       $fileid = $wgRequest->getArray( 'fileid' );
-       # For reviewing deleted files...
-       $file = $wgRequest->getVal( 'file' );
-       # If this is a revision, then we need a target page
-       $page = Title::newFromUrl( $target );
-       if( is_null($page) ) {
-               $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
-               return;
-       }
-       # Only one target set at a time please!
-       $i = (bool)$file + (bool)$oldid + (bool)$logid + (bool)$artimestamp + (bool)$fileid + (bool)$img;
-       if( $i !== 1 ) {
-               $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
-               return;
-       }
-       # Logs must have a type given
-       if( $logid && !strpos($page->getDBKey(),'/') ) {
-               $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
-               return;
-       }
-       # Either submit or create our form
-       $form = new RevisionDeleteForm( $page, $oldid, $logid, $artimestamp, $fileid, $img, $file );
-       if( $wgRequest->wasPosted() ) {
-               $form->submit( $wgRequest );
-       } else if( $oldid || $artimestamp ) {
-               $form->showRevs();
-       } else if( $fileid || $img ) {
-               $form->showImages();
-       } else if( $logid ) {
-               $form->showLogItems();
-       }
-       # Show relevant lines from the deletion log. This will show even if said ID
-       # does not exist...might be helpful
-       $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
-       LogEventsList::showLogExtract( $wgOut, 'delete', $page->getPrefixedText() );
-       if( $wgUser->isAllowed( 'suppressionlog' ) ){
-               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "</h2>\n" );
-               LogEventsList::showLogExtract( $wgOut, 'suppress', $page->getPrefixedText() );
-       }
-}
-
-/**
- * Implements the GUI for Revision Deletion.
- * @ingroup SpecialPage
- */
-class RevisionDeleteForm {
-       /**
-        * @param Title $page
-        * @param array $oldids
-        * @param array $logids
-        * @param array $artimestamps
-        * @param array $fileids
-        * @param array $img
-        * @param string $file
-        */
-       function __construct( $page, $oldids, $logids, $artimestamps, $fileids, $img, $file ) {
-               global $wgUser, $wgOut;
-
-               $this->page = $page;
-               # For reviewing deleted files...
-               if( $file ) {
-                       $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $page, $file );
-                       $oimage->load();
-                       // Check if user is allowed to see this file
-                       if( !$oimage->userCan(File::DELETED_FILE) ) {
-                               $wgOut->permissionRequired( 'suppressrevision' );
-                       } else {
-                               $this->showFile( $file );
-                       }
-                       return;
-               }
-               $this->skin = $wgUser->getSkin();
-               # Give a link to the log for this page
-               if( !is_null($this->page) && $this->page->getNamespace() > -1 ) {
-                       $links = array();
-
-                       $logtitle = SpecialPage::getTitleFor( 'Log' );
-                       $links[] = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'viewpagelogs' ),
-                               wfArrayToCGI( array( 'page' => $this->page->getPrefixedUrl() ) ) );
-                       # Give a link to the page history
-                       $links[] = $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml( 'pagehist' ),
-                               wfArrayToCGI( array( 'action' => 'history' ) ) );
-                       # Link to deleted edits
-                       if( $wgUser->isAllowed('undelete') ) {
-                               $undelete = SpecialPage::getTitleFor( 'Undelete' );
-                               $links[] = $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml( 'deletedhist' ),
-                                       wfArrayToCGI( array( 'target' => $this->page->getPrefixedUrl() ) ) );
-                       }
-                       # Logs themselves don't have histories or archived revisions
-                       $wgOut->setSubtitle( '<p>'.implode($links,' / ').'</p>' );
-               }
-               // At this point, we should only have one of these
-               if( $oldids ) {
-                       $this->revisions = $oldids;
-                       $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT );
-                       $this->deleteKey='oldid';
-               } else if( $artimestamps ) {
-                       $this->archrevs = $artimestamps;
-                       $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT );
-                       $this->deleteKey='artimestamp';
-               } else if( $img ) {
-                       $this->ofiles = $img;
-                       $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE );
-                       $this->deleteKey='oldimage';
-               } else if( $fileids ) {
-                       $this->afiles = $fileids;
-                       $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE );
-                       $this->deleteKey='fileid';
-               } else if( $logids ) {
-                       $this->events = $logids;
-                       $hide_content_name = array( 'revdelete-hide-name', 'wpHideName', LogPage::DELETED_ACTION );
-                       $this->deleteKey='logid';
-               }
-               // Our checkbox messages depends one what we are doing,
-               // e.g. we don't hide "text" for logs or images
-               $this->checks = array(
-                       $hide_content_name,
-                       array( 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ),
-                       array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ) );
-               if( $wgUser->isAllowed('suppressrevision') ) {
-                       $this->checks[] = array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED );
-               }
-       }
-
-       /**
-        * Show a deleted file version requested by the visitor.
-        */
-       private function showFile( $key ) {
-               global $wgOut, $wgRequest;
-               $wgOut->disable();
-
-               # We mustn't allow the output to be Squid cached, otherwise
-               # if an admin previews a deleted image, and it's cached, then
-               # a user without appropriate permissions can toddle off and
-               # nab the image, and Squid will serve it
-               $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
-               $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
-               $wgRequest->response()->header( 'Pragma: no-cache' );
-
-               $store = FileStore::get( 'deleted' );
-               $store->stream( $key );
-       }
-
-       /**
-        * This lets a user set restrictions for live and archived revisions
-        */
-       function showRevs() {
-               global $wgOut, $wgUser, $action;
-
-               $UserAllowed = true;
-
-               $count = ($this->deleteKey=='oldid') ?
-                       count($this->revisions) : count($this->archrevs);
-               $wgOut->addWikiMsg( 'revdelete-selected', $this->page->getPrefixedText(), $count );
-
-               $bitfields = 0;
-               $wgOut->addHtml( "<ul>" );
-
-               $where = $revObjs = array();
-               $dbr = wfGetDB( DB_SLAVE );
-               
-               $revisions = 0;
-               // Live revisions...
-               if( $this->deleteKey=='oldid' ) {
-                       // Run through and pull all our data in one query
-                       foreach( $this->revisions as $revid ) {
-                               $where[] = intval($revid);
-                       }
-                       $whereClause = 'rev_id IN(' . implode(',',$where) . ')';
-                       $result = $dbr->select( array('revision','page'), '*',
-                               array( 'rev_page' => $this->page->getArticleID(),
-                                       $whereClause, 'rev_page = page_id' ),
-                               __METHOD__ );
-                       while( $row = $dbr->fetchObject( $result ) ) {
-                               $revObjs[$row->rev_id] = new Revision( $row );
-                       }
-                       foreach( $this->revisions as $revid ) {
-                               // Hiding top revisison is bad
-                               if( !isset($revObjs[$revid]) || $revObjs[$revid]->isCurrent() ) {
-                                       continue;
-                               } else if( !$revObjs[$revid]->userCan(Revision::DELETED_RESTRICTED) ) {
-                               // If a rev is hidden from sysops
-                                       if( $action != 'submit') {
-                                               $wgOut->permissionRequired( 'suppressrevision' );
-                                               return;
-                                       }
-                                       $UserAllowed = false;
-                               }
-                               $revisions++;
-                               $wgOut->addHtml( $this->historyLine( $revObjs[$revid] ) );
-                               $bitfields |= $revObjs[$revid]->mDeleted;
-                       }
-               // The archives...
-               } else {
-                       // Run through and pull all our data in one query
-                       foreach( $this->archrevs as $timestamp ) {
-                               $where[] = $dbr->addQuotes( $timestamp );
-                       }
-                       $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')';
-                       $result = $dbr->select( 'archive', '*',
-                               array( 'ar_namespace' => $this->page->getNamespace(),
-                                       'ar_title' => $this->page->getDBKey(),
-                                               $whereClause ),
-                               __METHOD__ );
-                       while( $row = $dbr->fetchObject( $result ) ) {
-                               $revObjs[$row->ar_timestamp] = new Revision( array(
-                               'page'       => $this->page->getArticleId(),
-                               'id'         => $row->ar_rev_id,
-                               'text'       => $row->ar_text_id,
-                               'comment'    => $row->ar_comment,
-                               'user'       => $row->ar_user,
-                               'user_text'  => $row->ar_user_text,
-                               'timestamp'  => $row->ar_timestamp,
-                               'minor_edit' => $row->ar_minor_edit,
-                               'text_id'    => $row->ar_text_id,
-                               'deleted'    => $row->ar_deleted,
-                               'len'        => $row->ar_len) );
-                       }
-                       foreach( $this->archrevs as $timestamp ) {
-                               if( !isset($revObjs[$timestamp]) ) {
-                                       continue;
-                               } else if( !$revObjs[$timestamp]->userCan(Revision::DELETED_RESTRICTED) ) {
-                               // If a rev is hidden from sysops
-                                       if( $action != 'submit') {
-                                               $wgOut->permissionRequired( 'suppressrevision' );
-                                               return;
-                                       }
-                                       $UserAllowed = false;
-                               }
-                               $revisions++;
-                               $wgOut->addHtml( $this->historyLine( $revObjs[$timestamp] ) );
-                               $bitfields |= $revObjs[$timestamp]->mDeleted;
-                       }
-               }
-               if( !$revisions ) {
-                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
-                       return;
-               }
-               
-               $wgOut->addHtml( "</ul>" );
-
-               $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
-
-               // Normal sysops can always see what they did, but can't always change it
-               if( !$UserAllowed ) return;
-
-               $items = array(
-                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
-                       Xml::submitButton( wfMsg( 'revdelete-submit' ) )
-               );
-               $hidden = array(
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
-                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
-                       Xml::hidden( 'type', $this->deleteKey )
-               );
-               if( $this->deleteKey=='oldid' ) {
-                       foreach( $revObjs as $rev )
-                               $hidden[] = wfHidden( 'oldid[]', $rev->getId() );
-               } else {
-                       foreach( $revObjs as $rev )
-                               $hidden[] = wfHidden( 'artimestamp[]', $rev->getTimestamp() );
-               }
-               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
-               $wgOut->addHtml(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
-                               'id' => 'mw-revdel-form-revisions' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
-               );
-               // FIXME: all items checked for just one rev are checked, even if not set for the others
-               foreach( $this->checks as $item ) {
-                       list( $message, $name, $field ) = $item;
-                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
-               }
-               foreach( $items as $item ) {
-                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
-               }
-               foreach( $hidden as $item ) {
-                       $wgOut->addHtml( $item );
-               }
-               $wgOut->addHtml(
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-
-       }
-
-       /**
-        * This lets a user set restrictions for archived images
-        */
-       function showImages() {
-               global $wgOut, $wgUser, $action;
-
-               $UserAllowed = true;
-
-               $count = ($this->deleteKey=='oldimage') ? count($this->ofiles) : count($this->afiles);
-               $wgOut->addWikiText( wfMsgExt( 'revdelete-selected', array('parsemag'),
-                       $this->page->getPrefixedText(), $count ) );
-
-               $bitfields = 0;
-               $wgOut->addHtml( "<ul>" );
-
-               $where = $filesObjs = array();
-               $dbr = wfGetDB( DB_SLAVE );
-               // Live old revisions...
-               $revisions = 0;
-               if( $this->deleteKey=='oldimage' ) {
-                       // Run through and pull all our data in one query
-                       foreach( $this->ofiles as $timestamp ) {
-                               $where[] = $dbr->addQuotes( $timestamp.'!'.$this->page->getDbKey() );
-                       }
-                       $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')';
-                       $result = $dbr->select( 'oldimage', '*',
-                               array( 'oi_name' => $this->page->getDbKey(),
-                                       $whereClause ),
-                               __METHOD__ );
-                       while( $row = $dbr->fetchObject( $result ) ) {
-                               $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
-                               $filesObjs[$row->oi_archive_name]->user = $row->oi_user;
-                               $filesObjs[$row->oi_archive_name]->user_text = $row->oi_user_text;
-                       }
-                       // Check through our images
-                       foreach( $this->ofiles as $timestamp ) {
-                               $archivename = $timestamp.'!'.$this->page->getDbKey();
-                               if( !isset($filesObjs[$archivename]) ) {
-                                       continue;
-                               } else if( !$filesObjs[$archivename]->userCan(File::DELETED_RESTRICTED) ) {
-                                       // If a rev is hidden from sysops
-                                       if( $action != 'submit' ) {
-                                               $wgOut->permissionRequired( 'suppressrevision' );
-                                               return;
-                                       }
-                                       $UserAllowed = false;
-                               }
-                               $revisions++;
-                               // Inject history info
-                               $wgOut->addHtml( $this->fileLine( $filesObjs[$archivename] ) );
-                               $bitfields |= $filesObjs[$archivename]->deleted;
-                       }
-               // Archived files...
-               } else {
-                       // Run through and pull all our data in one query
-                       foreach( $this->afiles as $id ) {
-                               $where[] = intval($id);
-                       }
-                       $whereClause = 'fa_id IN(' . implode(',',$where) . ')';
-                       $result = $dbr->select( 'filearchive', '*',
-                               array( 'fa_name' => $this->page->getDbKey(),
-                                       $whereClause ),
-                               __METHOD__ );
-                       while( $row = $dbr->fetchObject( $result ) ) {
-                               $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row );
-                       }
-
-                       foreach( $this->afiles as $fileid ) {
-                               if( !isset($filesObjs[$fileid]) ) {
-                                       continue;
-                               } else if( !$filesObjs[$fileid]->userCan(File::DELETED_RESTRICTED) ) {
-                                       // If a rev is hidden from sysops
-                                       if( $action != 'submit' ) {
-                                               $wgOut->permissionRequired( 'suppressrevision' );
-                                               return;
-                                       }
-                                       $UserAllowed = false;
-                               }
-                               $revisions++;
-                               // Inject history info
-                               $wgOut->addHtml( $this->archivedfileLine( $filesObjs[$fileid] ) );
-                               $bitfields |= $filesObjs[$fileid]->deleted;
-                       }
-               }
-               if( !$revisions ) {
-                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
-                       return;
-               }
-               
-               $wgOut->addHtml( "</ul>" );
-
-               $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
-               //Normal sysops can always see what they did, but can't always change it
-               if( !$UserAllowed ) return;
-
-               $items = array(
-                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
-                       Xml::submitButton( wfMsg( 'revdelete-submit' ) )
-               );
-               $hidden = array(
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
-                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
-                       Xml::hidden( 'type', $this->deleteKey )
-               );
-               if( $this->deleteKey=='oldimage' ) {
-                       foreach( $this->ofiles as $filename )
-                               $hidden[] = wfHidden( 'oldimage[]', $filename );
-               } else {
-                       foreach( $this->afiles as $fileid )
-                               $hidden[] = wfHidden( 'fileid[]', $fileid );
-               }
-               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
-               $wgOut->addHtml(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
-                               'id' => 'mw-revdel-form-filerevisions' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
-               );
-               // FIXME: all items checked for just one file are checked, even if not set for the others
-               foreach( $this->checks as $item ) {
-                       list( $message, $name, $field ) = $item;
-                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
-               }
-               foreach( $items as $item ) {
-                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
-               }
-               foreach( $hidden as $item ) {
-                       $wgOut->addHtml( $item );
-               }
-
-               $wgOut->addHtml(
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-       }
-
-       /**
-        * This lets a user set restrictions for log items
-        */
-       function showLogItems() {
-               global $wgOut, $wgUser, $action, $wgMessageCache;
-
-               $UserAllowed = true;
-               $wgOut->addWikiText( wfMsgExt( 'logdelete-selected', array('parsemag'), count($this->events) ) );
-
-               $bitfields = 0;
-               $wgOut->addHtml( "<ul>" );
-
-               $where = $logRows = array();
-               $dbr = wfGetDB( DB_SLAVE );
-               // Run through and pull all our data in one query
-               $logItems = 0;
-               foreach( $this->events as $logid ) {
-                       $where[] = intval($logid);
-               }
-               list($log,$logtype) = explode( '/',$this->page->getDBKey(), 2 );
-               $whereClause = "log_type = '$logtype' AND log_id IN(" . implode(',',$where) . ")";
-               $result = $dbr->select( 'logging', '*',
-                       array( $whereClause ),
-                       __METHOD__ );
-               while( $row = $dbr->fetchObject( $result ) ) {
-                       $logRows[$row->log_id] = $row;
-               }
-               $wgMessageCache->loadAllMessages();
-               foreach( $this->events as $logid ) {
-                       // Don't hide from oversight log!!!
-                       if( !isset( $logRows[$logid] ) || $logRows[$logid]->log_type=='suppress' ) {
-                               continue;
-                       } else if( !LogEventsList::userCan( $logRows[$logid],Revision::DELETED_RESTRICTED) ) {
-                       // If an event is hidden from sysops
-                               if( $action != 'submit') {
-                                       $wgOut->permissionRequired( 'suppressrevision' );
-                                       return;
-                               }
-                               $UserAllowed = false;
-                       }
-                       $logItems++;
-                       $wgOut->addHtml( $this->logLine( $logRows[$logid] ) );
-                       $bitfields |= $logRows[$logid]->log_deleted;
-               }
-               if( !$logItems ) {
-                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
-                       return;
-               }
-               
-               $wgOut->addHtml( "</ul>" );
-
-               $wgOut->addWikiMsg( 'revdelete-text' );
-               // Normal sysops can always see what they did, but can't always change it
-               if( !$UserAllowed ) return;
-
-               $items = array(
-                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
-                       Xml::submitButton( wfMsg( 'revdelete-submit' ) ) );
-               $hidden = array(
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
-                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
-                       Xml::hidden( 'type', $this->deleteKey ) );
-               foreach( $this->events as $logid ) {
-                       $hidden[] = Xml::hidden( 'logid[]', $logid );
-               }
-
-               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
-               $wgOut->addHtml(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
-                               'id' => 'mw-revdel-form-logs' ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
-               );
-               // FIXME: all items checked for just on event are checked, even if not set for the others
-               foreach( $this->checks as $item ) {
-                       list( $message, $name, $field ) = $item;
-                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
-               }
-               foreach( $items as $item ) {
-                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
-               }
-               foreach( $hidden as $item ) {
-                       $wgOut->addHtml( $item );
-               }
-
-               $wgOut->addHtml(
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-       }
-
-       /**
-        * @param Revision $rev
-        * @returns string
-        */
-       private function historyLine( $rev ) {
-               global $wgContLang;
-
-               $date = $wgContLang->timeanddate( $rev->getTimestamp() );
-               $difflink = $del = '';
-               // Live revisions
-               if( $this->deleteKey=='oldid' ) {
-                       $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() );
-                       $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
-                               'diff=' . $rev->getId() . '&oldid=prev' ) . ')';
-               // Archived revisions
-               } else {
-                       $undelete = SpecialPage::getTitleFor( 'Undelete' );
-                       $target = $this->page->getPrefixedText();
-                       $revlink = $this->skin->makeLinkObj( $undelete, $date,
-                               "target=$target&timestamp=" . $rev->getTimestamp() );
-                       $difflink = '(' . $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml('diff'),
-                               "target=$target&diff=prev&timestamp=" . $rev->getTimestamp() ) . ')';
-               }
-
-               if( $rev->isDeleted(Revision::DELETED_TEXT) ) {
-                       $revlink = '<span class="history-deleted">'.$revlink.'</span>';
-                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
-                       if( !$rev->userCan(Revision::DELETED_TEXT) ) {
-                               $revlink = '<span class="history-deleted">'.$date.'</span>';
-                               $difflink = '(' . wfMsgHtml('diff') . ')';
-                       }
-               }
-
-               return "<li> $difflink $revlink ".$this->skin->revUserLink( $rev )." ".$this->skin->revComment( $rev )."$del</li>";
-       }
-
-       /**
-        * @param File $file
-        * @returns string
-        */
-       private function fileLine( $file ) {
-               global $wgContLang, $wgTitle;
-
-               $target = $this->page->getPrefixedText();
-               $date = $wgContLang->timeanddate( $file->getTimestamp(), true  );
-
-               $del = '';
-               # Hidden files...
-               if( $file->isDeleted(File::DELETED_FILE) ) {
-                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
-                       if( !$file->userCan(File::DELETED_FILE) ) {
-                               $pageLink = $date;
-                       } else {
-                               $pageLink = $this->skin->makeKnownLinkObj( $wgTitle, $date,
-                                       "target=$target&file=$file->sha1.".$file->getExtension() );
-                       }
-                       $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
-               # Regular files...
-               } else {
-                       $url = $file->getUrlRel();
-                       $pageLink = "<a href=\"{$url}\">{$date}</a>";
-               }
-
-               $data = wfMsgHtml( 'widthheight',
-                                       $wgContLang->formatNum( $file->getWidth() ),
-                                       $wgContLang->formatNum( $file->getHeight() ) ) .
-                       ' (' . wfMsgHtml( 'nbytes', $wgContLang->formatNum( $file->getSize() ) ) . ')';
-
-               return "<li>$pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
-       }
-
-       /**
-        * @param ArchivedFile $file
-        * @returns string
-        */
-       private function archivedfileLine( $file ) {
-               global $wgContLang, $wgTitle;
-
-               $target = $this->page->getPrefixedText();
-               $date = $wgContLang->timeanddate( $file->getTimestamp(), true  );
-
-               $undelete = SpecialPage::getTitleFor( 'Undelete' );
-               $pageLink = $this->skin->makeKnownLinkObj( $undelete, $date, "target=$target&file={$file->getKey()}" );
-
-               $del = '';
-               if( $file->isDeleted(File::DELETED_FILE) ) {
-                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
-               }
-
-               $data = wfMsgHtml( 'widthheight',
-                                       $wgContLang->formatNum( $file->getWidth() ),
-                                       $wgContLang->formatNum( $file->getHeight() ) ) .
-                       ' (' . wfMsgHtml( 'nbytes', $wgContLang->formatNum( $file->getSize() ) ) . ')';
-
-               return "<li> $pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
-       }
-
-       /**
-        * @param Array $row row
-        * @returns string
-        */
-       private function logLine( $row ) {
-               global $wgContLang;
-
-               $date = $wgContLang->timeanddate( $row->log_timestamp );
-               $paramArray = LogPage::extractParams( $row->log_params );
-               $title = Title::makeTitle( $row->log_namespace, $row->log_title );
-
-               $logtitle = SpecialPage::getTitleFor( 'Log' );
-               $loglink = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'log' ),
-                       wfArrayToCGI( array( 'page' => $title->getPrefixedUrl() ) ) );
-               // Action text
-               if( !LogEventsList::userCan($row,LogPage::DELETED_ACTION) ) {
-                       $action = '<span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
-               } else {
-                       $action = LogPage::actionText( $row->log_type, $row->log_action, $title,
-                               $this->skin, $paramArray, true, true );
-                       if( $row->log_deleted & LogPage::DELETED_ACTION )
-                               $action = '<span class="history-deleted">' . $action . '</span>';
-               }
-               // User links
-               $userLink = $this->skin->userLink( $row->log_user, User::WhoIs($row->log_user) );
-               if( LogEventsList::isDeleted($row,LogPage::DELETED_USER) ) {
-                       $userLink = '<span class="history-deleted">' . $userLink . '</span>';
-               }
-               // Comment
-               $comment = $wgContLang->getDirMark() . $this->skin->commentBlock( $row->log_comment );
-               if( LogEventsList::isDeleted($row,LogPage::DELETED_COMMENT) ) {
-                       $comment = '<span class="history-deleted">' . $comment . '</span>';
-               }
-               return "<li>($loglink) $date $userLink $action $comment</li>";
-       }
-
-       /**
-        * Generate a user tool link cluster if the current user is allowed to view it
-        * @param ArchivedFile $file
-        * @return string HTML
-        */
-       private function fileUserTools( $file ) {
-               if( $file->userCan( Revision::DELETED_USER ) ) {
-                       $link = $this->skin->userLink( $file->user, $file->user_text ) .
-                               $this->skin->userToolLinks( $file->user, $file->user_text );
-               } else {
-                       $link = wfMsgHtml( 'rev-deleted-user' );
-               }
-               if( $file->isDeleted( Revision::DELETED_USER ) ) {
-                       return '<span class="history-deleted">' . $link . '</span>';
-               }
-               return $link;
-       }
-
-       /**
-        * Wrap and format the given file's comment block, if the current
-        * user is allowed to view it.
-        *
-        * @param ArchivedFile $file
-        * @return string HTML
-        */
-       private function fileComment( $file ) {
-               if( $file->userCan( File::DELETED_COMMENT ) ) {
-                       $block = $this->skin->commentBlock( $file->description );
-               } else {
-                       $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
-               }
-               if( $file->isDeleted( File::DELETED_COMMENT ) ) {
-                       return "<span class=\"history-deleted\">$block</span>";
-               }
-               return $block;
-       }
-
-       /**
-        * @param WebRequest $request
-        */
-       function submit( $request ) {
-               global $wgUser, $wgOut;
-
-               $bitfield = $this->extractBitfield( $request );
-               $comment = $request->getText( 'wpReason' );
-               # Can the user set this field?
-               if( $bitfield & Revision::DELETED_RESTRICTED && !$wgUser->isAllowed('suppressrevision') ) {
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-               # If the save went through, go to success message. Otherwise
-               # bounce back to form...
-               if( $this->save( $bitfield, $comment, $this->page ) ) {
-                       $this->success();
-               } else if( $request->getCheck( 'oldid' ) || $request->getCheck( 'artimestamp' ) ) {
-                       return $this->showRevs();
-               } else if( $request->getCheck( 'logid' ) ) {
-                       return $this->showLogs();
-               } else if( $request->getCheck( 'oldimage' ) || $request->getCheck( 'fileid' ) ) {
-                       return $this->showImages();
-               }
-       }
-
-       private function success() {
-               global $wgOut;
-
-               $wgOut->setPagetitle( wfMsgHtml( 'actioncomplete' ) );
-
-               if( $this->deleteKey=='logid' ) {
-                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'logdelete-success' ) ), false );
-                       $this->showLogItems();
-               } else if( $this->deleteKey=='oldid' || $this->deleteKey=='artimestamp' ) {
-                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
-                       $this->showRevs();
-               } else if( $this->deleteKey=='fileid' ) {
-                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
-                       $this->showImages();
-               } else if( $this->deleteKey=='oldimage' ) {
-                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
-                       $this->showImages();
-               }
-       }
-
-       /**
-        * Put together a rev_deleted bitfield from the submitted checkboxes
-        * @param WebRequest $request
-        * @return int
-        */
-       private function extractBitfield( $request ) {
-               $bitfield = 0;
-               foreach( $this->checks as $item ) {
-                       list( /* message */ , $name, $field ) = $item;
-                       if( $request->getCheck( $name ) ) {
-                               $bitfield |= $field;
-                       }
-               }
-               return $bitfield;
-       }
-
-       private function save( $bitfield, $reason, $title ) {
-               $dbw = wfGetDB( DB_MASTER );
-               // Don't allow simply locking the interface for no reason
-               if( $bitfield == Revision::DELETED_RESTRICTED ) {
-                       $bitfield = 0;
-               }
-               $deleter = new RevisionDeleter( $dbw );
-               // By this point, only one of the below should be set
-               if( isset($this->revisions) ) {
-                       return $deleter->setRevVisibility( $title, $this->revisions, $bitfield, $reason );
-               } else if( isset($this->archrevs) ) {
-                       return $deleter->setArchiveVisibility( $title, $this->archrevs, $bitfield, $reason );
-               } else if( isset($this->ofiles) ) {
-                       return $deleter->setOldImgVisibility( $title, $this->ofiles, $bitfield, $reason );
-               } else if( isset($this->afiles) ) {
-                       return $deleter->setArchFileVisibility( $title, $this->afiles, $bitfield, $reason );
-               } else if( isset($this->events) ) {
-                       return $deleter->setEventVisibility( $title, $this->events, $bitfield, $reason );
-               }
-       }
-}
-
-/**
- * Implements the actions for Revision Deletion.
- * @ingroup SpecialPage
- */
-class RevisionDeleter {
-       function __construct( $db ) {
-               $this->dbw = $db;
-       }
-
-       /**
-        * @param $title, the page these events apply to
-        * @param array $items list of revision ID numbers
-        * @param int $bitfield new rev_deleted value
-        * @param string $comment Comment for log records
-        */
-       function setRevVisibility( $title, $items, $bitfield, $comment ) {
-               global $wgOut;
-
-               $userAllowedAll = $success = true;
-               $revIDs = array();
-               $revCount = 0;
-               // Run through and pull all our data in one query
-               foreach( $items as $revid ) {
-                       $where[] = intval($revid);
-               }
-               $whereClause = 'rev_id IN(' . implode(',',$where) . ')';
-               $result = $this->dbw->select( 'revision', '*',
-                       array( 'rev_page' => $title->getArticleID(),
-                               $whereClause ),
-                       __METHOD__ );
-               while( $row = $this->dbw->fetchObject( $result ) ) {
-                       $revObjs[$row->rev_id] = new Revision( $row );
-               }
-               // To work!
-               foreach( $items as $revid ) {
-                       if( !isset($revObjs[$revid]) || $revObjs[$revid]->isCurrent() ) {
-                               $success = false;
-                               continue; // Must exist
-                       } else if( !$revObjs[$revid]->userCan(Revision::DELETED_RESTRICTED) ) {
-                       $userAllowedAll=false;
-                               continue;
-                       }
-                       // For logging, maintain a count of revisions
-                       if( $revObjs[$revid]->mDeleted != $bitfield ) {
-                               $revCount++;
-                               $revIDs[]=$revid;
-
-                               $this->updateRevision( $revObjs[$revid], $bitfield );
-                               $this->updateRecentChangesEdits( $revObjs[$revid], $bitfield, false );
-                       }
-               }
-               // Clear caches...
-               // Don't log or touch if nothing changed
-               if( $revCount > 0 ) {
-                       $this->updateLog( $title, $revCount, $bitfield, $revObjs[$revid]->mDeleted,
-                               $comment, $title, 'oldid', $revIDs );
-                       $this->updatePage( $title );
-               }
-               // Where all revs allowed to be set?
-               if( !$userAllowedAll ) {
-                       //FIXME: still might be confusing???
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-
-               return $success;
-       }
-
-        /**
-        * @param $title, the page these events apply to
-        * @param array $items list of revision ID numbers
-        * @param int $bitfield new rev_deleted value
-        * @param string $comment Comment for log records
-        */
-       function setArchiveVisibility( $title, $items, $bitfield, $comment ) {
-               global $wgOut;
-
-               $userAllowedAll = $success = true;
-               $count = 0;
-               $Id_set = array();
-               // Run through and pull all our data in one query
-               foreach( $items as $timestamp ) {
-                       $where[] = $this->dbw->addQuotes( $timestamp );
-               }
-               $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')';
-               $result = $this->dbw->select( 'archive', '*',
-                       array( 'ar_namespace' => $title->getNamespace(),
-                               'ar_title' => $title->getDBKey(),
-                                       $whereClause ),
-                       __METHOD__ );
-               while( $row = $this->dbw->fetchObject( $result ) ) {
-                       $revObjs[$row->ar_timestamp] = new Revision( array(
-                       'page'       => $title->getArticleId(),
-                       'id'         => $row->ar_rev_id,
-                       'text'       => $row->ar_text_id,
-                       'comment'    => $row->ar_comment,
-                       'user'       => $row->ar_user,
-                       'user_text'  => $row->ar_user_text,
-                       'timestamp'  => $row->ar_timestamp,
-                       'minor_edit' => $row->ar_minor_edit,
-                       'text_id'    => $row->ar_text_id,
-                       'deleted'    => $row->ar_deleted,
-                       'len'        => $row->ar_len) );
-               }
-               // To work!
-               foreach( $items as $timestamp ) {
-                       // This will only select the first revision with this timestamp.
-                       // Since they are all selected/deleted at once, we can just check the
-                       // permissions of one. UPDATE is done via timestamp, so all revs are set.
-                       if( !is_object($revObjs[$timestamp]) ) {
-                               $success = false;
-                               continue; // Must exist
-                       } else if( !$revObjs[$timestamp]->userCan(Revision::DELETED_RESTRICTED) ) {
-                       $userAllowedAll=false;
-                               continue;
-                       }
-                       // Which revisions did we change anything about?
-                       if( $revObjs[$timestamp]->mDeleted != $bitfield ) {
-                          $Id_set[]=$timestamp;
-                          $count++;
-
-                          $this->updateArchive( $revObjs[$timestamp], $title, $bitfield );
-                       }
-               }
-               // For logging, maintain a count of revisions
-               if( $count > 0 ) {
-                       $this->updateLog( $title, $count, $bitfield, $revObjs[$timestamp]->mDeleted,
-                               $comment, $title, 'artimestamp', $Id_set );
-               }
-               // Where all revs allowed to be set?
-               if( !$userAllowedAll ) {
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-
-               return $success;
-       }
-
-        /**
-        * @param $title, the page these events apply to
-        * @param array $items list of revision ID numbers
-        * @param int $bitfield new rev_deleted value
-        * @param string $comment Comment for log records
-        */
-       function setOldImgVisibility( $title, $items, $bitfield, $comment ) {
-               global $wgOut;
-
-               $userAllowedAll = $success = true;
-               $count = 0;
-               $set = array();
-               // Run through and pull all our data in one query
-               foreach( $items as $timestamp ) {
-                       $where[] = $this->dbw->addQuotes( $timestamp.'!'.$title->getDbKey() );
-               }
-               $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')';
-               $result = $this->dbw->select( 'oldimage', '*',
-                       array( 'oi_name' => $title->getDbKey(),
-                               $whereClause ),
-                       __METHOD__ );
-               while( $row = $this->dbw->fetchObject( $result ) ) {
-                       $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
-                       $filesObjs[$row->oi_archive_name]->user = $row->oi_user;
-                       $filesObjs[$row->oi_archive_name]->user_text = $row->oi_user_text;
-               }
-               // To work!
-               foreach( $items as $timestamp ) {
-                       $archivename = $timestamp.'!'.$title->getDbKey();
-                       if( !isset($filesObjs[$archivename]) ) {
-                               $success = false;
-                               continue; // Must exist
-                       } else if( !$filesObjs[$archivename]->userCan(File::DELETED_RESTRICTED) ) {
-                       $userAllowedAll=false;
-                               continue;
-                       }
-
-                       $transaction = true;
-                       // Which revisions did we change anything about?
-                       if( $filesObjs[$archivename]->deleted != $bitfield ) {
-                               $count++;
-
-                               $this->dbw->begin();
-                               $this->updateOldFiles( $filesObjs[$archivename], $bitfield );
-                               // If this image is currently hidden...
-                               if( $filesObjs[$archivename]->deleted & File::DELETED_FILE ) {
-                                       if( $bitfield & File::DELETED_FILE ) {
-                                               # Leave it alone if we are not changing this...
-                                               $set[]=$archivename;
-                                               $transaction = true;
-                                       } else {
-                                               # We are moving this out
-                                               $transaction = $this->makeOldImagePublic( $filesObjs[$archivename] );
-                                               $set[]=$transaction;
-                                       }
-                               // Is it just now becoming hidden?
-                               } else if( $bitfield & File::DELETED_FILE ) {
-                                       $transaction = $this->makeOldImagePrivate( $filesObjs[$archivename] );
-                                       $set[]=$transaction;
-                               } else {
-                                       $set[]=$timestamp;
-                               }
-                               // If our file operations fail, then revert back the db
-                               if( $transaction==false ) {
-                                       $this->dbw->rollback();
-                                       return false;
-                               }
-                               $this->dbw->commit();
-                       }
-               }
-
-               // Log if something was changed
-               if( $count > 0 ) {
-                       $this->updateLog( $title, $count, $bitfield, $filesObjs[$archivename]->deleted,
-                               $comment, $title, 'oldimage', $set );
-                       # Purge page/history
-                       $file = wfLocalFile( $title );
-                       $file->purgeCache();
-                       $file->purgeHistory();
-                       # Invalidate cache for all pages using this file
-                       $update = new HTMLCacheUpdate( $title, 'imagelinks' );
-                       $update->doUpdate();
-               }
-               // Where all revs allowed to be set?
-               if( !$userAllowedAll ) {
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-
-               return $success;
-       }
-
-        /**
-        * @param $title, the page these events apply to
-        * @param array $items list of revision ID numbers
-        * @param int $bitfield new rev_deleted value
-        * @param string $comment Comment for log records
-        */
-       function setArchFileVisibility( $title, $items, $bitfield, $comment ) {
-               global $wgOut;
-
-               $userAllowedAll = $success = true;
-               $count = 0;
-               $Id_set = array();
-
-               // Run through and pull all our data in one query
-               foreach( $items as $id ) {
-                       $where[] = intval($id);
-               }
-               $whereClause = 'fa_id IN(' . implode(',',$where) . ')';
-               $result = $this->dbw->select( 'filearchive', '*',
-                       array( 'fa_name' => $title->getDbKey(),
-                               $whereClause ),
-                       __METHOD__ );
-               while( $row = $this->dbw->fetchObject( $result ) ) {
-                       $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row );
-               }
-               // To work!
-               foreach( $items as $fileid ) {
-                       if( !isset($filesObjs[$fileid]) ) {
-                               $success = false;
-                               continue; // Must exist
-                       } else if( !$filesObjs[$fileid]->userCan(File::DELETED_RESTRICTED) ) {
-                       $userAllowedAll=false;
-                               continue;
-                       }
-                       // Which revisions did we change anything about?
-                       if( $filesObjs[$fileid]->deleted != $bitfield ) {
-                          $Id_set[]=$fileid;
-                          $count++;
-
-                          $this->updateArchFiles( $filesObjs[$fileid], $bitfield );
-                       }
-               }
-               // Log if something was changed
-               if( $count > 0 ) {
-                       $this->updateLog( $title, $count, $bitfield, $comment,
-                               $filesObjs[$fileid]->deleted, $title, 'fileid', $Id_set );
-               }
-               // Where all revs allowed to be set?
-               if( !$userAllowedAll ) {
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-
-               return $success;
-       }
-
-       /**
-        * @param $title, the log page these events apply to
-        * @param array $items list of log ID numbers
-        * @param int $bitfield new log_deleted value
-        * @param string $comment Comment for log records
-        */
-       function setEventVisibility( $title, $items, $bitfield, $comment ) {
-               global $wgOut;
-
-               $userAllowedAll = $success = true;
-               $count = 0;
-               $log_Ids = array();
-
-               // Run through and pull all our data in one query
-               foreach( $items as $logid ) {
-                       $where[] = intval($logid);
-               }
-               list($log,$logtype) = explode( '/',$title->getDBKey(), 2 );
-               $whereClause = "log_type ='$logtype' AND log_id IN(" . implode(',',$where) . ")";
-               $result = $this->dbw->select( 'logging', '*',
-                       array( $whereClause ),
-                       __METHOD__ );
-               while( $row = $this->dbw->fetchObject( $result ) ) {
-                       $logRows[$row->log_id] = $row;
-               }
-               // To work!
-               foreach( $items as $logid ) {
-                       if( !isset($logRows[$logid]) ) {
-                               $success = false;
-                               continue; // Must exist
-                       } else if( !LogEventsList::userCan($logRows[$logid], LogPage::DELETED_RESTRICTED)
-                                || $logRows[$logid]->log_type == 'suppress' ) {
-                       // Don't hide from oversight log!!!
-                       $userAllowedAll=false;
-                       continue;
-                       }
-                       // Which logs did we change anything about?
-                       if( $logRows[$logid]->log_deleted != $bitfield ) {
-                               $log_Ids[]=$logid;
-                               $count++;
-
-                               $this->updateLogs( $logRows[$logid], $bitfield );
-                               $this->updateRecentChangesLog( $logRows[$logid], $bitfield, true );
-                       }
-               }
-               // Don't log or touch if nothing changed
-               if( $count > 0 ) {
-                       $this->updateLog( $title, $count, $bitfield, $logRows[$logid]->log_deleted,
-                               $comment, $title, 'logid', $log_Ids );
-               }
-               // Were all revs allowed to be set?
-               if( !$userAllowedAll ) {
-                       $wgOut->permissionRequired( 'suppressrevision' );
-                       return false;
-               }
-
-               return $success;
-       }
-
-       /**
-        * Moves an image to a safe private location
-        * Caller is responsible for clearing caches
-        * @param File $oimage
-        * @returns mixed, timestamp string on success, false on failure
-        */
-       function makeOldImagePrivate( $oimage ) {
-               $transaction = new FSTransaction();
-               if( !FileStore::lock() ) {
-                       wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
-                       return false;
-               }
-               $oldpath = $oimage->getArchivePath() . DIRECTORY_SEPARATOR . $oimage->archive_name;
-               // Dupe the file into the file store
-               if( file_exists( $oldpath ) ) {
-                       // Is our directory configured?
-                       if( $store = FileStore::get( 'deleted' ) ) {
-                               if( !$oimage->sha1 ) {
-                                       $oimage->upgradeRow(); // sha1 may be missing
-                               }
-                               $key = $oimage->sha1 . '.' . $oimage->getExtension();
-                               $transaction->add( $store->insert( $key, $oldpath, FileStore::DELETE_ORIGINAL ) );
-                       } else {
-                               $group = null;
-                               $key = null;
-                               $transaction = false; // Return an error and do nothing
-                       }
-               } else {
-                       wfDebug( __METHOD__." deleting already-missing '$oldpath'; moving on to database\n" );
-                       $group = null;
-                       $key = '';
-                       $transaction = new FSTransaction(); // empty
-               }
-
-               if( $transaction === false ) {
-                       // Fail to restore?
-                       wfDebug( __METHOD__.": import to file store failed, aborting\n" );
-                       throw new MWException( "Could not archive and delete file $oldpath" );
-                       return false;
-               }
-
-               wfDebug( __METHOD__.": set db items, applying file transactions\n" );
-               $transaction->commit();
-               FileStore::unlock();
-
-               $m = explode('!',$oimage->archive_name,2);
-               $timestamp = $m[0];
-
-               return $timestamp;
-       }
-
-       /**
-        * Moves an image from a safe private location
-        * Caller is responsible for clearing caches
-        * @param File $oimage
-        * @returns mixed, string timestamp on success, false on failure
-        */
-       function makeOldImagePublic( $oimage ) {
-               $transaction = new FSTransaction();
-               if( !FileStore::lock() ) {
-                       wfDebug( __METHOD__." could not acquire filestore lock\n" );
-                       return false;
-               }
-
-               $store = FileStore::get( 'deleted' );
-               if( !$store ) {
-                       wfDebug( __METHOD__.": skipping row with no file.\n" );
-                       return false;
-               }
-
-               $key = $oimage->sha1.'.'.$oimage->getExtension();
-               $destDir = $oimage->getArchivePath();
-               if( !is_dir( $destDir ) ) {
-                       wfMkdirParents( $destDir );
-               }
-               $destPath = $destDir . DIRECTORY_SEPARATOR . $oimage->archive_name;
-               // Check if any other stored revisions use this file;
-               // if so, we shouldn't remove the file from the hidden
-               // archives so they will still work. Check hidden files first.
-               $useCount = $this->dbw->selectField( 'oldimage', '1',
-                       array( 'oi_sha1' => $oimage->sha1,
-                               'oi_deleted & '.File::DELETED_FILE => File::DELETED_FILE ),
-                       __METHOD__, array( 'FOR UPDATE' ) );
-               // Check the rest of the deleted archives too.
-               // (these are the ones that don't show in the image history)
-               if( !$useCount ) {
-                       $useCount = $this->dbw->selectField( 'filearchive', '1',
-                               array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $key ),
-                               __METHOD__, array( 'FOR UPDATE' ) );
-               }
-
-               if( $useCount == 0 ) {
-                       wfDebug( __METHOD__.": nothing else using {$oimage->sha1}, will deleting after\n" );
-                       $flags = FileStore::DELETE_ORIGINAL;
-               } else {
-                       $flags = 0;
-               }
-               $transaction->add( $store->export( $key, $destPath, $flags ) );
-
-               wfDebug( __METHOD__.": set db items, applying file transactions\n" );
-               $transaction->commit();
-               FileStore::unlock();
-
-               $m = explode('!',$oimage->archive_name,2);
-               $timestamp = $m[0];
-
-               return $timestamp;
-       }
-
-       /**
-        * Update the revision's rev_deleted field
-        * @param Revision $rev
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateRevision( $rev, $bitfield ) {
-               $this->dbw->update( 'revision',
-                       array( 'rev_deleted' => $bitfield ),
-                       array( 'rev_id' => $rev->getId(),
-                               'rev_page' => $rev->getPage() ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the revision's rev_deleted field
-        * @param Revision $rev
-        * @param Title $title
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateArchive( $rev, $title, $bitfield ) {
-               $this->dbw->update( 'archive',
-                       array( 'ar_deleted' => $bitfield ),
-                       array( 'ar_namespace' => $title->getNamespace(),
-                               'ar_title'     => $title->getDBKey(),
-                               'ar_timestamp' => $this->dbw->timestamp( $rev->getTimestamp() ),
-                               'ar_rev_id' => $rev->getId() ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the images's oi_deleted field
-        * @param File $file
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateOldFiles( $file, $bitfield ) {
-               $this->dbw->update( 'oldimage',
-                       array( 'oi_deleted' => $bitfield ),
-                       array( 'oi_name' => $file->getName(),
-                               'oi_timestamp' => $this->dbw->timestamp( $file->getTimestamp() ) ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the images's fa_deleted field
-        * @param ArchivedFile $file
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateArchFiles( $file, $bitfield ) {
-               $this->dbw->update( 'filearchive',
-                       array( 'fa_deleted' => $bitfield ),
-                       array( 'fa_id' => $file->getId() ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the logging log_deleted field
-        * @param Row $row
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateLogs( $row, $bitfield ) {
-               $this->dbw->update( 'logging',
-                       array( 'log_deleted' => $bitfield ),
-                       array( 'log_id' => $row->log_id ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the revision's recentchanges record if fields have been hidden
-        * @param Revision $rev
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateRecentChangesEdits( $rev, $bitfield ) {
-               $this->dbw->update( 'recentchanges',
-                       array( 'rc_deleted' => $bitfield,
-                                  'rc_patrolled' => 1 ),
-                       array( 'rc_this_oldid' => $rev->getId(),
-                               'rc_timestamp' => $this->dbw->timestamp( $rev->getTimestamp() ) ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Update the revision's recentchanges record if fields have been hidden
-        * @param Row $row
-        * @param int $bitfield new rev_deleted bitfield value
-        */
-       function updateRecentChangesLog( $row, $bitfield ) {
-               $this->dbw->update( 'recentchanges',
-                       array( 'rc_deleted' => $bitfield,
-                                  'rc_patrolled' => 1 ),
-                       array( 'rc_logid' => $row->log_id,
-                               'rc_timestamp' => $row->log_timestamp ),
-                       __METHOD__ );
-       }
-
-       /**
-        * Touch the page's cache invalidation timestamp; this forces cached
-        * history views to refresh, so any newly hidden or shown fields will
-        * update properly.
-        * @param Title $title
-        */
-       function updatePage( $title ) {
-               $title->invalidateCache();
-               $title->purgeSquid();
-
-               // Extensions that require referencing previous revisions may need this
-               wfRunHooks( 'ArticleRevisionVisiblitySet', array( &$title ) );
-       }
-
-       /**
-        * Checks for a change in the bitfield for a certain option and updates the
-        * provided array accordingly.
-        *
-        * @param String $desc Description to add to the array if the option was
-        * enabled / disabled.
-        * @param int $field The bitmask describing the single option.
-        * @param int $diff The xor of the old and new bitfields.
-        * @param array $arr The array to update.
-        */
-       function checkItem ( $desc, $field, $diff, $new, &$arr ) {
-               if ( $diff & $field ) {
-                       $arr [ ( $new & $field ) ? 0 : 1 ][] = $desc;
-               }
-       }
-
-       /**
-        * Gets an array describing the changes made to the visibilit of the revision.
-        * If the resulting array is $arr, then $arr[0] will contain an array of strings
-        * describing the items that were hidden, $arr[2] will contain an array of strings
-        * describing the items that were unhidden, and $arr[3] will contain an array with
-        * a single string, which can be one of "applied restrictions to sysops",
-        * "removed restrictions from sysops", or null.
-        *
-        * @param int $n The new bitfield.
-        * @param int $o The old bitfield.
-        * @return An array as described above.
-        */
-       function getChanges ( $n, $o ) {
-               $diff = $n ^ $o;
-               $ret = array ( 0 => array(), 1 => array(), 2 => array() );
-
-               $this->checkItem ( wfMsgForContent ( 'revdelete-content' ),
-                               Revision::DELETED_TEXT, $diff, $n, $ret );
-               $this->checkItem ( wfMsgForContent ( 'revdelete-summary' ),
-                               Revision::DELETED_COMMENT, $diff, $n, $ret );
-               $this->checkItem ( wfMsgForContent ( 'revdelete-uname' ),
-                               Revision::DELETED_USER, $diff, $n, $ret );
-
-               // Restriction application to sysops
-               if ( $diff & Revision::DELETED_RESTRICTED ) {
-                       if ( $n & Revision::DELETED_RESTRICTED )
-                               $ret[2][] = wfMsgForContent ( 'revdelete-restricted' );
-                       else
-                               $ret[2][] = wfMsgForContent ( 'revdelete-unrestricted' );
-               }
-
-               return $ret;
-       }
-
-       /**
-        * Gets a log message to describe the given revision visibility change. This
-        * message will be of the form "[hid {content, edit summary, username}];
-        * [unhid {...}][applied restrictions to sysops] for $count revisions: $comment".
-        *
-        * @param int $count The number of effected revisions.
-        * @param int $nbitfield The new bitfield for the revision.
-        * @param int $obitfield The old bitfield for the revision.
-        * @param string $comment The comment associated with the change.
-        * @param bool $isForLog
-        */
-       function getLogMessage ( $count, $nbitfield, $obitfield, $comment, $isForLog = false ) {
-               global $wgContLang;
-
-               $s = '';
-               $changes = $this->getChanges( $nbitfield, $obitfield );
-
-               if ( count ( $changes[0] ) ) {
-                       $s .= wfMsgForContent ( 'revdelete-hid', implode ( ', ', $changes[0] ) );
-               }
-
-               if ( count ( $changes[1] ) ) {
-                       if ($s) $s .= '; ';
-
-                       $s .= wfMsgForContent ( 'revdelete-unhid', implode ( ', ', $changes[1] ) );
-               }
-
-               if ( count ( $changes[2] )) {
-                       if ($s)
-                               $s .= ' (' . $changes[2][0] . ')';
-                       else
-                               $s = $changes[2][0];
-               }
-
-               $msg = $isForLog ? 'logdelete-log-message' : 'revdelete-log-message';
-               $ret = wfMsgExt ( $msg, array( 'parsemag', 'content' ),
-                       $s, $wgContLang->formatNum( $count ) );
-
-               if ( $comment )
-                       $ret .= ": $comment";
-
-               return $ret;
-
-       }
-
-       /**
-        * Record a log entry on the action
-        * @param Title $title, page where item was removed from
-        * @param int $count the number of revisions altered for this page
-        * @param int $nbitfield the new _deleted value
-        * @param int $obitfield the old _deleted value
-        * @param string $comment
-        * @param Title $target, the relevant page
-        * @param string $param, URL param
-        * @param Array $items
-        */
-       function updateLog( $title, $count, $nbitfield, $obitfield, $comment, $target, $param, $items = array() ) {
-               // Put things hidden from sysops in the oversight log
-               $logtype = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ? 'suppress' : 'delete';
-               $log = new LogPage( $logtype );
-
-               $reason = $this->getLogMessage ( $count, $nbitfield, $obitfield, $comment, $param == 'logid' );
-
-               if( $param == 'logid' ) {
-                       $params = array( implode( ',', $items) );
-                       $log->addEntry( 'event', $title, $reason, $params );
-               } else {
-                       // Add params for effected page and ids
-                       $params = array( $param, implode( ',', $items) );
-                       $log->addEntry( 'revision', $title, $reason, $params );
-               }
-       }
-}
diff --git a/includes/specials/Search.php b/includes/specials/Search.php
deleted file mode 100644 (file)
index 0a483af..0000000
+++ /dev/null
@@ -1,651 +0,0 @@
-<?php
-# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-
-/**
- * Run text & title search and display the output
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Entry point
- *
- * @param $par String: (default '')
- */
-function wfSpecialSearch( $par = '' ) {
-       global $wgRequest, $wgUser;
-
-       $search = str_replace( "\n", " ", $wgRequest->getText( 'search', $par ) );
-       $searchPage = new SpecialSearch( $wgRequest, $wgUser );
-       if( $wgRequest->getVal( 'fulltext' ) 
-               || !is_null( $wgRequest->getVal( 'offset' )) 
-               || !is_null( $wgRequest->getVal( 'searchx' ))) {
-               $searchPage->showResults( $search, 'search' );
-       } else {
-               $searchPage->goResult( $search );
-       }
-}
-
-/**
- * implements Special:Search - Run text & title search and display the output
- * @ingroup SpecialPage
- */
-class SpecialSearch {
-
-       /**
-        * Set up basic search parameters from the request and user settings.
-        * Typically you'll pass $wgRequest and $wgUser.
-        *
-        * @param WebRequest $request
-        * @param User $user
-        * @public
-        */
-       function SpecialSearch( &$request, &$user ) {
-               list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' );
-
-               $this->namespaces = $this->powerSearch( $request );
-               if( empty( $this->namespaces ) ) {
-                       $this->namespaces = SearchEngine::userNamespaces( $user );
-               }
-
-               $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false;
-       }
-
-       /**
-        * If an exact title match can be found, jump straight ahead to it.
-        * @param string $term
-        * @public
-        */
-       function goResult( $term ) {
-               global $wgOut;
-               global $wgGoToEdit;
-
-               $this->setupPage( $term );
-
-               # Try to go to page as entered.
-               $t = Title::newFromText( $term );
-
-               # If the string cannot be used to create a title
-               if( is_null( $t ) ){
-                       return $this->showResults( $term );
-               }
-
-               # If there's an exact or very near match, jump right there.
-               $t = SearchEngine::getNearMatch( $term );
-               if( !is_null( $t ) ) {
-                       $wgOut->redirect( $t->getFullURL() );
-                       return;
-               }
-
-               # No match, generate an edit URL
-               $t = Title::newFromText( $term );
-               if( ! is_null( $t ) ) {
-                       wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) );
-                       # If the feature is enabled, go straight to the edit page
-                       if ( $wgGoToEdit ) {
-                               $wgOut->redirect( $t->getFullURL( 'action=edit' ) );
-                               return;
-                       }
-               }
-
-               $wgOut->wrapWikiMsg( "==$1==\n", 'notitlematches' );
-               if( $t->quickUserCan( 'create' ) && $t->quickUserCan( 'edit' ) ) {
-                       $wgOut->addWikiMsg( 'noexactmatch', wfEscapeWikiText( $term ) );
-               } else {
-                       $wgOut->addWikiMsg( 'noexactmatch-nocreate', wfEscapeWikiText( $term ) );
-               }
-
-               return $this->showResults( $term );
-       }
-
-       /**
-        * @param string $term
-        * @public
-        */
-       function showResults( $term ) {
-               $fname = 'SpecialSearch::showResults';
-               wfProfileIn( $fname );
-               global $wgOut, $wgUser;
-               $sk = $wgUser->getSkin();
-
-               $this->setupPage( $term );
-
-               $wgOut->addWikiMsg( 'searchresulttext' );
-
-               if( '' === trim( $term ) ) {
-                       // Empty query -- straight view of search form
-                       $wgOut->setSubtitle( '' );
-                       $wgOut->addHTML( $this->powerSearchBox( $term ) );
-                       $wgOut->addHTML( $this->powerSearchFocus() );
-                       wfProfileOut( $fname );
-                       return;
-               }
-
-               global $wgDisableTextSearch;
-               if ( $wgDisableTextSearch ) {
-                       global $wgForwardSearchUrl;
-                       if( $wgForwardSearchUrl ) {
-                               $url = str_replace( '$1', urlencode( $term ), $wgForwardSearchUrl );
-                               $wgOut->redirect( $url );
-                               return;
-                       }
-                       global $wgInputEncoding;
-                       $wgOut->addHTML(
-                               Xml::openElement( 'fieldset' ) .
-                               Xml::element( 'legend', null, wfMsg( 'search-external' ) ) .
-                               Xml::element( 'p', array( 'class' => 'mw-searchdisabled' ), wfMsg( 'searchdisabled' ) ) .
-                               wfMsg( 'googlesearch',
-                                       htmlspecialchars( $term ),
-                                       htmlspecialchars( $wgInputEncoding ),
-                                       htmlspecialchars( wfMsg( 'searchbutton' ) )
-                               ) .
-                               Xml::closeElement( 'fieldset' )
-                       );
-                       wfProfileOut( $fname );
-                       return;
-               }
-
-               $wgOut->addHTML( $this->shortDialog( $term ) );
-
-               $search = SearchEngine::create();
-               $search->setLimitOffset( $this->limit, $this->offset );
-               $search->setNamespaces( $this->namespaces );
-               $search->showRedirects = $this->searchRedirects;
-               $rewritten = $search->replacePrefixes($term);
-
-               $titleMatches = $search->searchTitle( $rewritten );
-
-               // Sometimes the search engine knows there are too many hits
-               if ($titleMatches instanceof SearchResultTooMany) {
-                       $wgOut->addWikiText( '==' . wfMsg( 'toomanymatches' ) . "==\n" );
-                       $wgOut->addHTML( $this->powerSearchBox( $term ) );
-                       $wgOut->addHTML( $this->powerSearchFocus() );
-                       wfProfileOut( $fname );
-                       return;
-               }
-               
-               $textMatches = $search->searchText( $rewritten );
-
-               // did you mean... suggestions
-               if($textMatches && $textMatches->hasSuggestion()){
-                       $st = SpecialPage::getTitleFor( 'Search' );                     
-                       $stParams = wfArrayToCGI( array( 
-                                       'search'        => $textMatches->getSuggestionQuery(), 
-                                       'fulltext'      => wfMsg('search')),
-                                       $this->powerSearchOptions());
-                                       
-                       $suggestLink = '<a href="'.$st->escapeLocalURL($stParams).'">'.
-                                       $textMatches->getSuggestionSnippet().'</a>';
-                                       
-                       $wgOut->addHTML('<div class="searchdidyoumean">'.wfMsg('search-suggest',$suggestLink).'</div>');
-               }
-
-               // show number of results
-               $num = ( $titleMatches ? $titleMatches->numRows() : 0 )
-                       + ( $textMatches ? $textMatches->numRows() : 0);
-               $totalNum = 0;
-               if($titleMatches && !is_null($titleMatches->getTotalHits()))
-                       $totalNum += $titleMatches->getTotalHits();
-               if($textMatches && !is_null($textMatches->getTotalHits()))
-                       $totalNum += $textMatches->getTotalHits();
-               if ( $num > 0 ) {
-                       if ( $totalNum > 0 ){
-                               $top = wfMsgExt('showingresultstotal', array( 'parseinline' ), 
-                                       $this->offset+1, $this->offset+$num, $totalNum );
-                       } elseif ( $num >= $this->limit ) {
-                               $top = wfShowingResults( $this->offset, $this->limit );
-                       } else {
-                               $top = wfShowingResultsNum( $this->offset, $this->limit, $num );
-                       }
-                       $wgOut->addHTML( "<p class='mw-search-numberresults'>{$top}</p>\n" );
-               }
-
-               // prev/next links
-               if( $num || $this->offset ) {
-                       $prevnext = wfViewPrevNext( $this->offset, $this->limit,
-                               SpecialPage::getTitleFor( 'Search' ),
-                               wfArrayToCGI(
-                                       $this->powerSearchOptions(),
-                                       array( 'search' => $term ) ),
-                                       ($num < $this->limit) );
-                       $wgOut->addHTML( "<p class='mw-search-pager-top'>{$prevnext}</p>\n" );
-                       wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) );
-               } else {
-                       wfRunHooks( 'SpecialSearchNoResults', array( $term ) );
-               }
-
-               if( $titleMatches ) {
-                       if( $titleMatches->numRows() ) {
-                               $wgOut->wrapWikiMsg( "==$1==\n", 'titlematches' );
-                               $wgOut->addHTML( $this->showMatches( $titleMatches ) );
-                       }
-                       $titleMatches->free();
-               }
-
-               if( $textMatches ) {
-                       // output appropriate heading
-                       if( $textMatches->numRows() ) {
-                               if($titleMatches)
-                                       $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' );
-                               else // if no title matches the heading is redundant
-                                       $wgOut->addHTML("<hr/>");                                                               
-                       } elseif( $num == 0 ) {
-                               # Don't show the 'no text matches' if we received title matches
-                               $wgOut->wrapWikiMsg( "==$1==\n", 'notextmatches' );
-                       }
-                       // show interwiki results if any
-                       if( $textMatches->hasInterwikiResults() )
-                               $wgOut->addHtml( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ));
-                       // show results
-                       if( $textMatches->numRows() )
-                               $wgOut->addHTML( $this->showMatches( $textMatches ) );
-
-                       $textMatches->free();
-               }
-
-               if ( $num == 0 ) {
-                       $wgOut->addWikiMsg( 'nonefound' );
-               }
-               if( $num || $this->offset ) {
-                       $wgOut->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" );
-               }
-               $wgOut->addHTML( $this->powerSearchBox( $term ) );
-               wfProfileOut( $fname );
-       }
-
-       #------------------------------------------------------------------
-       # Private methods below this line
-       
-       /**
-        *
-        */
-       function setupPage( $term ) {
-               global $wgOut;
-               if( !empty( $term ) )
-                       $wgOut->setPageTitle( wfMsg( 'searchresults' ) );                       
-               $subtitlemsg = ( Title::newFromText( $term ) ? 'searchsubtitle' : 'searchsubtitleinvalid' );
-               $wgOut->setSubtitle( $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ) );
-               $wgOut->setArticleRelated( false );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-       }
-
-       /**
-        * Extract "power search" namespace settings from the request object,
-        * returning a list of index numbers to search.
-        *
-        * @param WebRequest $request
-        * @return array
-        * @private
-        */
-       function powerSearch( &$request ) {
-               $arr = array();
-               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
-                       if( $request->getCheck( 'ns' . $ns ) ) {
-                               $arr[] = $ns;
-                       }
-               }
-               return $arr;
-       }
-
-       /**
-        * Reconstruct the 'power search' options for links
-        * @return array
-        * @private
-        */
-       function powerSearchOptions() {
-               $opt = array();
-               foreach( $this->namespaces as $n ) {
-                       $opt['ns' . $n] = 1;
-               }
-               $opt['redirs'] = $this->searchRedirects ? 1 : 0;
-               return $opt;
-       }
-
-       /**
-        * Show whole set of results 
-        * 
-        * @param SearchResultSet $matches
-        */
-       function showMatches( &$matches ) {
-               $fname = 'SpecialSearch::showMatches';
-               wfProfileIn( $fname );
-
-               global $wgContLang;
-               $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
-
-               $out = "";
-               
-               $infoLine = $matches->getInfo();
-               if( !is_null($infoLine) )
-                       $out .= "\n<!-- {$infoLine} -->\n";
-                       
-               
-               $off = $this->offset + 1;
-               $out .= "<ul class='mw-search-results'>\n";
-
-               while( $result = $matches->next() ) {
-                       $out .= $this->showHit( $result, $terms );
-               }
-               $out .= "</ul>\n";
-
-               // convert the whole thing to desired language variant
-               global $wgContLang;
-               $out = $wgContLang->convert( $out );
-               wfProfileOut( $fname );
-               return $out;
-       }
-
-       /**
-        * Format a single hit result
-        * @param SearchResult $result
-        * @param array $terms terms to highlight
-        */
-       function showHit( $result, $terms ) {
-               $fname = 'SpecialSearch::showHit';
-               wfProfileIn( $fname );
-               global $wgUser, $wgContLang, $wgLang;
-               
-               if( $result->isBrokenTitle() ) {
-                       wfProfileOut( $fname );
-                       return "<!-- Broken link in search result -->\n";
-               }
-               
-               $t = $result->getTitle();
-               $sk = $wgUser->getSkin();
-
-               $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms));
-
-               //If page content is not readable, just return the title.
-               //This is not quite safe, but better than showing excerpts from non-readable pages
-               //Note that hiding the entry entirely would screw up paging.
-               if (!$t->userCanRead()) {
-                       wfProfileOut( $fname );
-                       return "<li>{$link}</li>\n";
-               }
-
-               // If the page doesn't *exist*... our search index is out of date.
-               // The least confusing at this point is to drop the result.
-               // You may get less results, but... oh well. :P
-               if( $result->isMissingRevision() ) {
-                       wfProfileOut( $fname );
-                       return "<!-- missing page " .
-                               htmlspecialchars( $t->getPrefixedText() ) . "-->\n";
-               }
-
-               // format redirects / relevant sections
-               $redirectTitle = $result->getRedirectTitle();
-               $redirectText = $result->getRedirectSnippet($terms);
-               $sectionTitle = $result->getSectionTitle();
-               $sectionText = $result->getSectionSnippet($terms);
-               $redirect = '';
-               if( !is_null($redirectTitle) )
-                       $redirect = "<span class='searchalttitle'>"
-                               .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText))
-                               ."</span>";
-               $section = '';
-               if( !is_null($sectionTitle) )
-                       $section = "<span class='searchalttitle'>" 
-                               .wfMsg('search-section', $sk->makeKnownLinkObj( $sectionTitle, $sectionText))
-                               ."</span>";
-
-               // format text extract
-               $extract = "<div class='searchresult'>".$result->getTextSnippet($terms)."</div>";
-               
-               // format score
-               if( is_null( $result->getScore() ) ) {
-                       // Search engine doesn't report scoring info
-                       $score = '';
-               } else {
-                       $percent = sprintf( '%2.1f', $result->getScore() * 100 );
-                       $score = wfMsg( 'search-result-score', $wgLang->formatNum( $percent ) )
-                               . ' - ';
-               }
-
-               // format description
-               $byteSize = $result->getByteSize();
-               $wordCount = $result->getWordCount();
-               $timestamp = $result->getTimestamp();
-               $size = wfMsgExt( 'search-result-size', array( 'parsemag', 'escape' ),
-                       $sk->formatSize( $byteSize ),
-                       $wordCount );
-               $date = $wgLang->timeanddate( $timestamp );
-
-               // link to related articles if supported
-               $related = '';
-               if( $result->hasRelated() ){
-                       $st = SpecialPage::getTitleFor( 'Search' );
-                       $stParams = wfArrayToCGI( $this->powerSearchOptions(),
-                               array('search'    => wfMsgForContent('searchrelated').':'.$t->getPrefixedText(),
-                                     'fulltext'  => wfMsg('search') ));
-                       
-                       $related = ' -- <a href="'.$st->escapeLocalURL($stParams).'">'. 
-                               wfMsg('search-relatedarticle').'</a>';
-               }
-                               
-               // Include a thumbnail for media files...
-               if( $t->getNamespace() == NS_IMAGE ) {
-                       $img = wfFindFile( $t );
-                       if( $img ) {
-                               $thumb = $img->getThumbnail( 120, 120 );
-                               if( $thumb ) {
-                                       $desc = $img->getShortDesc();
-                                       wfProfileOut( $fname );
-                                       // Ugly table. :D
-                                       // Float doesn't seem to interact well with the bullets.
-                                       // Table messes up vertical alignment of the bullet, but I'm
-                                       // not sure what more I can do about that. :(
-                                       return "<li>" .
-                                               '<table class="searchResultImage">' .
-                                               '<tr>' .
-                                               '<td width="120" align="center">' .
-                                               $thumb->toHtml( array( 'desc-link' => true ) ) .
-                                               '</td>' .
-                                               '<td valign="top">' .
-                                               $link .
-                                               $extract .
-                                               "<div class='mw-search-result-data'>{$score}{$desc} - {$date}{$related}</div>" .
-                                               '</td>' .
-                                               '</tr>' .
-                                               '</table>' .
-                                               "</li>\n";
-                               }
-                       }
-               }
-
-               wfProfileOut( $fname );
-               return "<li>{$link} {$redirect} {$section} {$extract}\n" .
-                       "<div class='mw-search-result-data'>{$score}{$size} - {$date}{$related}</div>" .
-                       "</li>\n";
-
-       }
-
-       /**
-        * Show results from other wikis
-        * 
-        * @param SearchResultSet $matches
-        */
-       function showInterwiki( &$matches, $query ) {
-               $fname = 'SpecialSearch::showInterwiki';
-               wfProfileIn( $fname );
-
-               global $wgContLang;
-               $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
-
-               $out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>".wfMsg('search-interwiki-caption')."</div>\n";             
-               $off = $this->offset + 1;
-               $out .= "<ul start='{$off}' class='mw-search-iwresults'>\n";
-
-               // work out custom project captions
-               $customCaptions = array();
-               $customLines = explode("\n",wfMsg('search-interwiki-custom')); // format per line <iwprefix>:<caption>
-               foreach($customLines as $line){
-                       $parts = explode(":",$line,2);
-                       if(count($parts) == 2) // validate line
-                               $customCaptions[$parts[0]] = $parts[1]; 
-               }
-               
-               
-               $prev = null;
-               while( $result = $matches->next() ) {
-                       $out .= $this->showInterwikiHit( $result, $prev, $terms, $query, $customCaptions );
-                       $prev = $result->getInterwikiPrefix();
-               }
-               // FIXME: should support paging in a non-confusing way (not sure how though, maybe via ajax)..
-               $out .= "</ul></div>\n";
-
-               // convert the whole thing to desired language variant
-               global $wgContLang;
-               $out = $wgContLang->convert( $out );
-               wfProfileOut( $fname );
-               return $out;
-       }
-       
-       /**
-        * Show single interwiki link
-        *
-        * @param SearchResult $result
-        * @param string $lastInterwiki
-        * @param array $terms
-        * @param string $query 
-        * @param array $customCaptions iw prefix -> caption
-        */
-       function showInterwikiHit( $result, $lastInterwiki, $terms, $query, $customCaptions){
-               $fname = 'SpecialSearch::showInterwikiHit';
-               wfProfileIn( $fname );
-               global $wgUser, $wgContLang, $wgLang;
-               
-               if( $result->isBrokenTitle() ) {
-                       wfProfileOut( $fname );
-                       return "<!-- Broken link in search result -->\n";
-               }
-               
-               $t = $result->getTitle();
-               $sk = $wgUser->getSkin();
-               
-               $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms));
-                               
-               // format redirect if any
-               $redirectTitle = $result->getRedirectTitle();
-               $redirectText = $result->getRedirectSnippet($terms);
-               $redirect = '';
-               if( !is_null($redirectTitle) )
-                       $redirect = "<span class='searchalttitle'>"
-                               .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText))
-                               ."</span>";
-
-               $out = "";
-               // display project name 
-               if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()){
-                       if( key_exists($t->getInterwiki(),$customCaptions) )
-                               // captions from 'search-interwiki-custom'
-                               $caption = $customCaptions[$t->getInterwiki()];
-                       else{
-                               // default is to show the hostname of the other wiki which might suck 
-                               // if there are many wikis on one hostname
-                               $parsed = parse_url($t->getFullURL());
-                               $caption = wfMsg('search-interwiki-default', $parsed['host']); 
-                       }               
-                       // "more results" link (special page stuff could be localized, but we might not know target lang)
-                       $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search");                        
-                       $searchLink = $sk->makeKnownLinkObj( $searchTitle, wfMsg('search-interwiki-more'),
-                               wfArrayToCGI(array('search' => $query, 'fulltext' => 'Search'))); 
-                       $out .= "</ul><div class='mw-search-interwiki-project'><span class='mw-search-interwiki-more'>{$searchLink}</span>{$caption}</div>\n<ul>";
-               }
-
-               $out .= "<li>{$link} {$redirect}</li>\n"; 
-               wfProfileOut( $fname );
-               return $out;
-       }
-       
-
-       /**
-        * Generates the power search box at bottom of [[Special:Search]]
-        * @param $term string: search term
-        * @return $out string: HTML form
-        */
-       function powerSearchBox( $term ) {
-               global $wgScript;
-
-               $namespaces = '';
-               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
-                       $name = str_replace( '_', ' ', $name );
-                       if( '' == $name ) {
-                               $name = wfMsg( 'blanknamespace' );
-                       }
-                       $namespaces .= Xml::openElement( 'span', array( 'style' => 'white-space: nowrap' ) ) .
-                                       Xml::checkLabel( $name, "ns{$ns}", "mw-search-ns{$ns}", in_array( $ns, $this->namespaces ) ) .
-                                       Xml::closeElement( 'span' ) . "\n";
-               }
-
-               $redirect = Xml::check( 'redirs', $this->searchRedirects, array( 'value' => '1', 'id' => 'redirs' ) );
-               $redirectLabel = Xml::label( wfMsg( 'powersearch-redir' ), 'redirs' );
-               $searchField = Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'powerSearchText' ) );
-               $searchButton = Xml::submitButton( wfMsg( 'powersearch' ), array( 'name' => 'fulltext' ) ) . "\n";
-
-               $out = Xml::openElement( 'form', array( 'id' => 'powersearch', 'method' => 'get', 'action' => $wgScript ) ) .
-                       Xml::fieldset( wfMsg( 'powersearch-legend' ),
-                               Xml::hidden( 'title', 'Special:Search' ) .
-                               "<p>" .
-                               wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) .
-                               "<br />" .
-                               $namespaces .
-                               "</p>" .
-                               "<p>" .
-                               $redirect . " " . $redirectLabel .
-                               "</p>" .
-                               wfMsgExt( 'powersearch-field', array( 'parseinline' ) ) .
-                               "&nbsp;" .
-                               $searchField .
-                               "&nbsp;" .
-                               $searchButton ) .
-                       "</form>";
-
-               return $out;
-       }
-
-       function powerSearchFocus() {
-               global $wgJsMimeType;
-               return "<script type=\"$wgJsMimeType\">" .
-                       "hookEvent(\"load\", function(){" .
-                               "document.getElementById('powerSearchText').focus();" .
-                       "});" .
-                       "</script>";
-       }
-
-       function shortDialog($term) {
-               global $wgScript;
-
-               $out  = Xml::openElement( 'form', array(
-                       'id' => 'search',
-                       'method' => 'get',
-                       'action' => $wgScript
-               ));
-               $out .= Xml::hidden( 'title', 'Special:Search' );
-               $out .= Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'searchText' ) ) . ' ';
-               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
-                       if( in_array( $ns, $this->namespaces ) ) {
-                               $out .= Xml::hidden( "ns{$ns}", '1' );
-                       }
-               }
-               $out .= Xml::submitButton( wfMsg( 'searchbutton' ), array( 'name' => 'fulltext' ) );
-               $out .= Xml::closeElement( 'form' );
-
-               return $out;
-       }
-}
diff --git a/includes/specials/Shortpages.php b/includes/specials/Shortpages.php
deleted file mode 100644 (file)
index 2e7d24a..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * SpecialShortpages extends QueryPage. It is used to return the shortest
- * pages in the database.
- * @ingroup SpecialPage
- */
-class ShortPagesPage extends QueryPage {
-
-       function getName() {
-               return 'Shortpages';
-       }
-
-       /**
-        * This query is indexed as of 1.5
-        */
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() {
-               return false;
-       }
-
-       function getSQL() {
-               global $wgContentNamespaces;
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-               $name = $dbr->addQuotes( $this->getName() );
-
-               $forceindex = $dbr->useIndexClause("page_len");
-
-               if ($wgContentNamespaces)
-                       $nsclause = "page_namespace IN (" . $dbr->makeList($wgContentNamespaces) . ")";
-               else
-                       $nsclause = "page_namespace = " . NS_MAIN;
-
-               return
-                       "SELECT $name as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_len AS value
-                       FROM $page $forceindex
-                       WHERE $nsclause AND page_is_redirect=0";
-       }
-
-       function preprocessResults( $db, $res ) {
-               # There's no point doing a batch check if we aren't caching results;
-               # the page must exist for it to have been pulled out of the table
-               if( $this->isCached() ) {
-                       $batch = new LinkBatch();
-                       while( $row = $db->fetchObject( $res ) )
-                               $batch->add( $row->namespace, $row->title );
-                       $batch->execute();
-                       if( $db->numRows( $res ) > 0 )
-                               $db->dataSeek( $res, 0 );
-               }
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-               $dm = $wgContLang->getDirMark();
-
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               if ( !$title ) {
-                       return '<!-- Invalid title ' .  htmlspecialchars( "{$result->namespace}:{$result->title}" ). '-->';
-               }
-               $hlink = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
-               $plink = $this->isCached()
-                                       ? $skin->makeLinkObj( $title )
-                                       : $skin->makeKnownLinkObj( $title );
-               $size = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ), $wgLang->formatNum( htmlspecialchars( $result->value ) ) );
-
-               return $title->exists()
-                               ? "({$hlink}) {$dm}{$plink} {$dm}[{$size}]"
-                               : "<s>({$hlink}) {$dm}{$plink} {$dm}[{$size}]</s>";
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialShortpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $spp = new ShortPagesPage();
-
-       return $spp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/SpecialAllmessages.php b/includes/specials/SpecialAllmessages.php
new file mode 100644 (file)
index 0000000..c2a8de4
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+/**
+ * Use this special page to get a list of the MediaWiki system messages.
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor.
+ */
+function wfSpecialAllmessages() {
+       global $wgOut, $wgRequest, $wgMessageCache, $wgTitle;
+       global $wgUseDatabaseMessages;
+
+       # The page isn't much use if the MediaWiki namespace is not being used
+       if( !$wgUseDatabaseMessages ) {
+               $wgOut->addWikiMsg( 'allmessagesnotsupportedDB' );
+               return;
+       }
+
+       wfProfileIn( __METHOD__ );
+
+       wfProfileIn( __METHOD__ . '-setup' );
+       $ot = $wgRequest->getText( 'ot' );
+
+       $navText = wfMsg( 'allmessagestext' );
+
+       # Make sure all extension messages are available
+
+       $wgMessageCache->loadAllMessages();
+
+       $sortedArray = array_merge( Language::getMessagesFor( 'en' ), $wgMessageCache->getExtensionMessagesFor( 'en' ) );
+       ksort( $sortedArray );
+       $messages = array();
+
+       foreach ( $sortedArray as $key => $value ) {
+               $messages[$key]['enmsg'] = $value;
+               $messages[$key]['statmsg'] = wfMsgReal( $key, array(), false, false, false ); // wfMsgNoDbNoTrans doesn't exist
+               $messages[$key]['msg'] = wfMsgNoTrans( $key );
+       }
+
+       wfProfileOut( __METHOD__ . '-setup' );
+
+       wfProfileIn( __METHOD__ . '-output' );
+       $wgOut->addScriptFile( 'allmessages.js' );
+       if ( $ot == 'php' ) {
+               $navText .= wfAllMessagesMakePhp( $messages );
+               $wgOut->addHTML( 'PHP | <a href="' . $wgTitle->escapeLocalUrl( 'ot=html' ) . '">HTML</a> | ' .
+                       '<a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' .
+                       '<pre>' . htmlspecialchars( $navText ) . '</pre>' );
+       } else if ( $ot == 'xml' ) {
+               $wgOut->disable();
+               header( 'Content-type: text/xml' );
+               echo wfAllMessagesMakeXml( $messages );
+       } else {
+               $wgOut->addHTML( '<a href="' . $wgTitle->escapeLocalUrl( 'ot=php' ) . '">PHP</a> | ' .
+                       'HTML |  <a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' );
+               $wgOut->addWikiText( $navText );
+               $wgOut->addHTML( wfAllMessagesMakeHTMLText( $messages ) );
+       }
+       wfProfileOut( __METHOD__ . '-output' );
+
+       wfProfileOut( __METHOD__ );
+}
+
+function wfAllMessagesMakeXml( $messages ) {
+       global $wgLang;
+       $lang = $wgLang->getCode();
+       $txt = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
+       $txt .= "<messages lang=\"$lang\">\n";
+       foreach( $messages as $key => $m ) {
+               $txt .= "\t" . Xml::element( 'message', array( 'name' => $key ), $m['msg'] ) . "\n";
+       }
+       $txt .= "</messages>";
+       return $txt;
+}
+
+/**
+ * Create the messages array, formatted in PHP to copy to language files.
+ * @param $messages Messages array.
+ * @return The PHP messages array.
+ * @todo Make suitable for language files.
+ */
+function wfAllMessagesMakePhp( $messages ) {
+       global $wgLang;
+       $txt = "\n\n\$messages = array(\n";
+       foreach( $messages as $key => $m ) {
+               if( $wgLang->getCode() != 'en' && $m['msg'] == $m['enmsg'] ) {
+                       continue;
+               } else if ( wfEmptyMsg( $key, $m['msg'] ) ) {
+                       $m['msg'] = '';
+                       $comment = ' #empty';
+               } else {
+                       $comment = '';
+               }
+               $txt .= "'$key' => '" . preg_replace( '/(?<!\\\\)\'/', "\'", $m['msg']) . "',$comment\n";
+       }
+       $txt .= ');';
+       return $txt;
+}
+
+/**
+ * Create a list of messages, formatted in HTML as a list of messages and values and showing differences between the default language file message and the message in MediaWiki: namespace.
+ * @param $messages Messages array.
+ * @return The HTML list of messages.
+ */
+function wfAllMessagesMakeHTMLText( $messages ) {
+       global $wgLang, $wgContLang, $wgUser;
+       wfProfileIn( __METHOD__ );
+
+       $sk = $wgUser->getSkin();
+       $talk = wfMsg( 'talkpagelinktext' );
+
+       $input = Xml::element( 'input', array(
+               'type'    => 'text',
+               'id'      => 'allmessagesinput',
+               'onkeyup' => 'allmessagesfilter()'
+       ), '' );
+       $checkbox = Xml::element( 'input', array(
+               'type'    => 'button',
+               'value'   => wfMsgHtml( 'allmessagesmodified' ),
+               'id'      => 'allmessagescheckbox',
+               'onclick' => 'allmessagesmodified()'
+       ), '' );
+
+       $txt = '<span id="allmessagesfilter" style="display: none;">' . wfMsgHtml( 'allmessagesfilter' ) . " {$input}{$checkbox} " . '</span>';
+
+       $txt .= '
+<table border="1" cellspacing="0" width="100%" id="allmessagestable">
+       <tr>
+               <th rowspan="2">' . wfMsgHtml( 'allmessagesname' ) . '</th>
+               <th>' . wfMsgHtml( 'allmessagesdefault' ) . '</th>
+       </tr>
+       <tr>
+               <th>' . wfMsgHtml( 'allmessagescurrent' ) . '</th>
+       </tr>';
+
+       wfProfileIn( __METHOD__ . "-check" );
+
+       # This is a nasty hack to avoid doing independent existence checks
+       # without sending the links and table through the slow wiki parser.
+       $pageExists = array(
+               NS_MEDIAWIKI => array(),
+               NS_MEDIAWIKI_TALK => array()
+       );
+       $dbr = wfGetDB( DB_SLAVE );
+       $page = $dbr->tableName( 'page' );
+       $sql = "SELECT page_namespace,page_title FROM $page WHERE page_namespace IN (" . NS_MEDIAWIKI . ", " . NS_MEDIAWIKI_TALK . ")";
+       $res = $dbr->query( $sql );
+       while( $s = $dbr->fetchObject( $res ) ) {
+               $pageExists[$s->page_namespace][$s->page_title] = true;
+       }
+       $dbr->freeResult( $res );
+       wfProfileOut( __METHOD__ . "-check" );
+
+       wfProfileIn( __METHOD__ . "-output" );
+
+       $i = 0;
+
+       foreach( $messages as $key => $m ) {
+               $title = $wgLang->ucfirst( $key );
+               if( $wgLang->getCode() != $wgContLang->getCode() ) {
+                       $title .= '/' . $wgLang->getCode();
+               }
+
+               $titleObj =& Title::makeTitle( NS_MEDIAWIKI, $title );
+               $talkPage =& Title::makeTitle( NS_MEDIAWIKI_TALK, $title );
+
+               $changed = ( $m['statmsg'] != $m['msg'] );
+               $message = htmlspecialchars( $m['statmsg'] );
+               $mw = htmlspecialchars( $m['msg'] );
+
+               if( isset( $pageExists[NS_MEDIAWIKI][$title] ) ) {
+                       $pageLink = $sk->makeKnownLinkObj( $titleObj, "<span id=\"sp-allmessages-i-$i\">" .  htmlspecialchars( $key ) . '</span>' );
+               } else {
+                       $pageLink = $sk->makeBrokenLinkObj( $titleObj, "<span id=\"sp-allmessages-i-$i\">" .  htmlspecialchars( $key ) . '</span>' );
+               }
+               if( isset( $pageExists[NS_MEDIAWIKI_TALK][$title] ) ) {
+                       $talkLink = $sk->makeKnownLinkObj( $talkPage, htmlspecialchars( $talk ) );
+               } else {
+                       $talkLink = $sk->makeBrokenLinkObj( $talkPage, htmlspecialchars( $talk ) );
+               }
+
+               $anchor = 'msg_' . htmlspecialchars( strtolower( $title ) );
+               $anchor = "<a id=\"$anchor\" name=\"$anchor\"></a>";
+
+               if( $changed ) {
+                       $txt .= "
+       <tr class=\"orig\" id=\"sp-allmessages-r1-$i\">
+               <td rowspan=\"2\">
+                       $anchor$pageLink<br />$talkLink
+               </td><td>
+$message
+               </td>
+       </tr><tr class=\"new\" id=\"sp-allmessages-r2-$i\">
+               <td>
+$mw
+               </td>
+       </tr>";
+               } else {
+                       $txt .= "
+       <tr class=\"def\" id=\"sp-allmessages-r1-$i\">
+               <td>
+                       $anchor$pageLink<br />$talkLink
+               </td><td>
+$mw
+               </td>
+       </tr>";
+               }
+               $i++;
+       }
+       $txt .= '</table>';
+       wfProfileOut( __METHOD__ . '-output' );
+
+       wfProfileOut( __METHOD__ );
+       return $txt;
+}
diff --git a/includes/specials/SpecialAllpages.php b/includes/specials/SpecialAllpages.php
new file mode 100644 (file)
index 0000000..7223e31
--- /dev/null
@@ -0,0 +1,404 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Entry point : initialise variables and call subfunctions.
+ * @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL)
+ * @param $specialPage See the SpecialPage object.
+ */
+function wfSpecialAllpages( $par=NULL, $specialPage ) {
+       global $wgRequest, $wgOut, $wgContLang;
+
+       # GET values
+       $from = $wgRequest->getVal( 'from' );
+       $namespace = $wgRequest->getInt( 'namespace' );
+
+       $namespaces = $wgContLang->getNamespaces();
+
+       $indexPage = new SpecialAllpages();
+
+       $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) )  ?
+               wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
+               wfMsg( 'allarticles' )
+               );
+
+       if ( isset($par) ) {
+               $indexPage->showChunk( $namespace, $par, $specialPage->including() );
+       } elseif ( isset($from) ) {
+               $indexPage->showChunk( $namespace, $from, $specialPage->including() );
+       } else {
+               $indexPage->showToplevel ( $namespace, $specialPage->including() );
+       }
+}
+
+/**
+ * Implements Special:Allpages
+ * @ingroup SpecialPage
+ */
+class SpecialAllpages {
+       /**
+        * Maximum number of pages to show on single subpage.
+        */
+       protected $maxPerPage = 960;
+
+       /**
+        * Name of this special page. Used to make title objects that reference back
+        * to this page.
+        */
+       protected $name = 'Allpages';
+
+       /**
+        * Determines, which message describes the input field 'nsfrom'.
+        */
+       protected $nsfromMsg = 'allpagesfrom';
+
+/**
+ * HTML for the top form
+ * @param integer $namespace A namespace constant (default NS_MAIN).
+ * @param string $from Article name we are starting listing at.
+ */
+function namespaceForm ( $namespace = NS_MAIN, $from = '' ) {
+       global $wgScript;
+       $t = SpecialPage::getTitleFor( $this->name );
+
+       $out  = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
+       $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
+       $out .= Xml::hidden( 'title', $t->getPrefixedText() );
+       $out .= Xml::openElement( 'fieldset' );
+       $out .= Xml::element( 'legend', null, wfMsg( 'allpages' ) );
+       $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
+       $out .= "<tr>
+                       <td class='mw-label'>" .
+                               Xml::label( wfMsg( $this->nsfromMsg ), 'nsfrom' ) .
+                       "</td>
+                       <td class='mw-input'>" .
+                               Xml::input( 'from', 20, $from, array( 'id' => 'nsfrom' ) ) .
+                       "</td>
+               </tr>
+               <tr>
+                       <td class='mw-label'>" .
+                               Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
+                       "</td>
+                       <td class='mw-input'>" .
+                               Xml::namespaceSelector( $namespace, null ) . ' ' .
+                               Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
+                       "</td>
+                       </tr>";
+       $out .= Xml::closeElement( 'table' );
+       $out .= Xml::closeElement( 'fieldset' );
+       $out .= Xml::closeElement( 'form' );
+       $out .= Xml::closeElement( 'div' );
+       return $out;
+}
+
+/**
+ * @param integer $namespace (default NS_MAIN)
+ */
+function showToplevel ( $namespace = NS_MAIN, $including = false ) {
+       global $wgOut, $wgContLang;
+       $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+       # TODO: Either make this *much* faster or cache the title index points
+       # in the querycache table.
+
+       $dbr = wfGetDB( DB_SLAVE );
+       $out = "";
+       $where = array( 'page_namespace' => $namespace );
+
+       global $wgMemc;
+       $key = wfMemcKey( 'allpages', 'ns', $namespace );
+       $lines = $wgMemc->get( $key );
+
+       if( !is_array( $lines ) ) {
+               $options = array( 'LIMIT' => 1 );
+               if ( ! $dbr->implicitOrderby() ) {
+                       $options['ORDER BY'] = 'page_title';
+               }
+               $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
+               $lastTitle = $firstTitle;
+
+               # This array is going to hold the page_titles in order.
+               $lines = array( $firstTitle );
+
+               # If we are going to show n rows, we need n+1 queries to find the relevant titles.
+               $done = false;
+               for( $i = 0; !$done; ++$i ) {
+                       // Fetch the last title of this chunk and the first of the next
+                       $chunk = is_null( $lastTitle )
+                               ? ''
+                               : 'page_title >= ' . $dbr->addQuotes( $lastTitle );
+                       $res = $dbr->select(
+                               'page', /* FROM */
+                               'page_title', /* WHAT */
+                               $where + array($chunk),
+                               __METHOD__,
+                               array ('LIMIT' => 2, 'OFFSET' => $this->maxPerPage - 1, 'ORDER BY' => 'page_title') );
+
+                       if ( $s = $dbr->fetchObject( $res ) ) {
+                               array_push( $lines, $s->page_title );
+                       } else {
+                               // Final chunk, but ended prematurely. Go back and find the end.
+                               $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
+                                       array(
+                                               'page_namespace' => $namespace,
+                                               $chunk
+                                       ), __METHOD__ );
+                               array_push( $lines, $endTitle );
+                               $done = true;
+                       }
+                       if( $s = $dbr->fetchObject( $res ) ) {
+                               array_push( $lines, $s->page_title );
+                               $lastTitle = $s->page_title;
+                       } else {
+                               // This was a final chunk and ended exactly at the limit.
+                               // Rare but convenient!
+                               $done = true;
+                       }
+                       $dbr->freeResult( $res );
+               }
+               $wgMemc->add( $key, $lines, 3600 );
+       }
+
+       // If there are only two or less sections, don't even display them.
+       // Instead, display the first section directly.
+       if( count( $lines ) <= 2 ) {
+               $this->showChunk( $namespace, '', $including );
+               return;
+       }
+
+       # At this point, $lines should contain an even number of elements.
+       $out .= "<table class='allpageslist' style='background: inherit;'>";
+       while ( count ( $lines ) > 0 ) {
+               $inpoint = array_shift ( $lines );
+               $outpoint = array_shift ( $lines );
+               $out .= $this->showline ( $inpoint, $outpoint, $namespace, false );
+       }
+       $out .= '</table>';
+       $nsForm = $this->namespaceForm( $namespace, '', false );
+
+       # Is there more?
+       if ( $including ) {
+               $out2 = '';
+       } else {
+               $morelinks = '';
+               if ( $morelinks != '' ) {
+                       $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
+                       $out2 .= '<tr valign="top"><td>' . $nsForm;
+                       $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">';
+                       $out2 .= $morelinks . '</td></tr></table><hr />';
+               } else {
+                       $out2 = $nsForm . '<hr />';
+               }
+       }
+
+       $wgOut->addHtml( $out2 . $out );
+}
+
+/**
+ * @todo Document
+ * @param string $from
+ * @param integer $namespace (Default NS_MAIN)
+ */
+function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
+       global $wgContLang;
+       $align = $wgContLang->isRtl() ? 'left' : 'right';
+       $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
+       $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
+       $queryparams = ($namespace ? "namespace=$namespace" : '');
+       $special = SpecialPage::getTitleFor( $this->name, $inpoint );
+       $link = $special->escapeLocalUrl( $queryparams );
+
+       $out = wfMsgHtml(
+               'alphaindexline',
+               "<a href=\"$link\">$inpointf</a></td><td><a href=\"$link\">",
+               "</a></td><td><a href=\"$link\">$outpointf</a>"
+       );
+       return '<tr><td align="' . $align . '">'.$out.'</td></tr>';
+}
+
+/**
+ * @param integer $namespace (Default NS_MAIN)
+ * @param string $from list all pages from this name (default FALSE)
+ */
+function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
+       global $wgOut, $wgUser, $wgContLang;
+
+       $sk = $wgUser->getSkin();
+
+       $fromList = $this->getNamespaceKeyAndText($namespace, $from);
+       $namespaces = $wgContLang->getNamespaces();
+       $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+       $n = 0;
+
+       if ( !$fromList ) {
+               $out = wfMsgWikiHtml( 'allpagesbadtitle' );
+       } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
+               // Show errormessage and reset to NS_MAIN
+               $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
+               $namespace = NS_MAIN;
+       } else {
+               list( $namespace, $fromKey, $from ) = $fromList;
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'page',
+                       array( 'page_namespace', 'page_title', 'page_is_redirect' ),
+                       array(
+                               'page_namespace' => $namespace,
+                               'page_title >= ' . $dbr->addQuotes( $fromKey )
+                       ),
+                       __METHOD__,
+                       array(
+                               'ORDER BY'  => 'page_title',
+                               'LIMIT'     => $this->maxPerPage + 1,
+                               'USE INDEX' => 'name_title',
+                       )
+               );
+
+               if( $res->numRows() > 0 ) {
+                       $out = '<table style="background: inherit;" border="0" width="100%">';
+       
+                       while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
+                               $t = Title::makeTitle( $s->page_namespace, $s->page_title );
+                               if( $t ) {
+                                       $link = ($s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
+                                               $sk->makeKnownLinkObj( $t, htmlspecialchars( $t->getText() ), false, false ) .
+                                               ($s->page_is_redirect ? '</div>' : '' );
+                               } else {
+                                       $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
+                               }
+                               if( $n % 3 == 0 ) {
+                                       $out .= '<tr>';
+                               }
+                               $out .= "<td width=\"33%\">$link</td>";
+                               $n++;
+                               if( $n % 3 == 0 ) {
+                                       $out .= '</tr>';
+                               }
+                       }
+                       if( ($n % 3) != 0 ) {
+                               $out .= '</tr>';
+                       }
+                       $out .= '</table>';
+               } else {
+                       $out = '';
+               }
+       }
+
+       if ( $including ) {
+               $out2 = '';
+       } else {
+               if( $from == '' ) {
+                       // First chunk; no previous link.
+                       $prevTitle = null;
+               } else {
+                       # Get the last title from previous chunk
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $res_prev = $dbr->select(
+                               'page',
+                               'page_title',
+                               array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
+                               __METHOD__,
+                               array( 'ORDER BY' => 'page_title DESC', 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) )
+                       );
+
+                       # Get first title of previous complete chunk
+                       if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
+                               $pt = $dbr->fetchObject( $res_prev );
+                               $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
+                       } else {
+                               # The previous chunk is not complete, need to link to the very first title
+                               # available in the database
+                               $options = array( 'LIMIT' => 1 );
+                               if ( ! $dbr->implicitOrderby() ) {
+                                       $options['ORDER BY'] = 'page_title';
+                               }
+                               $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), __METHOD__, $options );
+                               # Show the previous link if it s not the current requested chunk
+                               if( $from != $reallyFirstPage_title ) {
+                                       $prevTitle =  Title::makeTitle( $namespace, $reallyFirstPage_title );
+                               } else {
+                                       $prevTitle = null;
+                               }
+                       }
+               }
+
+               $nsForm = $this->namespaceForm( $namespace, $from );
+               $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
+               $out2 .= '<tr valign="top"><td>' . $nsForm;
+               $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
+                               $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
+                                       wfMsgHtml ( 'allpages' ) );
+
+               $self = SpecialPage::getTitleFor( 'Allpages' );
+
+               # Do we put a previous link ?
+               if( isset( $prevTitle ) &&  $pt = $prevTitle->getText() ) {
+                       $q = 'from=' . $prevTitle->getPartialUrl()
+                               . ( $namespace ? '&namespace=' . $namespace : '' );
+                       $prevLink = $sk->makeKnownLinkObj( $self,
+                               wfMsgHTML( 'prevpage', htmlspecialchars( $pt ) ), $q );
+                       $out2 .= ' | ' . $prevLink;
+               }
+
+               if( $n == $this->maxPerPage && $s = $dbr->fetchObject($res) ) {
+                       # $s is the first link of the next chunk
+                       $t = Title::MakeTitle($namespace, $s->page_title);
+                       $q = 'from=' . $t->getPartialUrl()
+                               . ( $namespace ? '&namespace=' . $namespace : '' );
+                       $nextLink = $sk->makeKnownLinkObj( $self,
+                               wfMsgHtml( 'nextpage', htmlspecialchars( $t->getText() ) ), $q );
+                       $out2 .= ' | ' . $nextLink;
+               }
+               $out2 .= "</td></tr></table><hr />";
+       }
+
+       $wgOut->addHtml( $out2 . $out );
+       if( isset($prevLink) or isset($nextLink) ) {
+               $wgOut->addHtml( '<hr /><p style="font-size: smaller; float: ' . $align . '">' );
+               if( isset( $prevLink ) ) {
+                       $wgOut->addHTML( $prevLink );
+               }
+               if( isset( $prevLink ) && isset( $nextLink ) ) {
+                       $wgOut->addHTML( ' | ' );
+               }
+               if( isset( $nextLink ) ) {
+                       $wgOut->addHTML( $nextLink );
+               }
+               $wgOut->addHTML( '</p>' );
+
+       }
+
+}
+
+/**
+ * @param int $ns the namespace of the article
+ * @param string $text the name of the article
+ * @return array( int namespace, string dbkey, string pagename ) or NULL on error
+ * @static (sort of)
+ * @access private
+ */
+function getNamespaceKeyAndText ($ns, $text) {
+       if ( $text == '' )
+               return array( $ns, '', '' ); # shortcut for common case
+
+       $t = Title::makeTitleSafe($ns, $text);
+       if ( $t && $t->isLocal() ) {
+               return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
+       } else if ( $t ) {
+               return NULL;
+       }
+
+       # try again, in case the problem was an empty pagename
+       $text = preg_replace('/(#|$)/', 'X$1', $text);
+       $t = Title::makeTitleSafe($ns, $text);
+       if ( $t && $t->isLocal() ) {
+               return array( $t->getNamespace(), '', '' );
+       } else {
+               return NULL;
+       }
+}
+}
diff --git a/includes/specials/SpecialAncientpages.php b/includes/specials/SpecialAncientpages.php
new file mode 100644 (file)
index 0000000..724d34b
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Implements Special:Ancientpages
+ * @ingroup SpecialPage
+ */
+class AncientPagesPage extends QueryPage {
+
+       function getName() {
+               return "Ancientpages";
+       }
+
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               global $wgDBtype;
+               $db = wfGetDB( DB_SLAVE );
+               $page = $db->tableName( 'page' );
+               $revision = $db->tableName( 'revision' );
+               #$use_index = $db->useIndexClause( 'cur_timestamp' ); # FIXME! this is gone
+               $epoch = $wgDBtype == 'mysql' ? 'UNIX_TIMESTAMP(rev_timestamp)' :
+                       'EXTRACT(epoch FROM rev_timestamp)';
+               return
+                       "SELECT 'Ancientpages' as type,
+                                       page_namespace as namespace,
+                               page_title as title,
+                               $epoch as value
+                       FROM $page, $revision
+                       WHERE page_namespace=".NS_MAIN." AND page_is_redirect=0
+                         AND page_latest=rev_id";
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+
+               $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $result->value ), true );
+               $title = Title::makeTitle( $result->namespace, $result->title );
+               $link = $skin->makeKnownLinkObj( $title, htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) ) );
+               return wfSpecialList($link, $d);
+       }
+}
+
+function wfSpecialAncientpages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $app = new AncientPagesPage();
+
+       $app->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialBlockip.php b/includes/specials/SpecialBlockip.php
new file mode 100644 (file)
index 0000000..5ea25ca
--- /dev/null
@@ -0,0 +1,494 @@
+<?php
+/**
+ * Constructor for Special:Blockip page
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialBlockip( $par ) {
+       global $wgUser, $wgOut, $wgRequest;
+
+       # Can't block when the database is locked
+       if( wfReadOnly() ) {
+               $wgOut->readOnlyPage();
+               return;
+       }
+
+       # Permission check
+       if( !$wgUser->isAllowed( 'block' ) ) {
+               $wgOut->permissionRequired( 'block' );
+               return;
+       }
+
+       $ipb = new IPBlockForm( $par );
+
+       $action = $wgRequest->getVal( 'action' );
+       if ( 'success' == $action ) {
+               $ipb->showSuccess();
+       } else if ( $wgRequest->wasPosted() && 'submit' == $action &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $ipb->doSubmit();
+       } else {
+               $ipb->showForm( '' );
+       }
+}
+
+/**
+ * Form object for the Special:Blockip page.
+ *
+ * @ingroup SpecialPage
+ */
+class IPBlockForm {
+       var $BlockAddress, $BlockExpiry, $BlockReason;
+#      var $BlockEmail;
+
+       function IPBlockForm( $par ) {
+               global $wgRequest, $wgUser;
+
+               $this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
+               $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
+               $this->BlockReason = $wgRequest->getText( 'wpBlockReason' );
+               $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' );
+               $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg('ipbotheroption') );
+               $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );
+
+               # Unchecked checkboxes are not included in the form data at all, so having one
+               # that is true by default is a bit tricky
+               $byDefault = !$wgRequest->wasPosted();
+               $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
+               $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
+               $this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
+               $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false );
+               $this->BlockWatchUser = $wgRequest->getBool( 'wpWatchUser', false );
+               # Re-check user's rights to hide names, very serious, defaults to 0
+               $this->BlockHideName = ( $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' ) ) ? 1 : 0;
+       }
+
+       function showForm( $err ) {
+               global $wgOut, $wgUser, $wgSysopUserBans;
+
+               $wgOut->setPagetitle( wfMsg( 'blockip' ) );
+               $wgOut->addWikiMsg( 'blockiptext' );
+
+               if($wgSysopUserBans) {
+                       $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' );
+               } else {
+                       $mIpaddress = Xml::label( wfMsg( 'ipaddress' ), 'mw-bi-target' );
+               }
+               $mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' );
+               $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' );
+               $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' );
+               $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );
+
+               $titleObj = SpecialPage::getTitleFor( 'Blockip' );
+
+               if ( "" != $err ) {
+                       $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
+                       $wgOut->addHTML( Xml::tags( 'p', array( 'class' => 'error' ), $err ) );
+               }
+
+               $scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );
+
+               $showblockoptions = $scBlockExpiryOptions != '-';
+               if (!$showblockoptions)
+                       $mIpbother = $mIpbexpiry;
+
+               $blockExpiryFormOptions = Xml::option( wfMsg( 'ipbotheroption' ), 'other' );
+               foreach (explode(',', $scBlockExpiryOptions) as $option) {
+                       if ( strpos($option, ":") === false ) $option = "$option:$option";
+                       list($show, $value) = explode(":", $option);
+                       $show = htmlspecialchars($show);
+                       $value = htmlspecialchars($value);
+                       $blockExpiryFormOptions .= Xml::option( $show, $value, $this->BlockExpiry === $value ? true : false ) . "\n";
+               }
+
+               $reasonDropDown = Xml::listDropDown( 'wpBlockReasonList',
+                       wfMsgForContent( 'ipbreason-dropdown' ),
+                       wfMsgForContent( 'ipbreasonotherlist' ), '', 'wpBlockDropDown', 4 );
+
+               global $wgStylePath, $wgStyleVersion;
+               $wgOut->addHTML(
+                       Xml::tags( 'script', array( 'type' => 'text/javascript', 'src' => "$wgStylePath/common/block.js?$wgStyleVersion" ), '' ) .
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( "action=submit" ), 'id' => 'blockip' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'blockip-legend' ) ) .
+                       Xml::openElement( 'table', array ( 'border' => '0', 'id' => 'mw-blockip-table' ) ) .
+                       "<tr>
+                               <td class='mw-label'>
+                                       {$mIpaddress}
+                               </td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'wpBlockAddress', 45, $this->BlockAddress,
+                                               array(
+                                                       'tabindex' => '1',
+                                                       'id' => 'mw-bi-target',
+                                                       'onchange' => 'updateBlockOptions()' ) ). "
+                               </td>
+                       </tr>
+                       <tr>"
+               );
+               if ( $showblockoptions ) {
+                       $wgOut->addHTML("
+                               <td class='mw-label'>
+                                       {$mIpbexpiry}
+                               </td>
+                               <td class='mw-input'>" .
+                                       Xml::tags( 'select',
+                                               array(
+                                                       'id' => 'wpBlockExpiry',
+                                                       'name' => 'wpBlockExpiry',
+                                                       'onchange' => 'considerChangingExpiryFocus()',
+                                                       'tabindex' => '2' ),
+                                               $blockExpiryFormOptions ) .
+                               "</td>"
+                       );
+               }
+               $wgOut->addHTML("
+                       </tr>
+                       <tr id='wpBlockOther'>
+                               <td class='mw-label'>
+                                       {$mIpbother}
+                               </td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'wpBlockOther', 45, $this->BlockOther,
+                                               array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>
+                                       {$mIpbreasonother}
+                               </td>
+                               <td class='mw-input'>
+                                       {$reasonDropDown}
+                               </td>
+                       </tr>
+                       <tr id=\"wpBlockReason\">
+                               <td class='mw-label'>
+                                       {$mIpbreason}
+                               </td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'wpBlockReason', 45, $this->BlockReason,
+                                               array( 'tabindex' => '5', 'id' => 'mw-bi-reason', 'maxlength'=> '200' ) ) . "
+                               </td>
+                       </tr>
+                       <tr id='wpAnonOnlyRow'>
+                               <td>&nbsp;</td>
+                               <td class='mw-input'>" .
+                               Xml::checkLabel( wfMsg( 'ipbanononly' ),
+                                               'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
+                                               array( 'tabindex' => '6' ) ) . "
+                               </td>
+                       </tr>
+                       <tr id='wpCreateAccountRow'>
+                               <td>&nbsp;</td>
+                               <td class='mw-input'>" .
+                                       Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
+                                               'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
+                                               array( 'tabindex' => '7' ) ) . "
+                               </td>
+                       </tr>
+                       <tr id='wpEnableAutoblockRow'>
+                               <td>&nbsp;</td>
+                               <td class='mw-input'>" .
+                                       Xml::checkLabel( wfMsg( 'ipbenableautoblock' ),
+                                               'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
+                                               array( 'tabindex' => '8' ) ) . "
+                               </td>
+                       </tr>"
+               );
+
+               global $wgSysopEmailBans;
+               if ( $wgSysopEmailBans && $wgUser->isAllowed( 'blockemail' ) ) {
+                       $wgOut->addHTML("
+                               <tr id='wpEnableEmailBan'>
+                                       <td>&nbsp;</td>
+                                       <td class='mw-input'>" .
+                                               Xml::checkLabel( wfMsg( 'ipbemailban' ),
+                                                       'wpEmailBan', 'wpEmailBan', $this->BlockEmail,
+                                                       array( 'tabindex' => '9' )) . "
+                                       </td>
+                               </tr>"
+                       );
+               }
+
+               // Allow some users to hide name from block log, blocklist and listusers
+               if ( $wgUser->isAllowed( 'hideuser' ) ) {
+                       $wgOut->addHTML("
+                               <tr id='wpEnableHideUser'>
+                                       <td>&nbsp;</td>
+                                       <td class='mw-input'>" .
+                                               Xml::checkLabel( wfMsg( 'ipbhidename' ),
+                                                       'wpHideName', 'wpHideName', $this->BlockHideName,
+                                                       array( 'tabindex' => '10' ) ) . "
+                                       </td>
+                               </tr>"
+                       );
+               }
+               
+               # Watchlist their user page?
+               $wgOut->addHTML("
+                       <tr id='wpEnableWatchUser'>
+                               <td>&nbsp;</td>
+                               <td class='mw-input'>" .
+                                       Xml::checkLabel( wfMsg( 'ipbwatchuser' ),
+                                               'wpWatchUser', 'wpWatchUser', $this->BlockWatchUser,
+                                               array( 'tabindex' => '11' ) ) . "
+                               </td>
+                       </tr>"
+               );
+
+               $wgOut->addHTML("
+                       <tr>
+                               <td style='padding-top: 1em'>&nbsp;</td>
+                               <td  class='mw-submit' style='padding-top: 1em'>" .
+                                       Xml::submitButton( wfMsg( 'ipbsubmit' ),
+                                               array( 'name' => 'wpBlock', 'tabindex' => '12' ) ) . "
+                               </td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) .
+                       Xml::tags( 'script', array( 'type' => 'text/javascript' ), 'updateBlockOptions()' ) . "\n"
+               );
+
+               $wgOut->addHtml( $this->getConvenienceLinks() );
+
+               $user = User::newFromName( $this->BlockAddress );
+               if( is_object( $user ) ) {
+                       $this->showLogFragment( $wgOut, $user->getUserPage() );
+               } elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
+                       $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
+               } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) {
+                       $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
+               }
+       }
+
+       /**
+        * Backend block code.
+        * $userID and $expiry will be filled accordingly
+        * @return array(message key, arguments) on failure, empty array on success
+        */
+       function doBlock(&$userId = null, &$expiry = null)
+       {
+               global $wgUser, $wgSysopUserBans, $wgSysopRangeBans;
+
+               $userId = 0;
+               # Expand valid IPv6 addresses, usernames are left as is
+               $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
+               # isIPv4() and IPv6() are used for final validation
+               $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
+               $rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}';
+               $rxIP = "($rxIP4|$rxIP6)";
+
+               # Check for invalid specifications
+               if ( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
+                       $matches = array();
+                       if ( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
+                               # IPv4
+                               if ( $wgSysopRangeBans ) {
+                                       if ( !IP::isIPv4( $this->BlockAddress ) || $matches[2] < 16 || $matches[2] > 32 ) {
+                                               return array('ip_range_invalid');
+                                       }
+                                       $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
+                               } else {
+                                       # Range block illegal
+                                       return array('range_block_disabled');
+                               }
+                       } else if ( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) {
+                               # IPv6
+                               if ( $wgSysopRangeBans ) {
+                                       if ( !IP::isIPv6( $this->BlockAddress ) || $matches[2] < 64 || $matches[2] > 128 ) {
+                                               return array('ip_range_invalid');
+                                       }
+                                       $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
+                               } else {
+                                       # Range block illegal
+                                       return array('range_block_disabled');
+                               }
+                       } else {
+                               # Username block
+                               if ( $wgSysopUserBans ) {
+                                       $user = User::newFromName( $this->BlockAddress );
+                                       if( !is_null( $user ) && $user->getId() ) {
+                                               # Use canonical name
+                                               $userId = $user->getId();
+                                               $this->BlockAddress = $user->getName();
+                                       } else {
+                                               return array('nosuchusershort', htmlspecialchars( $user ? $user->getName() : $this->BlockAddress ) );
+                                       }
+                               } else {
+                                       return array('badipaddress');
+                               }
+                       }
+               }
+
+               $reasonstr = $this->BlockReasonList;
+               if ( $reasonstr != 'other' && $this->BlockReason != '') {
+                       // Entry from drop down menu + additional comment
+                       $reasonstr .= ': ' . $this->BlockReason;
+               } elseif ( $reasonstr == 'other' ) {
+                       $reasonstr = $this->BlockReason;
+               }
+
+               $expirestr = $this->BlockExpiry;
+               if( $expirestr == 'other' )
+                       $expirestr = $this->BlockOther;
+
+               if (strlen($expirestr) == 0) {
+                       return array('ipb_expiry_invalid');
+               }
+               
+               if ( false === ($expiry = Block::parseExpiryInput( $expirestr )) ) {
+                       // Bad expiry.
+                       return array('ipb_expiry_invalid');
+               }
+               
+               if( $this->BlockHideName && $expiry != 'infinity' ) {
+                       // Bad expiry.
+                       return array('ipb_expiry_temp');
+               }
+
+               # Create block
+               # Note: for a user block, ipb_address is only for display purposes
+               $block = new Block( $this->BlockAddress, $userId, $wgUser->getId(),
+                       $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
+                       $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,
+                       $this->BlockEmail );
+
+               if ( wfRunHooks('BlockIp', array(&$block, &$wgUser)) ) {
+
+                       if ( !$block->insert() ) {
+                               return array('ipb_already_blocked', htmlspecialchars($this->BlockAddress));
+                       }
+
+                       wfRunHooks('BlockIpComplete', array($block, $wgUser));
+
+                       if ( $this->BlockWatchUser ) { 
+                               $wgUser->addWatch ( Title::makeTitle( NS_USER, $this->BlockAddress ) );
+                       }
+
+                       # Prepare log parameters
+                       $logParams = array();
+                       $logParams[] = $expirestr;
+                       $logParams[] = $this->blockLogFlags();
+
+                       # Make log entry, if the name is hidden, put it in the oversight log
+                       $log_type = ($this->BlockHideName) ? 'suppress' : 'block';
+                       $log = new LogPage( $log_type );
+                       $log->addEntry( 'block', Title::makeTitle( NS_USER, $this->BlockAddress ),
+                         $reasonstr, $logParams );
+
+                       # Report to the user
+                       return array();
+               }
+               else
+                       return array('hookaborted');
+       }
+
+       /**
+        * UI entry point for blocking
+        * Wraps around doBlock()
+        */
+       function doSubmit()
+       {
+               global $wgOut;
+               $retval = $this->doBlock();
+               if(empty($retval)) {
+                       $titleObj = SpecialPage::getTitleFor( 'Blockip' );
+                       $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip=' .
+                               urlencode( $this->BlockAddress ) ) );
+                       return;
+               }
+               $key = array_shift($retval);
+               $this->showForm(wfMsgReal($key, $retval));
+       }
+
+       function showSuccess() {
+               global $wgOut;
+
+               $wgOut->setPagetitle( wfMsg( 'blockip' ) );
+               $wgOut->setSubtitle( wfMsg( 'blockipsuccesssub' ) );
+               $text = wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->BlockAddress );
+               $wgOut->addHtml( $text );
+       }
+
+       function showLogFragment( $out, $title ) {
+               $out->addHtml( Xml::element( 'h2', NULL, LogPage::logName( 'block' ) ) );
+               LogEventsList::showLogExtract( $out, 'block', $title->getPrefixedText() );
+       }
+
+       /**
+        * Return a comma-delimited list of "flags" to be passed to the log
+        * reader for this block, to provide more information in the logs
+        *
+        * @return array
+        */
+       private function blockLogFlags() {
+               $flags = array();
+               if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) )
+                                       // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
+                       $flags[] = 'anononly';
+               if( $this->BlockCreateAccount )
+                       $flags[] = 'nocreate';
+               if( !$this->BlockEnableAutoblock )
+                       $flags[] = 'noautoblock';
+               if ( $this->BlockEmail )
+                       $flags[] = 'noemail';
+               return implode( ',', $flags );
+       }
+
+       /**
+        * Builds unblock and block list links
+        *
+        * @return string
+        */
+       private function getConvenienceLinks() {
+               global $wgUser;
+               $skin = $wgUser->getSkin();
+               $links[] = $skin->makeLink ( 'MediaWiki:Ipbreason-dropdown', wfMsgHtml( 'ipb-edit-dropdown' ) );
+               $links[] = $this->getUnblockLink( $skin );
+               $links[] = $this->getBlockListLink( $skin );
+               return '<p class="mw-ipb-conveniencelinks">' . implode( ' | ', $links ) . '</p>';
+       }
+
+       /**
+        * Build a convenient link to unblock the given username or IP
+        * address, if available; otherwise link to a blank unblock
+        * form
+        *
+        * @param $skin Skin to use
+        * @return string
+        */
+       private function getUnblockLink( $skin ) {
+               $list = SpecialPage::getTitleFor( 'Ipblocklist' );
+               if( $this->BlockAddress ) {
+                       $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
+                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock-addr', $addr ),
+                               'action=unblock&ip=' . urlencode( $this->BlockAddress ) );
+               } else {
+                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock' ),      'action=unblock' );
+               }
+       }
+
+       /**
+        * Build a convenience link to the block list
+        *
+        * @param $skin Skin to use
+        * @return string
+        */
+       private function getBlockListLink( $skin ) {
+               $list = SpecialPage::getTitleFor( 'Ipblocklist' );
+               if( $this->BlockAddress ) {
+                       $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) );
+                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist-addr', $addr ),
+                               'ip=' . urlencode( $this->BlockAddress ) );
+               } else {
+                       return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist' ) );
+               }
+       }
+}
diff --git a/includes/specials/SpecialBlockme.php b/includes/specials/SpecialBlockme.php
new file mode 100644 (file)
index 0000000..f222e3c
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ */
+function wfSpecialBlockme() {
+       global $wgRequest, $wgBlockOpenProxies, $wgOut, $wgProxyKey;
+
+       $ip = wfGetIP();
+
+       if( !$wgBlockOpenProxies || $wgRequest->getText( 'ip' ) != md5( $ip . $wgProxyKey ) ) {
+               $wgOut->addWikiMsg( 'proxyblocker-disabled' );
+               return;
+       }
+
+       $blockerName = wfMsg( "proxyblocker" );
+       $reason = wfMsg( "proxyblockreason" );
+
+       $u = User::newFromName( $blockerName );
+       $id = $u->idForName();
+       if ( !$id ) {
+               $u = User::newFromName( $blockerName );
+               $u->addToDatabase();
+               $u->setPassword( bin2hex( mt_rand(0, 0x7fffffff ) ) );
+               $u->saveSettings();
+               $id = $u->getID();
+       }
+
+       $block = new Block( $ip, 0, $id, $reason, wfTimestampNow() );
+       $block->insert();
+
+       $wgOut->addWikiMsg( "proxyblocksuccess" );
+}
diff --git a/includes/specials/SpecialBooksources.php b/includes/specials/SpecialBooksources.php
new file mode 100644 (file)
index 0000000..0690c5c
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * Special page outputs information on sourcing a book with a particular ISBN
+ * The parser creates links to this page when dealing with ISBNs in wikitext
+ *
+ * @author Rob Church <robchur@gmail.com>
+ * @todo Validate ISBNs using the standard check-digit method
+ * @ingroup SpecialPages
+ */
+class SpecialBookSources extends SpecialPage {
+
+       /**
+        * ISBN passed to the page, if any
+        */
+       private $isbn = '';
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               parent::__construct( 'Booksources' );
+       }
+
+       /**
+        * Show the special page
+        *
+        * @param $isbn ISBN passed as a subpage parameter
+        */
+       public function execute( $isbn ) {
+               global $wgOut, $wgRequest;
+               $this->setHeaders();
+               $this->isbn = $this->cleanIsbn( $isbn ? $isbn : $wgRequest->getText( 'isbn' ) );
+               $wgOut->addWikiMsg( 'booksources-summary' );
+               $wgOut->addHtml( $this->makeForm() );
+               if( strlen( $this->isbn ) > 0 )
+                       $this->showList();
+       }
+
+       /**
+        * Trim ISBN and remove characters which aren't required
+        *
+        * @param $isbn Unclean ISBN
+        * @return string
+        */
+       private function cleanIsbn( $isbn ) {
+               return trim( preg_replace( '![^0-9X]!', '', $isbn ) );
+       }
+
+       /**
+        * Generate a form to allow users to enter an ISBN
+        *
+        * @return string
+        */
+       private function makeForm() {
+               global $wgScript;
+               $title = self::getTitleFor( 'Booksources' );
+               $form  = '<fieldset><legend>' . wfMsgHtml( 'booksources-search-legend' ) . '</legend>';
+               $form .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
+               $form .= Xml::hidden( 'title', $title->getPrefixedText() );
+               $form .= '<p>' . Xml::inputLabel( wfMsg( 'booksources-isbn' ), 'isbn', 'isbn', 20, $this->isbn );
+               $form .= '&nbsp;' . Xml::submitButton( wfMsg( 'booksources-go' ) ) . '</p>';
+               $form .= Xml::closeElement( 'form' );
+               $form .= '</fieldset>';
+               return $form;
+       }
+
+       /**
+        * Determine where to get the list of book sources from,
+        * format and output them
+        *
+        * @return string
+        */
+       private function showList() {
+               global $wgOut, $wgContLang;
+
+               # Hook to allow extensions to insert additional HTML,
+               # e.g. for API-interacting plugins and so on
+               wfRunHooks( 'BookInformation', array( $this->isbn, &$wgOut ) );
+
+               # Check for a local page such as Project:Book_sources and use that if available
+               $title = Title::makeTitleSafe( NS_PROJECT, wfMsgForContent( 'booksources' ) ); # Show list in content language
+               if( is_object( $title ) && $title->exists() ) {
+                       $rev = Revision::newFromTitle( $title );
+                       $wgOut->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $rev->getText() ) );
+                       return true;
+               }
+
+               # Fall back to the defaults given in the language file
+               $wgOut->addWikiMsg( 'booksources-text' );
+               $wgOut->addHtml( '<ul>' );
+               $items = $wgContLang->getBookstoreList();
+               foreach( $items as $label => $url )
+                       $wgOut->addHtml( $this->makeListItem( $label, $url ) );
+               $wgOut->addHtml( '</ul>' );
+               return true;
+       }
+
+       /**
+        * Format a book source list item
+        *
+        * @param $label Book source label
+        * @param $url Book source URL
+        * @return string
+        */
+       private function makeListItem( $label, $url ) {
+               $url = str_replace( '$1', $this->isbn, $url );
+               return '<li><a href="' . htmlspecialchars( $url ) . '">' . htmlspecialchars( $label ) . '</a></li>';
+       }
+}
diff --git a/includes/specials/SpecialBrokenRedirects.php b/includes/specials/SpecialBrokenRedirects.php
new file mode 100644 (file)
index 0000000..0a16e6d
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page listing redirects to non existent page. Those should be
+ * fixed to point to an existing page.
+ * @ingroup SpecialPage
+ */
+class BrokenRedirectsPage extends PageQueryPage {
+       var $targets = array();
+
+       function getName() {
+               return 'BrokenRedirects';
+       }
+
+       function isExpensive( ) { return true; }
+       function isSyndicated() { return false; }
+
+       function getPageHeader( ) {
+               return wfMsgExt( 'brokenredirectstext', array( 'parse' ) );
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
+
+               $sql = "SELECT 'BrokenRedirects'  AS type,
+                               p1.page_namespace AS namespace,
+                               p1.page_title     AS title,
+                               rd_namespace,
+                               rd_title
+                          FROM $redirect AS rd
+                   JOIN $page p1 ON (rd.rd_from=p1.page_id)
+                     LEFT JOIN $page AS p2 ON (rd_namespace=p2.page_namespace AND rd_title=p2.page_title )
+                                 WHERE rd_namespace >= 0
+                                   AND p2.page_namespace IS NULL";
+               return $sql;
+       }
+
+       function getOrder() {
+               return '';
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgUser, $wgContLang;
+
+               $fromObj = Title::makeTitle( $result->namespace, $result->title );
+               if ( isset( $result->rd_title ) ) {
+                       $toObj = Title::makeTitle( $result->rd_namespace, $result->rd_title );
+               } else {
+                       $blinks = $fromObj->getBrokenLinksFrom(); # TODO: check for redirect, not for links
+                       if ( $blinks ) {
+                               $toObj = $blinks[0];
+                       } else {
+                               $toObj = false;
+                       }
+               }
+
+               // $toObj may very easily be false if the $result list is cached
+               if ( !is_object( $toObj ) ) {
+                       return '<s>' . $skin->makeLinkObj( $fromObj ) . '</s>';
+               }
+
+               $from = $skin->makeKnownLinkObj( $fromObj ,'', 'redirect=no' );
+               $edit = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-edit' ), 'action=edit' );
+               $to   = $skin->makeBrokenLinkObj( $toObj );
+               $arr = $wgContLang->getArrow();
+
+               $out = "{$from} {$edit}";
+
+               if( $wgUser->isAllowed( 'delete' ) ) {
+                       $delete = $skin->makeKnownLinkObj( $fromObj, wfMsgHtml( 'brokenredirects-delete' ), 'action=delete' );
+                       $out .= " {$delete}";
+               }
+
+               $out .= " {$arr} {$to}";
+               return $out;
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialBrokenRedirects() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $sbr = new BrokenRedirectsPage();
+
+       return $sbr->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialCategories.php b/includes/specials/SpecialCategories.php
new file mode 100644 (file)
index 0000000..951c222
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+function wfSpecialCategories( $par=null ) {
+       global $wgOut, $wgRequest;
+
+       if( $par == '' ) {
+               $from = $wgRequest->getText( 'from' );
+       } else {
+               $from = $par;
+       }
+       $cap = new CategoryPager( $from );
+       $wgOut->addHTML(
+               wfMsgExt( 'categoriespagetext', array( 'parse' ) ) .
+               $cap->getStartForm( $from ) .
+               $cap->getNavigationBar() .
+               '<ul>' . $cap->getBody() . '</ul>' .
+               $cap->getNavigationBar()
+       );
+}
+
+/**
+ * TODO: Allow sorting by count.  We need to have a unique index to do this
+ * properly.
+ *
+ * @ingroup SpecialPage Pager
+ */
+class CategoryPager extends AlphabeticPager {
+       function __construct( $from ) {
+               parent::__construct();
+               $from = str_replace( ' ', '_', $from );
+               if( $from !== '' ) {
+                       global $wgCapitalLinks, $wgContLang;
+                       if( $wgCapitalLinks ) {
+                               $from = $wgContLang->ucfirst( $from );
+                       }
+                       $this->mOffset = $from;
+               }
+       }
+       
+       function getQueryInfo() {
+               global $wgRequest;
+               return array(
+                       'tables' => array( 'category' ),
+                       'fields' => array( 'cat_title','cat_pages' ),
+                       'conds' => array( 'cat_pages > 0' ), 
+                       'options' => array( 'USE INDEX' => 'cat_title' ),
+               );
+       }
+
+       function getIndexField() {
+#              return array( 'abc' => 'cat_title', 'count' => 'cat_pages' );
+               return 'cat_title';
+       }
+
+       function getDefaultQuery() {
+               parent::getDefaultQuery();
+               unset( $this->mDefaultQuery['from'] );
+       }
+#      protected function getOrderTypeMessages() {
+#              return array( 'abc' => 'special-categories-sort-abc',
+#                      'count' => 'special-categories-sort-count' );
+#      }
+
+       protected function getDefaultDirections() {
+#              return array( 'abc' => false, 'count' => true );
+               return false;
+       }
+
+       /* Override getBody to apply LinksBatch on resultset before actually outputting anything. */
+       public function getBody() {
+               if (!$this->mQueryDone) {
+                       $this->doQuery();
+               }
+               $batch = new LinkBatch;
+
+               $this->mResult->rewind();
+
+               while ( $row = $this->mResult->fetchObject() ) {
+                       $batch->addObj( Title::makeTitleSafe( NS_CATEGORY, $row->cat_title ) );
+               }
+               $batch->execute();
+               $this->mResult->rewind();
+               return parent::getBody();
+       }
+
+       function formatRow($result) {
+               global $wgLang;
+               $title = Title::makeTitle( NS_CATEGORY, $result->cat_title );
+               $titleText = $this->getSkin()->makeLinkObj( $title, htmlspecialchars( $title->getText() ) );
+               $count = wfMsgExt( 'nmembers', array( 'parsemag', 'escape' ),
+                               $wgLang->formatNum( $result->cat_pages ) );
+               return Xml::tags('li', null, "$titleText ($count)" ) . "\n";
+       }
+       
+       public function getStartForm( $from ) {
+               global $wgScript;
+               $t = SpecialPage::getTitleFor( 'Categories' );
+       
+               return
+                       Xml::tags( 'form', array( 'method' => 'get', 'action' => $wgScript ),
+                               Xml::hidden( 'title', $t->getPrefixedText() ) .
+                               Xml::fieldset( wfMsg( 'categories' ),
+                                       Xml::inputLabel( wfMsg( 'categoriesfrom' ),
+                                               'from', 'from', 20, $from ) .
+                                       ' ' .
+                                       Xml::submitButton( wfMsg( 'allpagessubmit' ) ) ) );
+       }
+}
diff --git a/includes/specials/SpecialConfirmemail.php b/includes/specials/SpecialConfirmemail.php
new file mode 100644 (file)
index 0000000..9075fb9
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * Special page allows users to request email confirmation message, and handles
+ * processing of the confirmation code when the link in the email is followed
+ *
+ * @ingroup SpecialPage
+ * @author Brion Vibber
+ * @author Rob Church <robchur@gmail.com>
+ */
+class EmailConfirmation extends UnlistedSpecialPage {
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               parent::__construct( 'Confirmemail' );
+       }
+
+       /**
+        * Main execution point
+        *
+        * @param $code Confirmation code passed to the page
+        */
+       function execute( $code ) {
+               global $wgUser, $wgOut;
+               $this->setHeaders();
+               if( empty( $code ) ) {
+                       if( $wgUser->isLoggedIn() ) {
+                               if( User::isValidEmailAddr( $wgUser->getEmail() ) ) {
+                                       $this->showRequestForm();
+                               } else {
+                                       $wgOut->addWikiMsg( 'confirmemail_noemail' );
+                               }
+                       } else {
+                               $title = SpecialPage::getTitleFor( 'Userlogin' );
+                               $self = SpecialPage::getTitleFor( 'Confirmemail' );
+                               $skin = $wgUser->getSkin();
+                               $llink = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $self->getPrefixedUrl() );
+                               $wgOut->addHtml( wfMsgWikiHtml( 'confirmemail_needlogin', $llink ) );
+                       }
+               } else {
+                       $this->attemptConfirm( $code );
+               }
+       }
+
+       /**
+        * Show a nice form for the user to request a confirmation mail
+        */
+       function showRequestForm() {
+               global $wgOut, $wgUser, $wgLang, $wgRequest;
+               if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getText( 'token' ) ) ) {
+                       $ok = $wgUser->sendConfirmationMail();
+                       if ( WikiError::isError( $ok ) ) {
+                               $wgOut->addWikiMsg( 'confirmemail_sendfailed', $ok->toString() );
+                       } else {
+                               $wgOut->addWikiMsg( 'confirmemail_sent' );
+                       }
+               } else {
+                       if( $wgUser->isEmailConfirmed() ) {
+                               $time = $wgLang->timeAndDate( $wgUser->mEmailAuthenticated, true );
+                               $wgOut->addWikiMsg( 'emailauthenticated', $time );
+                       }
+                       if( $wgUser->isEmailConfirmationPending() ) {
+                               $wgOut->addWikiMsg( 'confirmemail_pending' );
+                       }
+                       $wgOut->addWikiMsg( 'confirmemail_text' );
+                       $self = SpecialPage::getTitleFor( 'Confirmemail' );
+                       $form  = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
+                       $form .= wfHidden( 'token', $wgUser->editToken() );
+                       $form .= wfSubmitButton( wfMsgHtml( 'confirmemail_send' ) );
+                       $form .= wfCloseElement( 'form' );
+                       $wgOut->addHtml( $form );
+               }
+       }
+
+       /**
+        * Attempt to confirm the user's email address and show success or failure
+        * as needed; if successful, take the user to log in
+        *
+        * @param $code Confirmation code
+        */
+       function attemptConfirm( $code ) {
+               global $wgUser, $wgOut;
+               $user = User::newFromConfirmationCode( $code );
+               if( is_object( $user ) ) {
+                       $user->confirmEmail();
+                       $user->saveSettings();
+                       $message = $wgUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success';
+                       $wgOut->addWikiMsg( $message );
+                       if( !$wgUser->isLoggedIn() ) {
+                               $title = SpecialPage::getTitleFor( 'Userlogin' );
+                               $wgOut->returnToMain( true, $title );
+                       }
+               } else {
+                       $wgOut->addWikiMsg( 'confirmemail_invalid' );
+               }
+       }
+
+}
+
+/**
+ * Special page allows users to cancel an email confirmation using the e-mail
+ * confirmation code
+ *
+ * @ingroup SpecialPage
+ */
+class EmailInvalidation extends UnlistedSpecialPage {
+
+       public function __construct() {
+               parent::__construct( 'Invalidateemail' );
+       }
+
+       function execute( $code ) {
+               $this->setHeaders();
+               $this->attemptInvalidate( $code );
+       }
+
+       /**
+        * Attempt to invalidate the user's email address and show success or failure
+        * as needed; if successful, link to main page
+        *
+        * @param $code Confirmation code
+        */
+       function attemptInvalidate( $code ) {
+               global $wgUser, $wgOut;
+               $user = User::newFromConfirmationCode( $code );
+               if( is_object( $user ) ) {
+                       $user->invalidateEmail();
+                       $user->saveSettings();
+                       $wgOut->addWikiMsg( 'confirmemail_invalidated' );
+                       if( !$wgUser->isLoggedIn() ) {
+                               $wgOut->returnToMain();
+                       }
+               } else {
+                       $wgOut->addWikiMsg( 'confirmemail_invalid' );
+               }
+       }
+}
diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php
new file mode 100644 (file)
index 0000000..c9cbc18
--- /dev/null
@@ -0,0 +1,465 @@
+<?php
+/**
+ * Special:Contributions, show user contributions in a paged list
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Pager for Special:Contributions
+ * @ingroup SpecialPage Pager
+ */
+class ContribsPager extends ReverseChronologicalPager {
+       public $mDefaultDirection = true;
+       var $messages, $target;
+       var $namespace = '', $year = '', $month = '', $mDb;
+
+       function __construct( $target, $namespace = false, $year = false, $month = false ) {
+               parent::__construct();
+               foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist newpageletter minoreditletter' ) as $msg ) {
+                       $this->messages[$msg] = wfMsgExt( $msg, array( 'escape') );
+               }
+               $this->target = $target;
+               $this->namespace = $namespace;
+
+               $year = intval($year);
+               $month = intval($month);
+
+               $this->year = $year > 0 ? $year : false;
+               $this->month = ($month > 0 && $month < 13) ? $month : false;
+               $this->getDateCond();
+
+               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+       }
+
+       function getDefaultQuery() {
+               $query = parent::getDefaultQuery();
+               $query['target'] = $this->target;
+               $query['month'] = $this->month;
+               $query['year'] = $this->year;
+               return $query;
+       }
+
+       function getQueryInfo() {
+               list( $index, $userCond ) = $this->getUserCond();
+               $conds = array_merge( array('page_id=rev_page'), $userCond, $this->getNamespaceCond() );
+               return array(
+                       'tables' => array( 'page', 'revision' ),
+                       'fields' => array(
+                               'page_namespace', 'page_title', 'page_is_new', 'page_latest', 'rev_id', 'rev_page',
+                               'rev_text_id', 'rev_timestamp', 'rev_comment', 'rev_minor_edit', 'rev_user',
+                               'rev_user_text', 'rev_parent_id', 'rev_deleted'
+                       ),
+                       'conds' => $conds,
+                       'options' => array( 'USE INDEX' => $index )
+               );
+       }
+
+       function getUserCond() {
+               $condition = array();
+
+               if ( $this->target == 'newbies' ) {
+                       $max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
+                       $condition[] = 'rev_user >' . (int)($max - $max / 100);
+                       $index = 'user_timestamp';
+               } else {
+                       $condition['rev_user_text'] = $this->target;
+                       $index = 'usertext_timestamp';
+               }
+               return array( $index, $condition );
+       }
+
+       function getNamespaceCond() {
+               if ( $this->namespace !== '' ) {
+                       return array( 'page_namespace' => (int)$this->namespace );
+               } else {
+                       return array();
+               }
+       }
+
+       function getDateCond() {
+               // Given an optional year and month, we need to generate a timestamp
+               // to use as "WHERE rev_timestamp <= result"
+               // Examples: year = 2006 equals < 20070101 (+000000)
+               // year=2005, month=1    equals < 20050201
+               // year=2005, month=12   equals < 20060101
+
+               if (!$this->year && !$this->month)
+                       return;
+
+               if ( $this->year ) {
+                       $year = $this->year;
+               }
+               else {
+                       // If no year given, assume the current one
+                       $year = gmdate( 'Y' );
+                       // If this month hasn't happened yet this year, go back to last year's month
+                       if( $this->month > gmdate( 'n' ) ) {
+                               $year--;
+                       }
+               }
+
+               if ( $this->month ) {
+                       $month = $this->month + 1;
+                       // For December, we want January 1 of the next year
+                       if ($month > 12) {
+                               $month = 1;
+                               $year++;
+                       }
+               }
+               else {
+                       // No month implies we want up to the end of the year in question
+                       $month = 1;
+                       $year++;
+               }
+
+               if ($year > 2032)
+                       $year = 2032;
+               $ymd = (int)sprintf( "%04d%02d01", $year, $month );
+
+               // Y2K38 bug
+               if ($ymd > 20320101)
+                       $ymd = 20320101;
+
+               $this->mOffset = $this->mDb->timestamp( "${ymd}000000" );
+       }
+
+       function getIndexField() {
+               return 'rev_timestamp';
+       }
+
+       function getStartBody() {
+               return "<ul>\n";
+       }
+
+       function getEndBody() {
+               return "</ul>\n";
+       }
+
+       /**
+        * Generates each row in the contributions list.
+        *
+        * Contributions which are marked "top" are currently on top of the history.
+        * For these contributions, a [rollback] link is shown for users with roll-
+        * back privileges. The rollback link restores the most recent version that
+        * was not written by the target user.
+        *
+        * @todo This would probably look a lot nicer in a table.
+        */
+       function formatRow( $row ) {
+               wfProfileIn( __METHOD__ );
+
+               global $wgLang, $wgUser, $wgContLang;
+
+               $sk = $this->getSkin();
+               $rev = new Revision( $row );
+
+               $page = Title::makeTitle( $row->page_namespace, $row->page_title );
+               $link = $sk->makeKnownLinkObj( $page );
+               $difftext = $topmarktext = '';
+               if( $row->rev_id == $row->page_latest ) {
+                       $topmarktext .= '<strong>' . $this->messages['uctop'] . '</strong>';
+                       if( !$row->page_is_new ) {
+                               $difftext .= '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=0' ) . ')';
+                       } else {
+                               $difftext .= $this->messages['newarticle'];
+                       }
+
+                       if( !$page->getUserPermissionsErrors( 'rollback', $wgUser )
+                       &&  !$page->getUserPermissionsErrors( 'edit', $wgUser ) ) {
+                               $topmarktext .= ' '.$sk->generateRollback( $rev );
+                       }
+
+               }
+               # Is there a visible previous revision?
+               if( $rev->userCan(Revision::DELETED_TEXT) ) {
+                       $difftext = '(' . $sk->makeKnownLinkObj( $page, $this->messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
+               } else {
+                       $difftext = '(' . $this->messages['diff'] . ')';
+               }
+               $histlink='('.$sk->makeKnownLinkObj( $page, $this->messages['hist'], 'action=history' ) . ')';
+
+               $comment = $wgContLang->getDirMark() . $sk->revComment( $rev, false, true );
+               $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
+
+               if( $this->target == 'newbies' ) {
+                       $userlink = ' . . ' . $sk->userLink( $row->rev_user, $row->rev_user_text );
+                       $userlink .= ' (' . $sk->userTalkLink( $row->rev_user, $row->rev_user_text ) . ') ';
+               } else {
+                       $userlink = '';
+               }
+
+               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       $d = '<span class="history-deleted">' . $d . '</span>';
+               }
+
+               if( $rev->getParentId() === 0 ) {
+                       $nflag = '<span class="newpage">' . $this->messages['newpageletter'] . '</span>';
+               } else {
+                       $nflag = '';
+               }
+
+               if( $row->rev_minor_edit ) {
+                       $mflag = '<span class="minor">' . $this->messages['minoreditletter'] . '</span> ';
+               } else {
+                       $mflag = '';
+               }
+
+               $ret = "{$d} {$histlink} {$difftext} {$nflag}{$mflag} {$link}{$userlink}{$comment} {$topmarktext}";
+               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       $ret .= ' ' . wfMsgHtml( 'deletedrev' );
+               }
+               $ret = "<li>$ret</li>\n";
+               wfProfileOut( __METHOD__ );
+               return $ret;
+       }
+
+       /**
+        * Get the Database object in use
+        *
+        * @return Database
+        */
+       public function getDatabase() {
+               return $this->mDb;
+       }
+
+}
+
+/**
+ * Special page "user contributions".
+ * Shows a list of the contributions of a user.
+ *
+ * @return     none
+ * @param      $par    String: (optional) user name of the user for which to show the contributions
+ */
+function wfSpecialContributions( $par = null ) {
+       global $wgUser, $wgOut, $wgLang, $wgRequest;
+
+       $options = array();
+
+       if ( isset( $par ) && $par == 'newbies' ) {
+               $target = 'newbies';
+               $options['contribs'] = 'newbie';
+       } elseif ( isset( $par ) ) {
+               $target = $par;
+       } else {
+               $target = $wgRequest->getVal( 'target' );
+       }
+
+       // check for radiobox
+       if ( $wgRequest->getVal( 'contribs' ) == 'newbie' ) {
+               $target = 'newbies';
+               $options['contribs'] = 'newbie';
+       }
+
+       if ( !strlen( $target ) ) {
+               $wgOut->addHTML( contributionsForm( '' ) );
+               return;
+       }
+
+       $options['limit'] = $wgRequest->getInt( 'limit', 50 );
+       $options['target'] = $target;
+
+       $nt = Title::makeTitleSafe( NS_USER, $target );
+       if ( !$nt ) {
+               $wgOut->addHTML( contributionsForm( '' ) );
+               return;
+       }
+       $id = User::idFromName( $nt->getText() );
+
+       if ( $target != 'newbies' ) {
+               $target = $nt->getText();
+               $wgOut->setSubtitle( contributionsSub( $nt, $id ) );
+       } else {
+               $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') );
+       }
+
+       if ( ( $ns = $wgRequest->getVal( 'namespace', null ) ) !== null && $ns !== '' ) {
+               $options['namespace'] = intval( $ns );
+       } else {
+               $options['namespace'] = '';
+       }
+       if ( $wgUser->isAllowed( 'markbotedit' ) && $wgRequest->getBool( 'bot' ) ) {
+               $options['bot'] = '1';
+       }
+
+       $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
+       # Offset overrides year/month selection
+       if ( ( $month = $wgRequest->getIntOrNull( 'month' ) ) !== null && $month !== -1 ) {
+               $options['month'] = intval( $month );
+       } else {
+               $options['month'] = '';
+       }
+       if ( ( $year = $wgRequest->getIntOrNull( 'year' ) ) !== null ) {
+               $options['year'] = intval( $year );
+       } else if( $options['month'] ) {
+               $thisMonth = intval( gmdate( 'n' ) );
+               $thisYear = intval( gmdate( 'Y' ) );
+               if( intval( $options['month'] ) > $thisMonth ) {
+                       $thisYear--;
+               }
+               $options['year'] = $thisYear;
+       } else {
+               $options['year'] = '';
+       }
+
+       wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id );
+
+       if( $skip ) {
+               $options['year'] = '';
+               $options['month'] = '';
+       }
+
+       $wgOut->addHTML( contributionsForm( $options ) );
+
+       $pager = new ContribsPager( $target, $options['namespace'], $options['year'], $options['month'] );
+       if ( !$pager->getNumRows() ) {
+               $wgOut->addWikiMsg( 'nocontribs' );
+               return;
+       }
+
+       # Show a message about slave lag, if applicable
+       if( ( $lag = $pager->getDatabase()->getLag() ) > 0 )
+               $wgOut->showLagWarning( $lag );
+
+       $wgOut->addHTML(
+               '<p>' . $pager->getNavigationBar() . '</p>' .
+               $pager->getBody() .
+               '<p>' . $pager->getNavigationBar() . '</p>' );
+
+       # If there were contributions, and it was a valid user or IP, show
+       # the appropriate "footer" message - WHOIS tools, etc.
+       if( $target != 'newbies' ) {
+               $message = IP::isIPAddress( $target )
+                       ? 'sp-contributions-footer-anon'
+                       : 'sp-contributions-footer';
+
+
+               $text = wfMsgNoTrans( $message, $target );
+               if( !wfEmptyMsg( $message, $text ) && $text != '-' ) {
+                       $wgOut->addHtml( '<div class="mw-contributions-footer">' );
+                       $wgOut->addWikiText( $text );
+                       $wgOut->addHtml( '</div>' );
+               }
+       }
+}
+
+/**
+ * Generates the subheading with links
+ * @param Title $nt Title object for the target
+ * @param integer $id User ID for the target
+ * @return String: appropriately-escaped HTML to be output literally
+ */
+function contributionsSub( $nt, $id ) {
+       global $wgSysopUserBans, $wgLang, $wgUser;
+
+       $sk = $wgUser->getSkin();
+
+       if ( 0 == $id ) {
+               $user = $nt->getText();
+       } else {
+               $user = $sk->makeLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
+       }
+       $talk = $nt->getTalkPage();
+       if( $talk ) {
+               # Talk page link
+               $tools[] = $sk->makeLinkObj( $talk, wfMsgHtml( 'talkpagelinktext' ) );
+               if( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $nt->getText() ) ) ) {
+                       # Block link
+                       if( $wgUser->isAllowed( 'block' ) )
+                               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $nt->getDBkey() ), wfMsgHtml( 'blocklink' ) );
+                       # Block log link
+                       $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'sp-contributions-blocklog' ), 'type=block&page=' . $nt->getPrefixedUrl() );
+               }
+               # Other logs link
+               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
+
+               wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) );
+
+               $links = implode( ' | ', $tools );
+       }
+
+       // Old message 'contribsub' had one parameter, but that doesn't work for
+       // languages that want to put the "for" bit right after $user but before
+       // $links.  If 'contribsub' is around, use it for reverse compatibility,
+       // otherwise use 'contribsub2'.
+       if( wfEmptyMsg( 'contribsub', wfMsg( 'contribsub' ) ) ) {
+               return wfMsgHtml( 'contribsub2', $user, $links );
+       } else {
+               return wfMsgHtml( 'contribsub', "$user ($links)" );
+       }
+}
+
+/**
+ * Generates the namespace selector form with hidden attributes.
+ * @param $options Array: the options to be included.
+ */
+function contributionsForm( $options ) {
+       global $wgScript, $wgTitle, $wgRequest;
+
+       $options['title'] = $wgTitle->getPrefixedText();
+       if ( !isset( $options['target'] ) ) {
+               $options['target'] = '';
+       } else {
+               $options['target'] = str_replace( '_' , ' ' , $options['target'] );
+       }
+
+       if ( !isset( $options['namespace'] ) ) {
+               $options['namespace'] = '';
+       }
+
+       if ( !isset( $options['contribs'] ) ) {
+               $options['contribs'] = 'user';
+       }
+
+       if ( !isset( $options['year'] ) ) {
+               $options['year'] = '';
+       }
+
+       if ( !isset( $options['month'] ) ) {
+               $options['month'] = '';
+       }
+
+       if ( $options['contribs'] == 'newbie' ) {
+               $options['target'] = '';
+       }
+
+       $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
+
+       foreach ( $options as $name => $value ) {
+               if ( in_array( $name, array( 'namespace', 'target', 'contribs', 'year', 'month' ) ) ) {
+                       continue;
+               }
+               $f .= "\t" . Xml::hidden( $name, $value ) . "\n";
+       }
+
+       $f .= '<fieldset>' .
+               Xml::element( 'legend', array(), wfMsg( 'sp-contributions-search' ) ) .
+               Xml::radioLabel( wfMsgExt( 'sp-contributions-newbies', array( 'parseinline' ) ), 'contribs' , 'newbie' , 'newbie', $options['contribs'] == 'newbie' ? true : false ) . '<br />' .
+               Xml::radioLabel( wfMsgExt( 'sp-contributions-username', array( 'parseinline' ) ), 'contribs' , 'user', 'user', $options['contribs'] == 'user' ? true : false ) . ' ' .
+               Xml::input( 'target', 20, $options['target']) . ' '.
+               '<span style="white-space: nowrap">' .
+               Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' ' .
+               Xml::namespaceSelector( $options['namespace'], '' ) .
+               '</span>' .
+               Xml::openElement( 'p' ) .
+               '<span style="white-space: nowrap">' .
+               Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
+               Xml::input( 'year', 4, $options['year'], array('id' => 'year', 'maxlength' => 4) ) .
+               '</span>' .
+               ' '.
+               '<span style="white-space: nowrap">' .
+               Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
+               Xml::monthSelector( $options['month'], -1 ) . ' '.
+               '</span>' .
+               Xml::submitButton( wfMsg( 'sp-contributions-submit' ) ) .
+               Xml::closeElement( 'p' );
+
+       $explain = wfMsgExt( 'sp-contributions-explain', 'parseinline' );
+       if( !wfEmptyMsg( 'sp-contributions-explain', $explain ) )
+               $f .= "<p>{$explain}</p>";
+
+       $f .= '</fieldset>' .
+               Xml::closeElement( 'form' );
+       return $f;
+}
diff --git a/includes/specials/SpecialDeadendpages.php b/includes/specials/SpecialDeadendpages.php
new file mode 100644 (file)
index 0000000..a8416c9
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class DeadendPagesPage extends PageQueryPage {
+
+       function getName( ) {
+               return "Deadendpages";
+       }
+
+       function getPageHeader() {
+               return wfMsgExt( 'deadendpagestext', array( 'parse' ) );
+       }
+
+       /**
+        * LEFT JOIN is expensive
+        *
+        * @return true
+        */
+       function isExpensive( ) {
+               return 1;
+       }
+
+       function isSyndicated() { return false; }
+
+       /**
+        * @return false
+        */
+       function sortDescending() {
+               return false;
+       }
+
+       /**
+        * @return string an sqlquery
+        */
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
+               return "SELECT 'Deadendpages' as type, page_namespace AS namespace, page_title as title, page_title AS value " .
+       "FROM $page LEFT JOIN $pagelinks ON page_id = pl_from " .
+       "WHERE pl_from IS NULL " .
+       "AND page_namespace = 0 " .
+       "AND page_is_redirect = 0";
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialDeadendpages() {
+
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $depp = new DeadendPagesPage();
+
+       return $depp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialDisambiguations.php b/includes/specials/SpecialDisambiguations.php
new file mode 100644 (file)
index 0000000..3404566
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class DisambiguationsPage extends PageQueryPage {
+
+       function getName() {
+               return 'Disambiguations';
+       }
+
+       function isExpensive( ) { return true; }
+       function isSyndicated() { return false; }
+
+
+       function getPageHeader( ) {
+               return wfMsgExt( 'disambiguations-text', array( 'parse' ) );
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               $dMsgText = wfMsgForContent('disambiguationspage');
+
+               $linkBatch = new LinkBatch;
+
+               # If the text can be treated as a title, use it verbatim.
+               # Otherwise, pull the titles from the links table
+               $dp = Title::newFromText($dMsgText);
+               if( $dp ) {
+                       if($dp->getNamespace() != NS_TEMPLATE) {
+                               # FIXME we assume the disambiguation message is a template but
+                               # the page can potentially be from another namespace :/
+                               wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
+                       }
+                       $linkBatch->addObj( $dp );
+               } else {
+                               # Get all the templates linked from the Mediawiki:Disambiguationspage
+                               $disPageObj = Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage' );
+                               $res = $dbr->select(
+                                       array('pagelinks', 'page'),
+                                       'pl_title',
+                                       array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
+                                               'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
+                                       __METHOD__ );
+
+                               while ( $row = $dbr->fetchObject( $res ) ) {
+                                       $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
+                               }
+
+                               $dbr->freeResult( $res );
+               }
+
+               $set = $linkBatch->constructSet( 'lb.tl', $dbr );
+               if( $set === false ) {
+                       # We must always return a valid sql query, but this way DB will always quicly return an empty result
+                       $set = 'FALSE';
+                       wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
+               }
+
+               list( $page, $pagelinks, $templatelinks) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
+
+               $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace,"
+                       ." pb.page_title AS title, la.pl_from AS value"
+                       ." FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa"
+                       ." WHERE $set"  # disambiguation template(s)
+                       .' AND pa.page_id = la.pl_from'
+                       .' AND pa.page_namespace = ' . NS_MAIN  # Limit to just articles in the main namespace
+                       .' AND pb.page_id = lb.tl_from'
+                       .' AND pb.page_namespace = la.pl_namespace'
+                       .' AND pb.page_title = la.pl_title'
+                       .' ORDER BY lb.tl_namespace, lb.tl_title';
+
+               return $sql;
+       }
+
+       function getOrder() {
+               return '';
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang;
+               $title = Title::newFromId( $result->value );
+               $dp = Title::makeTitle( $result->namespace, $result->title );
+
+               $from = $skin->makeKnownLinkObj( $title, '' );
+               $edit = $skin->makeKnownLinkObj( $title, "(".wfMsgHtml("qbedit").")" , 'redirect=no&action=edit' );
+               $arr  = $wgContLang->getArrow();
+               $to   = $skin->makeKnownLinkObj( $dp, '' );
+
+               return "$from $edit $arr $to";
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialDisambiguations() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $sd = new DisambiguationsPage();
+
+       return $sd->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php
new file mode 100644 (file)
index 0000000..b1bad0c
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page listing redirects to redirecting page.
+ * The software will automatically not follow double redirects, to prevent loops.
+ * @ingroup SpecialPage
+ */
+class DoubleRedirectsPage extends PageQueryPage {
+
+       function getName() {
+               return 'DoubleRedirects';
+       }
+
+       function isExpensive( ) { return true; }
+       function isSyndicated() { return false; }
+
+       function getPageHeader( ) {
+               return wfMsgExt( 'doubleredirectstext', array( 'parse' ) );
+       }
+
+       function getSQLText( &$dbr, $namespace = null, $title = null ) {
+
+               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
+
+               $limitToTitle = !( $namespace === null && $title === null );
+               $sql = $limitToTitle ? "SELECT" : "SELECT 'DoubleRedirects' as type," ;
+               $sql .=
+                        " pa.page_namespace as namespace, pa.page_title as title," .
+                        " pb.page_namespace as nsb, pb.page_title as tb," .
+                        " pc.page_namespace as nsc, pc.page_title as tc" .
+                  " FROM $redirect AS ra, $redirect AS rb, $page AS pa, $page AS pb, $page AS pc" .
+                  " WHERE ra.rd_from=pa.page_id" .
+                        " AND ra.rd_namespace=pb.page_namespace" .
+                        " AND ra.rd_title=pb.page_title" .
+                        " AND rb.rd_from=pb.page_id" .
+                        " AND rb.rd_namespace=pc.page_namespace" .
+                        " AND rb.rd_title=pc.page_title";
+
+               if( $limitToTitle ) {
+                       $encTitle = $dbr->addQuotes( $title );
+                       $sql .= " AND pa.page_namespace=$namespace" .
+                                       " AND pa.page_title=$encTitle";
+               }
+
+               return $sql;
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               return $this->getSQLText( $dbr );
+       }
+
+       function getOrder() {
+               return '';
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang;
+
+               $fname = 'DoubleRedirectsPage::formatResult';
+               $titleA = Title::makeTitle( $result->namespace, $result->title );
+
+               if ( $result && !isset( $result->nsb ) ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $sql = $this->getSQLText( $dbr, $result->namespace, $result->title );
+                       $res = $dbr->query( $sql, $fname );
+                       if ( $res ) {
+                               $result = $dbr->fetchObject( $res );
+                               $dbr->freeResult( $res );
+                       }
+               }
+               if ( !$result ) {
+                       return '<s>' . $skin->makeLinkObj( $titleA, '', 'redirect=no' ) . '</s>';
+               }
+
+               $titleB = Title::makeTitle( $result->nsb, $result->tb );
+               $titleC = Title::makeTitle( $result->nsc, $result->tc );
+
+               $linkA = $skin->makeKnownLinkObj( $titleA, '', 'redirect=no' );
+               $edit = $skin->makeBrokenLinkObj( $titleA, "(".wfMsg("qbedit").")" , 'redirect=no');
+               $linkB = $skin->makeKnownLinkObj( $titleB, '', 'redirect=no' );
+               $linkC = $skin->makeKnownLinkObj( $titleC );
+               $arr = $wgContLang->getArrow() . $wgContLang->getDirMark();
+
+               return( "{$linkA} {$edit} {$arr} {$linkB} {$arr} {$linkC}" );
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialDoubleRedirects() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $sdr = new DoubleRedirectsPage();
+
+       return $sdr->doQuery( $offset, $limit );
+
+}
diff --git a/includes/specials/SpecialEmailuser.php b/includes/specials/SpecialEmailuser.php
new file mode 100644 (file)
index 0000000..596f16b
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @todo document
+ */
+function wfSpecialEmailuser( $par ) {
+       global $wgRequest, $wgUser, $wgOut;
+
+       $action = $wgRequest->getVal( 'action' );
+       $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
+       $targetUser = EmailUserForm::validateEmailTarget( $target );
+       
+       if ( !( $targetUser instanceof User ) ) {
+               $wgOut->showErrorPage( $targetUser[0], $targetUser[1] );
+               return;
+       }
+       
+       $form = new EmailUserForm( $targetUser,
+                       $wgRequest->getText( 'wpText' ),
+                       $wgRequest->getText( 'wpSubject' ),
+                       $wgRequest->getBool( 'wpCCMe' ) );
+       if ( $action == 'success' ) {
+               $form->showSuccess();
+               return;
+       }
+                                       
+       $error = EmailUserForm::getPermissionsError( $wgUser, $wgRequest->getVal( 'wpEditToken' ) );
+       if ( $error ) {
+               switch ( $error[0] ) {
+                       case 'blockedemailuser':
+                               $wgOut->blockedPage();
+                               return;
+                       case 'actionthrottledtext':
+                               $wgOut->rateLimited();
+                               return;
+                       case 'sessionfailure':
+                               $form->showForm();
+                               return;
+                       default:
+                               $wgOut->showErrorPage( $error[0], $error[1] );
+                               return;
+               }
+       }       
+               
+       
+       if ( "submit" == $action && $wgRequest->wasPosted() ) {
+               $result = $form->doSubmit();
+               
+               if ( !is_null( $result ) ) {
+                       $wgOut->addHTML( wfMsg( "usermailererror" ) .
+                                       ' ' . htmlspecialchars( $result->getMessage() ) );
+               } else {
+                       $titleObj = SpecialPage::getTitleFor( "Emailuser" );
+                       $encTarget = wfUrlencode( $form->getTarget()->getName() );
+                       $wgOut->redirect( $titleObj->getFullURL( "target={$encTarget}&action=success" ) );
+               }
+       } else {
+               $form->showForm();
+       }
+}
+
+/**
+ * Implements the Special:Emailuser web interface, and invokes userMailer for sending the email message.
+ * @ingroup SpecialPage
+ */
+class EmailUserForm {
+
+       var $target;
+       var $text, $subject;
+       var $cc_me;     // Whether user requested to be sent a separate copy of their email.
+
+       /**
+        * @param User $target
+        */
+       function EmailUserForm( $target, $text, $subject, $cc_me ) {
+               $this->target = $target;
+               $this->text = $text;
+               $this->subject = $subject;
+               $this->cc_me = $cc_me;
+       }
+
+       function showForm() {
+               global $wgOut, $wgUser;
+               $skin = $wgUser->getSkin();
+
+               $wgOut->setPagetitle( wfMsg( "emailpage" ) );
+               $wgOut->addWikiMsg( "emailpagetext" );
+
+               if ( $this->subject === "" ) {
+                       $this->subject = wfMsgForContent( "defemailsubject" );
+               }
+
+               $emf = wfMsg( "emailfrom" );
+               $senderLink = $skin->makeLinkObj(
+                       $wgUser->getUserPage(), htmlspecialchars( $wgUser->getName() ) );
+               $emt = wfMsg( "emailto" );
+               $recipientLink = $skin->makeLinkObj(
+                       $this->target->getUserPage(), htmlspecialchars( $this->target->getName() ) );
+               $emr = wfMsg( "emailsubject" );
+               $emm = wfMsg( "emailmessage" );
+               $ems = wfMsg( "emailsend" );
+               $emc = wfMsg( "emailccme" );
+               $encSubject = htmlspecialchars( $this->subject );
+
+               $titleObj = SpecialPage::getTitleFor( "Emailuser" );
+               $action = $titleObj->escapeLocalURL( "target=" .
+                       urlencode( $this->target->getName() ) . "&action=submit" );
+               $token = htmlspecialchars( $wgUser->editToken() );
+
+               $wgOut->addHTML( "
+<form id=\"emailuser\" method=\"post\" action=\"{$action}\">
+<table border='0' id='mailheader'><tr>
+<td align='right'>{$emf}:</td>
+<td align='left'><strong>{$senderLink}</strong></td>
+</tr><tr>
+<td align='right'>{$emt}:</td>
+<td align='left'><strong>{$recipientLink}</strong></td>
+</tr><tr>
+<td align='right'>{$emr}:</td>
+<td align='left'>
+<input type='text' size='60' maxlength='200' name=\"wpSubject\" value=\"{$encSubject}\" />
+</td>
+</tr>
+</table>
+<span id='wpTextLabel'><label for=\"wpText\">{$emm}:</label><br /></span>
+<textarea id=\"wpText\" name=\"wpText\" rows='20' cols='80' style=\"width: 100%;\">" . htmlspecialchars( $this->text ) .
+"</textarea>
+" . wfCheckLabel( $emc, 'wpCCMe', 'wpCCMe', $wgUser->getBoolOption( 'ccmeonemails' ) ) . "<br />
+<input type='submit' name=\"wpSend\" value=\"{$ems}\" />
+<input type='hidden' name='wpEditToken' value=\"$token\" />
+</form>\n" );
+
+       }
+
+       /*
+        * Really send a mail. Permissions should have been checked using 
+        * EmailUserForm::getPermissionsError. It is probably also a good idea to
+        * check the edit token and ping limiter in advance.
+        */
+       function doSubmit() {
+               global $wgUser, $wgUserEmailUseReplyTo, $wgSiteName;
+
+               $to = new MailAddress( $this->target );
+               $from = new MailAddress( $wgUser );
+               $subject = $this->subject;
+
+               $prefsTitle = Title::newFromText( 'Preferences', NS_SPECIAL );
+               
+               // Add a standard footer
+               $footerArgs[0] = $from->name;
+               $footerArgs[1] = $to->name;
+               $footerArgs[2] = $prefsTitle->getFullURL();
+               $footerArgs[3] = wfMsg ('allowemail');
+               $this->text = $this->text . "\n" . wfMsgExt( 'emailuserfooter', 'parsemag', $footerArgs );
+               
+               if( wfRunHooks( 'EmailUser', array( &$to, &$from, &$subject, &$this->text ) ) ) {
+
+                       if( $wgUserEmailUseReplyTo ) {
+                               // Put the generic wiki autogenerated address in the From:
+                               // header and reserve the user for Reply-To.
+                               //
+                               // This is a bit ugly, but will serve to differentiate
+                               // wiki-borne mails from direct mails and protects against
+                               // SPF and bounce problems with some mailers (see below).
+                               global $wgPasswordSender;
+                               $mailFrom = new MailAddress( $wgPasswordSender );
+                               $replyTo = $from;
+                       } else {
+                               // Put the sending user's e-mail address in the From: header.
+                               //
+                               // This is clean-looking and convenient, but has issues.
+                               // One is that it doesn't as clearly differentiate the wiki mail
+                               // from "directly" sent mails.
+                               //
+                               // Another is that some mailers (like sSMTP) will use the From
+                               // address as the envelope sender as well. For open sites this
+                               // can cause mails to be flunked for SPF violations (since the
+                               // wiki server isn't an authorized sender for various users'
+                               // domains) as well as creating a privacy issue as bounces
+                               // containing the recipient's e-mail address may get sent to
+                               // the sending user.
+                               $mailFrom = $from;
+                               $replyTo = null;
+                       }
+                       
+                       $mailResult = UserMailer::send( $to, $mailFrom, $subject, $this->text, $replyTo );
+
+                       if( WikiError::isError( $mailResult ) ) {
+                               return $mailResult;
+                               
+                       } else {
+
+                               // if the user requested a copy of this mail, do this now,
+                               // unless they are emailing themselves, in which case one copy of the message is sufficient.
+                               if ($this->cc_me && $to != $from) {
+                                       $cc_subject = wfMsg('emailccsubject', $this->target->getName(), $subject);
+                                       if( wfRunHooks( 'EmailUser', array( &$from, &$from, &$cc_subject, &$this->text ) ) ) {
+                                               $ccResult = UserMailer::send( $from, $from, $cc_subject, $this->text );
+                                               if( WikiError::isError( $ccResult ) ) {
+                                                       // At this stage, the user's CC mail has failed, but their
+                                                       // original mail has succeeded. It's unlikely, but still, what to do?
+                                                       // We can either show them an error, or we can say everything was fine,
+                                                       // or we can say we sort of failed AND sort of succeeded. Of these options,
+                                                       // simply saying there was an error is probably best.
+                                                       return $ccResult;
+                                               }
+                                       }
+                               }
+
+                               wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $this->text ) );
+                               return;
+                       }
+               }
+       }
+
+       function showSuccess( &$user = null ) {
+               global $wgOut;
+               
+               if ( is_null($user) )
+                       $user = $this->target;
+
+               $wgOut->setPagetitle( wfMsg( "emailsent" ) );
+               $wgOut->addHTML( wfMsg( "emailsenttext" ) );
+
+               $wgOut->returnToMain( false, $user->getUserPage() );
+       }
+       
+       function getTarget() {
+               return $this->target;
+       }
+       
+       static function validateEmailTarget ( $target ) {
+               global $wgEnableEmail, $wgEnableUserEmail;
+
+               if( !( $wgEnableEmail && $wgEnableUserEmail ) ) 
+                       return array( "nosuchspecialpage", "nospecialpagetext" );
+               
+               if ( "" == $target ) {
+                       wfDebug( "Target is empty.\n" );
+                       return array( "notargettitle", "notargettext" );
+               }
+       
+               $nt = Title::newFromURL( $target );
+               if ( is_null( $nt ) ) {
+                       wfDebug( "Target is invalid title.\n" );
+                       return array( "notargettitle", "notargettext" );
+               }
+       
+               $nu = User::newFromName( $nt->getText() );
+               if( is_null( $nu ) || !$nu->canReceiveEmail() ) {
+                       wfDebug( "Target is invalid user or can't receive.\n" );
+                       return array( "noemailtitle", "noemailtext" );
+               }
+               
+               return $nu;
+       }
+       static function getPermissionsError ( $user, $editToken ) {
+               if( !$user->canSendEmail() ) {
+                       wfDebug( "User can't send.\n" );
+                       return array( "mailnologin", "mailnologintext" );
+               }
+               
+               if( $user->isBlockedFromEmailuser() ) {
+                       wfDebug( "User is blocked from sending e-mail.\n" );
+                       return array( "blockedemailuser", "" );
+               }
+               
+               if( $user->pingLimiter( 'emailuser' ) ) {
+                       wfDebug( "Ping limiter triggered.\n" ); 
+                       return array( 'actionthrottledtext', '' );
+               }
+               
+               if( !$user->matchEditToken( $editToken ) ) {
+                       wfDebug( "Matching edit token failed.\n" );
+                       return array( 'sessionfailure', '' );
+               }
+               
+               return;
+       }
+       
+       static function newFromURL( $target, $text, $subject, $cc_me )
+       {
+               $nt = Title::newFromURL( $target );
+               $nu = User::newFromName( $nt->getText() );
+               return new EmailUserForm( $nu, $text, $subject, $cc_me );
+       }
+}
diff --git a/includes/specials/SpecialExport.php b/includes/specials/SpecialExport.php
new file mode 100644 (file)
index 0000000..38bfc83
--- /dev/null
@@ -0,0 +1,284 @@
+<?php
+# Copyright (C) 2003 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+function wfExportGetPagesFromCategory( $title ) {
+       global $wgContLang;
+
+       $name = $title->getDBkey();
+
+       $dbr = wfGetDB( DB_SLAVE );
+
+       list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
+       $sql = "SELECT page_namespace, page_title FROM $page " .
+               "JOIN $categorylinks ON cl_from = page_id " .
+               "WHERE cl_to = " . $dbr->addQuotes( $name );
+
+       $pages = array();
+       $res = $dbr->query( $sql, 'wfExportGetPagesFromCategory' );
+       while ( $row = $dbr->fetchObject( $res ) ) {
+               $n = $row->page_title;
+               if ($row->page_namespace) {
+                       $ns = $wgContLang->getNsText( $row->page_namespace );
+                       $n = $ns . ':' . $n;
+               }
+
+               $pages[] = $n;
+       }
+       $dbr->freeResult($res);
+
+       return $pages;
+}
+
+/**
+ * Expand a list of pages to include templates used in those pages.
+ * @param $inputPages array, list of titles to look up
+ * @param $pageSet array, associative array indexed by titles for output
+ * @return array associative array index by titles
+ */
+function wfExportGetTemplates( $inputPages, $pageSet ) {
+       return wfExportGetLinks( $inputPages, $pageSet,
+               'templatelinks',
+               array( 'tl_namespace AS namespace', 'tl_title AS title' ),
+               array( 'page_id=tl_from' ) );
+}
+
+/**
+ * Expand a list of pages to include images used in those pages.
+ * @param $inputPages array, list of titles to look up
+ * @param $pageSet array, associative array indexed by titles for output
+ * @return array associative array index by titles
+ */
+function wfExportGetImages( $inputPages, $pageSet ) {
+       return wfExportGetLinks( $inputPages, $pageSet,
+               'imagelinks',
+               array( NS_IMAGE . ' AS namespace', 'il_to AS title' ),
+               array( 'page_id=il_from' ) );
+}
+
+/**
+ * Expand a list of pages to include items used in those pages.
+ * @private
+ */
+function wfExportGetLinks( $inputPages, $pageSet, $table, $fields, $join ) {
+       $dbr = wfGetDB( DB_SLAVE );
+       foreach( $inputPages as $page ) {
+               $title = Title::newFromText( $page );
+               if( $title ) {
+                       $pageSet[$title->getPrefixedText()] = true;
+                       /// @fixme May or may not be more efficient to batch these
+                       ///        by namespace when given multiple input pages.
+                       $result = $dbr->select(
+                               array( 'page', $table ),
+                               $fields,
+                               array_merge( $join,
+                                       array(
+                                               'page_namespace' => $title->getNamespace(),
+                                               'page_title' => $title->getDbKey() ) ),
+                               __METHOD__ );
+                       foreach( $result as $row ) {
+                               $template = Title::makeTitle( $row->namespace, $row->title );
+                               $pageSet[$template->getPrefixedText()] = true;
+                       }
+               }
+       }
+       return $pageSet;
+}
+
+/**
+ * Callback function to remove empty strings from the pages array.
+ */
+function wfFilterPage( $page ) {
+       return $page !== '' && $page !== null;
+}
+
+/**
+ *
+ */
+function wfSpecialExport( $page = '' ) {
+       global $wgOut, $wgRequest, $wgSitename, $wgExportAllowListContributors;
+       global $wgExportAllowHistory, $wgExportMaxHistory;
+
+       $curonly = true;
+       $doexport = false;
+
+       if ( $wgRequest->getCheck( 'addcat' ) ) {
+               $page = $wgRequest->getText( 'pages' );
+               $catname = $wgRequest->getText( 'catname' );
+
+               if ( $catname !== '' && $catname !== NULL && $catname !== false ) {
+                       $t = Title::makeTitleSafe( NS_CATEGORY, $catname );
+                       if ( $t ) {
+                               /**
+                                * @fixme This can lead to hitting memory limit for very large
+                                * categories. Ideally we would do the lookup synchronously
+                                * during the export in a single query.
+                                */
+                               $catpages = wfExportGetPagesFromCategory( $t );
+                               if ( $catpages ) $page .= "\n" . implode( "\n", $catpages );
+                       }
+               }
+       }
+       else if( $wgRequest->wasPosted() && $page == '' ) {
+               $page = $wgRequest->getText( 'pages' );
+               $curonly = $wgRequest->getCheck( 'curonly' );
+               $rawOffset = $wgRequest->getVal( 'offset' );
+               if( $rawOffset ) {
+                       $offset = wfTimestamp( TS_MW, $rawOffset );
+               } else {
+                       $offset = null;
+               }
+               $limit = $wgRequest->getInt( 'limit' );
+               $dir = $wgRequest->getVal( 'dir' );
+               $history = array(
+                       'dir' => 'asc',
+                       'offset' => false,
+                       'limit' => $wgExportMaxHistory,
+               );
+               $historyCheck = $wgRequest->getCheck( 'history' );
+               if ( $curonly ) {
+                       $history = WikiExporter::CURRENT;
+               } elseif ( !$historyCheck ) {
+                       if ( $limit > 0 && $limit < $wgExportMaxHistory ) {
+                               $history['limit'] = $limit;
+                       }
+                       if ( !is_null( $offset ) ) {
+                               $history['offset'] = $offset;
+                       }
+                       if ( strtolower( $dir ) == 'desc' ) {
+                               $history['dir'] = 'desc';
+                       }
+               }
+
+               if( $page != '' ) $doexport = true;
+       } else {
+               // Default to current-only for GET requests
+               $page = $wgRequest->getText( 'pages', $page );
+               $historyCheck = $wgRequest->getCheck( 'history' );
+               if( $historyCheck ) {
+                       $history = WikiExporter::FULL;
+               } else {
+                       $history = WikiExporter::CURRENT;
+               }
+
+               if( $page != '' ) $doexport = true;
+       }
+
+       if( !$wgExportAllowHistory ) {
+               // Override
+               $history = WikiExporter::CURRENT;
+       }
+
+       $list_authors = $wgRequest->getCheck( 'listauthors' );
+       if ( !$curonly || !$wgExportAllowListContributors ) $list_authors = false ;
+
+       if ( $doexport ) {
+               $wgOut->disable();
+
+               // Cancel output buffering and gzipping if set
+               // This should provide safer streaming for pages with history
+               wfResetOutputBuffers();
+               header( "Content-type: application/xml; charset=utf-8" );
+               if( $wgRequest->getCheck( 'wpDownload' ) ) {
+                       // Provide a sane filename suggestion
+                       $filename = urlencode( $wgSitename . '-' . wfTimestampNow() . '.xml' );
+                       $wgRequest->response()->header( "Content-disposition: attachment;filename={$filename}" );
+               }
+
+               /* Split up the input and look up linked pages */
+               $inputPages = array_filter( explode( "\n", $page ), 'wfFilterPage' );
+               $pageSet = array_flip( $inputPages );
+
+               if( $wgRequest->getCheck( 'templates' ) ) {
+                       $pageSet = wfExportGetTemplates( $inputPages, $pageSet );
+               }
+
+               /*
+               // Enable this when we can do something useful exporting/importing image information. :)
+               if( $wgRequest->getCheck( 'images' ) ) {
+                       $pageSet = wfExportGetImages( $inputPages, $pageSet );
+               }
+               */
+
+               $pages = array_keys( $pageSet );
+
+               /* Ok, let's get to it... */
+
+               $db = wfGetDB( DB_SLAVE );
+               $exporter = new WikiExporter( $db, $history );
+               $exporter->list_authors = $list_authors ;
+               $exporter->openStream();
+
+               foreach( $pages as $page ) {
+                       /*
+                       if( $wgExportMaxHistory && !$curonly ) {
+                               $title = Title::newFromText( $page );
+                               if( $title ) {
+                                       $count = Revision::countByTitle( $db, $title );
+                                       if( $count > $wgExportMaxHistory ) {
+                                               wfDebug( __FUNCTION__ .
+                                                       ": Skipped $page, $count revisions too big\n" );
+                                               continue;
+                                       }
+                               }
+                       }*/
+
+                       #Bug 8824: Only export pages the user can read
+                       $title = Title::newFromText( $page );
+                       if( is_null( $title ) ) continue; #TODO: perhaps output an <error> tag or something.
+                       if( !$title->userCanRead() ) continue; #TODO: perhaps output an <error> tag or something.
+
+                       $exporter->pageByTitle( $title );
+               }
+
+               $exporter->closeStream();
+               return;
+       }
+
+       $self = SpecialPage::getTitleFor( 'Export' );
+       $wgOut->addHtml( wfMsgExt( 'exporttext', 'parse' ) );
+
+       $form = Xml::openElement( 'form', array( 'method' => 'post',
+               'action' => $self->getLocalUrl( 'action=submit' ) ) );
+
+       $form .= Xml::inputLabel( wfMsg( 'export-addcattext' )  , 'catname', 'catname', 40 ) . '&nbsp;';
+       $form .= Xml::submitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
+
+       $form .= Xml::openElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) );
+       $form .= htmlspecialchars( $page );
+       $form .= Xml::closeElement( 'textarea' );
+       $form .= '<br />';
+
+       if( $wgExportAllowHistory ) {
+               $form .= Xml::checkLabel( wfMsg( 'exportcuronly' ), 'curonly', 'curonly', true ) . '<br />';
+       } else {
+               $wgOut->addHtml( wfMsgExt( 'exportnohistory', 'parse' ) );
+       }
+       $form .= Xml::checkLabel( wfMsg( 'export-templates' ), 'templates', 'wpExportTemplates', false ) . '<br />';
+       // Enable this when we can do something useful exporting/importing image information. :)
+       //$form .= Xml::checkLabel( wfMsg( 'export-images' ), 'images', 'wpExportImages', false ) . '<br />';
+       $form .= Xml::checkLabel( wfMsg( 'export-download' ), 'wpDownload', 'wpDownload', true ) . '<br />';
+
+       $form .= Xml::submitButton( wfMsg( 'export-submit' ) );
+       $form .= Xml::closeElement( 'form' );
+       $wgOut->addHtml( $form );
+}
diff --git a/includes/specials/SpecialFewestrevisions.php b/includes/specials/SpecialFewestrevisions.php
new file mode 100644 (file)
index 0000000..5ad8136
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page for listing the articles with the fewest revisions.
+ *
+ * @ingroup SpecialPage
+ * @author Martin Drashkov
+ */
+class FewestrevisionsPage extends QueryPage {
+
+       function getName() {
+               return 'Fewestrevisions';
+       }
+
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() {
+               return false;
+       }
+
+       function getSql() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
+
+               return "SELECT 'Fewestrevisions' as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               COUNT(*) as value
+                       FROM $revision
+                       JOIN $page ON page_id = rev_page
+                       WHERE page_namespace = " . NS_MAIN . "
+                       GROUP BY 1,2,3
+                       HAVING COUNT(*) > 1";
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+
+               $nt = Title::makeTitleSafe( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getPrefixedText() );
+
+               $plink = $skin->makeKnownLinkObj( $nt, $text );
+
+               $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->value ) );
+               $nlink = $skin->makeKnownLinkObj( $nt, $nl, 'action=history' );
+
+               return wfSpecialList( $plink, $nlink );
+       }
+}
+
+function wfSpecialFewestrevisions() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $frp = new FewestrevisionsPage();
+       $frp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php
new file mode 100644 (file)
index 0000000..5236ca2
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * A special page to search for files by hash value as defined in the
+ * img_sha1 field in the image table
+ *
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Raimond Spekking, based on Special:MIMESearch by Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * Searches the database for files of the requested hash, comparing this with the
+ * 'img_sha1' field in the image table.
+ * @ingroup SpecialPage
+ */
+class FileDuplicateSearchPage extends QueryPage {
+       var $hash, $filename;
+
+       function FileDuplicateSearchPage( $hash, $filename ) {
+               $this->hash = $hash;
+               $this->filename = $filename;
+       }
+
+       function getName() { return 'FileDuplicateSearch'; }
+       function isExpensive() { return false; }
+       function isSyndicated() { return false; }
+
+       function linkParameters() {
+               return array( 'filename' => $this->filename );
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $image = $dbr->tableName( 'image' );
+               $hash = $dbr->addQuotes( $this->hash );
+
+               return "SELECT 'FileDuplicateSearch' AS type,
+                               img_name AS title,
+                               img_sha1 AS value,
+                               img_user_text,
+                               img_timestamp
+                       FROM $image
+                       WHERE img_sha1 = $hash
+                       ";
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang, $wgLang;
+
+               $nt = Title::makeTitle( NS_IMAGE, $result->title );
+               $text = $wgContLang->convert( $nt->getText() );
+               $plink = $skin->makeLink( $nt->getPrefixedText(), $text );
+
+               $user = $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ), $result->img_user_text );
+               $time = $wgLang->timeanddate( $result->img_timestamp );
+
+               return "$plink . . $user . . $time";
+       }
+}
+
+/**
+ * Output the HTML search form, and constructs the FileDuplicateSearch object.
+ */
+function wfSpecialFileDuplicateSearch( $par = null ) {
+       global $wgRequest, $wgTitle, $wgOut, $wgLang, $wgContLang;
+
+       $hash = '';
+       $filename =  isset( $par ) ?  $par : $wgRequest->getText( 'filename' );
+
+       $title = Title::newFromText( $filename );
+       if( $title && $title->getText() != '' ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $image = $dbr->tableName( 'image' );
+               $encFilename = $dbr->addQuotes( htmlspecialchars( $title->getDbKey() ) );
+               $sql = "SELECT img_sha1 from $image where img_name = $encFilename";
+               $res = $dbr->query( $sql );
+               $row = $dbr->fetchRow( $res );
+               if( $row !== false ) {
+                       $hash = $row[0];
+               }
+               $dbr->freeResult( $res );
+       }
+
+       # Create the input form
+       $wgOut->addHTML(
+               Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgTitle->getLocalUrl() ) ) .
+               Xml::openElement( 'fieldset' ) .
+               Xml::element( 'legend', null, wfMsg( 'fileduplicatesearch-legend' ) ) .
+               Xml::inputLabel( wfMsg( 'fileduplicatesearch-filename' ), 'filename', 'filename', 50, $filename ) . ' ' .
+               Xml::submitButton( wfMsg( 'fileduplicatesearch-submit' ) ) .
+               Xml::closeElement( 'fieldset' ) .
+               Xml::closeElement( 'form' )
+       );
+
+       if( $hash != '' ) {
+               $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+               # Show a thumbnail of the file
+               $img = wfFindFile( $title );
+               if ( $img ) {
+                       $thumb = $img->getThumbnail( 120, 120 );
+                       if( $thumb ) {
+                               $wgOut->addHTML( '<div style="float:' . $align . '" id="mw-fileduplicatesearch-icon">' .
+                                       $thumb->toHtml( array( 'desc-link' => false ) ) . '<br />' .
+                                       wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ),
+                                               $wgLang->formatNum( $img->getWidth() ),
+                                               $wgLang->formatNum( $img->getHeight() ),
+                                               $wgLang->formatSize( $img->getSize() ),
+                                               $img->getMimeType()
+                                       ) .
+                                       '</div>' );
+                       }
+               }
+
+               # Do the query
+               $wpp = new FileDuplicateSearchPage( $hash, $filename );
+               list( $limit, $offset ) = wfCheckLimits();
+               $count = $wpp->doQuery( $offset, $limit );
+
+               # Show a short summary
+               if( $count == 1 ) {
+                       $wgOut->addHTML( '<p class="mw-fileduplicatesearch-result-1">' .
+                               wfMsgHtml( 'fileduplicatesearch-result-1', $filename ) .
+                               '</p>'
+                       );
+               } elseif ( $count > 1 ) {
+                       $wgOut->addHTML( '<p class="mw-fileduplicatesearch-result-n">' .
+                               wfMsgExt( 'fileduplicatesearch-result-n', array( 'parseinline' ), $filename, $wgLang->formatNum( $count - 1 ) ) .
+                               '</p>'
+                       );
+               }
+       }
+}
diff --git a/includes/specials/SpecialFilepath.php b/includes/specials/SpecialFilepath.php
new file mode 100644 (file)
index 0000000..a2ba3e5
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+function wfSpecialFilepath( $par ) {
+       global $wgRequest, $wgOut;
+
+       $file = isset( $par ) ? $par : $wgRequest->getText( 'file' );
+
+       $title = Title::newFromText( $file, NS_IMAGE );
+
+       if ( ! $title instanceof Title || $title->getNamespace() != NS_IMAGE ) {
+               $cform = new FilepathForm( $title );
+               $cform->execute();
+       } else {
+               $file = wfFindFile( $title );
+               if ( $file && $file->exists() ) {
+                       $wgOut->redirect( $file->getURL() );
+               } else {
+                       $wgOut->setStatusCode( 404 );
+                       $cform = new FilepathForm( $title );
+                       $cform->execute();
+               }
+       }
+}
+
+/**
+ * @ingroup SpecialPage
+ */
+class FilepathForm {
+       var $mTitle;
+
+       function FilepathForm( &$title ) {
+               $this->mTitle =& $title;
+       }
+
+       function execute() {
+               global $wgOut, $wgTitle, $wgScript;
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'specialfilepath' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'filepath' ) ) .
+                       Xml::hidden( 'title', $wgTitle->getPrefixedText() ) .
+                       Xml::inputLabel( wfMsg( 'filepath-page' ), 'file', 'file', 25, is_object( $this->mTitle ) ? $this->mTitle->getText() : '' ) . ' ' .
+                       Xml::submitButton( wfMsg( 'filepath-submit' ) ) . "\n" .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' )
+               );
+       }
+}
diff --git a/includes/specials/SpecialImagelist.php b/includes/specials/SpecialImagelist.php
new file mode 100644 (file)
index 0000000..3d449b5
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ */
+function wfSpecialImagelist() {
+       global $wgOut;
+
+       $pager = new ImageListPager;
+
+       $limit = $pager->getForm();
+       $body = $pager->getBody();
+       $nav = $pager->getNavigationBar();
+       $wgOut->addHTML( "$limit<br />\n$body<br />\n$nav" );
+}
+
+/**
+ * @ingroup SpecialPage Pager
+ */
+class ImageListPager extends TablePager {
+       var $mFieldNames = null;
+       var $mQueryConds = array();
+
+       function __construct() {
+               global $wgRequest, $wgMiserMode;
+               if ( $wgRequest->getText( 'sort', 'img_date' ) == 'img_date' ) {
+                       $this->mDefaultDirection = true;
+               } else {
+                       $this->mDefaultDirection = false;
+               }
+               $search = $wgRequest->getText( 'ilsearch' );
+               if ( $search != '' && !$wgMiserMode ) {
+                       $nt = Title::newFromUrl( $search );
+                       if( $nt ) {
+                               $dbr = wfGetDB( DB_SLAVE );
+                               $m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
+                               $m = str_replace( "%", "\\%", $m );
+                               $m = str_replace( "_", "\\_", $m );
+                               $this->mQueryConds = array( "LOWER(img_name) LIKE '%{$m}%'" );
+                       }
+               }
+
+               parent::__construct();
+       }
+
+       function getFieldNames() {
+               if ( !$this->mFieldNames ) {
+                       $this->mFieldNames = array(
+                               'img_timestamp' => wfMsg( 'imagelist_date' ),
+                               'img_name' => wfMsg( 'imagelist_name' ),
+                               'img_user_text' => wfMsg( 'imagelist_user' ),
+                               'img_size' => wfMsg( 'imagelist_size' ),
+                               'img_description' => wfMsg( 'imagelist_description' ),
+                       );
+               }
+               return $this->mFieldNames;
+       }
+
+       function isFieldSortable( $field ) {
+               static $sortable = array( 'img_timestamp', 'img_name', 'img_size' );
+               return in_array( $field, $sortable );
+       }
+
+       function getQueryInfo() {
+               $fields = $this->getFieldNames();
+               $fields = array_keys( $fields );
+               $fields[] = 'img_user';
+               return array(
+                       'tables' => 'image',
+                       'fields' => $fields,
+                       'conds' => $this->mQueryConds
+               );
+       }
+
+       function getDefaultSort() {
+               return 'img_timestamp';
+       }
+
+       function getStartBody() {
+               # Do a link batch query for user pages
+               if ( $this->mResult->numRows() ) {
+                       $lb = new LinkBatch;
+                       $this->mResult->seek( 0 );
+                       while ( $row = $this->mResult->fetchObject() ) {
+                               if ( $row->img_user ) {
+                                       $lb->add( NS_USER, str_replace( ' ', '_', $row->img_user_text ) );
+                               }
+                       }
+                       $lb->execute();
+               }
+
+               return parent::getStartBody();
+       }
+
+       function formatValue( $field, $value ) {
+               global $wgLang;
+               switch ( $field ) {
+                       case 'img_timestamp':
+                               return $wgLang->timeanddate( $value, true );
+                       case 'img_name':
+                               static $imgfile = null;
+                               if ( $imgfile === null ) $imgfile = wfMsg( 'imgfile' );
+
+                               $name = $this->mCurrentRow->img_name;
+                               $link = $this->getSkin()->makeKnownLinkObj( Title::makeTitle( NS_IMAGE, $name ), $value );
+                               $image = wfLocalFile( $value );
+                               $url = $image->getURL();
+                               $download = Xml::element('a', array( 'href' => $url ), $imgfile );
+                               return "$link ($download)";
+                       case 'img_user_text':
+                               if ( $this->mCurrentRow->img_user ) {
+                                       $link = $this->getSkin()->makeLinkObj( Title::makeTitle( NS_USER, $value ),
+                                               htmlspecialchars( $value ) );
+                               } else {
+                                       $link = htmlspecialchars( $value );
+                               }
+                               return $link;
+                       case 'img_size':
+                               return $this->getSkin()->formatSize( $value );
+                       case 'img_description':
+                               return $this->getSkin()->commentBlock( $value );
+               }
+       }
+
+       function getForm() {
+               global $wgRequest, $wgMiserMode;
+               $search = $wgRequest->getText( 'ilsearch' );
+
+               $s = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $this->getTitle()->getLocalURL(), 'id' => 'mw-imagelist-form' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'imagelist' ) ) .
+                       Xml::tags( 'label', null, wfMsgHtml( 'table_pager_limit', $this->getLimitSelect() ) );
+
+               if ( !$wgMiserMode ) {
+                       $s .= "<br />\n" .
+                               Xml::inputLabel( wfMsg( 'imagelist_search_for' ), 'ilsearch', 'mw-ilsearch', 20, $search );
+               }
+               $s .= ' ' .
+                       Xml::submitButton( wfMsg( 'table_pager_limit_submit' ) ) ."\n" .
+                       $this->getHiddenFields( array( 'limit', 'ilsearch' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n";
+               return $s;
+       }
+
+       function getTableClass() {
+               return 'imagelist ' . parent::getTableClass();
+       }
+
+       function getNavClass() {
+               return 'imagelist_nav ' . parent::getNavClass();
+       }
+
+       function getSortHeaderClass() {
+               return 'imagelist_sort ' . parent::getSortHeaderClass();
+       }
+}
diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php
new file mode 100644 (file)
index 0000000..4c37f1f
--- /dev/null
@@ -0,0 +1,1154 @@
+<?php
+/**
+ * MediaWiki page data importer
+ * Copyright (C) 2003,2005 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialImport( $page = '' ) {
+       global $wgUser, $wgOut, $wgRequest, $wgTitle, $wgImportSources;
+       global $wgImportTargetNamespace;
+
+       $interwiki = false;
+       $namespace = $wgImportTargetNamespace;
+       $frompage = '';
+       $history = true;
+
+       if ( wfReadOnly() ) {
+               $wgOut->readOnlyPage();
+               return;
+       }
+
+       if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') {
+               $isUpload = false;
+               $namespace = $wgRequest->getIntOrNull( 'namespace' );
+
+               switch( $wgRequest->getVal( "source" ) ) {
+               case "upload":
+                       $isUpload = true;
+                       if( $wgUser->isAllowed( 'importupload' ) ) {
+                               $source = ImportStreamSource::newFromUpload( "xmlimport" );
+                       } else {
+                               return $wgOut->permissionRequired( 'importupload' );
+                       }
+                       break;
+               case "interwiki":
+                       $interwiki = $wgRequest->getVal( 'interwiki' );
+                       $history = $wgRequest->getCheck( 'interwikiHistory' );
+                       $frompage = $wgRequest->getText( "frompage" );
+                       $source = ImportStreamSource::newFromInterwiki(
+                               $interwiki,
+                               $frompage,
+                               $history );
+                       break;
+               default:
+                       $source = new WikiErrorMsg( "importunknownsource" );
+               }
+
+               if( WikiError::isError( $source ) ) {
+                       $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $source->getMessage() ) );
+               } else {
+                       $wgOut->addWikiMsg( "importstart" );
+
+                       $importer = new WikiImporter( $source );
+                       if( !is_null( $namespace ) ) {
+                               $importer->setTargetNamespace( $namespace );
+                       }
+                       $reporter = new ImportReporter( $importer, $isUpload, $interwiki );
+
+                       $reporter->open();
+                       $result = $importer->doImport();
+                       $resultCount = $reporter->close();
+
+                       if( WikiError::isError( $result ) ) {
+                               # No source or XML parse error
+                               $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $result->getMessage() ) );
+                       } elseif( WikiError::isError( $resultCount ) ) {
+                               # Zero revisions
+                               $wgOut->wrapWikiMsg( '<p class="error">$1</p>', array( 'importfailed', $resultCount->getMessage() ) );
+                       } else {
+                               # Success!
+                               $wgOut->addWikiMsg( 'importsuccess' );
+                       }
+                       $wgOut->addWikiText( '<hr />' );
+               }
+       }
+
+       $action = $wgTitle->getLocalUrl( 'action=submit' );
+
+       if( $wgUser->isAllowed( 'importupload' ) ) {
+               $wgOut->addWikiMsg( "importtext" );
+               $wgOut->addHTML(
+                       Xml::openElement( 'fieldset' ).
+                       Xml::element( 'legend', null, wfMsg( 'import-upload' ) ) .
+                       Xml::openElement( 'form', array( 'enctype' => 'multipart/form-data', 'method' => 'post', 'action' => $action ) ) .
+                       Xml::hidden( 'action', 'submit' ) .
+                       Xml::hidden( 'source', 'upload' ) .
+                       Xml::input( 'xmlimport', 50, '', array( 'type' => 'file' ) ) . ' ' .
+                       Xml::submitButton( wfMsg( 'uploadbtn' ) ) .
+                       Xml::closeElement( 'form' ) .
+                       Xml::closeElement( 'fieldset' )
+               );
+       } else {
+               if( empty( $wgImportSources ) ) {
+                       $wgOut->addWikiMsg( 'importnosources' );
+               }
+       }
+
+       if( !empty( $wgImportSources ) ) {
+               $wgOut->addHTML(
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'importinterwiki' ) ) .
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action ) ) .
+                       wfMsgExt( 'import-interwiki-text', array( 'parse' ) ) .
+                       Xml::hidden( 'action', 'submit' ) .
+                       Xml::hidden( 'source', 'interwiki' ) .
+                       Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
+                       "<tr>
+                               <td>" .
+                                       Xml::openElement( 'select', array( 'name' => 'interwiki' ) )
+               );
+               foreach( $wgImportSources as $prefix ) {
+                       $selected = ( $interwiki === $prefix ) ? ' selected="selected"' : '';
+                       $wgOut->addHTML( Xml::option( $prefix, $prefix, $selected ) );
+               }
+               $wgOut->addHTML(
+                                       Xml::closeElement( 'select' ) .
+                               "</td>
+                               <td>" .
+                                       Xml::input( 'frompage', 50, $frompage ) .
+                               "</td>
+                       </tr>
+                       <tr>
+                               <td>
+                               </td>
+                               <td>" .
+                                       Xml::checkLabel( wfMsg( 'import-interwiki-history' ), 'interwikiHistory', 'interwikiHistory', $history ) .
+                               "</td>
+                       </tr>
+                       <tr>
+                               <td>
+                               </td>
+                               <td>" .
+                                       Xml::label( wfMsg( 'import-interwiki-namespace' ), 'namespace' ) .
+                                       Xml::namespaceSelector( $namespace, '' ) .
+                               "</td>
+                       </tr>
+                       <tr>
+                               <td>
+                               </td>
+                               <td>" .
+                                       Xml::submitButton( wfMsg( 'import-interwiki-submit' ) ) .
+                               "</td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ).
+                       Xml::closeElement( 'form' ) .
+                       Xml::closeElement( 'fieldset' )
+               );
+       }
+}
+
+/**
+ * Reporting callback
+ * @ingroup SpecialPage
+ */
+class ImportReporter {
+       function __construct( $importer, $upload, $interwiki ) {
+               $importer->setPageOutCallback( array( $this, 'reportPage' ) );
+               $this->mPageCount = 0;
+               $this->mIsUpload = $upload;
+               $this->mInterwiki = $interwiki;
+       }
+
+       function open() {
+               global $wgOut;
+               $wgOut->addHtml( "<ul>\n" );
+       }
+
+       function reportPage( $title, $origTitle, $revisionCount, $successCount ) {
+               global $wgOut, $wgUser, $wgLang, $wgContLang;
+
+               $skin = $wgUser->getSkin();
+
+               $this->mPageCount++;
+
+               $localCount = $wgLang->formatNum( $successCount );
+               $contentCount = $wgContLang->formatNum( $successCount );
+
+               if( $successCount > 0 ) {
+                       $wgOut->addHtml( "<li>" . $skin->makeKnownLinkObj( $title ) . " " .
+                               wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
+                               "</li>\n"
+                       );
+
+                       $log = new LogPage( 'import' );
+                       if( $this->mIsUpload ) {
+                               $detail = wfMsgExt( 'import-logentry-upload-detail', array( 'content', 'parsemag' ),
+                                       $contentCount );
+                               $log->addEntry( 'upload', $title, $detail );
+                       } else {
+                               $interwiki = '[[:' . $this->mInterwiki . ':' .
+                                       $origTitle->getPrefixedText() . ']]';
+                               $detail = wfMsgExt( 'import-logentry-interwiki-detail', array( 'content', 'parsemag' ),
+                                       $contentCount, $interwiki );
+                               $log->addEntry( 'interwiki', $title, $detail );
+                       }
+
+                       $comment = $detail; // quick
+                       $dbw = wfGetDB( DB_MASTER );
+                       $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleId(), $comment, true );
+                       $nullRevision->insertOn( $dbw );
+                       $article = new Article( $title );
+                       # Update page record
+                       $article->updateRevisionOn( $dbw, $nullRevision );
+                       wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, false) );
+               } else {
+                       $wgOut->addHtml( '<li>' . wfMsgHtml( 'import-nonewrevisions' ) . '</li>' );
+               }
+       }
+
+       function close() {
+               global $wgOut;
+               if( $this->mPageCount == 0 ) {
+                       $wgOut->addHtml( "</ul>\n" );
+                       return new WikiErrorMsg( "importnopages" );
+               }
+               $wgOut->addHtml( "</ul>\n" );
+
+               return $this->mPageCount;
+       }
+}
+
+/**
+ *
+ * @ingroup SpecialPage
+ */
+class WikiRevision {
+       var $title = null;
+       var $id = 0;
+       var $timestamp = "20010115000000";
+       var $user = 0;
+       var $user_text = "";
+       var $text = "";
+       var $comment = "";
+       var $minor = false;
+
+       function setTitle( $title ) {
+               if( is_object( $title ) ) {
+                       $this->title = $title;
+               } elseif( is_null( $title ) ) {
+                       throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
+               } else {
+                       throw new MWException( "WikiRevision given non-object title in import." );
+               }
+       }
+
+       function setID( $id ) {
+               $this->id = $id;
+       }
+
+       function setTimestamp( $ts ) {
+               # 2003-08-05T18:30:02Z
+               $this->timestamp = wfTimestamp( TS_MW, $ts );
+       }
+
+       function setUsername( $user ) {
+               $this->user_text = $user;
+       }
+
+       function setUserIP( $ip ) {
+               $this->user_text = $ip;
+       }
+
+       function setText( $text ) {
+               $this->text = $text;
+       }
+
+       function setComment( $text ) {
+               $this->comment = $text;
+       }
+
+       function setMinor( $minor ) {
+               $this->minor = (bool)$minor;
+       }
+
+       function setSrc( $src ) {
+               $this->src = $src;
+       }
+
+       function setFilename( $filename ) {
+               $this->filename = $filename;
+       }
+
+       function setSize( $size ) {
+               $this->size = intval( $size );
+       }
+
+       function getTitle() {
+               return $this->title;
+       }
+
+       function getID() {
+               return $this->id;
+       }
+
+       function getTimestamp() {
+               return $this->timestamp;
+       }
+
+       function getUser() {
+               return $this->user_text;
+       }
+
+       function getText() {
+               return $this->text;
+       }
+
+       function getComment() {
+               return $this->comment;
+       }
+
+       function getMinor() {
+               return $this->minor;
+       }
+
+       function getSrc() {
+               return $this->src;
+       }
+
+       function getFilename() {
+               return $this->filename;
+       }
+
+       function getSize() {
+               return $this->size;
+       }
+
+       function importOldRevision() {
+               $dbw = wfGetDB( DB_MASTER );
+
+               # Sneak a single revision into place
+               $user = User::newFromName( $this->getUser() );
+               if( $user ) {
+                       $userId = intval( $user->getId() );
+                       $userText = $user->getName();
+               } else {
+                       $userId = 0;
+                       $userText = $this->getUser();
+               }
+
+               // avoid memory leak...?
+               $linkCache = LinkCache::singleton();
+               $linkCache->clear();
+
+               $article = new Article( $this->title );
+               $pageId = $article->getId();
+               if( $pageId == 0 ) {
+                       # must create the page...
+                       $pageId = $article->insertOn( $dbw );
+                       $created = true;
+               } else {
+                       $created = false;
+
+                       $prior = Revision::loadFromTimestamp( $dbw, $this->title, $this->timestamp );
+                       if( !is_null( $prior ) ) {
+                               // FIXME: this could fail slightly for multiple matches :P
+                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
+                                       $this->title->getPrefixedText() . "]], timestamp " .
+                                       $this->timestamp . "\n" );
+                               return false;
+                       }
+               }
+
+               # FIXME: Use original rev_id optionally
+               # FIXME: blah blah blah
+
+               #if( $numrows > 0 ) {
+               #       return wfMsg( "importhistoryconflict" );
+               #}
+
+               # Insert the row
+               $revision = new Revision( array(
+                       'page'       => $pageId,
+                       'text'       => $this->getText(),
+                       'comment'    => $this->getComment(),
+                       'user'       => $userId,
+                       'user_text'  => $userText,
+                       'timestamp'  => $this->timestamp,
+                       'minor_edit' => $this->minor,
+                       ) );
+               $revId = $revision->insertOn( $dbw );
+               $changed = $article->updateIfNewerOn( $dbw, $revision );
+
+               if( $created ) {
+                       wfDebug( __METHOD__ . ": running onArticleCreate\n" );
+                       Article::onArticleCreate( $this->title );
+
+                       wfDebug( __METHOD__ . ": running create updates\n" );
+                       $article->createUpdates( $revision );
+
+               } elseif( $changed ) {
+                       wfDebug( __METHOD__ . ": running onArticleEdit\n" );
+                       Article::onArticleEdit( $this->title );
+
+                       wfDebug( __METHOD__ . ": running edit updates\n" );
+                       $article->editUpdates(
+                               $this->getText(),
+                               $this->getComment(),
+                               $this->minor,
+                               $this->timestamp,
+                               $revId );
+               }
+
+               return true;
+       }
+
+       function importUpload() {
+               wfDebug( __METHOD__ . ": STUB\n" );
+
+               /**
+                       // from file revert...
+                       $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
+                       $comment = $wgRequest->getText( 'wpComment' );
+                       // TODO: Preserve file properties from database instead of reloading from file
+                       $status = $this->file->upload( $source, $comment, $comment );
+                       if( $status->isGood() ) {
+               */
+
+               /**
+                       // from file upload...
+               $this->mLocalFile = wfLocalFile( $nt );
+               $this->mDestName = $this->mLocalFile->getName();
+               //....
+                       $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
+                       File::DELETE_SOURCE, $this->mFileProps );
+                       if ( !$status->isGood() ) {
+                               $resultDetails = array( 'internal' => $status->getWikiText() );
+               */
+
+               // @fixme upload() uses $wgUser, which is wrong here
+               // it may also create a page without our desire, also wrong potentially.
+               // and, it will record a *current* upload, but we might want an archive version here
+
+               $file = wfLocalFile( $this->getTitle() );
+               if( !$file ) {
+                       var_dump( $file );
+                       wfDebug( "IMPORT: Bad file. :(\n" );
+                       return false;
+               }
+
+               $source = $this->downloadSource();
+               if( !$source ) {
+                       wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
+                       return false;
+               }
+
+               $status = $file->upload( $source,
+                       $this->getComment(),
+                       $this->getComment(), // Initial page, if none present...
+                       File::DELETE_SOURCE,
+                       false, // props...
+                       $this->getTimestamp() );
+
+               if( $status->isGood() ) {
+                       // yay?
+                       wfDebug( "IMPORT: is ok?\n" );
+                       return true;
+               }
+
+               wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
+               return false;
+
+       }
+
+       function downloadSource() {
+               global $wgEnableUploads;
+               if( !$wgEnableUploads ) {
+                       return false;
+               }
+
+               $tempo = tempnam( wfTempDir(), 'download' );
+               $f = fopen( $tempo, 'wb' );
+               if( !$f ) {
+                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
+                       return false;
+               }
+
+               // @fixme!
+               $src = $this->getSrc();
+               $data = Http::get( $src );
+               if( !$data ) {
+                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
+                       fclose( $f );
+                       unlink( $tempo );
+                       return false;
+               }
+
+               fwrite( $f, $data );
+               fclose( $f );
+
+               return $tempo;
+       }
+
+}
+
+/**
+ * implements Special:Import
+ * @ingroup SpecialPage
+ */
+class WikiImporter {
+       var $mDebug = false;
+       var $mSource = null;
+       var $mPageCallback = null;
+       var $mPageOutCallback = null;
+       var $mRevisionCallback = null;
+       var $mUploadCallback = null;
+       var $mTargetNamespace = null;
+       var $lastfield;
+       var $tagStack = array();
+
+       function __construct( $source ) {
+               $this->setRevisionCallback( array( $this, "importRevision" ) );
+               $this->setUploadCallback( array( $this, "importUpload" ) );
+               $this->mSource = $source;
+       }
+
+       function throwXmlError( $err ) {
+               $this->debug( "FAILURE: $err" );
+               wfDebug( "WikiImporter XML error: $err\n" );
+       }
+
+       # --------------
+
+       function doImport() {
+               if( empty( $this->mSource ) ) {
+                       return new WikiErrorMsg( "importnotext" );
+               }
+
+               $parser = xml_parser_create( "UTF-8" );
+
+               # case folding violates XML standard, turn it off
+               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+               xml_set_object( $parser, $this );
+               xml_set_element_handler( $parser, "in_start", "" );
+
+               $offset = 0; // for context extraction on error reporting
+               do {
+                       $chunk = $this->mSource->readChunk();
+                       if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
+                               wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
+                               return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
+                       }
+                       $offset += strlen( $chunk );
+               } while( $chunk !== false && !$this->mSource->atEnd() );
+               xml_parser_free( $parser );
+
+               return true;
+       }
+
+       function debug( $data ) {
+               if( $this->mDebug ) {
+                       wfDebug( "IMPORT: $data\n" );
+               }
+       }
+
+       function notice( $data ) {
+               global $wgCommandLineMode;
+               if( $wgCommandLineMode ) {
+                       print "$data\n";
+               } else {
+                       global $wgOut;
+                       $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
+               }
+       }
+
+       /**
+        * Set debug mode...
+        */
+       function setDebug( $debug ) {
+               $this->mDebug = $debug;
+       }
+
+       /**
+        * Sets the action to perform as each new page in the stream is reached.
+        * @param $callback callback
+        * @return callback
+        */
+       function setPageCallback( $callback ) {
+               $previous = $this->mPageCallback;
+               $this->mPageCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page in the stream is completed.
+        * Callback accepts the page title (as a Title object), a second object
+        * with the original title form (in case it's been overridden into a
+        * local namespace), and a count of revisions.
+        *
+        * @param $callback callback
+        * @return callback
+        */
+       function setPageOutCallback( $callback ) {
+               $previous = $this->mPageOutCallback;
+               $this->mPageOutCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page revision is reached.
+        * @param $callback callback
+        * @return callback
+        */
+       function setRevisionCallback( $callback ) {
+               $previous = $this->mRevisionCallback;
+               $this->mRevisionCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each file upload version is reached.
+        * @param $callback callback
+        * @return callback
+        */
+       function setUploadCallback( $callback ) {
+               $previous = $this->mUploadCallback;
+               $this->mUploadCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Set a target namespace to override the defaults
+        */
+       function setTargetNamespace( $namespace ) {
+               if( is_null( $namespace ) ) {
+                       // Don't override namespaces
+                       $this->mTargetNamespace = null;
+               } elseif( $namespace >= 0 ) {
+                       // FIXME: Check for validity
+                       $this->mTargetNamespace = intval( $namespace );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Default per-revision callback, performs the import.
+        * @param $revision WikiRevision
+        * @private
+        */
+       function importRevision( $revision ) {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
+       }
+
+       /**
+        * Dummy for now...
+        */
+       function importUpload( $revision ) {
+               //$dbw = wfGetDB( DB_MASTER );
+               //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
+               return false;
+       }
+
+       /**
+        * Alternate per-revision callback, for debugging.
+        * @param $revision WikiRevision
+        * @private
+        */
+       function debugRevisionHandler( &$revision ) {
+               $this->debug( "Got revision:" );
+               if( is_object( $revision->title ) ) {
+                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
+               } else {
+                       $this->debug( "-- Title: <invalid>" );
+               }
+               $this->debug( "-- User: " . $revision->user_text );
+               $this->debug( "-- Timestamp: " . $revision->timestamp );
+               $this->debug( "-- Comment: " . $revision->comment );
+               $this->debug( "-- Text: " . $revision->text );
+       }
+
+       /**
+        * Notify the callback function when a new <page> is reached.
+        * @param $title Title
+        * @private
+        */
+       function pageCallback( $title ) {
+               if( is_callable( $this->mPageCallback ) ) {
+                       call_user_func( $this->mPageCallback, $title );
+               }
+       }
+
+       /**
+        * Notify the callback function when a </page> is closed.
+        * @param $title Title
+        * @param $origTitle Title
+        * @param $revisionCount int
+        * @param $successCount Int: number of revisions for which callback returned true
+        * @private
+        */
+       function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
+               if( is_callable( $this->mPageOutCallback ) ) {
+                       call_user_func( $this->mPageOutCallback, $title, $origTitle,
+                               $revisionCount, $successCount );
+               }
+       }
+
+
+       # XML parser callbacks from here out -- beware!
+       function donothing( $parser, $x, $y="" ) {
+               #$this->debug( "donothing" );
+       }
+
+       function in_start( $parser, $name, $attribs ) {
+               $this->debug( "in_start $name" );
+               if( $name != "mediawiki" ) {
+                       return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
+               }
+               xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+       }
+
+       function in_mediawiki( $parser, $name, $attribs ) {
+               $this->debug( "in_mediawiki $name" );
+               if( $name == 'siteinfo' ) {
+                       xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
+               } elseif( $name == 'page' ) {
+                       $this->push( $name );
+                       $this->workRevisionCount = 0;
+                       $this->workSuccessCount = 0;
+                       $this->uploadCount = 0;
+                       $this->uploadSuccessCount = 0;
+                       xml_set_element_handler( $parser, "in_page", "out_page" );
+               } else {
+                       return $this->throwXMLerror( "Expected <page>, got <$name>" );
+               }
+       }
+       function out_mediawiki( $parser, $name ) {
+               $this->debug( "out_mediawiki $name" );
+               if( $name != "mediawiki" ) {
+                       return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
+               }
+               xml_set_element_handler( $parser, "donothing", "donothing" );
+       }
+
+
+       function in_siteinfo( $parser, $name, $attribs ) {
+               // no-ops for now
+               $this->debug( "in_siteinfo $name" );
+               switch( $name ) {
+               case "sitename":
+               case "base":
+               case "generator":
+               case "case":
+               case "namespaces":
+               case "namespace":
+                       break;
+               default:
+                       return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
+               }
+       }
+
+       function out_siteinfo( $parser, $name ) {
+               if( $name == "siteinfo" ) {
+                       xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+               }
+       }
+
+
+       function in_page( $parser, $name, $attribs ) {
+               $this->debug( "in_page $name" );
+               switch( $name ) {
+               case "id":
+               case "title":
+               case "restrictions":
+                       $this->appendfield = $name;
+                       $this->appenddata = "";
+                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
+                       xml_set_character_data_handler( $parser, "char_append" );
+                       break;
+               case "revision":
+                       $this->push( "revision" );
+                       if( is_object( $this->pageTitle ) ) {
+                               $this->workRevision = new WikiRevision;
+                               $this->workRevision->setTitle( $this->pageTitle );
+                               $this->workRevisionCount++;
+                       } else {
+                               // Skipping items due to invalid page title
+                               $this->workRevision = null;
+                       }
+                       xml_set_element_handler( $parser, "in_revision", "out_revision" );
+                       break;
+               case "upload":
+                       $this->push( "upload" );
+                       if( is_object( $this->pageTitle ) ) {
+                               $this->workRevision = new WikiRevision;
+                               $this->workRevision->setTitle( $this->pageTitle );
+                               $this->uploadCount++;
+                       } else {
+                               // Skipping items due to invalid page title
+                               $this->workRevision = null;
+                       }
+                       xml_set_element_handler( $parser, "in_upload", "out_upload" );
+                       break;
+               default:
+                       return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
+               }
+       }
+
+       function out_page( $parser, $name ) {
+               $this->debug( "out_page $name" );
+               $this->pop();
+               if( $name != "page" ) {
+                       return $this->throwXMLerror( "Expected </page>, got </$name>" );
+               }
+               xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+
+               $this->pageOutCallback( $this->pageTitle, $this->origTitle,
+                       $this->workRevisionCount, $this->workSuccessCount );
+
+               $this->workTitle = null;
+               $this->workRevision = null;
+               $this->workRevisionCount = 0;
+               $this->workSuccessCount = 0;
+               $this->pageTitle = null;
+               $this->origTitle = null;
+       }
+
+       function in_nothing( $parser, $name, $attribs ) {
+               $this->debug( "in_nothing $name" );
+               return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
+       }
+       function char_append( $parser, $data ) {
+               $this->debug( "char_append '$data'" );
+               $this->appenddata .= $data;
+       }
+       function out_append( $parser, $name ) {
+               $this->debug( "out_append $name" );
+               if( $name != $this->appendfield ) {
+                       return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
+               }
+
+               switch( $this->appendfield ) {
+               case "title":
+                       $this->workTitle = $this->appenddata;
+                       $this->origTitle = Title::newFromText( $this->workTitle );
+                       if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
+                               $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
+                                       $this->origTitle->getDBkey() );
+                       } else {
+                               $this->pageTitle = Title::newFromText( $this->workTitle );
+                       }
+                       if( is_null( $this->pageTitle ) ) {
+                               // Invalid page title? Ignore the page
+                               $this->notice( "Skipping invalid page title '$this->workTitle'" );
+                       } else {
+                               $this->pageCallback( $this->workTitle );
+                       }
+                       break;
+               case "id":
+                       if ( $this->parentTag() == 'revision' ) {
+                               if( $this->workRevision )
+                                       $this->workRevision->setID( $this->appenddata );
+                       }
+                       break;
+               case "text":
+                       if( $this->workRevision )
+                               $this->workRevision->setText( $this->appenddata );
+                       break;
+               case "username":
+                       if( $this->workRevision )
+                               $this->workRevision->setUsername( $this->appenddata );
+                       break;
+               case "ip":
+                       if( $this->workRevision )
+                               $this->workRevision->setUserIP( $this->appenddata );
+                       break;
+               case "timestamp":
+                       if( $this->workRevision )
+                               $this->workRevision->setTimestamp( $this->appenddata );
+                       break;
+               case "comment":
+                       if( $this->workRevision )
+                               $this->workRevision->setComment( $this->appenddata );
+                       break;
+               case "minor":
+                       if( $this->workRevision )
+                               $this->workRevision->setMinor( true );
+                       break;
+               case "filename":
+                       if( $this->workRevision )
+                               $this->workRevision->setFilename( $this->appenddata );
+                       break;
+               case "src":
+                       if( $this->workRevision )
+                               $this->workRevision->setSrc( $this->appenddata );
+                       break;
+               case "size":
+                       if( $this->workRevision )
+                               $this->workRevision->setSize( intval( $this->appenddata ) );
+                       break;
+               default:
+                       $this->debug( "Bad append: {$this->appendfield}" );
+               }
+               $this->appendfield = "";
+               $this->appenddata = "";
+
+               $parent = $this->parentTag();
+               xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
+               xml_set_character_data_handler( $parser, "donothing" );
+       }
+
+       function in_revision( $parser, $name, $attribs ) {
+               $this->debug( "in_revision $name" );
+               switch( $name ) {
+               case "id":
+               case "timestamp":
+               case "comment":
+               case "minor":
+               case "text":
+                       $this->appendfield = $name;
+                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
+                       xml_set_character_data_handler( $parser, "char_append" );
+                       break;
+               case "contributor":
+                       $this->push( "contributor" );
+                       xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
+                       break;
+               default:
+                       return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
+               }
+       }
+
+       function out_revision( $parser, $name ) {
+               $this->debug( "out_revision $name" );
+               $this->pop();
+               if( $name != "revision" ) {
+                       return $this->throwXMLerror( "Expected </revision>, got </$name>" );
+               }
+               xml_set_element_handler( $parser, "in_page", "out_page" );
+
+               if( $this->workRevision ) {
+                       $ok = call_user_func_array( $this->mRevisionCallback,
+                               array( $this->workRevision, $this ) );
+                       if( $ok ) {
+                               $this->workSuccessCount++;
+                       }
+               }
+       }
+
+       function in_upload( $parser, $name, $attribs ) {
+               $this->debug( "in_upload $name" );
+               switch( $name ) {
+               case "timestamp":
+               case "comment":
+               case "text":
+               case "filename":
+               case "src":
+               case "size":
+                       $this->appendfield = $name;
+                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
+                       xml_set_character_data_handler( $parser, "char_append" );
+                       break;
+               case "contributor":
+                       $this->push( "contributor" );
+                       xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
+                       break;
+               default:
+                       return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
+               }
+       }
+
+       function out_upload( $parser, $name ) {
+               $this->debug( "out_revision $name" );
+               $this->pop();
+               if( $name != "upload" ) {
+                       return $this->throwXMLerror( "Expected </upload>, got </$name>" );
+               }
+               xml_set_element_handler( $parser, "in_page", "out_page" );
+
+               if( $this->workRevision ) {
+                       $ok = call_user_func_array( $this->mUploadCallback,
+                               array( $this->workRevision, $this ) );
+                       if( $ok ) {
+                               $this->workUploadSuccessCount++;
+                       }
+               }
+       }
+
+       function in_contributor( $parser, $name, $attribs ) {
+               $this->debug( "in_contributor $name" );
+               switch( $name ) {
+               case "username":
+               case "ip":
+               case "id":
+                       $this->appendfield = $name;
+                       xml_set_element_handler( $parser, "in_nothing", "out_append" );
+                       xml_set_character_data_handler( $parser, "char_append" );
+                       break;
+               default:
+                       $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
+               }
+       }
+
+       function out_contributor( $parser, $name ) {
+               $this->debug( "out_contributor $name" );
+               $this->pop();
+               if( $name != "contributor" ) {
+                       return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
+               }
+               $parent = $this->parentTag();
+               xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
+       }
+
+       private function push( $name ) {
+               array_push( $this->tagStack, $name );
+               $this->debug( "PUSH $name" );
+       }
+
+       private function pop() {
+               $name = array_pop( $this->tagStack );
+               $this->debug( "POP $name" );
+               return $name;
+       }
+
+       private function parentTag() {
+               $name = $this->tagStack[count( $this->tagStack ) - 1];
+               $this->debug( "PARENT $name" );
+               return $name;
+       }
+
+}
+
+/**
+ * @todo document (e.g. one-sentence class description).
+ * @ingroup SpecialPage
+ */
+class ImportStringSource {
+       function __construct( $string ) {
+               $this->mString = $string;
+               $this->mRead = false;
+       }
+
+       function atEnd() {
+               return $this->mRead;
+       }
+
+       function readChunk() {
+               if( $this->atEnd() ) {
+                       return false;
+               } else {
+                       $this->mRead = true;
+                       return $this->mString;
+               }
+       }
+}
+
+/**
+ * @todo document (e.g. one-sentence class description).
+ * @ingroup SpecialPage
+ */
+class ImportStreamSource {
+       function __construct( $handle ) {
+               $this->mHandle = $handle;
+       }
+
+       function atEnd() {
+               return feof( $this->mHandle );
+       }
+
+       function readChunk() {
+               return fread( $this->mHandle, 32768 );
+       }
+
+       static function newFromFile( $filename ) {
+               $file = @fopen( $filename, 'rt' );
+               if( !$file ) {
+                       return new WikiErrorMsg( "importcantopen" );
+               }
+               return new ImportStreamSource( $file );
+       }
+
+       static function newFromUpload( $fieldname = "xmlimport" ) {
+               $upload =& $_FILES[$fieldname];
+
+               if( !isset( $upload ) || !$upload['name'] ) {
+                       return new WikiErrorMsg( 'importnofile' );
+               }
+               if( !empty( $upload['error'] ) ) {
+                       switch($upload['error']){
+                               case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
+                                       return new WikiErrorMsg( 'importuploaderrorsize' );
+                               case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
+                                       return new WikiErrorMsg( 'importuploaderrorsize' );
+                               case 3: # The uploaded file was only partially uploaded
+                                       return new WikiErrorMsg( 'importuploaderrorpartial' );
+                           case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
+                               return new WikiErrorMsg( 'importuploaderrortemp' );
+                           # case else: # Currently impossible
+                       }
+
+               }
+               $fname = $upload['tmp_name'];
+               if( is_uploaded_file( $fname ) ) {
+                       return ImportStreamSource::newFromFile( $fname );
+               } else {
+                       return new WikiErrorMsg( 'importnofile' );
+               }
+       }
+
+       static function newFromURL( $url, $method = 'GET' ) {
+               wfDebug( __METHOD__ . ": opening $url\n" );
+               # Use the standard HTTP fetch function; it times out
+               # quicker and sorts out user-agent problems which might
+               # otherwise prevent importing from large sites, such
+               # as the Wikimedia cluster, etc.
+               $data = Http::request( $method, $url );
+               if( $data !== false ) {
+                       $file = tmpfile();
+                       fwrite( $file, $data );
+                       fflush( $file );
+                       fseek( $file, 0 );
+                       return new ImportStreamSource( $file );
+               } else {
+                       return new WikiErrorMsg( 'importcantopen' );
+               }
+       }
+
+       public static function newFromInterwiki( $interwiki, $page, $history=false ) {
+               if( $page == '' ) {
+                       return new WikiErrorMsg( 'import-noarticle' );
+               }
+               $link = Title::newFromText( "$interwiki:Special:Export/$page" );
+               if( is_null( $link ) || $link->getInterwiki() == '' ) {
+                       return new WikiErrorMsg( 'importbadinterwiki' );
+               } else {
+                       $params = $history ? 'history=1' : '';
+                       $url = $link->getFullUrl( $params );
+                       # For interwikis, use POST to avoid redirects.
+                       return ImportStreamSource::newFromURL( $url, "POST" );
+               }
+       }
+}
diff --git a/includes/specials/SpecialIpblocklist.php b/includes/specials/SpecialIpblocklist.php
new file mode 100644 (file)
index 0000000..696c7ef
--- /dev/null
@@ -0,0 +1,427 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @todo document
+ */
+function wfSpecialIpblocklist() {
+       global $wgUser, $wgOut, $wgRequest;
+
+       $ip = $wgRequest->getVal( 'wpUnblockAddress', $wgRequest->getVal( 'ip' ) );
+       $id = $wgRequest->getVal( 'id' );
+       $reason = $wgRequest->getText( 'wpUnblockReason' );
+       $action = $wgRequest->getText( 'action' );
+       $successip = $wgRequest->getVal( 'successip' );
+
+       $ipu = new IPUnblockForm( $ip, $id, $reason );
+
+       if( $action == 'unblock' ) {
+               # Check permissions
+               if( !$wgUser->isAllowed( 'block' ) ) {
+                       $wgOut->permissionRequired( 'block' );
+                       return;
+               }
+               # Check for database lock
+               if( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+               # Show unblock form
+               $ipu->showForm( '' );
+       } elseif( $action == 'submit' && $wgRequest->wasPosted()
+               && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               # Check permissions
+               if( !$wgUser->isAllowed( 'block' ) ) {
+                       $wgOut->permissionRequired( 'block' );
+                       return;
+               }
+               # Check for database lock
+               if( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+               # Remove blocks and redirect user to success page
+               $ipu->doSubmit();
+       } elseif( $action == 'success' ) {
+               # Inform the user of a successful unblock
+               # (No need to check permissions or locks here,
+               # if something was done, then it's too late!)
+               if ( substr( $successip, 0, 1) == '#' ) {
+                       // A block ID was unblocked
+                       $ipu->showList( $wgOut->parse( wfMsg( 'unblocked-id', $successip ) ) );
+               } else {
+                       // A username/IP was unblocked
+                       $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) );
+               }
+       } else {
+               # Just show the block list
+               $ipu->showList( '' );
+       }
+
+}
+
+/**
+ * implements Special:ipblocklist GUI
+ * @ingroup SpecialPage
+ */
+class IPUnblockForm {
+       var $ip, $reason, $id;
+
+       function IPUnblockForm( $ip, $id, $reason ) {
+               $this->ip = strtr( $ip, '_', ' ' );
+               $this->id = $id;
+               $this->reason = $reason;
+       }
+
+       /**
+        * Generates the unblock form
+        * @param $err string: error message
+        * @return $out string: HTML form
+        */
+       function showForm( $err ) {
+               global $wgOut, $wgUser, $wgSysopUserBans;
+
+               $wgOut->setPagetitle( wfMsg( 'unblockip' ) );
+               $wgOut->addWikiMsg( 'unblockiptext' );
+
+               $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
+               $action = $titleObj->getLocalURL( "action=submit" );
+
+               if ( "" != $err ) {
+                       $wgOut->setSubtitle( wfMsg( "formerror" ) );
+                       $wgOut->addWikiText( Xml::tags( 'span', array( 'class' => 'error' ), $err ) . "\n" );
+               }
+
+               $addressPart = false;
+               if ( $this->id ) {
+                       $block = Block::newFromID( $this->id );
+                       if ( $block ) {
+                               $encName = htmlspecialchars( $block->getRedactedName() );
+                               $encId = $this->id;
+                               $addressPart = $encName . Xml::hidden( 'id', $encId );
+                               $ipa = wfMsgHtml( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' );
+                       }
+               }
+               if ( !$addressPart ) {
+                       $addressPart = Xml::input( 'wpUnblockAddress', 40, $this->ip, array( 'type' => 'text', 'tabindex' => '1' ) );
+                       $ipa = Xml::label( wfMsg( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' ), 'wpUnblockAddress' );
+               }
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'unblockip' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'ipb-unblock' ) ) .
+                       Xml::openElement( 'table', array( 'id' => 'mw-unblock-table' ) ).
+                       "<tr>
+                               <td class='mw-label'>
+                                       {$ipa}
+                               </td>
+                               <td class='mw-input'>
+                                       {$addressPart}
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>" .
+                                       Xml::label( wfMsg( 'ipbreason' ), 'wpUnblockReason' ) .
+                               "</td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'wpUnblockReason', 40, $this->reason, array( 'type' => 'text', 'tabindex' => '2' ) ) .
+                               "</td>
+                       </tr>
+                       <tr>
+                               <td>&nbsp;</td>
+                               <td class='mw-submit'>" .
+                                       Xml::submitButton( wfMsg( 'ipusubmit' ), array( 'name' => 'wpBlock', 'tabindex' => '3' ) ) .
+                               "</td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+
+       }
+
+       const UNBLOCK_SUCCESS = 0; // Success
+       const UNBLOCK_NO_SUCH_ID = 1; // No such block ID
+       const UNBLOCK_USER_NOT_BLOCKED = 2; // IP wasn't blocked
+       const UNBLOCK_BLOCKED_AS_RANGE = 3; // IP is part of a range block
+       const UNBLOCK_UNKNOWNERR = 4; // Unknown error
+
+       /**
+        * Backend code for unblocking. doSubmit() wraps around this.
+        * $range is only used when UNBLOCK_BLOCKED_AS_RANGE is returned, in which
+        * case it contains the range $ip is part of.
+        * @return array array(message key, parameters) on failure, empty array on success
+        */
+
+       static function doUnblock(&$id, &$ip, &$reason, &$range = null)
+       {
+               if ( $id ) {
+                       $block = Block::newFromID( $id );
+                       if ( !$block ) {
+                               return array('ipb_cant_unblock', htmlspecialchars($id));
+                       }
+                       $ip = $block->getRedactedName();
+               } else {
+                       $block = new Block();
+                       $ip = trim( $ip );
+                       if ( substr( $ip, 0, 1 ) == "#" ) {
+                               $id = substr( $ip, 1 );
+                               $block = Block::newFromID( $id );
+                               if( !$block ) {
+                                       return array('ipb_cant_unblock', htmlspecialchars($id));
+                               }
+                               $ip = $block->getRedactedName();
+                       } else {
+                               $block = Block::newFromDB( $ip );
+                               if ( !$block ) {
+                                       return array('ipb_cant_unblock', htmlspecialchars($id));
+                               }
+                               if( $block->mRangeStart != $block->mRangeEnd
+                                               && !strstr( $ip, "/" ) ) {
+                                       /* If the specified IP is a single address, and the block is
+                                        * a range block, don't unblock the range. */
+                                        $range = $block->mAddress;
+                                        return array('ipb_blocked_as_range', $ip, $range);
+                               }
+                       }
+               }
+               // Yes, this is really necessary
+               $id = $block->mId;
+
+               # Delete block
+               if ( !$block->delete() ) {
+                       return array('ipb_cant_unblock', htmlspecialchars($id));
+               }
+
+               # Make log entry
+               $log = new LogPage( 'block' );
+               $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $ip ), $reason );
+               return array();
+       }
+
+       function doSubmit() {
+               global $wgOut;
+               $retval = self::doUnblock($this->id, $this->ip, $this->reason, $range);
+               if(!empty($retval))
+               {
+                       $key = array_shift($retval);
+                       $this->showForm(wfMsgReal($key, $retval));
+                       return;
+               }
+               # Report to the user
+               $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
+               $success = $titleObj->getFullURL( "action=success&successip=" . urlencode( $this->ip ) );
+               $wgOut->redirect( $success );
+       }
+
+       function showList( $msg ) {
+               global $wgOut, $wgUser;
+
+               $wgOut->setPagetitle( wfMsg( "ipblocklist" ) );
+               if ( "" != $msg ) {
+                       $wgOut->setSubtitle( $msg );
+               }
+
+               // Purge expired entries on one in every 10 queries
+               if ( !mt_rand( 0, 10 ) ) {
+                       Block::purgeExpired();
+               }
+
+               $conds = array();
+               $matches = array();
+               // Is user allowed to see all the blocks?
+               if ( !$wgUser->isAllowed( 'suppress' ) )
+                       $conds['ipb_deleted'] = 0;
+               if ( $this->ip == '' ) {
+                       // No extra conditions
+               } elseif ( substr( $this->ip, 0, 1 ) == '#' ) {
+                       $conds['ipb_id'] = substr( $this->ip, 1 );
+               } elseif ( IP::toUnsigned( $this->ip ) !== false ) {
+                       $conds['ipb_address'] = $this->ip;
+                       $conds['ipb_auto'] = 0;
+               } elseif( preg_match( '/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\\/(\\d{1,2})$/', $this->ip, $matches ) ) {
+                       $conds['ipb_address'] = Block::normaliseRange( $this->ip );
+                       $conds['ipb_auto'] = 0;
+               } else {
+                       $user = User::newFromName( $this->ip );
+                       if ( $user && ( $id = $user->getId() ) != 0 ) {
+                               $conds['ipb_user'] = $id;
+                       } else {
+                               // Uh...?
+                               $conds['ipb_address'] = $this->ip;
+                               $conds['ipb_auto'] = 0;
+                       }
+               }
+
+               $pager = new IPBlocklistPager( $this, $conds );
+               if ( $pager->getNumRows() ) {
+                       $wgOut->addHTML(
+                               $this->searchForm() .
+                               $pager->getNavigationBar() .
+                               Xml::tags( 'ul', null, $pager->getBody() ) .
+                               $pager->getNavigationBar()
+                       );
+               } elseif ( $this->ip != '') {
+                       $wgOut->addHTML( $this->searchForm() );
+                       $wgOut->addWikiMsg( 'ipblocklist-no-results' );
+               } else {
+                       $wgOut->addWikiMsg( 'ipblocklist-empty' );
+               }
+       }
+
+       function searchForm() {
+               global $wgTitle, $wgScript, $wgRequest;
+               return
+                       Xml::tags( 'form', array( 'action' => $wgScript ),
+                               Xml::hidden( 'title', $wgTitle->getPrefixedDbKey() ) .
+                               Xml::openElement( 'fieldset' ) .
+                               Xml::element( 'legend', null, wfMsg( 'ipblocklist-legend' ) ) .
+                               Xml::inputLabel( wfMsg( 'ipblocklist-username' ), 'ip', 'ip', /* size */ false, $this->ip ) .
+                               '&nbsp;' .
+                               Xml::submitButton( wfMsg( 'ipblocklist-submit' ) ) .
+                               Xml::closeElement( 'fieldset' )
+                       );
+       }
+
+       /**
+        * Callback function to output a block
+        */
+       function formatRow( $block ) {
+               global $wgUser, $wgLang;
+
+               wfProfileIn( __METHOD__ );
+
+               static $sk=null, $msg=null;
+
+               if( is_null( $sk ) )
+                       $sk = $wgUser->getSkin();
+               if( is_null( $msg ) ) {
+                       $msg = array();
+                       $keys = array( 'infiniteblock', 'expiringblock', 'unblocklink',
+                               'anononlyblock', 'createaccountblock', 'noautoblockblock', 'emailblock' );
+                       foreach( $keys as $key ) {
+                               $msg[$key] = wfMsgHtml( $key );
+                       }
+                       $msg['blocklistline'] = wfMsg( 'blocklistline' );
+               }
+
+               # Prepare links to the blocker's user and talk pages
+               $blocker_id = $block->getBy();
+               $blocker_name = $block->getByName();
+               $blocker = $sk->userLink( $blocker_id, $blocker_name );
+               $blocker .= $sk->userToolLinks( $blocker_id, $blocker_name );
+
+               # Prepare links to the block target's user and contribs. pages (as applicable, don't do it for autoblocks)
+               if( $block->mAuto ) {
+                       $target = $block->getRedactedName(); # Hide the IP addresses of auto-blocks; privacy
+               } else {
+                       $target = $sk->userLink( $block->mUser, $block->mAddress )
+                               . $sk->userToolLinks( $block->mUser, $block->mAddress, false, Linker::TOOL_LINKS_NOBLOCK );
+               }
+
+               $formattedTime = $wgLang->timeanddate( $block->mTimestamp, true );
+
+               $properties = array();
+               $properties[] = Block::formatExpiry( $block->mExpiry );
+               if ( $block->mAnonOnly ) {
+                       $properties[] = $msg['anononlyblock'];
+               }
+               if ( $block->mCreateAccount ) {
+                       $properties[] = $msg['createaccountblock'];
+               }
+               if (!$block->mEnableAutoblock && $block->mUser ) {
+                       $properties[] = $msg['noautoblockblock'];
+               }
+
+               if ( $block->mBlockEmail && $block->mUser ) {
+                       $properties[] = $msg['emailblock'];
+               }
+
+               $properties = implode( ', ', $properties );
+
+               $line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) );
+
+               $unblocklink = '';
+               if ( $wgUser->isAllowed('block') ) {
+                       $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
+                       $unblocklink = ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')';
+               }
+
+               $comment = $sk->commentBlock( $block->mReason );
+
+               $s = "{$line} $comment";
+               if ( $block->mHideName )
+                       $s = '<span class="history-deleted">' . $s . '</span>';
+
+               wfProfileOut( __METHOD__ );
+               return "<li>$s $unblocklink</li>\n";
+       }
+}
+
+/**
+ * @todo document
+ * @ingroup Pager
+ */
+class IPBlocklistPager extends ReverseChronologicalPager {
+       public $mForm, $mConds;
+
+       function __construct( $form, $conds = array() ) {
+               $this->mForm = $form;
+               $this->mConds = $conds;
+               parent::__construct();
+       }
+
+       function getStartBody() {
+               wfProfileIn( __METHOD__ );
+               # Do a link batch query
+               $this->mResult->seek( 0 );
+               $lb = new LinkBatch;
+
+               /*
+               while ( $row = $this->mResult->fetchObject() ) {
+                       $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
+                       $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) );
+                       $lb->addObj( Title::makeTitleSafe( NS_USER, $row->ipb_address ) );
+                       $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ipb_address ) );
+               }*/
+               # Faster way
+               # Usernames and titles are in fact related by a simple substitution of space -> underscore
+               # The last few lines of Title::secureAndSplit() tell the story.
+               while ( $row = $this->mResult->fetchObject() ) {
+                       $name = str_replace( ' ', '_', $row->ipb_by_text );
+                       $lb->add( NS_USER, $name );
+                       $lb->add( NS_USER_TALK, $name );
+                       $name = str_replace( ' ', '_', $row->ipb_address );
+                       $lb->add( NS_USER, $name );
+                       $lb->add( NS_USER_TALK, $name );
+               }
+               $lb->execute();
+               wfProfileOut( __METHOD__ );
+               return '';
+       }
+
+       function formatRow( $row ) {
+               $block = new Block;
+               $block->initFromRow( $row );
+               return $this->mForm->formatRow( $block );
+       }
+
+       function getQueryInfo() {
+               $conds = $this->mConds;
+               $conds[] = 'ipb_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
+               return array(
+                       'tables' => 'ipblocks',
+                       'fields' => '*',
+                       'conds' => $conds,
+               );
+       }
+
+       function getIndexField() {
+               return 'ipb_timestamp';
+       }
+}
diff --git a/includes/specials/SpecialListgrouprights.php b/includes/specials/SpecialListgrouprights.php
new file mode 100644 (file)
index 0000000..131c060
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * This special page lists all defined user groups and the associated rights.
+ * See also @ref $wgGroupPermissions.
+ *
+ * @ingroup SpecialPage
+ * @author Petr Kadlec <mormegil@centrum.cz>
+ */
+class SpecialListGroupRights extends SpecialPage {
+
+       var $skin;
+
+       /**
+        * Constructor
+        */
+       function __construct() {
+               global $wgUser;
+               parent::__construct( 'Listgrouprights' );
+               $this->skin = $wgUser->getSkin();
+       }
+
+       /**
+        * Show the special page
+        */
+       public function execute( $par ) {
+               global $wgOut, $wgGroupPermissions, $wgImplicitGroups, $wgMessageCache;
+               $wgMessageCache->loadAllMessages();
+
+               $this->setHeaders();
+               $this->outputHeader();
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'table', array( 'class' => 'mw-listgrouprights-table' ) ) .
+                               '<tr>' .
+                                       Xml::element( 'th', null, wfMsg( 'listgrouprights-group' ) ) .
+                                       Xml::element( 'th', null, wfMsg( 'listgrouprights-rights' ) ) .
+                               '</tr>'
+               );
+
+               foreach( $wgGroupPermissions as $group => $permissions ) {
+                       $groupname = ( $group == '*' ) ? 'all' : htmlspecialchars( $group ); // Replace * with a more descriptive groupname
+
+                       $msg = wfMsg( 'group-' . $groupname );
+                       if ( wfEmptyMsg( 'group-' . $groupname, $msg ) || $msg == '' ) {
+                               $groupnameLocalized = $groupname;
+                       } else {
+                               $groupnameLocalized = $msg;
+                       }
+
+                       $msg = wfMsgForContent( 'grouppage-' . $groupname );
+                       if ( wfEmptyMsg( 'grouppage-' . $groupname, $msg ) || $msg == '' ) {
+                               $grouppageLocalized = MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname;
+                       } else {
+                               $grouppageLocalized = $msg;
+                       }
+
+                       if( $group == '*' ) {
+                               // Do not make a link for the generic * group
+                               $grouppage = $groupnameLocalized;
+                       } else {
+                               $grouppage = $this->skin->makeLink( $grouppageLocalized, $groupnameLocalized );
+                       }
+
+                       if ( !in_array( $group, $wgImplicitGroups ) ) {
+                               $grouplink = '<br />' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Listusers' ), wfMsgHtml( 'listgrouprights-members' ), 'group=' . $group );
+                       } else {
+                               // No link to Special:listusers for implicit groups as they are unlistable
+                               $grouplink = '';
+                       }
+
+                       $wgOut->addHTML(
+                               '<tr>
+                                       <td>' .
+                                               $grouppage . $grouplink .
+                                       '</td>
+                                       <td>' .
+                                               self::formatPermissions( $permissions ) .
+                                       '</td>
+                               </tr>'
+                       );
+               }
+               $wgOut->addHTML(
+                       Xml::closeElement( 'table' ) . "\n"
+               );
+       }
+
+       /**
+        * Create a user-readable list of permissions from the given array.
+        *
+        * @param $permissions Array of permission => bool (from $wgGroupPermissions items)
+        * @return string List of all granted permissions, separated by comma separator
+        */
+        private static function formatPermissions( $permissions ) {
+               $r = array();
+               foreach( $permissions as $permission => $granted ) {
+                       if ( $granted ) {
+                               $description = wfMsgHTML( 'listgrouprights-right-display',
+                                       User::getRightDescription($permission),
+                                       $permission
+                               );
+                               $r[] = $description;
+                       }
+               }
+               sort( $r );
+               if( empty( $r ) ) {
+                       return '';
+               } else {
+                       return '<ul><li>' . implode( "</li>\n<li>", $r ) . '</li></ul>';
+               }
+       }
+}
diff --git a/includes/specials/SpecialListredirects.php b/includes/specials/SpecialListredirects.php
new file mode 100644 (file)
index 0000000..808aab1
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Rob Church <robchur@gmail.com>
+ * @copyright Â© 2006 Rob Church
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * Special:Listredirects - Lists all the redirects on the wiki.
+ * @ingroup SpecialPage
+ */
+class ListredirectsPage extends QueryPage {
+
+       function getName() { return( 'Listredirects' ); }
+       function isExpensive() { return( true ); }
+       function isSyndicated() { return( false ); }
+       function sortDescending() { return( false ); }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $page = $dbr->tableName( 'page' );
+               $sql = "SELECT 'Listredirects' AS type, page_title AS title, page_namespace AS namespace, 0 AS value FROM $page WHERE page_is_redirect = 1";
+               return( $sql );
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang;
+
+               # Make a link to the redirect itself
+               $rd_title = Title::makeTitle( $result->namespace, $result->title );
+               $rd_link = $skin->makeLinkObj( $rd_title, '', 'redirect=no' );
+
+               # Find out where the redirect leads
+               $revision = Revision::newFromTitle( $rd_title );
+               if( $revision ) {
+                       # Make a link to the destination page
+                       $target = Title::newFromRedirect( $revision->getText() );
+                       if( $target ) {
+                               $arr = $wgContLang->getArrow() . $wgContLang->getDirMark();
+                               $targetLink = $skin->makeLinkObj( $target );
+                               return "$rd_link $arr $targetLink";
+                       } else {
+                               return "<s>$rd_link</s>";
+                       }
+               } else {
+                       return "<s>$rd_link</s>";
+               }
+       }
+}
+
+function wfSpecialListredirects() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $lrp = new ListredirectsPage();
+       $lrp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialListusers.php b/includes/specials/SpecialListusers.php
new file mode 100644 (file)
index 0000000..7dba44e
--- /dev/null
@@ -0,0 +1,235 @@
+<?php
+
+# Copyright (C) 2004 Brion Vibber, lcrocker, Tim Starling,
+# Domas Mituzas, Ashar Voultoiz, Jens Frank, Zhengzhu.
+#
+# Â© 2006 Rob Church <robchur@gmail.com>
+#
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * This class is used to get a list of user. The ones with specials
+ * rights (sysop, bureaucrat, developer) will have them displayed
+ * next to their names.
+ *
+ * @ingroup SpecialPage
+ */
+class UsersPager extends AlphabeticPager {
+
+       function __construct($group=null) {
+               global $wgRequest;
+               $this->requestedGroup = $group != "" ? $group : $wgRequest->getVal( 'group' );
+               $un = $wgRequest->getText( 'username' );
+               $this->requestedUser = '';
+               if ( $un != '' ) {
+                       $username = Title::makeTitleSafe( NS_USER, $un );
+                       if( ! is_null( $username ) ) {
+                               $this->requestedUser = $username->getText();
+                       }
+               }
+               parent::__construct();
+       }
+
+
+       function getIndexField() {
+               return 'user_name';
+       }
+
+       function getQueryInfo() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $conds=array();
+               // don't show hidden names
+               $conds[]='ipb_deleted IS NULL OR ipb_deleted = 0';
+               if ($this->requestedGroup != "") {
+                       $conds['ug_group'] = $this->requestedGroup;
+                       $useIndex = '';
+               } else {
+                       $useIndex = $dbr->useIndexClause('user_name');
+               }
+               if ($this->requestedUser != "") {
+                       $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser );
+               }
+
+               list ($user,$user_groups,$ipblocks) = $dbr->tableNamesN('user','user_groups','ipblocks');
+
+               $query = array(
+                       'tables' => " $user $useIndex LEFT JOIN $user_groups ON user_id=ug_user
+                               LEFT JOIN $ipblocks ON user_id=ipb_user AND ipb_auto=0 ",
+                       'fields' => array('user_name',
+                               'MAX(user_id) AS user_id',
+                               'COUNT(ug_group) AS numgroups',
+                               'MAX(ug_group) AS singlegroup'),
+                       'options' => array('GROUP BY' => 'user_name'),
+                       'conds' => $conds
+               );
+
+               wfRunHooks( 'SpecialListusersQueryInfo', array( $this, &$query ) );
+               return $query;
+       }
+
+       function formatRow( $row ) {
+               $userPage = Title::makeTitle( NS_USER, $row->user_name );
+               $name = $this->getSkin()->makeLinkObj( $userPage, htmlspecialchars( $userPage->getText() ) );
+
+               if( $row->numgroups > 1 || ( $this->requestedGroup && $row->numgroups == 1 ) ) {
+                       $list = array();
+                       foreach( self::getGroups( $row->user_id ) as $group )
+                               $list[] = self::buildGroupLink( $group );
+                       $groups = implode( ', ', $list );
+               } elseif( $row->numgroups == 1 ) {
+                       $groups = self::buildGroupLink( $row->singlegroup );
+               } else {
+                       $groups = '';
+               }
+
+               $item = wfSpecialList( $name, $groups );
+               wfRunHooks( 'SpecialListusersFormatRow', array( &$item, $row ) );
+               return "<li>{$item}</li>";
+       }
+
+       function getBody() {
+               if (!$this->mQueryDone) {
+                       $this->doQuery();
+               }
+               $batch = new LinkBatch;
+
+               $this->mResult->rewind();
+
+               while ( $row = $this->mResult->fetchObject() ) {
+                       $batch->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
+               }
+               $batch->execute();
+               $this->mResult->rewind();
+               return parent::getBody();
+       }
+
+       function getPageHeader( ) {
+               global $wgScript, $wgRequest;
+               $self = $this->getTitle();
+
+               # Form tag
+               $out  = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
+                       '<fieldset>' .
+                       Xml::element( 'legend', array(), wfMsg( 'listusers' ) );
+               $out .= Xml::hidden( 'title', $self->getPrefixedDbKey() );
+
+               # Username field
+               $out .= Xml::label( wfMsg( 'listusersfrom' ), 'offset' ) . ' ' .
+                       Xml::input( 'username', 20, $this->requestedUser, array( 'id' => 'offset' ) ) . ' ';
+
+               # Group drop-down list
+               $out .= Xml::label( wfMsg( 'group' ), 'group' ) . ' ' .
+                       Xml::openElement('select',  array( 'name' => 'group', 'id' => 'group' ) ) .
+                       Xml::option( wfMsg( 'group-all' ), '' );
+               foreach( $this->getAllGroups() as $group => $groupText )
+                       $out .= Xml::option( $groupText, $group, $group == $this->requestedGroup );
+               $out .= Xml::closeElement( 'select' ) . ' ';
+
+               wfRunHooks( 'SpecialListusersHeaderForm', array( $this, &$out ) );
+
+               # Submit button and form bottom
+               if( $this->mLimit )
+                       $out .= Xml::hidden( 'limit', $this->mLimit );
+               $out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
+               wfRunHooks( 'SpecialListusersHeader', array( $this, &$out ) );
+               $out .= '</fieldset>' .
+                       Xml::closeElement( 'form' );
+
+               return $out;
+       }
+
+       function getAllGroups() {
+               $result = array();
+               foreach( User::getAllGroups() as $group ) {
+                       $result[$group] = User::getGroupName( $group );
+               }
+               return $result;
+       }
+
+       /**
+        * Preserve group and username offset parameters when paging
+        * @return array
+        */
+       function getDefaultQuery() {
+               $query = parent::getDefaultQuery();
+               if( $this->requestedGroup != '' )
+                       $query['group'] = $this->requestedGroup;
+               if( $this->requestedUser != '' )
+                       $query['username'] = $this->requestedUser;
+               wfRunHooks( 'SpecialListusersDefaultQuery', array( $this, &$query ) );
+               return $query;
+       }
+
+       /**
+        * Get a list of groups the specified user belongs to
+        *
+        * @param int $uid
+        * @return array
+        */
+       protected static function getGroups( $uid ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $groups = array();
+               $res = $dbr->select( 'user_groups', 'ug_group', array( 'ug_user' => $uid ), __METHOD__ );
+               if( $res && $dbr->numRows( $res ) > 0 ) {
+                       while( $row = $dbr->fetchObject( $res ) )
+                               $groups[] = $row->ug_group;
+                       $dbr->freeResult( $res );
+               }
+               return $groups;
+       }
+
+       /**
+        * Format a link to a group description page
+        *
+        * @param string $group
+        * @return string
+        */
+       protected static function buildGroupLink( $group ) {
+               static $cache = array();
+               if( !isset( $cache[$group] ) )
+                       $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupMember( $group ) );
+               return $cache[$group];
+       }
+}
+
+/**
+ * constructor
+ * $par string (optional) A group to list users from
+ */
+function wfSpecialListusers( $par = null ) {
+       global $wgRequest, $wgOut;
+
+       $up = new UsersPager($par);
+
+       # getBody() first to check, if empty
+       $usersbody = $up->getBody();
+       $s = $up->getPageHeader();
+       if( $usersbody ) {
+               $s .=   $up->getNavigationBar();
+               $s .=   '<ul>' . $usersbody . '</ul>';
+               $s .=   $up->getNavigationBar() ;
+       } else {
+               $s .=   '<p>' . wfMsgHTML('listusers-noresult') . '</p>';
+       };
+
+       $wgOut->addHTML( $s );
+}
diff --git a/includes/specials/SpecialLockdb.php b/includes/specials/SpecialLockdb.php
new file mode 100644 (file)
index 0000000..0401922
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialLockdb() {
+       global $wgUser, $wgOut, $wgRequest;
+
+       if( !$wgUser->isAllowed( 'siteadmin' ) ) {
+               $wgOut->permissionRequired( 'siteadmin' );
+               return;
+       }
+
+       # If the lock file isn't writable, we can do sweet bugger all
+       global $wgReadOnlyFile;
+       if( !is_writable( dirname( $wgReadOnlyFile ) ) ) {
+               DBLockForm::notWritable();
+               return;
+       }
+
+       $action = $wgRequest->getVal( 'action' );
+       $f = new DBLockForm();
+
+       if ( 'success' == $action ) {
+               $f->showSuccess();
+       } else if ( 'submit' == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( '' );
+       }
+}
+
+/**
+ * A form to make the database readonly (eg for maintenance purposes).
+ * @ingroup SpecialPage
+ */
+class DBLockForm {
+       var $reason = '';
+
+       function DBLockForm() {
+               global $wgRequest;
+               $this->reason = $wgRequest->getText( 'wpLockReason' );
+       }
+
+       function showForm( $err ) {
+               global $wgOut, $wgUser;
+
+               $wgOut->setPagetitle( wfMsg( 'lockdb' ) );
+               $wgOut->addWikiMsg( 'lockdbtext' );
+
+               if ( "" != $err ) {
+                       $wgOut->setSubtitle( wfMsg( 'formerror' ) );
+                       $wgOut->addHTML( '<p class="error">' . htmlspecialchars( $err ) . "</p>\n" );
+               }
+               $lc = htmlspecialchars( wfMsg( 'lockconfirm' ) );
+               $lb = htmlspecialchars( wfMsg( 'lockbtn' ) );
+               $elr = htmlspecialchars( wfMsg( 'enterlockreason' ) );
+               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
+               $action = $titleObj->escapeLocalURL( 'action=submit' );
+               $reason = htmlspecialchars( $this->reason );
+               $token = htmlspecialchars( $wgUser->editToken() );
+
+               $wgOut->addHTML( <<<END
+<form id="lockdb" method="post" action="{$action}">
+{$elr}:
+<textarea name="wpLockReason" rows="10" cols="60" wrap="virtual">{$reason}</textarea>
+<table border="0">
+       <tr>
+               <td align="right">
+                       <input type="checkbox" name="wpLockConfirm" />
+               </td>
+               <td align="left">{$lc}</td>
+       </tr>
+       <tr>
+               <td>&nbsp;</td>
+               <td align="left">
+                       <input type="submit" name="wpLock" value="{$lb}" />
+               </td>
+       </tr>
+</table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
+</form>
+END
+);
+
+       }
+
+       function doSubmit() {
+               global $wgOut, $wgUser, $wgLang, $wgRequest;
+               global $wgReadOnlyFile;
+
+               if ( ! $wgRequest->getCheck( 'wpLockConfirm' ) ) {
+                       $this->showForm( wfMsg( 'locknoconfirm' ) );
+                       return;
+               }
+               $fp = @fopen( $wgReadOnlyFile, 'w' );
+
+               if ( false === $fp ) {
+                       # This used to show a file not found error, but the likeliest reason for fopen()
+                       # to fail at this point is insufficient permission to write to the file...good old
+                       # is_writable() is plain wrong in some cases, it seems...
+                       self::notWritable();
+                       return;
+               }
+               fwrite( $fp, $this->reason );
+               fwrite( $fp, "\n<p>(by " . $wgUser->getName() . " at " .
+                 $wgLang->timeanddate( wfTimestampNow() ) . ")\n" );
+               fclose( $fp );
+
+               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
+               $wgOut->redirect( $titleObj->getFullURL( 'action=success' ) );
+       }
+
+       function showSuccess() {
+               global $wgOut;
+
+               $wgOut->setPagetitle( wfMsg( 'lockdb' ) );
+               $wgOut->setSubtitle( wfMsg( 'lockdbsuccesssub' ) );
+               $wgOut->addWikiMsg( 'lockdbsuccesstext' );
+       }
+
+       public static function notWritable() {
+               global $wgOut;
+               $wgOut->showErrorPage( 'lockdb', 'lockfilenotwritable' );
+       }
+}
diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php
new file mode 100644 (file)
index 0000000..3154ed1
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+# Copyright (C) 2008 Aaron Schulz
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * constructor
+ */
+function wfSpecialLog( $par = '' ) {
+       global $wgRequest, $wgOut, $wgUser;
+       # Get parameters
+       $type = $wgRequest->getVal( 'type', $par );
+       $user = $wgRequest->getText( 'user' );
+       $title = $wgRequest->getText( 'page' );
+       $pattern = $wgRequest->getBool( 'pattern' );
+       $y = $wgRequest->getIntOrNull( 'year' );
+       $m = $wgRequest->getIntOrNull( 'month' );
+       # Don't let the user get stuck with a certain date
+       $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
+       if( $skip ) {
+               $y = '';
+               $m = '';
+       }
+       # Create a LogPager item to get the results and a LogEventsList
+       # item to format them...
+       $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
+       $pager = new LogPager( $loglist, $type, $user, $title, $pattern, array(), $y, $m );
+       # Set title and add header
+       $loglist->showHeader( $pager->getType() );
+       # Show form options
+       $loglist->showOptions( $pager->getType(), $pager->getUser(), $pager->getPage(), $pager->getPattern(),
+               $pager->getYear(), $pager->getMonth() );
+       # Insert list
+       $logBody = $pager->getBody();
+       if( $logBody ) {
+               $wgOut->addHTML(
+                       $pager->getNavigationBar() .
+                       $loglist->beginLogEventsList() .
+                       $logBody .
+                       $loglist->endLogEventsList() .
+                       $pager->getNavigationBar()
+               );
+       } else {
+               $wgOut->addWikiMsg( 'logempty' );
+       }
+}
diff --git a/includes/specials/SpecialLonelypages.php b/includes/specials/SpecialLonelypages.php
new file mode 100644 (file)
index 0000000..5aafac7
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page looking for articles with no article linking to them,
+ * thus being lonely.
+ * @ingroup SpecialPage
+ */
+class LonelyPagesPage extends PageQueryPage {
+
+       function getName() {
+               return "Lonelypages";
+       }
+       function getPageHeader() {
+               return wfMsgExt( 'lonelypagestext', array( 'parse' ) );
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function isExpensive() {
+               return true;
+       }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
+
+               return
+                 "SELECT 'Lonelypages'  AS type,
+                         page_namespace AS namespace,
+                         page_title     AS title,
+                         page_title     AS value
+                    FROM $page
+               LEFT JOIN $pagelinks
+                      ON page_namespace=pl_namespace AND page_title=pl_title
+                   WHERE pl_namespace IS NULL
+                     AND page_namespace=".NS_MAIN."
+                     AND page_is_redirect=0";
+
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialLonelypages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $lpp = new LonelyPagesPage();
+
+       return $lpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialLongpages.php b/includes/specials/SpecialLongpages.php
new file mode 100644 (file)
index 0000000..be16a02
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ * @ingroup SpecialPage
+ */
+class LongPagesPage extends ShortPagesPage {
+
+       function getName() {
+               return "Longpages";
+       }
+
+       function sortDescending() {
+               return true;
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialLongpages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $lpp = new LongPagesPage();
+
+       $lpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMIMEsearch.php b/includes/specials/SpecialMIMEsearch.php
new file mode 100644 (file)
index 0000000..82ee4be
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/**
+ * A special page to search for files by MIME type as defined in the
+ * img_major_mime and img_minor_mime fields in the image table
+ *
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * Searches the database for files of the requested MIME type, comparing this with the
+ * 'img_major_mime' and 'img_minor_mime' fields in the image table.
+ * @ingroup SpecialPage
+ */
+class MIMEsearchPage extends QueryPage {
+       var $major, $minor;
+
+       function MIMEsearchPage( $major, $minor ) {
+               $this->major = $major;
+               $this->minor = $minor;
+       }
+
+       function getName() { return 'MIMEsearch'; }
+
+       /**
+        * Due to this page relying upon extra fields being passed in the SELECT it
+        * will fail if it's set as expensive and misermode is on
+        */
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function linkParameters() {
+               $arr = array( $this->major, $this->minor );
+               $mime = implode( '/', $arr );
+               return array( 'mime' => $mime );
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $image = $dbr->tableName( 'image' );
+               $major = $dbr->addQuotes( $this->major );
+               $minor = $dbr->addQuotes( $this->minor );
+
+               return
+                       "SELECT 'MIMEsearch' AS type,
+                               " . NS_IMAGE . " AS namespace,
+                               img_name AS title,
+                               img_major_mime AS value,
+
+                               img_size,
+                               img_width,
+                               img_height,
+                               img_user_text,
+                               img_timestamp
+                       FROM $image
+                       WHERE img_major_mime = $major AND img_minor_mime = $minor
+                       ";
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang, $wgLang;
+
+               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getText() );
+               $plink = $skin->makeLink( $nt->getPrefixedText(), $text );
+
+               $download = $skin->makeMediaLinkObj( $nt, wfMsgHtml( 'download' ) );
+               $bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->img_size ) );
+               $dimensions = wfMsgHtml( 'widthheight', $wgLang->formatNum( $result->img_width ),
+                       $wgLang->formatNum( $result->img_height ) );
+               $user = $skin->makeLinkObj( Title::makeTitle( NS_USER, $result->img_user_text ), $result->img_user_text );
+               $time = $wgLang->timeanddate( $result->img_timestamp );
+
+               return "($download) $plink . . $dimensions . . $bytes . . $user . . $time";
+       }
+}
+
+/**
+ * Output the HTML search form, and constructs the MIMEsearchPage object.
+ */
+function wfSpecialMIMEsearch( $par = null ) {
+       global $wgRequest, $wgTitle, $wgOut;
+
+       $mime = isset( $par ) ? $par : $wgRequest->getText( 'mime' );
+
+       $wgOut->addHTML(
+               Xml::openElement( 'form', array( 'id' => 'specialmimesearch', 'method' => 'get', 'action' => $wgTitle->getLocalUrl() ) ) .
+               Xml::openElement( 'fieldset' ) .
+               Xml::element( 'legend', null, wfMsg( 'mimesearch' ) ) .
+               Xml::inputLabel( wfMsg( 'mimetype' ), 'mime', 'mime', 20, $mime ) . ' ' .
+               Xml::submitButton( wfMsg( 'ilsubmit' ) ) .
+               Xml::closeElement( 'fieldset' ) .
+               Xml::closeElement( 'form' )
+       );
+
+       list( $major, $minor ) = wfSpecialMIMEsearchParse( $mime );
+       if ( $major == '' or $minor == '' or !wfSpecialMIMEsearchValidType( $major ) )
+               return;
+       $wpp = new MIMEsearchPage( $major, $minor );
+
+       list( $limit, $offset ) = wfCheckLimits();
+       $wpp->doQuery( $offset, $limit );
+}
+
+function wfSpecialMIMEsearchParse( $str ) {
+       // searched for an invalid MIME type.
+       if( strpos( $str, '/' ) === false) {
+               return array ('', '');
+       }
+
+       list( $major, $minor ) = explode( '/', $str, 2 );
+
+       return array(
+               ltrim( $major, ' ' ),
+               rtrim( $minor, ' ' )
+       );
+}
+
+function wfSpecialMIMEsearchValidType( $type ) {
+       // From maintenance/tables.sql => img_major_mime
+       $types = array(
+               'unknown',
+               'application',
+               'audio',
+               'image',
+               'text',
+               'video',
+               'message',
+               'model',
+               'multipart'
+       );
+
+       return in_array( $type, $types );
+}
diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php
new file mode 100644 (file)
index 0000000..6183374
--- /dev/null
@@ -0,0 +1,451 @@
+<?php
+/**
+ * Special page allowing users with the appropriate permissions to
+ * merge article histories, with some restrictions
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialMergehistory( $par ) {
+       global $wgRequest;
+
+       $form = new MergehistoryForm( $wgRequest, $par );
+       $form->execute();
+}
+
+/**
+ * The HTML form for Special:MergeHistory, which allows users with the appropriate
+ * permissions to view and restore deleted content.
+ * @ingroup SpecialPage
+ */
+class MergehistoryForm {
+       var $mAction, $mTarget, $mDest, $mTimestamp, $mTargetID, $mDestID, $mComment;
+       var $mTargetObj, $mDestObj;
+
+       function MergehistoryForm( $request, $par = "" ) {
+               global $wgUser;
+
+               $this->mAction = $request->getVal( 'action' );
+               $this->mTarget = $request->getVal( 'target' );
+               $this->mDest = $request->getVal( 'dest' );
+               $this->mSubmitted = $request->getBool( 'submitted' );
+
+               $this->mTargetID = intval( $request->getVal( 'targetID' ) );
+               $this->mDestID = intval( $request->getVal( 'destID' ) );
+               $this->mTimestamp = $request->getVal( 'mergepoint' );
+               if( !preg_match("/[0-9]{14}/",$this->mTimestamp) ) {
+                       $this->mTimestamp = '';
+               }
+               $this->mComment = $request->getText( 'wpComment' );
+
+               $this->mMerge = $request->wasPosted() && $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
+               // target page
+               if( $this->mSubmitted ) {
+                       $this->mTargetObj = Title::newFromURL( $this->mTarget );
+                       $this->mDestObj = Title::newFromURL( $this->mDest );
+               } else {
+                       $this->mTargetObj = null;
+                       $this->mDestObj = null;
+               }
+
+               $this->preCacheMessages();
+       }
+
+       /**
+        * As we use the same small set of messages in various methods and that
+        * they are called often, we call them once and save them in $this->message
+        */
+       function preCacheMessages() {
+               // Precache various messages
+               if( !isset( $this->message ) ) {
+                       $this->message['last'] = wfMsgExt( 'last', array( 'escape') );
+               }
+       }
+
+       function execute() {
+               global $wgOut, $wgUser;
+
+               $wgOut->setPagetitle( wfMsgHtml( "mergehistory" ) );
+
+               if( $this->mTargetID && $this->mDestID && $this->mAction=="submit" && $this->mMerge ) {
+                       return $this->merge();
+               }
+
+               if ( !$this->mSubmitted ) {
+                       $this->showMergeForm();
+                       return;
+               }
+
+               $errors = array();
+               if ( !$this->mTargetObj instanceof Title ) {
+                       $errors[] = wfMsgExt( 'mergehistory-invalid-source', array( 'parse' ) );
+               } elseif( !$this->mTargetObj->exists() ) {
+                       $errors[] = wfMsgExt( 'mergehistory-no-source', array( 'parse' ),
+                               wfEscapeWikiText( $this->mTargetObj->getPrefixedText() )
+                       );
+               }
+
+               if ( !$this->mDestObj instanceof Title) {
+                       $errors[] = wfMsgExt( 'mergehistory-invalid-destination', array( 'parse' ) );
+               } elseif( !$this->mDestObj->exists() ) {
+                       $errors[] = wfMsgExt( 'mergehistory-no-destination', array( 'parse' ),
+                               wfEscapeWikiText( $this->mDestObj->getPrefixedText() )
+                       );
+               }
+
+               // TODO: warn about target = dest?
+
+               if ( count( $errors ) ) {
+                       $this->showMergeForm();
+                       $wgOut->addHTML( implode( "\n", $errors ) );
+               } else {
+                       $this->showHistory();
+               }
+
+       }
+
+       function showMergeForm() {
+               global $wgOut, $wgScript;
+
+               $wgOut->addWikiMsg( 'mergehistory-header' );
+
+               $wgOut->addHtml(
+                       Xml::openElement( 'form', array(
+                               'method' => 'get',
+                               'action' => $wgScript ) ) .
+                       '<fieldset>' .
+                       Xml::element( 'legend', array(),
+                               wfMsg( 'mergehistory-box' ) ) .
+                       Xml::hidden( 'title',
+                               SpecialPage::getTitleFor( 'Mergehistory' )->getPrefixedDbKey() ) .
+                       Xml::hidden( 'submitted', '1' ) .
+                       Xml::hidden( 'mergepoint', $this->mTimestamp ) .
+                       Xml::openElement( 'table' ) .
+                       "<tr>
+                               <td>".Xml::label( wfMsg( 'mergehistory-from' ), 'target' )."</td>
+                               <td>".Xml::input( 'target', 30, $this->mTarget, array('id'=>'target') )."</td>
+                       </tr><tr>
+                               <td>".Xml::label( wfMsg( 'mergehistory-into' ), 'dest' )."</td>
+                               <td>".Xml::input( 'dest', 30, $this->mDest, array('id'=>'dest') )."</td>
+                       </tr><tr><td>" .
+                       Xml::submitButton( wfMsg( 'mergehistory-go' ) ) .
+                       "</td></tr>" .
+                       Xml::closeElement( 'table' ) .
+                       '</fieldset>' .
+                       '</form>' );
+       }
+
+       private function showHistory() {
+               global $wgLang, $wgContLang, $wgUser, $wgOut;
+
+               $this->sk = $wgUser->getSkin();
+
+               $wgOut->setPagetitle( wfMsg( "mergehistory" ) );
+
+               $this->showMergeForm();
+
+               # List all stored revisions
+               $revisions = new MergeHistoryPager( $this, array(), $this->mTargetObj, $this->mDestObj );
+               $haveRevisions = $revisions && $revisions->getNumRows() > 0;
+
+               $titleObj = SpecialPage::getTitleFor( "Mergehistory" );
+               $action = $titleObj->getLocalURL( "action=submit" );
+               # Start the form here
+               $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'merge' ) );
+               $wgOut->addHtml( $top );
+
+               if( $haveRevisions ) {
+                       # Format the user-visible controls (comment field, submission button)
+                       # in a nice little table
+                       $align = $wgContLang->isRtl() ? 'left' : 'right';
+                       $table =
+                               Xml::openElement( 'fieldset' ) .
+                               Xml::openElement( 'table' ) .
+                                       "<tr>
+                                               <td colspan='2'>" .
+                                                       wfMsgExt( 'mergehistory-merge', array('parseinline'),
+                                                               $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText() ) .
+                                               "</td>
+                                       </tr>
+                                       <tr>
+                                               <td align='$align'>" .
+                                                       Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) .
+                                               "</td>
+                                               <td>" .
+                                                       Xml::input( 'wpComment', 50, $this->mComment ) .
+                                               "</td>
+                                       </tr>
+                                       <tr>
+                                               <td>&nbsp;</td>
+                                               <td>" .
+                                                       Xml::submitButton( wfMsg( 'mergehistory-submit' ), array( 'name' => 'merge', 'id' => 'mw-merge-submit' ) ) .
+                                               "</td>
+                                       </tr>" .
+                               Xml::closeElement( 'table' ) .
+                               Xml::closeElement( 'fieldset' );
+
+                       $wgOut->addHtml( $table );
+               }
+
+               $wgOut->addHTML( "<h2 id=\"mw-mergehistory\">" . wfMsgHtml( "mergehistory-list" ) . "</h2>\n" );
+
+               if( $haveRevisions ) {
+                       $wgOut->addHTML( $revisions->getNavigationBar() );
+                       $wgOut->addHTML( "<ul>" );
+                       $wgOut->addHTML( $revisions->getBody() );
+                       $wgOut->addHTML( "</ul>" );
+                       $wgOut->addHTML( $revisions->getNavigationBar() );
+               } else {
+                       $wgOut->addWikiMsg( "mergehistory-empty" );
+               }
+
+               # Show relevant lines from the deletion log:
+               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'merge' ) ) . "</h2>\n" );
+               LogEventsList::showLogExtract( $wgOut, 'merge', $this->mTargetObj->getPrefixedText() );
+
+               # When we submit, go by page ID to avoid some nasty but unlikely collisions.
+               # Such would happen if a page was renamed after the form loaded, but before submit
+               $misc = Xml::hidden( 'targetID', $this->mTargetObj->getArticleID() );
+               $misc .= Xml::hidden( 'destID', $this->mDestObj->getArticleID() );
+               $misc .= Xml::hidden( 'target', $this->mTarget );
+               $misc .= Xml::hidden( 'dest', $this->mDest );
+               $misc .= Xml::hidden( 'wpEditToken', $wgUser->editToken() );
+               $misc .= Xml::closeElement( 'form' );
+               $wgOut->addHtml( $misc );
+
+               return true;
+       }
+
+       function formatRevisionRow( $row ) {
+               global $wgUser, $wgLang;
+
+               $rev = new Revision( $row );
+
+               $stxt = '';
+               $last = $this->message['last'];
+
+               $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
+               $checkBox = wfRadio( "mergepoint", $ts, false );
+
+               $pageLink = $this->sk->makeKnownLinkObj( $rev->getTitle(),
+                       htmlspecialchars( $wgLang->timeanddate( $ts ) ), 'oldid=' . $rev->getId() );
+               if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
+               }
+
+               # Last link
+               if( !$rev->userCan( Revision::DELETED_TEXT ) )
+                       $last = $this->message['last'];
+               else if( isset($this->prevId[$row->rev_id]) )
+                       $last = $this->sk->makeKnownLinkObj( $rev->getTitle(), $this->message['last'],
+                               "diff=" . $row->rev_id . "&oldid=" . $this->prevId[$row->rev_id] );
+
+               $userLink = $this->sk->revUserTools( $rev );
+
+               if(!is_null($size = $row->rev_len)) {
+                       if($size == 0)
+                               $stxt = wfMsgHtml('historyempty');
+                       else
+                               $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
+               }
+               $comment = $this->sk->revComment( $rev );
+
+               return "<li>$checkBox ($last) $pageLink . . $userLink $stxt $comment</li>";
+       }
+
+       /**
+        * Fetch revision text link if it's available to all users
+        * @return string
+        */
+       function getPageLink( $row, $titleObj, $ts, $target ) {
+               global $wgLang;
+
+               if( !$this->userCan($row, Revision::DELETED_TEXT) ) {
+                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
+               } else {
+                       $link = $this->sk->makeKnownLinkObj( $titleObj,
+                               $wgLang->timeanddate( $ts, true ), "target=$target&timestamp=$ts" );
+                       if( $this->isDeleted($row, Revision::DELETED_TEXT) )
+                               $link = '<span class="history-deleted">' . $link . '</span>';
+                       return $link;
+               }
+       }
+
+       function merge() {
+               global $wgOut, $wgUser;
+               # Get the titles directly from the IDs, in case the target page params
+               # were spoofed. The queries are done based on the IDs, so it's best to
+               # keep it consistent...
+               $targetTitle = Title::newFromID( $this->mTargetID );
+               $destTitle = Title::newFromID( $this->mDestID );
+               if( is_null($targetTitle) || is_null($destTitle) )
+                       return false; // validate these
+               if( $targetTitle->getArticleId() == $destTitle->getArticleId() )
+                       return false;
+               # Verify that this timestamp is valid
+               # Must be older than the destination page
+               $dbw = wfGetDB( DB_MASTER );
+               # Get timestamp into DB format
+               $this->mTimestamp = $this->mTimestamp ? $dbw->timestamp($this->mTimestamp) : '';
+               # Max timestamp should be min of destination page
+               $maxtimestamp = $dbw->selectField( 'revision', 'MIN(rev_timestamp)',
+                       array('rev_page' => $this->mDestID ),
+                       __METHOD__ );
+               # Destination page must exist with revisions
+               if( !$maxtimestamp ) {
+                       $wgOut->addWikiMsg('mergehistory-fail');
+                       return false;
+               }
+               # Get the latest timestamp of the source
+               $lasttimestamp = $dbw->selectField( array('page','revision'),
+                       'rev_timestamp',
+                       array('page_id' => $this->mTargetID, 'page_latest = rev_id' ),
+                       __METHOD__ );
+               # $this->mTimestamp must be older than $maxtimestamp
+               if( $this->mTimestamp >= $maxtimestamp ) {
+                       $wgOut->addWikiMsg('mergehistory-fail');
+                       return false;
+               }
+               # Update the revisions
+               if( $this->mTimestamp ) {
+                       $timewhere = "rev_timestamp <= {$this->mTimestamp}";
+                       $TimestampLimit = wfTimestamp(TS_MW,$this->mTimestamp);
+               } else {
+                       $timewhere = "rev_timestamp <= {$maxtimestamp}";
+                       $TimestampLimit = wfTimestamp(TS_MW,$lasttimestamp);
+               }
+               # Do the moving...
+               $dbw->update( 'revision',
+                       array( 'rev_page' => $this->mDestID ),
+                       array( 'rev_page' => $this->mTargetID,
+                               $timewhere ),
+                       __METHOD__ );
+
+               $count = $dbw->affectedRows();
+               # Make the source page a redirect if no revisions are left
+               $haveRevisions = $dbw->selectField( 'revision',
+                       'rev_timestamp',
+                       array( 'rev_page' => $this->mTargetID  ),
+                       __METHOD__,
+                       array( 'FOR UPDATE' ) );
+               if( !$haveRevisions ) {
+                       if( $this->mComment ) {
+                               $comment = wfMsgForContent( 'mergehistory-comment', $targetTitle->getPrefixedText(),
+                                       $destTitle->getPrefixedText(), $this->mComment );
+                       } else {
+                               $comment = wfMsgForContent( 'mergehistory-autocomment', $targetTitle->getPrefixedText(),
+                                       $destTitle->getPrefixedText() );
+                       }
+                       $mwRedir = MagicWord::get( 'redirect' );
+                       $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $destTitle->getPrefixedText() . "]]\n";
+                       $redirectArticle = new Article( $targetTitle );
+                       $redirectRevision = new Revision( array(
+                               'page'    => $this->mTargetID,
+                               'comment' => $comment,
+                               'text'    => $redirectText ) );
+                       $redirectRevision->insertOn( $dbw );
+                       $redirectArticle->updateRevisionOn( $dbw, $redirectRevision );
+
+                       # Now, we record the link from the redirect to the new title.
+                       # It should have no other outgoing links...
+                       $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mDestID ), __METHOD__ );
+                       $dbw->insert( 'pagelinks',
+                               array(
+                                       'pl_from'      => $this->mDestID,
+                                       'pl_namespace' => $destTitle->getNamespace(),
+                                       'pl_title'     => $destTitle->getDBkey() ),
+                               __METHOD__ );
+               } else {
+                       $targetTitle->invalidateCache(); // update histories
+               }
+               $destTitle->invalidateCache(); // update histories
+               # Check if this did anything
+               if( !$count ) {
+                       $wgOut->addWikiMsg('mergehistory-fail');
+                       return false;
+               }
+               # Update our logs
+               $log = new LogPage( 'merge' );
+               $log->addEntry( 'merge', $targetTitle, $this->mComment,
+                       array($destTitle->getPrefixedText(),$TimestampLimit) );
+
+               $wgOut->addHtml( wfMsgExt( 'mergehistory-success', array('parseinline'),
+                       $targetTitle->getPrefixedText(), $destTitle->getPrefixedText(), $count ) );
+
+               wfRunHooks( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) );
+
+               return true;
+       }
+}
+
+class MergeHistoryPager extends ReverseChronologicalPager {
+       public $mForm, $mConds;
+
+       function __construct( $form, $conds = array(), $source, $dest ) {
+               $this->mForm = $form;
+               $this->mConds = $conds;
+               $this->title = $source;
+               $this->articleID = $source->getArticleID();
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $maxtimestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)',
+                       array('rev_page' => $dest->getArticleID() ),
+                       __METHOD__ );
+               $this->maxTimestamp = $maxtimestamp;
+
+               parent::__construct();
+       }
+
+       function getStartBody() {
+               wfProfileIn( __METHOD__ );
+               # Do a link batch query
+               $this->mResult->seek( 0 );
+               $batch = new LinkBatch();
+               # Give some pointers to make (last) links
+               $this->mForm->prevId = array();
+               while( $row = $this->mResult->fetchObject() ) {
+                       $batch->addObj( Title::makeTitleSafe( NS_USER, $row->rev_user_text ) );
+                       $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->rev_user_text ) );
+
+                       $rev_id = isset($rev_id) ? $rev_id : $row->rev_id;
+                       if( $rev_id > $row->rev_id )
+                               $this->mForm->prevId[$rev_id] = $row->rev_id;
+                       else if( $rev_id < $row->rev_id )
+                               $this->mForm->prevId[$row->rev_id] = $rev_id;
+
+                       $rev_id = $row->rev_id;
+               }
+
+               $batch->execute();
+               $this->mResult->seek( 0 );
+
+               wfProfileOut( __METHOD__ );
+               return '';
+       }
+
+       function formatRow( $row ) {
+               $block = new Block;
+               return $this->mForm->formatRevisionRow( $row );
+       }
+
+       function getQueryInfo() {
+               $conds = $this->mConds;
+               $conds['rev_page'] = $this->articleID;
+               $conds[] = "rev_timestamp < {$this->maxTimestamp}";
+
+               return array(
+                       'tables' => array('revision'),
+                       'fields' => array( 'rev_minor_edit', 'rev_timestamp', 'rev_user', 'rev_user_text', 'rev_comment',
+                                'rev_id', 'rev_page', 'rev_text_id', 'rev_len', 'rev_deleted' ),
+                       'conds' => $conds
+               );
+       }
+
+       function getIndexField() {
+               return 'rev_timestamp';
+       }
+}
diff --git a/includes/specials/SpecialMostcategories.php b/includes/specials/SpecialMostcategories.php
new file mode 100644 (file)
index 0000000..5df9c86
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * implements Special:Mostcategories
+ * @ingroup SpecialPage
+ */
+class MostcategoriesPage extends QueryPage {
+
+       function getName() { return 'Mostcategories'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $categorylinks, $page) = $dbr->tableNamesN( 'categorylinks', 'page' );
+               return
+                       "
+                       SELECT
+                               'Mostcategories' as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               COUNT(*) as value
+                       FROM $categorylinks
+                       LEFT JOIN $page ON cl_from = page_id
+                       WHERE page_namespace = " . NS_MAIN . "
+                       GROUP BY 1,2,3
+                       HAVING COUNT(*) > 1
+                       ";
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang;
+               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               if ( !$title instanceof Title ) { throw new MWException('Invalid title in database'); }
+               $count = wfMsgExt( 'ncategories', array( 'parsemag', 'escape' ), $wgLang->formatNum( $result->value ) );
+               $link = $skin->makeKnownLinkObj( $title, $title->getText() );
+               return wfSpecialList( $link, $count );
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialMostcategories() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new MostcategoriesPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMostimages.php b/includes/specials/SpecialMostimages.php
new file mode 100644 (file)
index 0000000..2fed0bd
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * implements Special:Mostimages
+ * @ingroup SpecialPage
+ */
+class MostimagesPage extends ImageQueryPage {
+
+       function getName() { return 'Mostimages'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $imagelinks = $dbr->tableName( 'imagelinks' );
+               return
+                       "
+                       SELECT
+                               'Mostimages' as type,
+                               " . NS_IMAGE . " as namespace,
+                               il_to as title,
+                               COUNT(*) as value
+                       FROM $imagelinks
+                       GROUP BY 1,2,3
+                       HAVING COUNT(*) > 1
+                       ";
+       }
+
+       function getCellHtml( $row ) {
+               global $wgLang;
+               return wfMsgExt( 'nlinks',  array( 'parsemag', 'escape' ),
+                       $wgLang->formatNum( $row->value ) ) . '<br />';
+       }
+
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialMostimages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new MostimagesPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMostlinked.php b/includes/specials/SpecialMostlinked.php
new file mode 100644 (file)
index 0000000..a56ac26
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page to show pages ordered by the number of pages linking to them.
+ * Implements Special:Mostlinked
+ *
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @author Rob Church <robchur@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @copyright Â© 2006 Rob Church
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+class MostlinkedPage extends QueryPage {
+
+       function getName() { return 'Mostlinked'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       /**
+        * Note: Getting page_namespace only works if $this->isCached() is false
+        */
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $pagelinks, $page ) = $dbr->tableNamesN( 'pagelinks', 'page' );
+               return
+                       "SELECT 'Mostlinked' AS type,
+                               pl_namespace AS namespace,
+                               pl_title AS title,
+                               COUNT(*) AS value,
+                               page_namespace
+                       FROM $pagelinks
+                       LEFT JOIN $page ON pl_namespace=page_namespace AND pl_title=page_title
+                       GROUP BY 1,2,3,5
+                       HAVING COUNT(*) > 1";
+       }
+
+       /**
+        * Pre-fill the link cache
+        */
+       function preprocessResults( $db, $res ) {
+               if( $db->numRows( $res ) > 0 ) {
+                       $linkBatch = new LinkBatch();
+                       while( $row = $db->fetchObject( $res ) )
+                               $linkBatch->add( $row->namespace, $row->title );
+                       $db->dataSeek( $res, 0 );
+                       $linkBatch->execute();
+               }
+       }
+
+       /**
+        * Make a link to "what links here" for the specified title
+        *
+        * @param $title Title being queried
+        * @param $skin Skin to use
+        * @return string
+        */
+       function makeWlhLink( &$title, $caption, &$skin ) {
+               $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedDBkey() );
+               return $skin->makeKnownLinkObj( $wlh, $caption );
+       }
+
+       /**
+        * Make links to the page corresponding to the item, and the "what links here" page for it
+        *
+        * @param $skin Skin to be used
+        * @param $result Result row
+        * @return string
+        */
+       function formatResult( $skin, $result ) {
+               global $wgLang;
+               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               $link = $skin->makeLinkObj( $title );
+               $wlh = $this->makeWlhLink( $title,
+                       wfMsgExt( 'nlinks', array( 'parsemag', 'escape'),
+                               $wgLang->formatNum( $result->value ) ), $skin );
+               return wfSpecialList( $link, $wlh );
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialMostlinked() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new MostlinkedPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMostlinkedcategories.php b/includes/specials/SpecialMostlinkedcategories.php
new file mode 100644 (file)
index 0000000..1b66d48
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A querypage to show categories ordered in descending order by the pages  in them
+ *
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+class MostlinkedCategoriesPage extends QueryPage {
+
+       function getName() { return 'Mostlinkedcategories'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $categorylinks = $dbr->tableName( 'categorylinks' );
+               $name = $dbr->addQuotes( $this->getName() );
+               return
+                       "
+                       SELECT
+                               $name as type,
+                               " . NS_CATEGORY . " as namespace,
+                               cl_to as title,
+                               COUNT(*) as value
+                       FROM $categorylinks
+                       GROUP BY 1,2,3
+                       ";
+       }
+
+       function sortDescending() { return true; }
+
+       /**
+        * Fetch user page links and cache their existence
+        */
+       function preprocessResults( $db, $res ) {
+               $batch = new LinkBatch;
+               while ( $row = $db->fetchObject( $res ) )
+                       $batch->add( $row->namespace, $row->title );
+               $batch->execute();
+
+               // Back to start for display
+               if ( $db->numRows( $res ) > 0 )
+                       // If there are no rows we get an error seeking.
+                       $db->dataSeek( $res, 0 );
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+
+               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getText() );
+
+               $plink = $skin->makeLinkObj( $nt, htmlspecialchars( $text ) );
+
+               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->value ) );
+               return wfSpecialList($plink, $nlinks);
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialMostlinkedCategories() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new MostlinkedCategoriesPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMostlinkedtemplates.php b/includes/specials/SpecialMostlinkedtemplates.php
new file mode 100644 (file)
index 0000000..b8d47e6
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+/**
+ * Special page lists templates with a large number of
+ * transclusion links, i.e. "most used" templates
+ *
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class SpecialMostlinkedtemplates extends QueryPage {
+
+       /**
+        * Name of the report
+        *
+        * @return string
+        */
+       public function getName() {
+               return 'Mostlinkedtemplates';
+       }
+
+       /**
+        * Is this report expensive, i.e should it be cached?
+        *
+        * @return bool
+        */
+       public function isExpensive() {
+               return true;
+       }
+
+       /**
+        * Is there a feed available?
+        *
+        * @return bool
+        */
+       public function isSyndicated() {
+               return false;
+       }
+
+       /**
+        * Sort the results in descending order?
+        *
+        * @return bool
+        */
+       public function sortDescending() {
+               return true;
+       }
+
+       /**
+        * Generate SQL for the report
+        *
+        * @return string
+        */
+       public function getSql() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $templatelinks = $dbr->tableName( 'templatelinks' );
+               $name = $dbr->addQuotes( $this->getName() );
+               return "SELECT {$name} AS type,
+                       " . NS_TEMPLATE . " AS namespace,
+                       tl_title AS title,
+                       COUNT(*) AS value
+                       FROM {$templatelinks}
+                       WHERE tl_namespace = " . NS_TEMPLATE . "
+                       GROUP BY 1, 2, 3";
+       }
+
+       /**
+        * Pre-cache page existence to speed up link generation
+        *
+        * @param Database $dbr Database connection
+        * @param int $res Result pointer
+        */
+       public function preprocessResults( $db, $res ) {
+               $batch = new LinkBatch();
+               while( $row = $db->fetchObject( $res ) ) {
+                       $batch->add( $row->namespace, $row->title );
+               }
+               $batch->execute();
+               if( $db->numRows( $res ) > 0 )
+                       $db->dataSeek( $res, 0 );
+       }
+
+       /**
+        * Format a result row
+        *
+        * @param Skin $skin Skin to use for UI elements
+        * @param object $result Result row
+        * @return string
+        */
+       public function formatResult( $skin, $result ) {
+               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               if( $title instanceof Title ) {
+                       return wfSpecialList(
+                               $skin->makeLinkObj( $title ),
+                               $this->makeWlhLink( $title, $skin, $result )
+                       );
+               } else {
+                       $tsafe = htmlspecialchars( $result->title );
+                       return "Invalid title in result set; {$tsafe}";
+               }
+       }
+
+       /**
+        * Make a "what links here" link for a given title
+        *
+        * @param Title $title Title to make the link for
+        * @param Skin $skin Skin to use
+        * @param object $result Result row
+        * @return string
+        */
+       private function makeWlhLink( $title, $skin, $result ) {
+               global $wgLang;
+               $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
+               $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
+                       $wgLang->formatNum( $result->value ) );
+               return $skin->makeKnownLinkObj( $wlh, $label, 'target=' . $title->getPrefixedUrl() );
+       }
+}
+
+/**
+ * Execution function
+ *
+ * @param mixed $par Parameters passed to the page
+ */
+function wfSpecialMostlinkedtemplates( $par = false ) {
+       list( $limit, $offset ) = wfCheckLimits();
+       $mlt = new SpecialMostlinkedtemplates();
+       $mlt->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMostrevisions.php b/includes/specials/SpecialMostrevisions.php
new file mode 100644 (file)
index 0000000..001a08b
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * A special page to show pages in the
+ *
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class MostrevisionsPage extends QueryPage {
+
+       function getName() { return 'Mostrevisions'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
+               return
+                       "
+                       SELECT
+                               'Mostrevisions' as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               COUNT(*) as value
+                       FROM $revision
+                       JOIN $page ON page_id = rev_page
+                       WHERE page_namespace = " . NS_MAIN . "
+                       GROUP BY 1,2,3
+                       HAVING COUNT(*) > 1
+                       ";
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+
+               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getPrefixedText() );
+
+               $plink = $skin->makeKnownLinkObj( $nt, $text );
+
+               $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->value ) );
+               $nlink = $skin->makeKnownLinkObj( $nt, $nl, 'action=history' );
+
+               return wfSpecialList($plink, $nlink);
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialMostrevisions() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new MostrevisionsPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php
new file mode 100644 (file)
index 0000000..d08fb66
--- /dev/null
@@ -0,0 +1,428 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialMovepage( $par = null ) {
+       global $wgUser, $wgOut, $wgRequest, $action;
+
+       # Check for database lock
+       if ( wfReadOnly() ) {
+               $wgOut->readOnlyPage();
+               return;
+       }
+
+       $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
+       $oldTitle = $wgRequest->getText( 'wpOldTitle', $target );
+       $newTitle = $wgRequest->getText( 'wpNewTitle' );
+
+       # Variables beginning with 'o' for old article 'n' for new article
+       $ot = Title::newFromText( $oldTitle );
+       $nt = Title::newFromText( $newTitle );
+
+       if( is_null( $ot ) ) {
+               $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
+               return;
+       }
+       if( !$ot->exists() ) {
+               $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' );
+               return;
+       }
+
+       # Check rights
+       $permErrors = $ot->getUserPermissionsErrors( 'move', $wgUser );
+       if( !empty( $permErrors ) ) {
+               $wgOut->showPermissionsErrorPage( $permErrors );
+               return;
+       }
+
+       $f = new MovePageForm( $ot, $nt );
+
+       if ( 'submit' == $action && $wgRequest->wasPosted()
+               && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( '' );
+       }
+}
+
+/**
+ * HTML form for Special:Movepage
+ * @ingroup SpecialPage
+ */
+class MovePageForm {
+       var $oldTitle, $newTitle, $reason; # Text input
+       var $moveTalk, $deleteAndMove, $moveSubpages;
+
+       private $watch = false;
+
+       function MovePageForm( $oldTitle, $newTitle ) {
+               global $wgRequest;
+               $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
+               $this->oldTitle = $oldTitle;
+               $this->newTitle = $newTitle;
+               $this->reason = $wgRequest->getText( 'wpReason' );
+               if ( $wgRequest->wasPosted() ) {
+                       $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', false );
+               } else {
+                       $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', true );
+               }
+               $this->moveSubpages = $wgRequest->getBool( 'wpMovesubpages', false );
+               $this->deleteAndMove = $wgRequest->getBool( 'wpDeleteAndMove' ) && $wgRequest->getBool( 'wpConfirm' );
+               $this->watch = $wgRequest->getCheck( 'wpWatch' );
+       }
+
+       function showForm( $err, $hookErr = '' ) {
+               global $wgOut, $wgUser;
+
+               $ot = $this->oldTitle;
+               $sk = $wgUser->getSkin();
+
+               $oldTitleLink = $sk->makeLinkObj( $ot );
+               $oldTitle = $ot->getPrefixedText();
+
+               $wgOut->setPagetitle( wfMsg( 'move-page', $oldTitle ) );
+               $wgOut->setSubtitle( wfMsg( 'move-page-backlink', $oldTitleLink ) );
+
+               if( $this->newTitle == '' ) {
+                       # Show the current title as a default
+                       # when the form is first opened.
+                       $newTitle = $oldTitle;
+               } else {
+                       if( $err == '' ) {
+                               $nt = Title::newFromURL( $this->newTitle );
+                               if( $nt ) {
+                                       # If a title was supplied, probably from the move log revert
+                                       # link, check for validity. We can then show some diagnostic
+                                       # information and save a click.
+                                       $newerr = $ot->isValidMoveOperation( $nt );
+                                       if( is_string( $newerr ) ) {
+                                               $err = $newerr;
+                                       }
+                               }
+                       }
+                       $newTitle = $this->newTitle;
+               }
+
+               if ( $err == 'articleexists' && $wgUser->isAllowed( 'delete' ) ) {
+                       $wgOut->addWikiMsg( 'delete_and_move_text', $newTitle );
+                       $movepagebtn = wfMsg( 'delete_and_move' );
+                       $submitVar = 'wpDeleteAndMove';
+                       $confirm = "
+                               <tr>
+                                       <td></td>
+                                       <td class='mw-input'>" .
+                                               Xml::checkLabel( wfMsg( 'delete_and_move_confirm' ), 'wpConfirm', 'wpConfirm' ) .
+                                       "</td>
+                               </tr>";
+                       $err = '';
+               } else {
+                       $wgOut->addWikiMsg( 'movepagetext' );
+                       $movepagebtn = wfMsg( 'movepagebtn' );
+                       $submitVar = 'wpMove';
+                       $confirm = false;
+               }
+
+               $oldTalk = $ot->getTalkPage();
+               $considerTalk = ( !$ot->isTalkPage() && $oldTalk->exists() );
+
+               if ( $considerTalk ) {
+                       $wgOut->addWikiMsg( 'movepagetalktext' );
+               }
+
+               $titleObj = SpecialPage::getTitleFor( 'Movepage' );
+               $token = htmlspecialchars( $wgUser->editToken() );
+
+               if ( $err != '' ) {
+                       $wgOut->setSubtitle( wfMsg( 'formerror' ) );
+                       $errMsg = "";
+                       if( $err == 'hookaborted' ) {
+                               $errMsg = "<p><strong class=\"error\">$hookErr</strong></p>\n";
+                       } else if (is_array($err)) {
+                               $errMsg = '<p><strong class="error">' . call_user_func_array( 'wfMsgWikiHtml', $err ) . "</strong></p>\n";
+                       } else {
+                               $errMsg = '<p><strong class="error">' . wfMsgWikiHtml( $err ) . "</strong></p>\n";
+                       }
+                       $wgOut->addHTML( $errMsg );
+               }
+
+               $wgOut->addHTML(
+                        Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) .
+                        Xml::openElement( 'fieldset' ) .
+                        Xml::element( 'legend', null, wfMsg( 'move-page-legend' ) ) .
+                        Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-movepage-table' ) ) .
+                        "<tr>
+                               <td class='mw-label'>" .
+                                       wfMsgHtml( 'movearticle' ) .
+                               "</td>
+                               <td class='mw-input'>
+                                       <strong>{$oldTitleLink}</strong>
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>" .
+                                       Xml::label( wfMsg( 'newtitle' ), 'wpNewTitle' ) .
+                               "</td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'wpNewTitle', 40, $newTitle, array( 'type' => 'text', 'id' => 'wpNewTitle' ) ) .
+                                       Xml::hidden( 'wpOldTitle', $oldTitle ) .
+                               "</td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>" .
+                                       Xml::label( wfMsg( 'movereason' ), 'wpReason' ) .
+                               "</td>
+                               <td class='mw-input'>" .
+                                       Xml::tags( 'textarea', array( 'name' => 'wpReason', 'id' => 'wpReason', 'cols' => 60, 'rows' => 2 ), htmlspecialchars( $this->reason ) ) .
+                               "</td>
+                       </tr>"
+               );
+
+               if( $considerTalk ) {
+                       $wgOut->addHTML( "
+                               <tr>
+                                       <td></td>
+                                       <td class='mw-input'>" .
+                                               Xml::checkLabel( wfMsg( 'movetalk' ), 'wpMovetalk', 'wpMovetalk', $this->moveTalk ) .
+                                       "</td>
+                               </tr>"
+                       );
+               }
+
+               if( ($ot->hasSubpages() || $ot->getTalkPage()->hasSubpages())
+               && $ot->userCan( 'move-subpages' ) ) {
+                       $wgOut->addHTML( "
+                               <tr>
+                                       <td></td>
+                                       <td class=\"mw-input\">" .
+                               Xml::checkLabel( wfMsgHtml(
+                                               $ot->hasSubpages()
+                                               ? 'move-subpages'
+                                               : 'move-talk-subpages'
+                                       ),
+                                       'wpMovesubpages', 'wpMovesubpages',
+                                       # Don't check the box if we only have talk subpages to
+                                       # move and we aren't moving the talk page.
+                                       $this->moveSubpages && ($ot->hasSubpages() || $this->moveTalk)
+                               ) .
+                                       "</td>
+                               </tr>"
+                       );
+               }
+
+               $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) || $ot->userIsWatching();
+               $wgOut->addHTML( "
+                       <tr>
+                               <td></td>
+                               <td class='mw-input'>" .
+                                       Xml::checkLabel( wfMsg( 'move-watch' ), 'wpWatch', 'watch', $watchChecked ) .
+                               "</td>
+                       </tr>
+                               {$confirm}
+                       <tr>
+                               <td>&nbsp;</td>
+                               <td class='mw-submit'>" .
+                                       Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
+                               "</td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::hidden( 'wpEditToken', $token ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) .
+                       "\n"
+               );
+
+               $this->showLogFragment( $ot, $wgOut );
+
+       }
+
+       function doSubmit() {
+               global $wgOut, $wgUser, $wgRequest, $wgMaximumMovedPages, $wgLang;
+
+               if ( $wgUser->pingLimiter( 'move' ) ) {
+                       $wgOut->rateLimited();
+                       return;
+               }
+
+               $ot = $this->oldTitle;
+               $nt = $this->newTitle;
+
+               # Delete to make way if requested
+               if ( $wgUser->isAllowed( 'delete' ) && $this->deleteAndMove ) {
+                       $article = new Article( $nt );
+
+                       # Disallow deletions of big articles
+                       $bigHistory = $article->isBigDeletion();
+                       if( $bigHistory && !$nt->userCan( 'bigdelete' ) ) {
+                               global $wgLang, $wgDeleteRevisionsLimit;
+                               $this->showForm( array('delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
+                               return;
+                       }
+
+                       // This may output an error message and exit
+                       $article->doDelete( wfMsgForContent( 'delete_and_move_reason' ) );
+               }
+
+               # don't allow moving to pages with # in
+               if ( !$nt || $nt->getFragment() != '' ) {
+                       $this->showForm( 'badtitletext' );
+                       return;
+               }
+
+               $error = $ot->moveTo( $nt, true, $this->reason );
+               if ( $error !== true ) {
+                       # FIXME: showForm() should handle multiple errors
+                       call_user_func_array(array($this, 'showForm'), $error[0]);
+                       return;
+               }
+
+               wfRunHooks( 'SpecialMovepageAfterMove', array( &$this , &$ot , &$nt ) ) ;
+
+               $wgOut->setPagetitle( wfMsg( 'pagemovedsub' ) );
+
+               $oldUrl = $ot->getFullUrl( 'redirect=no' );
+               $newUrl = $nt->getFullUrl();
+               $oldText = $ot->getPrefixedText();
+               $newText = $nt->getPrefixedText();
+               $oldLink = "<span class='plainlinks'>[$oldUrl $oldText]</span>";
+               $newLink = "<span class='plainlinks'>[$newUrl $newText]</span>";
+
+               $wgOut->addWikiMsg( 'movepage-moved', $oldLink, $newLink, $oldText, $newText );
+
+               # Now we move extra pages we've been asked to move: subpages and talk
+               # pages.  First, if the old page or the new page is a talk page, we
+               # can't move any talk pages: cancel that.
+               if( $ot->isTalkPage() || $nt->isTalkPage() ) {
+                       $this->moveTalk = false;
+               }
+
+               if( !$ot->userCan( 'move-subpages' ) ) {
+                       $this->moveSubpages = false;
+               }
+
+               # Next make a list of id's.  This might be marginally less efficient
+               # than a more direct method, but this is not a highly performance-cri-
+               # tical code path and readable code is more important here.
+               #
+               # Note: this query works nicely on MySQL 5, but the optimizer in MySQL
+               # 4 might get confused.  If so, consider rewriting as a UNION.
+               #
+               # If the target namespace doesn't allow subpages, moving with subpages
+               # would mean that you couldn't move them back in one operation, which
+               # is bad.  FIXME: A specific error message should be given in this
+               # case.
+               $dbr = wfGetDB( DB_MASTER );
+               if( $this->moveSubpages && (
+                       MWNamespace::hasSubpages( $nt->getNamespace() ) || (
+                               $this->moveTalk &&
+                               MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() )
+                       )
+               ) ) {
+                       $conds = array(
+                               'page_title LIKE '.$dbr->addQuotes( $dbr->escapeLike( $ot->getDBkey() ) . '/%' )
+                                       .' OR page_title = ' . $dbr->addQuotes( $ot->getDBkey() )
+                       );
+                       $conds['page_namespace'] = array();
+                       if( MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
+                               $conds['page_namespace'] []= $ot->getNamespace();
+                       }
+                       if( $this->moveTalk && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) ) {
+                               $conds['page_namespace'] []= $ot->getTalkPage()->getNamespace();
+                       }
+               } elseif( $this->moveTalk ) {
+                       $conds = array(
+                               'page_namespace' => $ot->getTalkPage()->getNamespace(),
+                               'page_title' => $ot->getDBKey()
+                       );
+               } else {
+                       # Skip the query
+                       $conds = null;
+               }
+
+               $extrapages = array();
+               if( !is_null( $conds ) ) {
+                       $extrapages = $dbr->select( 'page',
+                               array( 'page_id', 'page_namespace', 'page_title' ),
+                               $conds,
+                               __METHOD__
+                       );
+               }
+
+               $extraOutput = array();
+               $skin = $wgUser->getSkin();
+               $count = 1;
+               foreach( $extrapages as $row ) {
+                       if( $row->page_id == $ot->getArticleId() ) {
+                               # Already did this one.
+                               continue;
+                       }
+
+                       $oldPage = Title::newFromRow( $row );
+                       $newPageName = preg_replace(
+                               '#^'.preg_quote( $ot->getDBKey(), '#' ).'#',
+                               $nt->getDBKey(),
+                               $oldPage->getDBKey()
+                       );
+                       if( $oldPage->isTalkPage() ) {
+                               $newNs = $nt->getTalkPage()->getNamespace();
+                       } else {
+                               $newNs = $nt->getSubjectPage()->getNamespace();
+                       }
+                       # Bug 14385: we need makeTitleSafe because the new page names may
+                       # be longer than 255 characters.
+                       $newPage = Title::makeTitleSafe( $newNs, $newPageName );
+                       if( !$newPage ) {
+                               $oldLink = $skin->makeKnownLinkObj( $oldPage );
+                               $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink,
+                                       htmlspecialchars(Title::makeName( $newNs, $newPageName )));
+                               continue;
+                       }
+
+                       # This was copy-pasted from Renameuser, bleh.
+                       if ( $newPage->exists() && !$oldPage->isValidMoveTarget( $newPage ) ) {
+                               $link = $skin->makeKnownLinkObj( $newPage );
+                               $extraOutput []= wfMsgHtml( 'movepage-page-exists', $link );
+                       } else {
+                               $success = $oldPage->moveTo( $newPage, true, $this->reason );
+                               if( $success === true ) {
+                                       $oldLink = $skin->makeKnownLinkObj( $oldPage, '', 'redirect=no' );
+                                       $newLink = $skin->makeKnownLinkObj( $newPage );
+                                       $extraOutput []= wfMsgHtml( 'movepage-page-moved', $oldLink, $newLink );
+                               } else {
+                                       $oldLink = $skin->makeKnownLinkObj( $oldPage );
+                                       $newLink = $skin->makeLinkObj( $newPage );
+                                       $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink, $newLink );
+                               }
+                       }
+
+                       ++$count;
+                       if( $count >= $wgMaximumMovedPages ) {
+                               $extraOutput []= wfMsgExt( 'movepage-max-pages', array( 'parsemag', 'escape' ), $wgLang->formatNum( $wgMaximumMovedPages ) );
+                               break;
+                       }
+               }
+
+               if( $extraOutput !== array() ) {
+                       $wgOut->addHTML( "<ul>\n<li>" . implode( "</li>\n<li>", $extraOutput ) . "</li>\n</ul>" );
+               }
+
+               # Deal with watches (we don't watch subpages)
+               if( $this->watch ) {
+                       $wgUser->addWatch( $ot );
+                       $wgUser->addWatch( $nt );
+               } else {
+                       $wgUser->removeWatch( $ot );
+                       $wgUser->removeWatch( $nt );
+               }
+       }
+
+       function showLogFragment( $title, &$out ) {
+               $out->addHTML( Xml::element( 'h2', NULL, LogPage::logName( 'move' ) ) );
+               LogEventsList::showLogExtract( $out, 'move', $title->getPrefixedText() );
+       }
+
+}
diff --git a/includes/specials/SpecialNewimages.php b/includes/specials/SpecialNewimages.php
new file mode 100644 (file)
index 0000000..5fd37e8
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ * FIXME: this code is crap, should use Pager and Database::select().
+ */
+
+/**
+ *
+ */
+function wfSpecialNewimages( $par, $specialPage ) {
+       global $wgUser, $wgOut, $wgLang, $wgRequest, $wgGroupPermissions, $wgMiserMode;
+
+       $wpIlMatch = $wgRequest->getText( 'wpIlMatch' );
+       $dbr = wfGetDB( DB_SLAVE );
+       $sk = $wgUser->getSkin();
+       $shownav = !$specialPage->including();
+       $hidebots = $wgRequest->getBool('hidebots',1);
+
+       $hidebotsql = '';
+       if ($hidebots) {
+
+               /** Make a list of group names which have the 'bot' flag
+                   set.
+               */
+               $botconds=array();
+               foreach ($wgGroupPermissions as $groupname=>$perms) {
+                       if(array_key_exists('bot',$perms) && $perms['bot']) {
+                               $botconds[]="ug_group='$groupname'";
+                       }
+               }
+
+               /* If not bot groups, do not set $hidebotsql */
+               if ($botconds) {
+                       $isbotmember=$dbr->makeList($botconds, LIST_OR);
+
+                       /** This join, in conjunction with WHERE ug_group
+                           IS NULL, returns only those rows from IMAGE
+                       where the uploading user is not a member of
+                       a group which has the 'bot' permission set.
+                       */
+                       $ug = $dbr->tableName('user_groups');
+                       $hidebotsql = " LEFT OUTER JOIN $ug ON img_user=ug_user AND ($isbotmember)";
+               }
+       }
+
+       $image = $dbr->tableName('image');
+
+       $sql="SELECT img_timestamp from $image";
+       if ($hidebotsql) {
+               $sql .= "$hidebotsql WHERE ug_group IS NULL";
+       }
+       $sql.=' ORDER BY img_timestamp DESC LIMIT 1';
+       $res = $dbr->query($sql, 'wfSpecialNewImages');
+       $row = $dbr->fetchRow($res);
+       if($row!==false) {
+               $ts=$row[0];
+       } else {
+               $ts=false;
+       }
+       $dbr->freeResult($res);
+       $sql='';
+
+       /** If we were clever, we'd use this to cache. */
+       $latestTimestamp = wfTimestamp( TS_MW, $ts);
+
+       /** Hardcode this for now. */
+       $limit = 48;
+
+       if ( $parval = intval( $par ) ) {
+               if ( $parval <= $limit && $parval > 0 ) {
+                       $limit = $parval;
+               }
+       }
+
+       $where = array();
+       $searchpar = '';
+       if ( $wpIlMatch != '' && !$wgMiserMode) {
+               $nt = Title::newFromUrl( $wpIlMatch );
+               if($nt ) {
+                       $m = $dbr->strencode( strtolower( $nt->getDBkey() ) );
+                       $m = str_replace( '%', "\\%", $m );
+                       $m = str_replace( '_', "\\_", $m );
+                       $where[] = "LOWER(img_name) LIKE '%{$m}%'";
+                       $searchpar = '&wpIlMatch=' . urlencode( $wpIlMatch );
+               }
+       }
+
+       $invertSort = false;
+       if( $until = $wgRequest->getVal( 'until' ) ) {
+               $where[] = "img_timestamp < '" . $dbr->timestamp( $until ) . "'";
+       }
+       if( $from = $wgRequest->getVal( 'from' ) ) {
+               $where[] = "img_timestamp >= '" . $dbr->timestamp( $from ) . "'";
+               $invertSort = true;
+       }
+       $sql='SELECT img_size, img_name, img_user, img_user_text,'.
+            "img_description,img_timestamp FROM $image";
+
+       if($hidebotsql) {
+               $sql .= $hidebotsql;
+               $where[]='ug_group IS NULL';
+       }
+       if(count($where)) {
+               $sql.=' WHERE '.$dbr->makeList($where, LIST_AND);
+       }
+       $sql.=' ORDER BY img_timestamp '. ( $invertSort ? '' : ' DESC' );
+       $sql.=' LIMIT '.($limit+1);
+       $res = $dbr->query($sql, 'wfSpecialNewImages');
+
+       /**
+        * We have to flip things around to get the last N after a certain date
+        */
+       $images = array();
+       while ( $s = $dbr->fetchObject( $res ) ) {
+               if( $invertSort ) {
+                       array_unshift( $images, $s );
+               } else {
+                       array_push( $images, $s );
+               }
+       }
+       $dbr->freeResult( $res );
+
+       $gallery = new ImageGallery();
+       $firstTimestamp = null;
+       $lastTimestamp = null;
+       $shownImages = 0;
+       foreach( $images as $s ) {
+               if( ++$shownImages > $limit ) {
+                       # One extra just to test for whether to show a page link;
+                       # don't actually show it.
+                       break;
+               }
+
+               $name = $s->img_name;
+               $ut = $s->img_user_text;
+
+               $nt = Title::newFromText( $name, NS_IMAGE );
+               $ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut );
+
+               $gallery->add( $nt, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
+
+               $timestamp = wfTimestamp( TS_MW, $s->img_timestamp );
+               if( empty( $firstTimestamp ) ) {
+                       $firstTimestamp = $timestamp;
+               }
+               $lastTimestamp = $timestamp;
+       }
+
+       $bydate = wfMsg( 'bydate' );
+       $lt = $wgLang->formatNum( min( $shownImages, $limit ) );
+       if ($shownav) {
+               $text = wfMsgExt( 'imagelisttext', array('parse'), $lt, $bydate );
+               $wgOut->addHTML( $text . "\n" );
+       }
+
+       $sub = wfMsg( 'ilsubmit' );
+       $titleObj = SpecialPage::getTitleFor( 'Newimages' );
+       $action = $titleObj->escapeLocalURL( $hidebots ? '' : 'hidebots=0' );
+       if ($shownav && !$wgMiserMode) {
+               $wgOut->addHTML( "<form id=\"imagesearch\" method=\"post\" action=\"" .
+                 "{$action}\">" .
+                       Xml::input( 'wpIlMatch', 20, $wpIlMatch ) . ' ' .
+                 Xml::submitButton( $sub, array( 'name' => 'wpIlSubmit' ) ) .
+                 "</form>" );
+       }
+
+       /**
+        * Paging controls...
+        */
+
+       # If we change bot visibility, this needs to be carried along.
+       if(!$hidebots) {
+               $botpar='&hidebots=0';
+       } else {
+               $botpar='';
+       }
+       $now = wfTimestampNow();
+       $d = $wgLang->date( $now, true );
+       $t = $wgLang->time( $now, true );
+       $dateLink = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'sp-newimages-showfrom', $d, $t ), 
+               'from='.$now.$botpar.$searchpar );
+
+       $botLink = $sk->makeKnownLinkObj($titleObj, wfMsgHtml( 'showhidebots', 
+               ($hidebots ? wfMsgHtml('show') : wfMsgHtml('hide'))),'hidebots='.($hidebots ? '0' : '1').$searchpar);
+
+       $prevLink = wfMsgHtml( 'prevn', $wgLang->formatNum( $limit ) );
+       if( $firstTimestamp && $firstTimestamp != $latestTimestamp ) {
+               $prevLink = $sk->makeKnownLinkObj( $titleObj, $prevLink, 'from=' . $firstTimestamp . $botpar . $searchpar );
+       }
+
+       $nextLink = wfMsgHtml( 'nextn', $wgLang->formatNum( $limit ) );
+       if( $shownImages > $limit && $lastTimestamp ) {
+               $nextLink = $sk->makeKnownLinkObj( $titleObj, $nextLink, 'until=' . $lastTimestamp.$botpar.$searchpar );
+       }
+
+       $prevnext = '<p>' . $botLink . ' '. wfMsgHtml( 'viewprevnext', $prevLink, $nextLink, $dateLink ) .'</p>';
+
+       if ($shownav)
+               $wgOut->addHTML( $prevnext );
+
+       if( count( $images ) ) {
+               $wgOut->addHTML( $gallery->toHTML() );
+               if ($shownav)
+                       $wgOut->addHTML( $prevnext );
+       } else {
+               $wgOut->addWikiMsg( 'noimages' );
+       }
+}
diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php
new file mode 100644 (file)
index 0000000..72a0183
--- /dev/null
@@ -0,0 +1,437 @@
+<?php
+
+/**
+ * implements Special:Newpages
+ * @ingroup SpecialPage
+ */
+class SpecialNewpages extends SpecialPage {
+
+       // Stored objects
+       protected $opts, $skin;
+
+       // Some internal settings
+       protected $showNavigation = false;
+
+       public function __construct(){
+               parent::__construct( 'Newpages' );
+               $this->includable( true );      
+       }
+
+       protected function setup( $par ) {
+               global $wgRequest, $wgUser, $wgEnableNewpagesUserFilter;
+
+               // Options
+               $opts = new FormOptions();
+               $this->opts = $opts; // bind
+               $opts->add( 'hideliu', false );
+               $opts->add( 'hidepatrolled', false );
+               $opts->add( 'hidebots', false );
+               $opts->add( 'limit', 50 );
+               $opts->add( 'offset', '' );
+               $opts->add( 'namespace', '0' );
+               $opts->add( 'username', '' );
+               $opts->add( 'feed', '' );
+
+               // Set values
+               $opts->fetchValuesFromRequest( $wgRequest );
+               if ( $par ) $this->parseParams( $par );
+
+               // Validate
+               $opts->validateIntBounds( 'limit', 0, 5000 );
+               if( !$wgEnableNewpagesUserFilter ) {
+                       $opts->setValue( 'username', '' );
+               }
+
+               // Store some objects
+               $this->skin = $wgUser->getSkin();
+       }
+
+       protected function parseParams( $par ) {
+               global $wgLang;
+               $bits = preg_split( '/\s*,\s*/', trim( $par ) );
+               foreach ( $bits as $bit ) {
+                       if ( 'shownav' == $bit )
+                               $this->showNavigation = true;
+                       if ( 'hideliu' === $bit )
+                               $this->opts->setValue( 'hideliu', true );
+                       if ( 'hidepatrolled' == $bit )
+                               $this->opts->setValue( 'hidepatrolled', true );
+                       if ( 'hidebots' == $bit )
+                               $this->opts->setValue( 'hidebots', true );
+                       if ( is_numeric( $bit ) )
+                               $this->opts->setValue( 'limit', intval( $bit ) );
+
+                       $m = array();
+                       if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) )
+                               $this->opts->setValue( 'limit', intval($m[1]) );
+                       // PG offsets not just digits!
+                       if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) )
+                               $this->opts->setValue( 'offset',  intval($m[1]) );
+                       if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
+                               $ns = $wgLang->getNsIndex( $m[1] );
+                               if( $ns !== false ) {
+                                       $this->opts->setValue( 'namespace',  $ns );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Show a form for filtering namespace and username
+        *
+        * @param string $par
+        * @return string
+        */
+       public function execute( $par ) {
+               global $wgLang, $wgGroupPermissions, $wgUser, $wgOut;
+
+               $this->setHeaders();
+               $this->outputHeader();
+
+               $this->showNavigation = !$this->including(); // Maybe changed in setup
+               $this->setup( $par );
+
+               if( !$this->including() ) {
+                       // Settings
+                       $this->form();
+
+                       $this->setSyndicated();
+                       $feedType = $this->opts->getValue( 'feed' );
+                       if( $feedType ) {
+                               return $this->feed( $feedType );
+                       }
+               }
+
+               $pager = new NewPagesPager( $this, $this->opts );
+               $pager->mLimit = $this->opts->getValue( 'limit' );
+               $pager->mOffset = $this->opts->getValue( 'offset' );
+
+               if( $pager->getNumRows() ) {
+                       $navigation = '';
+                       if ( $this->showNavigation ) $navigation = $pager->getNavigationBar();
+                       $wgOut->addHTML( $navigation . $pager->getBody() . $navigation );
+               } else {
+                       $wgOut->addWikiMsg( 'specialpage-empty' );
+               }
+       }
+
+       protected function filterLinks() {
+               global $wgGroupPermissions, $wgUser;
+
+               // show/hide links
+               $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
+
+               // Option value -> message mapping
+               $filters = array(
+                       'hideliu' => 'rcshowhideliu',
+                       'hidepatrolled' => 'rcshowhidepatr',
+                       'hidebots' => 'rcshowhidebots'
+               );
+
+               // Disable some if needed
+               if ( $wgGroupPermissions['*']['createpage'] !== true )
+                       unset($filters['hideliu']);
+
+               if ( !$wgUser->useNPPatrol() )
+                       unset($filters['hidepatrolled']);
+
+               $links = array();
+               $changed = $this->opts->getChangedValues();
+               unset($changed['offset']); // Reset offset if query type changes
+
+               $self = $this->getTitle();
+               foreach ( $filters as $key => $msg ) {
+                       $onoff = 1 - $this->opts->getValue($key);
+                       $link = $this->skin->makeKnownLinkObj( $self, $showhide[$onoff],
+                               wfArrayToCGI( array( $key => $onoff ), $changed )
+                       );
+                       $links[$key] = wfMsgHtml( $msg, $link );
+               }
+
+               return implode( ' | ', $links );
+       }
+
+       protected function form() {
+               global $wgOut, $wgEnableNewpagesUserFilter, $wgScript;
+
+               // Consume values
+               $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
+               $namespace = $this->opts->consumeValue( 'namespace' );
+               $username = $this->opts->consumeValue( 'username' );
+
+               // Check username input validity
+               $ut = Title::makeTitleSafe( NS_USER, $username );
+               $userText = $ut ? $ut->getText() : '';
+
+               // Store query values in hidden fields so that form submission doesn't lose them
+               $hidden = array();
+               foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
+                       $hidden[] = Xml::hidden( $key, $value );
+               }
+               $hidden = implode( "\n", $hidden );
+
+               $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
+                       Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
+                       Xml::fieldset( wfMsg( 'newpages' ) ) .
+                       Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
+                       "<tr>
+                               <td class='mw-label'>" .
+                                       Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
+                               "</td>
+                               <td class='mw-input'>" .
+                                       Xml::namespaceSelector( $namespace, 'all' ) .
+                               "</td>
+                       </tr>" .
+                       ($wgEnableNewpagesUserFilter ?
+                       "<tr>
+                               <td class='mw-label'>" .
+                                       Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
+                               "</td>
+                               <td class='mw-input'>" .
+                                       Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
+                               "</td>
+                       </tr>" : "" ) .
+                       "<tr> <td></td>
+                               <td class='mw-submit'>" .
+                                       Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
+                               "</td>
+                       </tr>" .
+                       "<tr>
+                               <td></td>
+                               <td class='mw-input'>" .
+                                       $this->filterLinks() .
+                               "</td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       $hidden .
+                       Xml::closeElement( 'form' );
+
+               $wgOut->addHTML( $form );
+       }
+
+       protected function setSyndicated() {
+               global $wgOut;
+               $queryParams = array(
+                       'namespace' => $this->opts->getValue( 'namespace' ),
+                       'username' => $this->opts->getValue( 'username' )
+               );
+               $wgOut->setSyndicated( true );
+               $wgOut->setFeedAppendQuery( wfArrayToCGI( $queryParams ) );
+       }
+
+       /**
+        * Format a row, providing the timestamp, links to the page/history, size, user links, and a comment
+        *
+        * @param $skin Skin to use
+        * @param $result Result row
+        * @return string
+        */
+       public function formatRow( $result ) {
+               global $wgLang, $wgContLang, $wgUser;
+               $dm = $wgContLang->getDirMark();
+
+               $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
+               $time = $wgLang->timeAndDate( $result->rc_timestamp, true );
+               $plink = $this->skin->makeKnownLinkObj( $title, '', $this->patrollable( $result ) ? 'rcid=' . $result->rc_id : '' );
+               $hist = $this->skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
+               $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
+                       $wgLang->formatNum( $result->length ) );
+               $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
+                       $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
+               $comment = $this->skin->commentBlock( $result->rc_comment );
+               $css = $this->patrollable( $result ) ? " class='not-patrolled'" : '';
+
+               return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment}</li>\n";
+       }
+
+       /**
+        * Should a specific result row provide "patrollable" links?
+        *
+        * @param $result Result row
+        * @return bool
+        */
+       protected function patrollable( $result ) {
+               global $wgUser;
+               return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
+       }
+
+       /**
+        * Output a subscription feed listing recent edits to this page.
+        * @param string $type
+        */
+       protected function feed( $type ) {
+               global $wgFeed, $wgFeedClasses;
+
+               if ( !$wgFeed ) {
+                       global $wgOut;
+                       $wgOut->addWikiMsg( 'feed-unavailable' );
+                       return;
+               }
+
+               if( !isset( $wgFeedClasses[$type] ) ) {
+                       global $wgOut;
+                       $wgOut->addWikiMsg( 'feed-invalid' );
+                       return;
+               }
+
+               $feed = new $wgFeedClasses[$type](
+                       $this->feedTitle(),
+                       wfMsg( 'tagline' ),
+                       $this->getTitle()->getFullUrl() );
+
+               $pager = new NewPagesPager( $this, $this->opts );
+               $limit = $this->opts->getValue( 'limit' );
+               global $wgFeedLimit;
+               if( $limit > $wgFeedLimit ) {
+                       $limit = $wgFeedLimit;
+               }
+               $pager->mLimit = $limit;
+
+               $feed->outHeader();
+               if( $pager->getNumRows() > 0 ) {
+                       while( $row = $pager->mResult->fetchObject() ) {
+                               $feed->outItem( $this->feedItem( $row ) );
+                       }
+               }
+               $feed->outFooter();
+       }
+
+       protected function feedTitle() {
+               global $wgContLanguageCode, $wgSitename;
+               $page = SpecialPage::getPage( 'Newpages' );
+               $desc = $page->getDescription();
+               return "$wgSitename - $desc [$wgContLanguageCode]";
+       }
+
+       protected function feedItem( $row ) {
+               $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
+               if( $title ) {
+                       $date = $row->rc_timestamp;
+                       $comments = $title->getTalkPage()->getFullURL();
+
+                       return new FeedItem(
+                               $title->getPrefixedText(),
+                               $this->feedItemDesc( $row ),
+                               $title->getFullURL(),
+                               $date,
+                               $this->feedItemAuthor( $row ),
+                               $comments);
+               } else {
+                       return NULL;
+               }
+       }
+
+       /**
+        * Quickie hack... strip out wikilinks to more legible form from the comment.
+        */
+       protected function stripComment( $text ) {
+               return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
+       }
+
+       protected function feedItemAuthor( $row ) {
+               return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
+       }
+
+       protected function feedItemDesc( $row ) {
+               $revision = Revision::newFromId( $row->rev_id );
+               if( $revision ) {
+                       return '<p>' . htmlspecialchars( $revision->getUserText() ) . ': ' .
+                               htmlspecialchars( $revision->getComment() ) . 
+                               "</p>\n<hr />\n<div>" .
+                               nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
+               }
+               return '';
+       }
+}
+
+/**
+ * @ingroup SpecialPage Pager
+ */
+class NewPagesPager extends ReverseChronologicalPager {
+       // Stored opts
+       protected $opts, $mForm;
+
+       private $hideliu, $hidepatrolled, $hidebots, $namespace, $user, $spTitle;
+
+       function __construct( $form, FormOptions $opts ) {
+               parent::__construct();
+               $this->mForm = $form;
+               $this->opts = $opts;
+       }
+
+       function getTitle(){
+               static $title = null;
+               if ( $title === null )
+                       $title = $this->mForm->getTitle();
+               return $title;
+       }
+
+       function getQueryInfo() {
+               global $wgEnableNewpagesUserFilter, $wgGroupPermissions, $wgUser;
+               $conds = array();
+               $conds['rc_new'] = 1;
+
+               $namespace = $this->opts->getValue( 'namespace' );
+               $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
+
+               $username = $this->opts->getValue( 'username' );
+               $user = Title::makeTitleSafe( NS_USER, $username );
+
+               if( $namespace !== false ) {
+                       $conds['rc_namespace'] = $namespace;
+                       $rcIndexes = array( 'new_name_timestamp' );
+               } else {
+                       $rcIndexes = array( 'rc_timestamp' );
+               }
+               $conds[] = 'page_id = rc_cur_id';
+               $conds['page_is_redirect'] = 0;
+               # $wgEnableNewpagesUserFilter - temp WMF hack
+               if( $wgEnableNewpagesUserFilter && $user ) {
+                       $conds['rc_user_text'] = $user->getText();
+                       $rcIndexes = 'rc_user_text';
+               # If anons cannot make new pages, don't "exclude logged in users"!
+               } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
+                       $conds['rc_user'] = 0;
+               }
+               # If this user cannot see patrolled edits or they are off, don't do dumb queries!
+               if( $this->opts->getValue( 'hidepatrolled' ) && $wgUser->useNPPatrol() ) {
+                       $conds['rc_patrolled'] = 0;
+               }
+               if( $this->opts->getValue( 'hidebots' ) ) {
+                       $conds['rc_bot'] = 0;
+               }
+
+               return array(
+                       'tables' => array( 'recentchanges', 'page' ),
+                       'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
+                               rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id',
+                       'conds' => $conds,
+                       'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) )
+               );
+       }
+
+       function getIndexField() {
+               return 'rc_timestamp';
+       }
+
+       function formatRow( $row ) {
+               return $this->mForm->formatRow( $row );
+       }
+
+       function getStartBody() {
+               # Do a batch existence check on pages
+               $linkBatch = new LinkBatch();
+               while( $row = $this->mResult->fetchObject() ) {
+                       $linkBatch->add( NS_USER, $row->rc_user_text );
+                       $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
+                       $linkBatch->add( $row->rc_namespace, $row->rc_title );
+               }
+               $linkBatch->execute();
+               return "<ul>";
+       }
+
+       function getEndBody() {
+               return "</ul>";
+       }
+}
diff --git a/includes/specials/SpecialPopularpages.php b/includes/specials/SpecialPopularpages.php
new file mode 100644 (file)
index 0000000..eb57273
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * implements Special:Popularpages
+ * @ingroup SpecialPage
+ */
+class PopularPagesPage extends QueryPage {
+
+       function getName() {
+               return "Popularpages";
+       }
+
+       function isExpensive() {
+               # page_counter is not indexed
+               return true;
+       }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $page = $dbr->tableName( 'page' );
+
+               $query =
+                       "SELECT 'Popularpages' as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               page_counter as value
+                       FROM $page ";
+               $where =
+                       "WHERE page_is_redirect=0 AND page_namespace";
+
+               global $wgContentNamespaces;
+               if( empty( $wgContentNamespaces ) ) {
+                       $where .= '='.NS_MAIN;
+               } else if( count( $wgContentNamespaces ) > 1 ) {
+                       $where .= ' in (' . implode( ', ', $wgContentNamespaces ) . ')';
+               } else {
+                       $where .= '='.$wgContentNamespaces[0];
+               }
+
+               return $query . $where;
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+               $title = Title::makeTitle( $result->namespace, $result->title );
+               $link = $skin->makeKnownLinkObj( $title, htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) ) );
+               $nv = wfMsgExt( 'nviews', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->value ) );
+               return wfSpecialList($link, $nv);
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialPopularpages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $ppp = new PopularPagesPage();
+
+       return $ppp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialPreferences.php b/includes/specials/SpecialPreferences.php
new file mode 100644 (file)
index 0000000..6a16f1c
--- /dev/null
@@ -0,0 +1,1122 @@
+<?php
+/**
+ * Hold things related to displaying and saving user preferences.
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Entry point that create the "Preferences" object
+ */
+function wfSpecialPreferences() {
+       global $wgRequest;
+
+       $form = new PreferencesForm( $wgRequest );
+       $form->execute();
+}
+
+/**
+ * Preferences form handling
+ * This object will show the preferences form and can save it as well.
+ * @ingroup SpecialPage
+ */
+class PreferencesForm {
+       var $mQuickbar, $mOldpass, $mNewpass, $mRetypePass, $mStubs;
+       var $mRows, $mCols, $mSkin, $mMath, $mDate, $mUserEmail, $mEmailFlag, $mNick;
+       var $mUserLanguage, $mUserVariant;
+       var $mSearch, $mRecent, $mRecentDays, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
+       var $mReset, $mPosted, $mToggles, $mUseAjaxSearch, $mSearchNs, $mRealName, $mImageSize;
+       var $mUnderline, $mWatchlistEdits;
+
+       /**
+        * Constructor
+        * Load some values
+        */
+       function PreferencesForm( &$request ) {
+               global $wgContLang, $wgUser, $wgAllowRealName;
+
+               $this->mQuickbar = $request->getVal( 'wpQuickbar' );
+               $this->mOldpass = $request->getVal( 'wpOldpass' );
+               $this->mNewpass = $request->getVal( 'wpNewpass' );
+               $this->mRetypePass =$request->getVal( 'wpRetypePass' );
+               $this->mStubs = $request->getVal( 'wpStubs' );
+               $this->mRows = $request->getVal( 'wpRows' );
+               $this->mCols = $request->getVal( 'wpCols' );
+               $this->mSkin = $request->getVal( 'wpSkin' );
+               $this->mMath = $request->getVal( 'wpMath' );
+               $this->mDate = $request->getVal( 'wpDate' );
+               $this->mUserEmail = $request->getVal( 'wpUserEmail' );
+               $this->mRealName = $wgAllowRealName ? $request->getVal( 'wpRealName' ) : '';
+               $this->mEmailFlag = $request->getCheck( 'wpEmailFlag' ) ? 0 : 1;
+               $this->mNick = $request->getVal( 'wpNick' );
+               $this->mUserLanguage = $request->getVal( 'wpUserLanguage' );
+               $this->mUserVariant = $request->getVal( 'wpUserVariant' );
+               $this->mSearch = $request->getVal( 'wpSearch' );
+               $this->mRecent = $request->getVal( 'wpRecent' );
+               $this->mRecentDays = $request->getVal( 'wpRecentDays' );
+               $this->mHourDiff = $request->getVal( 'wpHourDiff' );
+               $this->mSearchLines = $request->getVal( 'wpSearchLines' );
+               $this->mSearchChars = $request->getVal( 'wpSearchChars' );
+               $this->mImageSize = $request->getVal( 'wpImageSize' );
+               $this->mThumbSize = $request->getInt( 'wpThumbSize' );
+               $this->mUnderline = $request->getInt( 'wpOpunderline' );
+               $this->mAction = $request->getVal( 'action' );
+               $this->mReset = $request->getCheck( 'wpReset' );
+               $this->mPosted = $request->wasPosted();
+               $this->mSuccess = $request->getCheck( 'success' );
+               $this->mWatchlistDays = $request->getVal( 'wpWatchlistDays' );
+               $this->mWatchlistEdits = $request->getVal( 'wpWatchlistEdits' );
+               $this->mUseAjaxSearch = $request->getCheck( 'wpUseAjaxSearch' );
+               $this->mDisableMWSuggest = $request->getCheck( 'wpDisableMWSuggest' );
+
+               $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) &&
+                       $this->mPosted &&
+                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
+
+               # User toggles  (the big ugly unsorted list of checkboxes)
+               $this->mToggles = array();
+               if ( $this->mPosted ) {
+                       $togs = User::getToggles();
+                       foreach ( $togs as $tname ) {
+                               $this->mToggles[$tname] = $request->getCheck( "wpOp$tname" ) ? 1 : 0;
+                       }
+               }
+
+               $this->mUsedToggles = array();
+
+               # Search namespace options
+               # Note: namespaces don't necessarily have consecutive keys
+               $this->mSearchNs = array();
+               if ( $this->mPosted ) {
+                       $namespaces = $wgContLang->getNamespaces();
+                       foreach ( $namespaces as $i => $namespace ) {
+                               if ( $i >= 0 ) {
+                                       $this->mSearchNs[$i] = $request->getCheck( "wpNs$i" ) ? 1 : 0;
+                               }
+                       }
+               }
+
+               # Validate language
+               if ( !preg_match( '/^[a-z\-]*$/', $this->mUserLanguage ) ) {
+                       $this->mUserLanguage = 'nolanguage';
+               }
+
+               wfRunHooks( 'InitPreferencesForm', array( $this, $request ) );
+       }
+
+       function execute() {
+               global $wgUser, $wgOut;
+
+               if ( $wgUser->isAnon() ) {
+                       $wgOut->showErrorPage( 'prefsnologin', 'prefsnologintext' );
+                       return;
+               }
+               if ( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+               if ( $this->mReset ) {
+                       $this->resetPrefs();
+                       $this->mainPrefsForm( 'reset', wfMsg( 'prefsreset' ) );
+               } else if ( $this->mSaveprefs ) {
+                       $this->savePreferences();
+               } else {
+                       $this->resetPrefs();
+                       $this->mainPrefsForm( '' );
+               }
+       }
+       /**
+        * @access private
+        */
+       function validateInt( &$val, $min=0, $max=0x7fffffff ) {
+               $val = intval($val);
+               $val = min($val, $max);
+               $val = max($val, $min);
+               return $val;
+       }
+
+       /**
+        * @access private
+        */
+       function validateFloat( &$val, $min, $max=0x7fffffff ) {
+               $val = floatval( $val );
+               $val = min( $val, $max );
+               $val = max( $val, $min );
+               return( $val );
+       }
+
+       /**
+        * @access private
+        */
+       function validateIntOrNull( &$val, $min=0, $max=0x7fffffff ) {
+               $val = trim($val);
+               if($val === '') {
+                       return null;
+               } else {
+                       return $this->validateInt( $val, $min, $max );
+               }
+       }
+
+       /**
+        * @access private
+        */
+       function validateDate( $val ) {
+               global $wgLang, $wgContLang;
+               if ( $val !== false && (
+                       in_array( $val, (array)$wgLang->getDatePreferences() ) ||
+                       in_array( $val, (array)$wgContLang->getDatePreferences() ) ) )
+               {
+                       return $val;
+               } else {
+                       return $wgLang->getDefaultDateFormat();
+               }
+       }
+
+       /**
+        * Used to validate the user inputed timezone before saving it as
+        * 'timecorrection', will return '00:00' if fed bogus data.
+        * Note: It's not a 100% correct implementation timezone-wise, it will
+        * accept stuff like '14:30',
+        * @access private
+        * @param string $s the user input
+        * @return string
+        */
+       function validateTimeZone( $s ) {
+               if ( $s !== '' ) {
+                       if ( strpos( $s, ':' ) ) {
+                               # HH:MM
+                               $array = explode( ':' , $s );
+                               $hour = intval( $array[0] );
+                               $minute = intval( $array[1] );
+                       } else {
+                               $minute = intval( $s * 60 );
+                               $hour = intval( $minute / 60 );
+                               $minute = abs( $minute ) % 60;
+                       }
+                       # Max is +14:00 and min is -12:00, see:
+                       # http://en.wikipedia.org/wiki/Timezone
+                       $hour = min( $hour, 14 );
+                       $hour = max( $hour, -12 );
+                       $minute = min( $minute, 59 );
+                       $minute = max( $minute, 0 );
+                       $s = sprintf( "%02d:%02d", $hour, $minute );
+               }
+               return $s;
+       }
+
+       /**
+        * @access private
+        */
+       function savePreferences() {
+               global $wgUser, $wgOut, $wgParser;
+               global $wgEnableUserEmail, $wgEnableEmail;
+               global $wgEmailAuthentication, $wgRCMaxAge;
+               global $wgAuth, $wgEmailConfirmToEdit;
+
+
+               if ( '' != $this->mNewpass && $wgAuth->allowPasswordChange() ) {
+                       if ( $this->mNewpass != $this->mRetypePass ) {
+                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'badretype' ) );
+                               $this->mainPrefsForm( 'error', wfMsg( 'badretype' ) );
+                               return;
+                       }
+
+                       if (!$wgUser->checkPassword( $this->mOldpass )) {
+                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'wrongpassword' ) );
+                               $this->mainPrefsForm( 'error', wfMsg( 'wrongpassword' ) );
+                               return;
+                       }
+
+                       try {
+                               $wgUser->setPassword( $this->mNewpass );
+                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'success' ) );
+                               $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
+                       } catch( PasswordError $e ) {
+                               wfRunHooks( 'PrefsPasswordAudit', array( $wgUser, $this->mNewpass, 'error' ) );
+                               $this->mainPrefsForm( 'error', $e->getMessage() );
+                               return;
+                       }
+               }
+               $wgUser->setRealName( $this->mRealName );
+               $oldOptions = $wgUser->mOptions;
+
+               if( $wgUser->getOption( 'language' ) !== $this->mUserLanguage ) {
+                       $needRedirect = true;
+               } else {
+                       $needRedirect = false;
+               }
+
+               # Validate the signature and clean it up as needed
+               global $wgMaxSigChars;
+               if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
+                       global $wgLang;
+                       $this->mainPrefsForm( 'error',
+                               wfMsgExt( 'badsiglength', 'parsemag', $wgLang->formatNum( $wgMaxSigChars ) ) );
+                       return;
+               } elseif( $this->mToggles['fancysig'] ) {
+                       if( $wgParser->validateSig( $this->mNick ) !== false ) {
+                               $this->mNick = $wgParser->cleanSig( $this->mNick );
+                       } else {
+                               $this->mainPrefsForm( 'error', wfMsg( 'badsig' ) );
+                               return;
+                       }
+               } else {
+                       // When no fancy sig used, make sure ~{3,5} get removed.
+                       $this->mNick = $wgParser->cleanSigInSig( $this->mNick );
+               }
+
+               $wgUser->setOption( 'language', $this->mUserLanguage );
+               $wgUser->setOption( 'variant', $this->mUserVariant );
+               $wgUser->setOption( 'nickname', $this->mNick );
+               $wgUser->setOption( 'quickbar', $this->mQuickbar );
+               $wgUser->setOption( 'skin', $this->mSkin );
+               global $wgUseTeX;
+               if( $wgUseTeX ) {
+                       $wgUser->setOption( 'math', $this->mMath );
+               }
+               $wgUser->setOption( 'date', $this->validateDate( $this->mDate ) );
+               $wgUser->setOption( 'searchlimit', $this->validateIntOrNull( $this->mSearch ) );
+               $wgUser->setOption( 'contextlines', $this->validateIntOrNull( $this->mSearchLines ) );
+               $wgUser->setOption( 'contextchars', $this->validateIntOrNull( $this->mSearchChars ) );
+               $wgUser->setOption( 'rclimit', $this->validateIntOrNull( $this->mRecent ) );
+               $wgUser->setOption( 'rcdays', $this->validateInt($this->mRecentDays, 1, ceil($wgRCMaxAge / (3600*24))));
+               $wgUser->setOption( 'wllimit', $this->validateIntOrNull( $this->mWatchlistEdits, 0, 1000 ) );
+               $wgUser->setOption( 'rows', $this->validateInt( $this->mRows, 4, 1000 ) );
+               $wgUser->setOption( 'cols', $this->validateInt( $this->mCols, 4, 1000 ) );
+               $wgUser->setOption( 'stubthreshold', $this->validateIntOrNull( $this->mStubs ) );
+               $wgUser->setOption( 'timecorrection', $this->validateTimeZone( $this->mHourDiff, -12, 14 ) );
+               $wgUser->setOption( 'imagesize', $this->mImageSize );
+               $wgUser->setOption( 'thumbsize', $this->mThumbSize );
+               $wgUser->setOption( 'underline', $this->validateInt($this->mUnderline, 0, 2) );
+               $wgUser->setOption( 'watchlistdays', $this->validateFloat( $this->mWatchlistDays, 0, 7 ) );
+               $wgUser->setOption( 'ajaxsearch', $this->mUseAjaxSearch );
+               $wgUser->setOption( 'disablesuggest', $this->mDisableMWSuggest );
+
+               # Set search namespace options
+               foreach( $this->mSearchNs as $i => $value ) {
+                       $wgUser->setOption( "searchNs{$i}", $value );
+               }
+
+               if( $wgEnableEmail && $wgEnableUserEmail ) {
+                       $wgUser->setOption( 'disablemail', $this->mEmailFlag );
+               }
+
+               # Set user toggles
+               foreach ( $this->mToggles as $tname => $tvalue ) {
+                       $wgUser->setOption( $tname, $tvalue );
+               }
+
+               $error = false;
+               if( $wgEnableEmail ) {
+                       $newadr = $this->mUserEmail;
+                       $oldadr = $wgUser->getEmail();
+                       if( ($newadr != '') && ($newadr != $oldadr) ) {
+                               # the user has supplied a new email address on the login page
+                               if( $wgUser->isValidEmailAddr( $newadr ) ) {
+                                       # new behaviour: set this new emailaddr from login-page into user database record
+                                       $wgUser->setEmail( $newadr );
+                                       # but flag as "dirty" = unauthenticated
+                                       $wgUser->invalidateEmail();
+                                       if ($wgEmailAuthentication) {
+                                               # Mail a temporary password to the dirty address.
+                                               # User can come back through the confirmation URL to re-enable email.
+                                               $result = $wgUser->sendConfirmationMail();
+                                               if( WikiError::isError( $result ) ) {
+                                                       $error = wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) );
+                                               } else {
+                                                       $error = wfMsg( 'eauthentsent', $wgUser->getName() );
+                                               }
+                                       }
+                               } else {
+                                       $error = wfMsg( 'invalidemailaddress' );
+                               }
+                       } else {
+                               if( $wgEmailConfirmToEdit && empty( $newadr ) ) {
+                                       $this->mainPrefsForm( 'error', wfMsg( 'noemailtitle' ) );
+                                       return;
+                               }
+                               $wgUser->setEmail( $this->mUserEmail );
+                       }
+                       if( $oldadr != $newadr ) {
+                               wfRunHooks( 'PrefsEmailAudit', array( $wgUser, $oldadr, $newadr ) );
+                       }
+               }
+
+               if( !$wgAuth->updateExternalDB( $wgUser ) ){
+                       $this->mainPrefsForm( 'error', wfMsg( 'externaldberror' ) );
+                       return;
+               }
+
+               $msg = '';
+               if ( !wfRunHooks( 'SavePreferences', array( $this, $wgUser, &$msg, $oldOptions ) ) ) {
+                       $this->mainPrefsForm( 'error', $msg );
+                       return;
+               }
+
+               $wgUser->setCookies();
+               $wgUser->saveSettings();
+
+               if( $needRedirect && $error === false ) {
+                       $title = SpecialPage::getTitleFor( 'Preferences' );
+                       $wgOut->redirect( $title->getFullURL( 'success' ) );
+                       return;
+               }
+
+               $wgOut->parserOptions( ParserOptions::newFromUser( $wgUser ) );
+               $this->mainPrefsForm( $error === false ? 'success' : 'error', $error);
+       }
+
+       /**
+        * @access private
+        */
+       function resetPrefs() {
+               global $wgUser, $wgLang, $wgContLang, $wgContLanguageCode, $wgAllowRealName;
+
+               $this->mOldpass = $this->mNewpass = $this->mRetypePass = '';
+               $this->mUserEmail = $wgUser->getEmail();
+               $this->mUserEmailAuthenticationtimestamp = $wgUser->getEmailAuthenticationtimestamp();
+               $this->mRealName = ($wgAllowRealName) ? $wgUser->getRealName() : '';
+
+               # language value might be blank, default to content language
+               $this->mUserLanguage = $wgUser->getOption( 'language', $wgContLanguageCode );
+
+               $this->mUserVariant = $wgUser->getOption( 'variant');
+               $this->mEmailFlag = $wgUser->getOption( 'disablemail' ) == 1 ? 1 : 0;
+               $this->mNick = $wgUser->getOption( 'nickname' );
+
+               $this->mQuickbar = $wgUser->getOption( 'quickbar' );
+               $this->mSkin = Skin::normalizeKey( $wgUser->getOption( 'skin' ) );
+               $this->mMath = $wgUser->getOption( 'math' );
+               $this->mDate = $wgUser->getDatePreference();
+               $this->mRows = $wgUser->getOption( 'rows' );
+               $this->mCols = $wgUser->getOption( 'cols' );
+               $this->mStubs = $wgUser->getOption( 'stubthreshold' );
+               $this->mHourDiff = $wgUser->getOption( 'timecorrection' );
+               $this->mSearch = $wgUser->getOption( 'searchlimit' );
+               $this->mSearchLines = $wgUser->getOption( 'contextlines' );
+               $this->mSearchChars = $wgUser->getOption( 'contextchars' );
+               $this->mImageSize = $wgUser->getOption( 'imagesize' );
+               $this->mThumbSize = $wgUser->getOption( 'thumbsize' );
+               $this->mRecent = $wgUser->getOption( 'rclimit' );
+               $this->mRecentDays = $wgUser->getOption( 'rcdays' );
+               $this->mWatchlistEdits = $wgUser->getOption( 'wllimit' );
+               $this->mUnderline = $wgUser->getOption( 'underline' );
+               $this->mWatchlistDays = $wgUser->getOption( 'watchlistdays' );
+               $this->mUseAjaxSearch = $wgUser->getBoolOption( 'ajaxsearch' );
+               $this->mDisableMWSuggest = $wgUser->getBoolOption( 'disablesuggest' );
+
+               $togs = User::getToggles();
+               foreach ( $togs as $tname ) {
+                       $this->mToggles[$tname] = $wgUser->getOption( $tname );
+               }
+
+               $namespaces = $wgContLang->getNamespaces();
+               foreach ( $namespaces as $i => $namespace ) {
+                       if ( $i >= NS_MAIN ) {
+                               $this->mSearchNs[$i] = $wgUser->getOption( 'searchNs'.$i );
+                       }
+               }
+
+               wfRunHooks( 'ResetPreferences', array( $this, $wgUser ) );
+       }
+
+       /**
+        * @access private
+        */
+       function namespacesCheckboxes() {
+               global $wgContLang;
+
+               # Determine namespace checkboxes
+               $namespaces = $wgContLang->getNamespaces();
+               $r1 = null;
+
+               foreach ( $namespaces as $i => $name ) {
+                       if ($i < 0)
+                               continue;
+                       $checked = $this->mSearchNs[$i] ? "checked='checked'" : '';
+                       $name = str_replace( '_', ' ', $namespaces[$i] );
+
+                       if ( empty($name) )
+                               $name = wfMsg( 'blanknamespace' );
+
+                       $r1 .= "<input type='checkbox' value='1' name='wpNs$i' id='wpNs$i' {$checked}/> <label for='wpNs$i'>{$name}</label><br />\n";
+               }
+               return $r1;
+       }
+
+
+       function getToggle( $tname, $trailer = false, $disabled = false ) {
+               global $wgUser, $wgLang;
+
+               $this->mUsedToggles[$tname] = true;
+               $ttext = $wgLang->getUserToggle( $tname );
+
+               $checked = $wgUser->getOption( $tname ) == 1 ? ' checked="checked"' : '';
+               $disabled = $disabled ? ' disabled="disabled"' : '';
+               $trailer = $trailer ? $trailer : '';
+               return "<div class='toggle'><input type='checkbox' value='1' id=\"$tname\" name=\"wpOp$tname\"$checked$disabled />" .
+                       " <span class='toggletext'><label for=\"$tname\">$ttext</label>$trailer</span></div>\n";
+       }
+
+       function getToggles( $items ) {
+               $out = "";
+               foreach( $items as $item ) {
+                       if( $item === false )
+                               continue;
+                       if( is_array( $item ) ) {
+                               list( $key, $trailer ) = $item;
+                       } else {
+                               $key = $item;
+                               $trailer = false;
+                       }
+                       $out .= $this->getToggle( $key, $trailer );
+               }
+               return $out;
+       }
+
+       function addRow($td1, $td2) {
+               return "<tr><td class='mw-label'>$td1</td><td class='mw-input'>$td2</td></tr>";
+       }
+
+       /**
+        * Helper function for user information panel
+        * @param $td1 label for an item
+        * @param $td2 item or null
+        * @param $td3 optional help or null
+        * @return xhtml block
+        */
+       function tableRow( $td1, $td2 = null, $td3 = null ) {
+
+               if ( is_null( $td3 ) ) {
+                       $td3 = '';
+               } else {
+                       $td3 = Xml::tags( 'tr', null,
+                               Xml::tags( 'td', array( 'class' => 'pref-label', 'colspan' => '2' ), $td3 )
+                       );
+               }
+
+               if ( is_null( $td2 ) ) {
+                       $td1 = Xml::tags( 'td', array( 'class' => 'pref-label', 'colspan' => '2' ), $td1 );
+                       $td2 = '';
+               } else {
+                       $td1 = Xml::tags( 'td', array( 'class' => 'pref-label' ), $td1 );
+                       $td2 = Xml::tags( 'td', array( 'class' => 'pref-input' ), $td2 );
+               }
+
+               return Xml::tags( 'tr', null, $td1 . $td2 ). $td3 . "\n";
+
+       }
+
+       /**
+        * @access private
+        */
+       function mainPrefsForm( $status , $message = '' ) {
+               global $wgUser, $wgOut, $wgLang, $wgContLang;
+               global $wgAllowRealName, $wgImageLimits, $wgThumbLimits;
+               global $wgDisableLangConversion;
+               global $wgEnotifWatchlist, $wgEnotifUserTalk,$wgEnotifMinorEdits;
+               global $wgRCShowWatchingUsers, $wgEnotifRevealEditorAddress;
+               global $wgEnableEmail, $wgEnableUserEmail, $wgEmailAuthentication;
+               global $wgContLanguageCode, $wgDefaultSkin, $wgSkipSkins, $wgAuth;
+               global $wgEmailConfirmToEdit, $wgAjaxSearch, $wgEnableMWSuggest;
+
+               $wgOut->setPageTitle( wfMsg( 'preferences' ) );
+               $wgOut->setArticleRelated( false );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->addScriptFile( 'prefs.js' );
+
+               $wgOut->disallowUserJs();  # Prevent hijacked user scripts from sniffing passwords etc.
+
+               if ( $this->mSuccess || 'success' == $status ) {
+                       $wgOut->wrapWikiMsg( '<div class="successbox"><strong>$1</strong></div>', 'savedprefs' );
+               } else  if ( 'error' == $status ) {
+                       $wgOut->addWikiText( '<div class="errorbox"><strong>' . $message  . '</strong></div>' );
+               } else if ( '' != $status ) {
+                       $wgOut->addWikiText( $message . "\n----" );
+               }
+
+               $qbs = $wgLang->getQuickbarSettings();
+               $skinNames = $wgLang->getSkinNames();
+               $mathopts = $wgLang->getMathNames();
+               $dateopts = $wgLang->getDatePreferences();
+               $togs = User::getToggles();
+
+               $titleObj = SpecialPage::getTitleFor( 'Preferences' );
+               $action = $titleObj->escapeLocalURL();
+
+               # Pre-expire some toggles so they won't show if disabled
+               $this->mUsedToggles[ 'shownumberswatching' ] = true;
+               $this->mUsedToggles[ 'showupdated' ] = true;
+               $this->mUsedToggles[ 'enotifwatchlistpages' ] = true;
+               $this->mUsedToggles[ 'enotifusertalkpages' ] = true;
+               $this->mUsedToggles[ 'enotifminoredits' ] = true;
+               $this->mUsedToggles[ 'enotifrevealaddr' ] = true;
+               $this->mUsedToggles[ 'ccmeonemails' ] = true;
+               $this->mUsedToggles[ 'uselivepreview' ] = true;
+
+
+               if ( !$this->mEmailFlag ) { $emfc = 'checked="checked"'; }
+               else { $emfc = ''; }
+
+
+               if ($wgEmailAuthentication && ($this->mUserEmail != '') ) {
+                       if( $wgUser->getEmailAuthenticationTimestamp() ) {
+                               $emailauthenticated = wfMsg('emailauthenticated',$wgLang->timeanddate($wgUser->getEmailAuthenticationTimestamp(), true ) ).'<br />';
+                               $disableEmailPrefs = false;
+                       } else {
+                               $disableEmailPrefs = true;
+                               $skin = $wgUser->getSkin();
+                               $emailauthenticated = wfMsg('emailnotauthenticated').'<br />' .
+                                       $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Confirmemail' ),
+                                               wfMsg( 'emailconfirmlink' ) ) . '<br />';
+                       }
+               } else {
+                       $emailauthenticated = '';
+                       $disableEmailPrefs = false;
+               }
+
+               if ($this->mUserEmail == '') {
+                       $emailauthenticated = wfMsg( 'noemailprefs' ) . '<br />';
+               }
+
+               $ps = $this->namespacesCheckboxes();
+
+               $enotifwatchlistpages = ($wgEnotifWatchlist) ? $this->getToggle( 'enotifwatchlistpages', false, $disableEmailPrefs ) : '';
+               $enotifusertalkpages = ($wgEnotifUserTalk) ? $this->getToggle( 'enotifusertalkpages', false, $disableEmailPrefs ) : '';
+               $enotifminoredits = ($wgEnotifWatchlist && $wgEnotifMinorEdits) ? $this->getToggle( 'enotifminoredits', false, $disableEmailPrefs ) : '';
+               $enotifrevealaddr = (($wgEnotifWatchlist || $wgEnotifUserTalk) && $wgEnotifRevealEditorAddress) ? $this->getToggle( 'enotifrevealaddr', false, $disableEmailPrefs ) : '';
+
+               # </FIXME>
+
+               $wgOut->addHTML( "<form action=\"$action\" method='post'>" );
+               $wgOut->addHTML( "<div id='preferences'>" );
+
+               # User data
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'fieldset ' ) .
+                       Xml::element( 'legend', null, wfMsg('prefs-personal') ) .
+                       Xml::openElement( 'table' ) .
+                       $this->tableRow( Xml::element( 'h2', null, wfMsg( 'prefs-personal' ) ) )
+               );
+
+               # Get groups to which the user belongs
+               $userEffectiveGroups = $wgUser->getEffectiveGroups();
+               $userEffectiveGroupsArray = array();
+               foreach( $userEffectiveGroups as $ueg ) {
+                       if( $ueg == '*' ) {
+                               // Skip the default * group, seems useless here
+                               continue;
+                       }
+                       $userEffectiveGroupsArray[] = User::makeGroupLinkHTML( $ueg );
+               }
+               asort( $userEffectiveGroupsArray );
+
+               $sk = $wgUser->getSkin();
+               $toolLinks = array();
+               $toolLinks[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'ListGroupRights' ), wfMsg( 'listgrouprights' ) );
+               # At the moment one tool link only but be prepared for the future...
+               # FIXME: Add a link to Special:Userrights for users who are allowed to use it. 
+               # $wgUser->isAllowed( 'userrights' ) seems to strict in some cases
+
+               $userInformationHtml =
+                       $this->tableRow( wfMsgHtml( 'username' ), htmlspecialchars( $wgUser->getName() ) ) .
+                       $this->tableRow( wfMsgHtml( 'uid' ), htmlspecialchars( $wgUser->getId() ) ) .
+
+                       $this->tableRow(
+                               wfMsgExt( 'prefs-memberingroups', array( 'parseinline' ), count( $userEffectiveGroupsArray ) ),
+                               implode( wfMsg( 'comma-separator' ), $userEffectiveGroupsArray ) . 
+                               '<br />(' . implode( ' | ', $toolLinks ) . ')'
+                       ) .
+
+                       $this->tableRow(
+                               wfMsgHtml( 'prefs-edits' ),
+                               $wgLang->formatNum( User::edits( $wgUser->getId() ) )
+                       );
+
+               if( wfRunHooks( 'PreferencesUserInformationPanel', array( $this, &$userInformationHtml ) ) ) {
+                       $wgOut->addHtml( $userInformationHtml );
+               }
+
+               if ( $wgAllowRealName ) {
+                       $wgOut->addHTML(
+                               $this->tableRow(
+                                       Xml::label( wfMsg('yourrealname'), 'wpRealName' ),
+                                       Xml::input( 'wpRealName', 25, $this->mRealName, array( 'id' => 'wpRealName' ) ),
+                                       Xml::tags('div', array( 'class' => 'prefsectiontip' ),
+                                               wfMsgExt( 'prefs-help-realname', 'parseinline' )
+                                       )
+                               )
+                       );
+               }
+               if ( $wgEnableEmail ) {
+                       $wgOut->addHTML(
+                               $this->tableRow(
+                                       Xml::label( wfMsg('youremail'), 'wpUserEmail' ),
+                                       Xml::input( 'wpUserEmail', 25, $this->mUserEmail, array( 'id' => 'wpUserEmail' ) ),
+                                       Xml::tags('div', array( 'class' => 'prefsectiontip' ),
+                                               wfMsgExt( $wgEmailConfirmToEdit ? 'prefs-help-email-required' : 'prefs-help-email', 'parseinline' )
+                                       )
+                               )
+                       );
+               }
+
+               global $wgParser, $wgMaxSigChars;
+               if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
+                       $invalidSig = $this->tableRow(
+                               '&nbsp;',
+                               Xml::element( 'span', array( 'class' => 'error' ),
+                                       wfMsgExt( 'badsiglength', 'parsemag', $wgLang->formatNum( $wgMaxSigChars ) ) )
+                       );
+               } elseif( !empty( $this->mToggles['fancysig'] ) &&
+                       false === $wgParser->validateSig( $this->mNick ) ) {
+                       $invalidSig = $this->tableRow(
+                               '&nbsp;',
+                               Xml::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) )
+                       );
+               } else {
+                       $invalidSig = '';
+               }
+
+               $wgOut->addHTML(
+                       $this->tableRow(
+                               Xml::label( wfMsg( 'yournick' ), 'wpNick' ),
+                               Xml::input( 'wpNick', 25, $this->mNick,
+                                       array(
+                                               'id' => 'wpNick',
+                                               // Note: $wgMaxSigChars is enforced in Unicode characters,
+                                               // both on the backend and now in the browser.
+                                               // Badly-behaved requests may still try to submit
+                                               // an overlong string, however.
+                                               'maxlength' => $wgMaxSigChars ) )
+                       ) .
+                       $invalidSig .
+                       $this->tableRow( '&nbsp;', $this->getToggle( 'fancysig' ) )
+               );
+
+               list( $lsLabel, $lsSelect) = Xml::languageSelector( $this->mUserLanguage );
+               $wgOut->addHTML(
+                       $this->tableRow( $lsLabel, $lsSelect )
+               );
+
+               /* see if there are multiple language variants to choose from*/
+               if(!$wgDisableLangConversion) {
+                       $variants = $wgContLang->getVariants();
+                       $variantArray = array();
+
+                       $languages = Language::getLanguageNames( true );
+                       foreach($variants as $v) {
+                               $v = str_replace( '_', '-', strtolower($v));
+                               if( array_key_exists( $v, $languages ) ) {
+                                       // If it doesn't have a name, we'll pretend it doesn't exist
+                                       $variantArray[$v] = $languages[$v];
+                               }
+                       }
+
+                       $options = "\n";
+                       foreach( $variantArray as $code => $name ) {
+                               $selected = ($code == $this->mUserVariant);
+                               $options .= Xml::option( "$code - $name", $code, $selected ) . "\n";
+                       }
+
+                       if(count($variantArray) > 1) {
+                               $wgOut->addHtml(
+                                       $this->tableRow(
+                                               Xml::label( wfMsg( 'yourvariant' ), 'wpUserVariant' ),
+                                               Xml::tags( 'select',
+                                                       array( 'name' => 'wpUserVariant', 'id' => 'wpUserVariant' ),
+                                                       $options
+                                               )
+                                       )
+                               );
+                       }
+               }
+
+               # Password
+               if( $wgAuth->allowPasswordChange() ) {
+                       $wgOut->addHTML(
+                               $this->tableRow( Xml::element( 'h2', null, wfMsg( 'changepassword' ) ) ) .
+                               $this->tableRow(
+                                       Xml::label( wfMsg( 'oldpassword' ), 'wpOldpass' ),
+                                       Xml::password( 'wpOldpass', 25, $this->mOldpass, array( 'id' => 'wpOldpass' ) )
+                               ) .
+                               $this->tableRow(
+                                       Xml::label( wfMsg( 'newpassword' ), 'wpNewpass' ),
+                                       Xml::password( 'wpNewpass', 25, $this->mNewpass, array( 'id' => 'wpNewpass' ) )
+                               ) .
+                               $this->tableRow(
+                                       Xml::label( wfMsg( 'retypenew' ), 'wpRetypePass' ),
+                                       Xml::password( 'wpRetypePass', 25, $this->mRetypePass, array( 'id' => 'wpRetypePass' ) )
+                               ) .
+                               Xml::tags( 'tr', null,
+                                       Xml::tags( 'td', array( 'colspan' => '2' ),
+                                               $this->getToggle( "rememberpassword" )
+                                       )
+                               )
+                       );
+               }
+
+               # <FIXME>
+               # Enotif
+               if ( $wgEnableEmail ) {
+
+                       $moreEmail = '';
+                       if ($wgEnableUserEmail) {
+                               // fixme -- the "allowemail" pseudotoggle is a hacked-together
+                               // inversion for the "disableemail" preference.
+                               $emf = wfMsg( 'allowemail' );
+                               $disabled = $disableEmailPrefs ? ' disabled="disabled"' : '';
+                               $moreEmail =
+                                       "<input type='checkbox' $emfc $disabled value='1' name='wpEmailFlag' id='wpEmailFlag' /> <label for='wpEmailFlag'>$emf</label>" .
+                                       $this->getToggle( 'ccmeonemails', '', $disableEmailPrefs );
+                       }
+
+
+                       $wgOut->addHTML(
+                               $this->tableRow( Xml::element( 'h2', null, wfMsg( 'email' ) ) ) .
+                               $this->tableRow(
+                                       $emailauthenticated.
+                                       $enotifrevealaddr.
+                                       $enotifwatchlistpages.
+                                       $enotifusertalkpages.
+                                       $enotifminoredits.
+                                       $moreEmail
+                               )
+                       );
+               }
+               # </FIXME>
+
+               $wgOut->addHTML(
+                       Xml::closeElement( 'table' ) .
+                       Xml::closeElement( 'fieldset' )
+               );
+
+
+               # Quickbar
+               #
+               if ($this->mSkin == 'cologneblue' || $this->mSkin == 'standard') {
+                       $wgOut->addHtml( "<fieldset>\n<legend>" . wfMsg( 'qbsettings' ) . "</legend>\n" );
+                       for ( $i = 0; $i < count( $qbs ); ++$i ) {
+                               if ( $i == $this->mQuickbar ) { $checked = ' checked="checked"'; }
+                               else { $checked = ""; }
+                               $wgOut->addHTML( "<div><label><input type='radio' name='wpQuickbar' value=\"$i\"$checked />{$qbs[$i]}</label></div>\n" );
+                       }
+                       $wgOut->addHtml( "</fieldset>\n\n" );
+               } else {
+                       # Need to output a hidden option even if the relevant skin is not in use,
+                       # otherwise the preference will get reset to 0 on submit
+                       $wgOut->addHtml( wfHidden( 'wpQuickbar', $this->mQuickbar ) );
+               }
+
+               # Skin
+               #
+               $wgOut->addHTML( "<fieldset>\n<legend>\n" . wfMsg('skin') . "</legend>\n" );
+               $mptitle = Title::newMainPage();
+               $previewtext = wfMsg('skinpreview');
+               # Only show members of Skin::getSkinNames() rather than
+               # $skinNames (skins is all skin names from Language.php)
+               $validSkinNames = Skin::getSkinNames();
+               # Sort by UI skin name. First though need to update validSkinNames as sometimes
+               # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI).
+               foreach ($validSkinNames as $skinkey => & $skinname ) {
+                       if ( isset( $skinNames[$skinkey] ) )  {
+                               $skinname = $skinNames[$skinkey];
+                       }
+               }
+               asort($validSkinNames);
+               foreach ($validSkinNames as $skinkey => $sn ) {
+                       if ( in_array( $skinkey, $wgSkipSkins ) ) {
+                               continue;
+                       }
+                       $checked = $skinkey == $this->mSkin ? ' checked="checked"' : '';
+
+                       $mplink = htmlspecialchars($mptitle->getLocalURL("useskin=$skinkey"));
+                       $previewlink = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
+                       if( $skinkey == $wgDefaultSkin )
+                               $sn .= ' (' . wfMsg( 'default' ) . ')';
+                       $wgOut->addHTML( "<input type='radio' name='wpSkin' id=\"wpSkin$skinkey\" value=\"$skinkey\"$checked /> <label for=\"wpSkin$skinkey\">{$sn}</label> $previewlink<br />\n" );
+               }
+               $wgOut->addHTML( "</fieldset>\n\n" );
+
+               # Math
+               #
+               global $wgUseTeX;
+               if( $wgUseTeX ) {
+                       $wgOut->addHTML( "<fieldset>\n<legend>" . wfMsg('math') . '</legend>' );
+                       foreach ( $mathopts as $k => $v ) {
+                               $checked = ($k == $this->mMath);
+                               $wgOut->addHTML(
+                                       Xml::openElement( 'div' ) .
+                                       Xml::radioLabel( wfMsg( $v ), 'wpMath', $k, "mw-sp-math-$k", $checked ) .
+                                       Xml::closeElement( 'div' ) . "\n"
+                               );
+                       }
+                       $wgOut->addHTML( "</fieldset>\n\n" );
+               }
+
+               # Files
+               #
+               $wgOut->addHTML(
+                       "<fieldset>\n" . Xml::element( 'legend', null, wfMsg( 'files' ) ) . "\n"
+               );
+
+               $imageLimitOptions = null;
+               foreach ( $wgImageLimits as $index => $limits ) {
+                       $selected = ($index == $this->mImageSize);
+                       $imageLimitOptions .= Xml::option( "{$limits[0]}×{$limits[1]}" .
+                               wfMsg('unit-pixel'), $index, $selected );
+               }
+
+               $imageSizeId = 'wpImageSize';
+               $wgOut->addHTML(
+                       "<div>" . Xml::label( wfMsg('imagemaxsize'), $imageSizeId ) . " " .
+                       Xml::openElement( 'select', array( 'name' => $imageSizeId, 'id' => $imageSizeId ) ) .
+                               $imageLimitOptions .
+                       Xml::closeElement( 'select' ) . "</div>\n"
+               );
+
+               $imageThumbOptions = null;
+               foreach ( $wgThumbLimits as $index => $size ) {
+                       $selected = ($index == $this->mThumbSize);
+                       $imageThumbOptions .= Xml::option($size . wfMsg('unit-pixel'), $index,
+                               $selected);
+               }
+
+               $thumbSizeId = 'wpThumbSize';
+               $wgOut->addHTML(
+                       "<div>" . Xml::label( wfMsg('thumbsize'), $thumbSizeId ) . " " .
+                       Xml::openElement( 'select', array( 'name' => $thumbSizeId, 'id' => $thumbSizeId ) ) .
+                               $imageThumbOptions .
+                       Xml::closeElement( 'select' ) . "</div>\n"
+               );
+
+               $wgOut->addHTML( "</fieldset>\n\n" );
+
+               # Date format
+               #
+               # Date/Time
+               #
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'datetime' ) ) . "\n"
+               );
+
+               if ($dateopts) {
+                       $wgOut->addHTML(
+                               Xml::openElement( 'fieldset' ) .
+                               Xml::element( 'legend', null, wfMsg( 'dateformat' ) ) . "\n"
+                       );
+                       $idCnt = 0;
+                       $epoch = '20010115161234'; # Wikipedia day
+                       foreach( $dateopts as $key ) {
+                               if( $key == 'default' ) {
+                                       $formatted = wfMsg( 'datedefault' );
+                               } else {
+                                       $formatted = $wgLang->timeanddate( $epoch, false, $key );
+                               }
+                               $wgOut->addHTML(
+                                       Xml::tags( 'div', null,
+                                               Xml::radioLabel( $formatted, 'wpDate', $key, "wpDate$idCnt", $key == $this->mDate )
+                                       ) . "\n"
+                               );
+                               $idCnt++;
+                       }
+                       $wgOut->addHTML( Xml::closeElement( 'fieldset' ) . "\n" );
+               }
+
+               $nowlocal = $wgLang->time( $now = wfTimestampNow(), true );
+               $nowserver = $wgLang->time( $now, false );
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'timezonelegend' ) ) .
+                       Xml::openElement( 'table' ) .
+                       $this->addRow( wfMsg( 'servertime' ), $nowserver ) .
+                       $this->addRow( wfMsg( 'localtime' ), $nowlocal ) .
+                       $this->addRow(
+                               Xml::label( wfMsg( 'timezoneoffset' ), 'wpHourDiff'  ),
+                               Xml::input( 'wpHourDiff', 6, $this->mHourDiff, array( 'id' => 'wpHourDiff' ) ) ) .
+                       "<tr>
+                               <td></td>
+                               <td class='mw-submit'>" .
+                                       Xml::element( 'input',
+                                               array( 'type' => 'button',
+                                                       'value' => wfMsg( 'guesstimezone' ),
+                                                       'onclick' => 'javascript:guessTimezone()',
+                                                       'id' => 'guesstimezonebutton',
+                                                       'style' => 'display:none;' ) ) .
+                               "</td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::tags( 'div', array( 'class' => 'prefsectiontip' ), wfMsgExt( 'timezonetext', 'parseinline' ) ).
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'fieldset' ) . "\n\n"
+               );
+
+               # Editing
+               #
+               global $wgLivePreview;
+               $wgOut->addHTML( '<fieldset><legend>' . wfMsg( 'textboxsize' ) . '</legend>
+                       <div>' .
+                               wfInputLabel( wfMsg( 'rows' ), 'wpRows', 'wpRows', 3, $this->mRows ) .
+                               ' ' .
+                               wfInputLabel( wfMsg( 'columns' ), 'wpCols', 'wpCols', 3, $this->mCols ) .
+                       "</div>" .
+                       $this->getToggles( array(
+                               'editsection',
+                               'editsectiononrightclick',
+                               'editondblclick',
+                               'editwidth',
+                               'showtoolbar',
+                               'previewonfirst',
+                               'previewontop',
+                               'minordefault',
+                               'externaleditor',
+                               'externaldiff',
+                               $wgLivePreview ? 'uselivepreview' : false,
+                               'forceeditsummary',
+                       ) ) . '</fieldset>'
+               );
+
+               # Recent changes
+               $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-rc' ) . '</legend>' );
+
+               $rc  = '<table><tr>';
+               $rc .= '<td>' . Xml::label( wfMsg( 'recentchangesdays' ), 'wpRecentDays' ) . '</td>';
+               $rc .= '<td>' . Xml::input( 'wpRecentDays', 3, $this->mRecentDays, array( 'id' => 'wpRecentDays' ) ) . '</td>';
+               $rc .= '</tr><tr>';
+               $rc .= '<td>' . Xml::label( wfMsg( 'recentchangescount' ), 'wpRecent' ) . '</td>';
+               $rc .= '<td>' . Xml::input( 'wpRecent', 3, $this->mRecent, array( 'id' => 'wpRecent' ) ) . '</td>';
+               $rc .= '</tr></table>';
+               $wgOut->addHtml( $rc );
+
+               $wgOut->addHtml( '<br />' );
+
+               $toggles[] = 'hideminor';
+               if( $wgRCShowWatchingUsers )
+                       $toggles[] = 'shownumberswatching';
+               $toggles[] = 'usenewrc';
+               $wgOut->addHtml( $this->getToggles( $toggles ) );
+
+               $wgOut->addHtml( '</fieldset>' );
+
+               # Watchlist
+               $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'prefs-watchlist' ) . '</legend>' );
+
+               $wgOut->addHtml( wfInputLabel( wfMsg( 'prefs-watchlist-days' ), 'wpWatchlistDays', 'wpWatchlistDays', 3, $this->mWatchlistDays ) );
+               $wgOut->addHtml( '<br /><br />' );
+
+               $wgOut->addHtml( $this->getToggle( 'extendwatchlist' ) );
+               $wgOut->addHtml( wfInputLabel( wfMsg( 'prefs-watchlist-edits' ), 'wpWatchlistEdits', 'wpWatchlistEdits', 3, $this->mWatchlistEdits ) );
+               $wgOut->addHtml( '<br /><br />' );
+
+               $wgOut->addHtml( $this->getToggles( array( 'watchlisthideown', 'watchlisthidebots', 'watchlisthideminor' ) ) );
+
+               if( $wgUser->isAllowed( 'createpage' ) || $wgUser->isAllowed( 'createtalk' ) )
+                       $wgOut->addHtml( $this->getToggle( 'watchcreations' ) );
+               foreach( array( 'edit' => 'watchdefault', 'move' => 'watchmoves', 'delete' => 'watchdeletion' ) as $action => $toggle ) {
+                       if( $wgUser->isAllowed( $action ) )
+                               $wgOut->addHtml( $this->getToggle( $toggle ) );
+               }
+               $this->mUsedToggles['watchcreations'] = true;
+               $this->mUsedToggles['watchdefault'] = true;
+               $this->mUsedToggles['watchmoves'] = true;
+               $this->mUsedToggles['watchdeletion'] = true;
+
+               $wgOut->addHtml( '</fieldset>' );
+
+               # Search
+               $ajaxsearch = $wgAjaxSearch ?
+                       $this->addRow(
+                               Xml::label( wfMsg( 'useajaxsearch' ), 'wpUseAjaxSearch' ),
+                               Xml::check( 'wpUseAjaxSearch', $this->mUseAjaxSearch, array( 'id' => 'wpUseAjaxSearch' ) )
+                       ) : '';
+               $mwsuggest = $wgEnableMWSuggest ?
+                       $this->addRow(
+                               Xml::label( wfMsg( 'mwsuggest-disable' ), 'wpDisableMWSuggest' ),
+                               Xml::check( 'wpDisableMWSuggest', $this->mDisableMWSuggest, array( 'id' => 'wpDisableMWSuggest' ) )
+                       ) : '';
+               $wgOut->addHTML(
+                       // Elements for the search tab itself
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'searchresultshead' ) ) .
+                       // Elements for the search options in the search tab
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'prefs-searchoptions' ) ) .
+                       Xml::openElement( 'table' ) .
+                       $ajaxsearch .
+                       $this->addRow(
+                               Xml::label( wfMsg( 'resultsperpage' ), 'wpSearch' ),
+                               Xml::input( 'wpSearch', 4, $this->mSearch, array( 'id' => 'wpSearch' ) )
+                       ) .
+                       $this->addRow(
+                               Xml::label( wfMsg( 'contextlines' ), 'wpSearchLines' ),
+                               Xml::input( 'wpSearchLines', 4, $this->mSearchLines, array( 'id' => 'wpSearchLines' ) )
+                       ) .
+                       $this->addRow(
+                               Xml::label( wfMsg( 'contextchars' ), 'wpSearchChars' ),
+                               Xml::input( 'wpSearchChars', 4, $this->mSearchChars, array( 'id' => 'wpSearchChars' ) )
+                       ) .
+                       $mwsuggest .
+                       Xml::closeElement( 'table' ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       // Elements for the namespace options in the search tab
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'prefs-namespaces' ) ) .
+                       wfMsgExt( 'defaultns', array( 'parse' ) ) .
+                       $ps .
+                       Xml::closeElement( 'fieldset' ) .
+                       // End of the search tab
+                       Xml::closeElement( 'fieldset' )
+               );
+
+               # Misc
+               #
+               $wgOut->addHTML('<fieldset><legend>' . wfMsg('prefs-misc') . '</legend>');
+               $wgOut->addHtml( '<label for="wpStubs">' . wfMsg( 'stub-threshold' ) . '</label>&nbsp;' );
+               $wgOut->addHtml( Xml::input( 'wpStubs', 6, $this->mStubs, array( 'id' => 'wpStubs' ) ) );
+               $msgUnderline = htmlspecialchars( wfMsg ( 'tog-underline' ) );
+               $msgUnderlinenever = htmlspecialchars( wfMsg ( 'underline-never' ) );
+               $msgUnderlinealways = htmlspecialchars( wfMsg ( 'underline-always' ) );
+               $msgUnderlinedefault = htmlspecialchars( wfMsg ( 'underline-default' ) );
+               $uopt = $wgUser->getOption("underline");
+               $s0 = $uopt == 0 ? ' selected="selected"' : '';
+               $s1 = $uopt == 1 ? ' selected="selected"' : '';
+               $s2 = $uopt == 2 ? ' selected="selected"' : '';
+               $wgOut->addHTML("
+<div class='toggle'><p><label for='wpOpunderline'>$msgUnderline</label>
+<select name='wpOpunderline' id='wpOpunderline'>
+<option value=\"0\"$s0>$msgUnderlinenever</option>
+<option value=\"1\"$s1>$msgUnderlinealways</option>
+<option value=\"2\"$s2>$msgUnderlinedefault</option>
+</select></p></div>");
+
+               foreach ( $togs as $tname ) {
+                       if( !array_key_exists( $tname, $this->mUsedToggles ) ) {
+                               $wgOut->addHTML( $this->getToggle( $tname ) );
+                       }
+               }
+               $wgOut->addHTML( '</fieldset>' );
+
+               wfRunHooks( 'RenderPreferencesForm', array( $this, $wgOut ) );
+
+               $token = htmlspecialchars( $wgUser->editToken() );
+               $skin = $wgUser->getSkin();
+               $wgOut->addHTML( "
+       <div id='prefsubmit'>
+       <div>
+               <input type='submit' name='wpSaveprefs' class='btnSavePrefs' value=\"" . wfMsgHtml( 'saveprefs' ) . '"'.$skin->tooltipAndAccesskey('save')." />
+               <input type='submit' name='wpReset' value=\"" . wfMsgHtml( 'resetprefs' ) . "\" />
+       </div>
+
+       </div>
+
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
+       </div></form>\n" );
+
+               $wgOut->addHtml( Xml::tags( 'div', array( 'class' => "prefcache" ),
+                       wfMsgExt( 'clearyourcache', 'parseinline' ) )
+               );
+       }
+}
diff --git a/includes/specials/SpecialPrefixindex.php b/includes/specials/SpecialPrefixindex.php
new file mode 100644 (file)
index 0000000..1819d4e
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Entry point : initialise variables and call subfunctions.
+ * @param $par String: becomes "FOO" when called like Special:Prefixindex/FOO (default NULL)
+ * @param $specialPage SpecialPage object.
+ */
+function wfSpecialPrefixIndex( $par=NULL, $specialPage ) {
+       global $wgRequest, $wgOut, $wgContLang;
+
+       # GET values
+       $from = $wgRequest->getVal( 'from' );
+       $prefix = $wgRequest->getVal( 'prefix' );
+       $namespace = $wgRequest->getInt( 'namespace' );
+       $namespaces = $wgContLang->getNamespaces();
+
+       $indexPage = new SpecialPrefixIndex();
+
+       $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces ) ) )
+               ? wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) )
+               : wfMsg( 'allarticles' )
+       );
+
+       if ( isset($par) ) {
+               $indexPage->showChunk( $namespace, $par, $specialPage->including(), $from );
+       } elseif ( isset($prefix) ) {
+               $indexPage->showChunk( $namespace, $prefix, $specialPage->including(), $from );
+       } elseif ( isset($from) ) {
+               $indexPage->showChunk( $namespace, $from, $specialPage->including(), $from );
+       } else {
+               $wgOut->addHtml($indexPage->namespaceForm ( $namespace, null ));
+       }
+}
+
+/**
+ * implements Special:Prefixindex
+ * @ingroup SpecialPage
+ */
+class SpecialPrefixindex extends SpecialAllpages {
+       // Inherit $maxPerPage
+
+       // Define other properties
+       protected $name = 'Prefixindex';
+       protected $nsfromMsg = 'allpagesprefix';
+
+       /**
+        * @param integer $namespace (Default NS_MAIN)
+        * @param string $from list all pages from this name (default FALSE)
+        */
+       function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = null ) {
+               global $wgOut, $wgUser, $wgContLang;
+
+               $fname = 'indexShowChunk';
+
+               $sk = $wgUser->getSkin();
+
+               if (!isset($from)) $from = $prefix;
+
+               $fromList = $this->getNamespaceKeyAndText($namespace, $from);
+               $prefixList = $this->getNamespaceKeyAndText($namespace, $prefix);
+               $namespaces = $wgContLang->getNamespaces();
+               $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+               if ( !$prefixList || !$fromList ) {
+                       $out = wfMsgWikiHtml( 'allpagesbadtitle' );
+               } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
+                       // Show errormessage and reset to NS_MAIN
+                       $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
+                       $namespace = NS_MAIN;
+               } else {
+                       list( $namespace, $prefixKey, $prefix ) = $prefixList;
+                       list( /* $fromNs */, $fromKey, $from ) = $fromList;
+
+                       ### FIXME: should complain if $fromNs != $namespace
+
+                       $dbr = wfGetDB( DB_SLAVE );
+
+                       $res = $dbr->select( 'page',
+                               array( 'page_namespace', 'page_title', 'page_is_redirect' ),
+                               array(
+                                       'page_namespace' => $namespace,
+                                       'page_title LIKE \'' . $dbr->escapeLike( $prefixKey ) .'%\'',
+                                       'page_title >= ' . $dbr->addQuotes( $fromKey ),
+                               ),
+                               $fname,
+                               array(
+                                       'ORDER BY'  => 'page_title',
+                                       'LIMIT'     => $this->maxPerPage + 1,
+                                       'USE INDEX' => 'name_title',
+                               )
+                       );
+
+                       ### FIXME: side link to previous
+
+                       $n = 0;
+                       if( $res->numRows() > 0 ) {
+                               $out = '<table style="background: inherit;" border="0" width="100%">';
+       
+                               while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
+                                       $t = Title::makeTitle( $s->page_namespace, $s->page_title );
+                                       if( $t ) {
+                                               $link = ($s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
+                                                       $sk->makeKnownLinkObj( $t, htmlspecialchars( $t->getText() ), false, false ) .
+                                                       ($s->page_is_redirect ? '</div>' : '' );
+                                       } else {
+                                               $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
+                                       }
+                                       if( $n % 3 == 0 ) {
+                                               $out .= '<tr>';
+                                       }
+                                       $out .= "<td>$link</td>";
+                                       $n++;
+                                       if( $n % 3 == 0 ) {
+                                               $out .= '</tr>';
+                                       }
+                               }
+                               if( ($n % 3) != 0 ) {
+                                       $out .= '</tr>';
+                               }
+                               $out .= '</table>';
+                       } else {
+                               $out = '';
+                       }
+               }
+
+               if ( $including ) {
+                       $out2 = '';
+               } else {
+                       $nsForm = $this->namespaceForm ( $namespace, $prefix );
+                       $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
+                       $out2 .= '<tr valign="top"><td>' . $nsForm;
+                       $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
+                                       $sk->makeKnownLink( $wgContLang->specialPage( $this->name ),
+                                               wfMsg ( 'allpages' ) );
+                       if ( isset($dbr) && $dbr && ($n == $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
+                               $namespaceparam = $namespace ? "&namespace=$namespace" : "";
+                               $out2 .= " | " . $sk->makeKnownLink(
+                                       $wgContLang->specialPage( $this->name ),
+                                       wfMsg ( 'nextpage', $s->page_title ),
+                                       "from=" . wfUrlEncode ( $s->page_title ) .
+                                       "&prefix=" . wfUrlEncode ( $prefix ) . $namespaceparam );
+                       }
+                       $out2 .= "</td></tr></table><hr />";
+               }
+
+               $wgOut->addHtml( $out2 . $out );
+       }
+}
diff --git a/includes/specials/SpecialProtectedpages.php b/includes/specials/SpecialProtectedpages.php
new file mode 100644 (file)
index 0000000..e168902
--- /dev/null
@@ -0,0 +1,313 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @todo document
+ * @ingroup SpecialPage
+ */
+class ProtectedPagesForm {
+
+       protected $IdLevel = 'level';
+       protected $IdType  = 'type';
+
+       public function showList( $msg = '' ) {
+               global $wgOut, $wgRequest;
+
+               $wgOut->setPagetitle( wfMsg( "protectedpages" ) );
+               if ( "" != $msg ) {
+                       $wgOut->setSubtitle( $msg );
+               }
+
+               // Purge expired entries on one in every 10 queries
+               if ( !mt_rand( 0, 10 ) ) {
+                       Title::purgeExpiredRestrictions();
+               }
+
+               $type = $wgRequest->getVal( $this->IdType );
+               $level = $wgRequest->getVal( $this->IdLevel );
+               $sizetype = $wgRequest->getVal( 'sizetype' );
+               $size = $wgRequest->getIntOrNull( 'size' );
+               $NS = $wgRequest->getIntOrNull( 'namespace' );
+               $indefOnly = $wgRequest->getBool( 'indefonly' ) ? 1 : 0;
+
+               $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $sizetype, $size, $indefOnly );
+
+               $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size, $indefOnly ) );
+
+               if ( $pager->getNumRows() ) {
+                       $s = $pager->getNavigationBar();
+                       $s .= "<ul>" .
+                               $pager->getBody() .
+                               "</ul>";
+                       $s .= $pager->getNavigationBar();
+               } else {
+                       $s = '<p>' . wfMsgHtml( 'protectedpagesempty' ) . '</p>';
+               }
+               $wgOut->addHTML( $s );
+       }
+
+       /**
+        * Callback function to output a restriction
+        * @param $row object Protected title
+        * @return string Formatted <li> element
+        */
+       public function formatRow( $row ) {
+               global $wgUser, $wgLang, $wgContLang;
+
+               wfProfileIn( __METHOD__ );
+
+               static $skin=null;
+
+               if( is_null( $skin ) )
+                       $skin = $wgUser->getSkin();
+
+               $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
+               $link = $skin->makeLinkObj( $title );
+
+               $description_items = array ();
+
+               $protType = wfMsgHtml( 'restriction-level-' . $row->pr_level );
+
+               $description_items[] = $protType;
+
+               if ( $row->pr_cascade ) {
+                       $description_items[] = wfMsg( 'protect-summary-cascade' );
+               }
+
+               $expiry_description = '';
+               $stxt = '';
+
+               if ( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) {
+                       $expiry = Block::decodeExpiry( $row->pr_expiry );
+
+                       $expiry_description = wfMsgForContent( 'protect-expiring', $wgLang->timeanddate( $expiry ) );
+
+                       $description_items[] = $expiry_description;
+               }
+
+               if (!is_null($size = $row->page_len)) {
+                       if ($size == 0)
+                               $stxt = ' <small>' . wfMsgHtml('historyempty') . '</small>';
+                       else
+                               $stxt = ' <small>' . wfMsgHtml('historysize', $wgLang->formatNum( $size ) ) . '</small>';
+                       $stxt = $wgContLang->getDirMark() . $stxt;
+               }
+
+               # Show a link to the change protection form for allowed users otherwise a link to the protection log
+               if( $wgUser->isAllowed( 'protect' ) ) {
+                       $changeProtection = ' (' . $skin->makeKnownLinkObj( $title, wfMsgHtml( 'protect_change' ), 'action=unprotect' ) . ')';
+               } else {
+                       $ltitle = SpecialPage::getTitleFor( 'Log' );
+                       $changeProtection = ' (' . $skin->makeKnownLinkObj( $ltitle, wfMsgHtml( 'protectlogpage' ), 'type=protect&page=' . $title->getPrefixedUrl() ) . ')';
+               }
+
+               wfProfileOut( __METHOD__ );
+
+               return '<li>' . wfSpecialList( $link . $stxt, implode( $description_items, ', ' ) ) . $changeProtection . "</li>\n";
+       }
+
+       /**
+        * @param $namespace int
+        * @param $type string
+        * @param $level string
+        * @param $minsize int
+        * @param $indefOnly bool
+        * @return string Input form
+        * @private
+        */
+       protected function showOptions( $namespace, $type='edit', $level, $sizetype, $size, $indefOnly ) {
+               global $wgScript;
+               $title = SpecialPage::getTitleFor( 'ProtectedPages' );
+               return Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', array(), wfMsg( 'protectedpages' ) ) .
+                       Xml::hidden( 'title', $title->getPrefixedDBkey() ) . "&nbsp;\n" .
+                       $this->getNamespaceMenu( $namespace ) . "&nbsp;\n" .
+                       $this->getTypeMenu( $type ) . "&nbsp;\n" .
+                       $this->getLevelMenu( $level ) . "&nbsp;\n" .
+                       "<br /><span style='white-space: nowrap'>&nbsp;&nbsp;" .
+                       $this->getExpiryCheck( $indefOnly ) . "&nbsp;\n" .
+                       $this->getSizeLimit( $sizetype, $size ) . "&nbsp;\n" .
+                       "</span>" .
+                       "&nbsp;" . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' );
+       }
+
+       /**
+        * Prepare the namespace filter drop-down; standard namespace
+        * selector, sans the MediaWiki namespace
+        *
+        * @param mixed $namespace Pre-select namespace
+        * @return string
+        */
+       protected function getNamespaceMenu( $namespace = null ) {
+               return "<span style='white-space: nowrap'>" .
+                       Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;'
+                       . Xml::namespaceSelector( $namespace, '' ) . "</span>";
+       }
+
+       /**
+        * @return string Formatted HTML
+        */
+       protected function getExpiryCheck( $indefOnly ) {
+               return
+                       Xml::checkLabel( wfMsg('protectedpages-indef'), 'indefonly', 'indefonly', $indefOnly ) . "\n";
+       }
+
+       /**
+        * @return string Formatted HTML
+        */
+       protected function getSizeLimit( $sizetype, $size ) {
+               $max = $sizetype === 'max';
+
+               return
+                       Xml::radioLabel( wfMsg('minimum-size'), 'sizetype', 'min', 'wpmin', !$max ) .
+                       '&nbsp;' .
+                       Xml::radioLabel( wfMsg('maximum-size'), 'sizetype', 'max', 'wpmax', $max ) .
+                       '&nbsp;' .
+                       Xml::input( 'size', 9, $size, array( 'id' => 'wpsize' ) ) .
+                       '&nbsp;' .
+                       Xml::label( wfMsg('pagesize'), 'wpsize' );
+       }
+
+       /**
+        * @return string Formatted HTML
+        */
+       protected function getTypeMenu( $pr_type ) {
+               global $wgRestrictionTypes;
+
+               $m = array(); // Temporary array
+               $options = array();
+
+               // First pass to load the log names
+               foreach( $wgRestrictionTypes as $type ) {
+                       $text = wfMsg("restriction-$type");
+                       $m[$text] = $type;
+               }
+
+               // Third pass generates sorted XHTML content
+               foreach( $m as $text => $type ) {
+                       $selected = ($type == $pr_type );
+                       $options[] = Xml::option( $text, $type, $selected ) . "\n";
+               }
+
+               return "<span style='white-space: nowrap'>" .
+                       Xml::label( wfMsg('restriction-type') , $this->IdType ) . '&nbsp;' .
+                       Xml::tags( 'select',
+                               array( 'id' => $this->IdType, 'name' => $this->IdType ),
+                               implode( "\n", $options ) ) . "</span>";
+       }
+
+       /**
+        * @return string Formatted HTML
+        */
+       protected function getLevelMenu( $pr_level ) {
+               global $wgRestrictionLevels;
+
+               $m = array( wfMsg('restriction-level-all') => 0 ); // Temporary array
+               $options = array();
+
+               // First pass to load the log names
+               foreach( $wgRestrictionLevels as $type ) {
+                       if ( $type !='' && $type !='*') {
+                               $text = wfMsg("restriction-level-$type");
+                               $m[$text] = $type;
+                       }
+               }
+
+               // Third pass generates sorted XHTML content
+               foreach( $m as $text => $type ) {
+                       $selected = ($type == $pr_level );
+                       $options[] = Xml::option( $text, $type, $selected );
+               }
+
+               return
+                       Xml::label( wfMsg('restriction-level') , $this->IdLevel ) . '&nbsp;' .
+                       Xml::tags( 'select',
+                               array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ),
+                               implode( "\n", $options ) );
+       }
+}
+
+/**
+ * @todo document
+ * @ingroup Pager
+ */
+class ProtectedPagesPager extends AlphabeticPager {
+       public $mForm, $mConds;
+       private $type, $level, $namespace, $sizetype, $size, $indefonly;
+
+       function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0, $indefonly=false ) {
+               $this->mForm = $form;
+               $this->mConds = $conds;
+               $this->type = ( $type ) ? $type : 'edit';
+               $this->level = $level;
+               $this->namespace = $namespace;
+               $this->sizetype = $sizetype;
+               $this->size = intval($size);
+               $this->indefonly = (bool)$indefonly;
+               parent::__construct();
+       }
+
+       function getStartBody() {
+               wfProfileIn( __METHOD__ );
+               # Do a link batch query
+               $lb = new LinkBatch;
+               while( $row = $this->mResult->fetchObject() ) {
+                       $lb->add( $row->page_namespace, $row->page_title );
+               }
+               $lb->execute();
+
+               wfProfileOut( __METHOD__ );
+               return '';
+       }
+
+       function formatRow( $row ) {
+               return $this->mForm->formatRow( $row );
+       }
+
+       function getQueryInfo() {
+               $conds = $this->mConds;
+               $conds[] = 'pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
+               $conds[] = 'page_id=pr_page';
+               $conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type );
+
+               if( $this->sizetype=='min' ) {
+                       $conds[] = 'page_len>=' . $this->size;
+               } else if( $this->sizetype=='max' ) {
+                       $conds[] = 'page_len<=' . $this->size;
+               }
+
+               if( $this->indefonly ) {
+                       $conds[] = "pr_expiry = 'infinity' OR pr_expiry IS NULL";
+               }
+
+               if( $this->level )
+                       $conds[] = 'pr_level=' . $this->mDb->addQuotes( $this->level );
+               if( !is_null($this->namespace) )
+                       $conds[] = 'page_namespace=' . $this->mDb->addQuotes( $this->namespace );
+               return array(
+                       'tables' => array( 'page_restrictions', 'page' ),
+                       'fields' => 'pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry,pr_cascade',
+                       'conds' => $conds
+               );
+       }
+
+       function getIndexField() {
+               return 'pr_id';
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialProtectedpages() {
+
+       $ppForm = new ProtectedPagesForm();
+
+       $ppForm->showList();
+}
diff --git a/includes/specials/SpecialProtectedtitles.php b/includes/specials/SpecialProtectedtitles.php
new file mode 100644 (file)
index 0000000..2ec68a6
--- /dev/null
@@ -0,0 +1,216 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @todo document
+ * @ingroup SpecialPage
+ */
+class ProtectedTitlesForm {
+
+       protected $IdLevel = 'level';
+       protected $IdType  = 'type';
+
+       function showList( $msg = '' ) {
+               global $wgOut, $wgRequest;
+
+               $wgOut->setPagetitle( wfMsg( "protectedtitles" ) );
+               if ( "" != $msg ) {
+                       $wgOut->setSubtitle( $msg );
+               }
+
+               // Purge expired entries on one in every 10 queries
+               if ( !mt_rand( 0, 10 ) ) {
+                       Title::purgeExpiredRestrictions();
+               }
+
+               $type = $wgRequest->getVal( $this->IdType );
+               $level = $wgRequest->getVal( $this->IdLevel );
+               $sizetype = $wgRequest->getVal( 'sizetype' );
+               $size = $wgRequest->getIntOrNull( 'size' );
+               $NS = $wgRequest->getIntOrNull( 'namespace' );
+
+               $pager = new ProtectedTitlesPager( $this, array(), $type, $level, $NS, $sizetype, $size );
+
+               $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size ) );
+
+               if ( $pager->getNumRows() ) {
+                       $s = $pager->getNavigationBar();
+                       $s .= "<ul>" .
+                               $pager->getBody() .
+                               "</ul>";
+                       $s .= $pager->getNavigationBar();
+               } else {
+                       $s = '<p>' . wfMsgHtml( 'protectedtitlesempty' ) . '</p>';
+               }
+               $wgOut->addHTML( $s );
+       }
+
+       /**
+        * Callback function to output a restriction
+        */
+       function formatRow( $row ) {
+               global $wgUser, $wgLang, $wgContLang;
+
+               wfProfileIn( __METHOD__ );
+
+               static $skin=null;
+
+               if( is_null( $skin ) )
+                       $skin = $wgUser->getSkin();
+
+               $title = Title::makeTitleSafe( $row->pt_namespace, $row->pt_title );
+               $link = $skin->makeLinkObj( $title );
+
+               $description_items = array ();
+
+               $protType = wfMsgHtml( 'restriction-level-' . $row->pt_create_perm );
+
+               $description_items[] = $protType;
+
+               $expiry_description = ''; $stxt = '';
+
+               if ( $row->pt_expiry != 'infinity' && strlen($row->pt_expiry) ) {
+                       $expiry = Block::decodeExpiry( $row->pt_expiry );
+
+                       $expiry_description = wfMsgForContent( 'protect-expiring', $wgLang->timeanddate( $expiry ) );
+
+                       $description_items[] = $expiry_description;
+               }
+
+               wfProfileOut( __METHOD__ );
+
+               return '<li>' . wfSpecialList( $link . $stxt, implode( $description_items, ', ' ) ) . "</li>\n";
+       }
+
+       /**
+        * @param $namespace int
+        * @param $type string
+        * @param $level string
+        * @param $minsize int
+        * @private
+        */
+       function showOptions( $namespace, $type='edit', $level, $sizetype, $size ) {
+               global $wgScript;
+               $action = htmlspecialchars( $wgScript );
+               $title = SpecialPage::getTitleFor( 'ProtectedTitles' );
+               $special = htmlspecialchars( $title->getPrefixedDBkey() );
+               return "<form action=\"$action\" method=\"get\">\n" .
+                       '<fieldset>' .
+                       Xml::element( 'legend', array(), wfMsg( 'protectedtitles' ) ) .
+                       Xml::hidden( 'title', $special ) . "&nbsp;\n" .
+                       $this->getNamespaceMenu( $namespace ) . "&nbsp;\n" .
+                       // $this->getLevelMenu( $level ) . "<br/>\n" .
+                       "&nbsp;" . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+                       "</fieldset></form>";
+       }
+
+       /**
+        * Prepare the namespace filter drop-down; standard namespace
+        * selector, sans the MediaWiki namespace
+        *
+        * @param mixed $namespace Pre-select namespace
+        * @return string
+        */
+       function getNamespaceMenu( $namespace = null ) {
+               return Xml::label( wfMsg( 'namespace' ), 'namespace' )
+                       . '&nbsp;'
+                       . Xml::namespaceSelector( $namespace, '' );
+       }
+
+       /**
+        * @return string Formatted HTML
+        * @private
+        */
+       function getLevelMenu( $pr_level ) {
+               global $wgRestrictionLevels;
+
+               $m = array( wfMsg('restriction-level-all') => 0 ); // Temporary array
+               $options = array();
+
+               // First pass to load the log names
+               foreach( $wgRestrictionLevels as $type ) {
+                       if ( $type !='' && $type !='*') {
+                               $text = wfMsg("restriction-level-$type");
+                               $m[$text] = $type;
+                       }
+               }
+
+               // Third pass generates sorted XHTML content
+               foreach( $m as $text => $type ) {
+                       $selected = ($type == $pr_level );
+                       $options[] = Xml::option( $text, $type, $selected );
+               }
+
+               return
+                       Xml::label( wfMsg('restriction-level') , $this->IdLevel ) . '&nbsp;' .
+                       Xml::tags( 'select',
+                               array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ),
+                               implode( "\n", $options ) );
+       }
+}
+
+/**
+ * @todo document
+ * @ingroup Pager
+ */
+class ProtectedtitlesPager extends AlphabeticPager {
+       public $mForm, $mConds;
+
+       function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0 ) {
+               $this->mForm = $form;
+               $this->mConds = $conds;
+               $this->level = $level;
+               $this->namespace = $namespace;
+               $this->size = intval($size);
+               parent::__construct();
+       }
+
+       function getStartBody() {
+               wfProfileIn( __METHOD__ );
+               # Do a link batch query
+               $this->mResult->seek( 0 );
+               $lb = new LinkBatch;
+
+               while ( $row = $this->mResult->fetchObject() ) {
+                       $lb->add( $row->pt_namespace, $row->pt_title );
+               }
+
+               $lb->execute();
+               wfProfileOut( __METHOD__ );
+               return '';
+       }
+
+       function formatRow( $row ) {
+               return $this->mForm->formatRow( $row );
+       }
+
+       function getQueryInfo() {
+               $conds = $this->mConds;
+               $conds[] = 'pt_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
+
+               if( !is_null($this->namespace) )
+                       $conds[] = 'pt_namespace=' . $this->mDb->addQuotes( $this->namespace );
+               return array(
+                       'tables' => 'protected_titles',
+                       'fields' => 'pt_namespace,pt_title,pt_create_perm,pt_expiry,pt_timestamp',
+                       'conds' => $conds
+               );
+       }
+
+       function getIndexField() {
+               return 'pt_timestamp';
+       }
+}
+
+/**
+ * Constructor
+ */
+function wfSpecialProtectedtitles() {
+
+       $ppForm = new ProtectedTitlesForm();
+
+       $ppForm->showList();
+}
diff --git a/includes/specials/SpecialRandompage.php b/includes/specials/SpecialRandompage.php
new file mode 100644 (file)
index 0000000..0e7ada1
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * Special page to direct the user to a random page
+ *
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
+ * @license GNU General Public Licence 2.0 or later
+ */
+class RandomPage extends SpecialPage {
+       private $namespace = NS_MAIN;  // namespace to select pages from
+
+       function __construct( $name = 'Randompage' ){
+               parent::__construct( $name );
+       }
+
+       public function getNamespace() {
+               return $this->namespace;
+       }
+
+       public function setNamespace ( $ns ) {
+               if( $ns < NS_MAIN ) $ns = NS_MAIN;
+               $this->namespace = $ns;
+       }
+
+       // select redirects instead of normal pages?
+       // Overriden by SpecialRandomredirect
+       public function isRedirect(){
+               return false;
+       }
+
+       public function execute( $par ) {
+               global $wgOut, $wgContLang;
+
+               if ($par)
+                       $this->setNamespace( $wgContLang->getNsIndex( $par ) );
+
+               $title = $this->getRandomTitle();
+
+               if( is_null( $title ) ) {
+                       $this->setHeaders();
+                       $wgOut->addWikiMsg( strtolower( $this->mName ) . '-nopages' );
+                       return;
+               }
+
+               $query = $this->isRedirect() ? 'redirect=no' : '';
+               $wgOut->redirect( $title->getFullUrl( $query ) );
+       }
+
+
+       /**
+        * Choose a random title.
+        * @return Title object (or null if nothing to choose from)
+        */
+       public function getRandomTitle() {
+               $randstr = wfRandom();
+               $row = $this->selectRandomPageFromDB( $randstr );
+
+               /* If we picked a value that was higher than any in
+                * the DB, wrap around and select the page with the
+                * lowest value instead!  One might think this would
+                * skew the distribution, but in fact it won't cause
+                * any more bias than what the page_random scheme
+                * causes anyway.  Trust me, I'm a mathematician. :)
+                */
+               if( !$row )
+                       $row = $this->selectRandomPageFromDB( "0" );
+
+               if( $row )
+                       return Title::makeTitleSafe( $this->namespace, $row->page_title );
+               else
+                       return null;
+       }
+
+       private function selectRandomPageFromDB( $randstr ) {
+               global $wgExtraRandompageSQL;
+               $fname = 'RandomPage::selectRandomPageFromDB';
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               $use_index = $dbr->useIndexClause( 'page_random' );
+               $page = $dbr->tableName( 'page' );
+
+               $ns = (int) $this->namespace;
+               $redirect = $this->isRedirect() ? 1 : 0;
+
+               $extra = $wgExtraRandompageSQL ? "AND ($wgExtraRandompageSQL)" : "";
+               $sql = "SELECT page_title
+                       FROM $page $use_index
+                       WHERE page_namespace = $ns
+                       AND page_is_redirect = $redirect
+                       AND page_random >= $randstr
+                       $extra
+                       ORDER BY page_random";
+
+               $sql = $dbr->limitResult( $sql, 1, 0 );
+               $res = $dbr->query( $sql, $fname );
+               return $dbr->fetchObject( $res );
+       }
+}
diff --git a/includes/specials/SpecialRandomredirect.php b/includes/specials/SpecialRandomredirect.php
new file mode 100644 (file)
index 0000000..629d5b3
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Special page to direct the user to a random redirect page (minus the second redirect)
+ *
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
+ * @license GNU General Public Licence 2.0 or later
+ */
+class SpecialRandomredirect extends RandomPage {
+       function __construct(){
+               parent::__construct( 'Randomredirect' );
+       }
+
+       // Override parent::isRedirect()
+       public function isRedirect(){
+               return true;
+       }
+}
diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php
new file mode 100644 (file)
index 0000000..16904b9
--- /dev/null
@@ -0,0 +1,613 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+class SpecialRecentChanges extends SpecialPage {
+       public function __construct() {
+       SpecialPage::SpecialPage( 'Recentchanges' );
+               $this->includable( true );
+       }
+
+       public function getDefaultOptions() {
+               $opts = new FormOptions();
+
+               $opts->add( 'days',  (int)User::getDefaultOption( 'rcdays' ) );
+               $opts->add( 'limit', (int)User::getDefaultOption( 'rclimit' ) );
+               $opts->add( 'from', '' );
+
+               $opts->add( 'hideminor',     false );
+               $opts->add( 'hidebots',      true  );
+               $opts->add( 'hideanons',     false );
+               $opts->add( 'hideliu',       false );
+               $opts->add( 'hidepatrolled', false );
+               $opts->add( 'hidemyself',    false );
+
+               $opts->add( 'namespace', '', FormOptions::INTNULL );
+               $opts->add( 'invert', false );
+
+               $opts->add( 'categories', '' );
+               $opts->add( 'categories_any', false );
+
+               return $opts;
+}
+
+       public function setup( $parameters ) {
+               global $wgUser, $wgRequest;
+
+               $opts = $this->getDefaultOptions();
+               $opts['days'] = (int)$wgUser->getOption( 'rcdays', $opts['days'] );
+               $opts['limit'] = (int)$wgUser->getOption( 'rclimit', $opts['limit'] );
+               $opts['hideminor'] = $wgUser->getOption( 'hideminor', $opts['hideminor'] );
+               $opts->fetchValuesFromRequest( $wgRequest );
+
+               // Give precedence to subpage syntax
+               if ( $parameters !== null ) {
+                       $this->parseParameters( $parameters, $opts );
+               }
+
+               $opts->validateIntBounds( 'limit', 0, 5000 );
+               return $opts;
+       }
+
+       public function feedSetup() {
+               global $wgFeedLimit, $wgRequest;
+               $opts = $this->getDefaultOptions();
+               $opts->fetchValuesFromRequest( $wgRequest, array( 'days', 'limit', 'hideminor' ) );
+               $opts->validateIntBounds( 'limit', 0, $wgFeedLimit );
+               return $opts;
+       }
+
+       public function execute( $parameters ) {
+               global $wgRequest, $wgOut;
+               $feedFormat = $wgRequest->getVal( 'feed' );
+
+               # 10 seconds server-side caching max
+               $wgOut->setSquidMaxage( 10 );
+
+               $lastmod = $this->checkLastModified( $feedFormat );
+               if( $lastmod === false ){
+                       return;
+               }
+
+               $opts = $feedFormat ? $this->feedSetup() : $this->setup( $parameters );
+               $this->setHeaders();
+
+               // Fetch results, prepare a batch link existence check query
+               $rows = array();
+               $batch = new LinkBatch;
+               $conds = $this->buildMainQueryConds( $opts );
+               $res = $this->doMainQuery( $conds, $opts );
+               $dbr = wfGetDB( DB_SLAVE );
+               while( $row = $dbr->fetchObject( $res ) ){
+                       $rows[] = $row;
+                       if ( !$feedFormat ) {
+                               // User page and talk links
+                               $batch->add( NS_USER, $row->rc_user_text  );
+                               $batch->add( NS_USER_TALK, $row->rc_user_text  );
+                       }
+
+               }
+               $dbr->freeResult( $res );
+
+               if ( $feedFormat ) {
+                       $feed = new ChangesFeed( $feedFormat, 'rcfeed' );
+                       $feedObj = $feed->getFeedObject(
+                               wfMsgForContent( 'recentchanges' ),
+                               wfMsgForContent( 'recentchanges-feed-description' )
+                       );
+                       $feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod );
+               } else {
+                       $batch->execute();
+                       $this->webOutput( $rows, $opts );
+               }
+       
+       }
+
+       public function parseParameters( $par, FormOptions $opts ) {
+               $bits = preg_split( '/\s*,\s*/', trim( $par ) );
+               foreach ( $bits as $bit ) {
+                       if ( 'hidebots' === $bit ) $opts['hidebots'] = true;
+                       if ( 'bots' === $bit ) $opts['hidebots'] = false;
+                       if ( 'hideminor' === $bit ) $opts['hideminor'] = true;
+                       if ( 'minor' === $bit ) $opts['hideminor'] = false;
+                       if ( 'hideliu' === $bit ) $opts['hideliu'] = true;
+                       if ( 'hidepatrolled' === $bit ) $opts['hidepatrolled'] = true;
+                       if ( 'hideanons' === $bit ) $opts['hideanons'] = true;
+                       if ( 'hidemyself' === $bit ) $opts['hidemyself'] = true;
+
+                       if ( is_numeric( $bit ) ) $opts['limit'] =  $bit;
+
+                       $m = array();
+                       if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) $opts['limit'] = $m[1];
+                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) $opts['days'] = $m[1];
+               }
+       }
+
+       # Get last modified date, for client caching
+       # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp
+       public function checkLastModified( $feedFormat ) {
+               global $wgUseRCPatrol, $wgOut;
+               $dbr = wfGetDB( DB_SLAVE );
+               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __FUNCTION__ );
+               if ( $feedFormat || !$wgUseRCPatrol ) {
+                       if( $lastmod && $wgOut->checkLastModified( $lastmod ) ){
+                               # Client cache fresh and headers sent, nothing more to do.
+                               return false;
+                       }
+               }
+               return $lastmod;
+       }
+
+       public function buildMainQueryConds( FormOptions $opts ) {
+               global $wgUser;
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $conds = array();
+
+               # It makes no sense to hide both anons and logged-in users
+               # Where this occurs, force anons to be shown
+               $forcebot = false;
+               if( $opts['hideanons'] && $opts['hideliu'] ){
+                       # Check if the user wants to show bots only
+                       if( $opts['hidebots'] ){
+                               $opts['hideanons'] = false;
+                       } else {
+                               $forcebot = true;
+                               $opts['hidebots'] = false;
+                       }
+               }
+
+               // Calculate cutoff
+               $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
+               $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400);
+               $cutoff = $dbr->timestamp( $cutoff_unixtime );
+
+               $fromValid = preg_match('/^[0-9]{14}$/', $opts['from']);
+               if( $fromValid && $opts['from'] > wfTimestamp(TS_MW,$cutoff) ) {
+                       $cutoff = $dbr->timestamp($opts['from']);
+               } else {
+                       $opts->reset( 'from' );
+               }
+
+               $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff );
+
+
+               $hidePatrol = $wgUser->useRCPatrol() && $opts['hidepatrolled'];
+               $hideLoggedInUsers = $opts['hideliu'] && !$forcebot;
+               $hideAnonymousUsers = $opts['hideanons'] && !$forcebot;
+
+               if ( $opts['hideminor'] )  $conds['rc_minor'] = 0;
+               if ( $opts['hidebots'] )   $conds['rc_bot'] = 0;
+               if ( $hidePatrol )         $conds['rc_patrolled'] = 0;
+               if ( $forcebot )           $conds['rc_bot'] = 1;
+               if ( $hideLoggedInUsers )  $conds[] = 'rc_user = 0';
+               if ( $hideAnonymousUsers ) $conds[] = 'rc_user != 0';
+
+               if( $opts['hidemyself'] ) {
+                       if( $wgUser->getId() ) {
+                               $conds[] = 'rc_user != ' . $dbr->addQuotes( $wgUser->getId() );
+                       } else {
+                               $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $wgUser->getName() );
+                       }
+               }
+               
+               # Namespace filtering
+               if ( $opts['namespace'] !== '' ) {
+                       if ( !$opts['invert'] ) {
+                               $conds[] = 'rc_namespace = ' . $dbr->addQuotes( $opts['namespace'] );
+                       } else {
+                               $conds[] = 'rc_namespace != ' . $dbr->addQuotes( $opts['namespace'] );
+                       }
+               }
+
+               return $conds;
+       }
+
+       public function doMainQuery( $conds, $opts ) {
+               global $wgUser;
+
+               $tables = array( 'recentchanges' );
+               $join_conds = array();
+
+               $uid = $wgUser->getId();
+               $dbr = wfGetDB( DB_SLAVE );
+               $limit = $opts['limit'];
+               $namespace = $opts['namespace'];
+               $invert = $opts['invert'];
+
+               // JOIN on watchlist for users
+               if( $wgUser->getId() ) {
+                       $tables[] = 'watchlist';
+                       $join_conds = array( 'watchlist' => array('LEFT JOIN',"wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") );
+               }
+
+               wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) );
+
+               // Is there either one namespace selected or excluded?
+               // Also, if this is "all" or main namespace, just use timestamp index.
+               if( is_null($namespace) || $invert || $namespace == NS_MAIN ) {
+                       $res = $dbr->select( $tables, '*', $conds, __METHOD__,
+                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
+                                       'USE INDEX' => array('recentchanges' => 'rc_timestamp') ),
+                               $join_conds );
+               // We have a new_namespace_time index! UNION over new=(0,1) and sort result set!
+               } else {
+                       // New pages
+                       $sqlNew = $dbr->selectSQLText( $tables, '*',
+                               array( 'rc_new' => 1 ) + $conds,
+                               __METHOD__,
+                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
+                                       'USE INDEX' =>  array('recentchanges' => 'new_name_timestamp') ),
+                               $join_conds );
+                       // Old pages
+                       $sqlOld = $dbr->selectSQLText( $tables, '*',
+                               array( 'rc_new' => 0 ) + $conds,
+                               __METHOD__,
+                               array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 
+                                       'USE INDEX' =>  array('recentchanges' => 'new_name_timestamp') ),
+                               $join_conds );
+                       # Join the two fast queries, and sort the result set
+                       $sql = "($sqlNew) UNION ($sqlOld) ORDER BY rc_timestamp DESC LIMIT $limit";
+                       $res = $dbr->query( $sql, __METHOD__ );
+               }
+
+               return $res;
+       }
+
+       public function webOutput( $rows, $opts ) {
+               global $wgOut, $wgUser, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
+               global $wgAllowCategorizedRecentChanges;
+
+               $limit = $opts['limit'];
+
+               if ( !$this->including() ) {
+                       // Output options box
+                       $this->doHeader( $opts );
+               }
+
+               // And now for the content
+               $wgOut->setSyndicated( true );
+
+               $list = ChangesList::newFromUser( $wgUser );
+
+               if ( $wgAllowCategorizedRecentChanges ) {
+                       rcFilterByCategories( $rows, $opts );
+               }
+
+               $s = $list->beginRecentChangesList();
+               $counter = 1;
+
+               $showWatcherCount = $wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' );
+               $watcherCache = array();
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               foreach( $rows as $obj ){
+                       if( $limit == 0) {
+                               break;
+                       }
+
+                       if ( ! ( $opts['hideminor']     && $obj->rc_minor     ) &&
+                            ! ( $opts['hidepatrolled'] && $obj->rc_patrolled ) ) {
+                               $rc = RecentChange::newFromRow( $obj );
+                               $rc->counter = $counter++;
+
+                               if ($wgShowUpdatedMarker
+                                       && !empty( $obj->wl_notificationtimestamp )
+                                       && ($obj->rc_timestamp >= $obj->wl_notificationtimestamp)) {
+                                               $rc->notificationtimestamp = true;
+                               } else {
+                                       $rc->notificationtimestamp = false;
+                               }
+
+                               $rc->numberofWatchingusers = 0; // Default
+                               if ($showWatcherCount && $obj->rc_namespace >= 0) {
+                                       if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
+                                               $watcherCache[$obj->rc_namespace][$obj->rc_title] =
+                                                       $dbr->selectField( 'watchlist',
+                                                               'COUNT(*)',
+                                                               array(
+                                                                       'wl_namespace' => $obj->rc_namespace,
+                                                                       'wl_title' => $obj->rc_title,
+                                                               ),
+                                                               __METHOD__ . '-watchers' );
+                                       }
+                                       $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
+                               }
+                               $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ) );
+                               --$limit;
+                       }
+               }
+               $s .= $list->endRecentChangesList();
+               $wgOut->addHTML( $s );
+       }
+
+       public function doHeader( $opts ) {
+               global $wgScript, $wgOut;
+               $wgOut->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) );
+
+               $defaults = $opts->getAllValues();
+               $nondefaults = $opts->getChangedValues();
+               $opts->consumeValues( array( 'namespace', 'invert' ) );
+
+               $panel = array();
+               $panel[] = rcOptionsPanel( $defaults, $nondefaults );
+               $panel[] = '<hr />';
+
+               $extraOpts = array();
+               $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
+
+               global $wgAllowCategorizedRecentChanges;
+               if ( $wgAllowCategorizedRecentChanges ) {
+                       $extraOpts['category'] = $this->categoryFilterForm( $opts );
+               }
+
+               wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
+               $extraOpts['submit'] = Xml::submitbutton( wfMsg('allpagessubmit') );
+
+               $out = Xml::openElement( 'table' );
+               foreach ( $extraOpts as $optionRow ) {
+                       $out .= Xml::openElement( 'tr' );
+                       if ( is_array($optionRow) ) {
+                               $out .= Xml::tags( 'td', null, $optionRow[0] );
+                               $out .= Xml::tags( 'td', null, $optionRow[1] );
+                       } else {
+                               $out .= Xml::tags( 'td', array( 'colspan' => 2 ), $optionRow );
+                       }
+                       $out .= Xml::closeElement( 'tr' );
+               }
+               $out .= Xml::closeElement( 'table' );
+
+               $unconsumed = $opts->getUnconsumedValues();
+               foreach ( $unconsumed as $key => $value ) {
+                       $out .= Xml::hidden( $key, $value );
+               }
+
+               $t = $this->getTitle();
+               $out .= Xml::hidden( 'title', $t->getPrefixedText() );
+               $form = Xml::tags( 'form', array( 'action' => $wgScript ), $out );
+               $panel[] = $form;
+               $panelString = implode( "\n", $panel );
+
+               $wgOut->addHTML(
+                       Xml::fieldset( wfMsg( 'recentchanges' ), $panelString, array( 'class' => 'rcoptions' ) )
+               );
+       }
+
+       /**
+       * Creates the choose namespace selection
+       *
+       * @return string
+       */
+       protected function namespaceFilterForm( FormOptions $opts ) {
+               $nsSelect = HTMLnamespaceselector( $opts['namespace'], '' );
+               $nsLabel = Xml::label( wfMsg('namespace'), 'namespace' );
+               $invert = Xml::checkLabel( wfMsg('invert'), 'invert', 'nsinvert', $opts['invert'] );
+               return array( $nsLabel, "$nsSelect $invert" );
+       }
+
+       protected function categoryFilterForm( FormOptions $opts ) {
+               list( $label, $input ) = Xml::inputLabelSep( wfMsg('rc_categories'),
+                       'categories', 'mw-categories', false, $opts['categories'] );
+
+               $input .= ' ' . Xml::checkLabel( wfMsg('rc_categories_any'),
+                       'categories_any', 'mw-categories_any', $opts['categories_any'] );
+
+               return array( $label, $input );
+       }
+
+}
+
+function rcFilterByCategories ( &$rows, FormOptions $opts ) {
+       $categories = array_map( 'trim', explode( "|" , $categories ) );
+
+       if( empty($categories) ) {
+               return;
+       }
+
+       # Filter categories
+       $cats = array();
+       foreach ( $opts['categories'] AS $cat ) {
+               $cat = trim( $cat );
+               if ( $cat == "" ) continue;
+               $cats[] = $cat;
+       }
+
+       # Filter articles
+       $articles = array();
+       $a2r = array();
+       foreach ( $rows AS $k => $r ) {
+               $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
+               $id = $nt->getArticleID();
+               if ( $id == 0 ) continue; # Page might have been deleted...
+               if ( !in_array($id, $articles) ) {
+                       $articles[] = $id;
+               }
+               if ( !isset($a2r[$id]) ) {
+                       $a2r[$id] = array();
+               }
+               $a2r[$id][] = $k;
+       }
+
+       # Shortcut?
+       if ( !count($articles) || !count($cats) )
+               return ;
+
+       # Look up
+       $c = new Categoryfinder ;
+       $c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ;
+       $match = $c->run();
+
+       # Filter
+       $newrows = array();
+       foreach ( $match AS $id ) {
+               foreach ( $a2r[$id] AS $rev ) {
+                       $k = $rev;
+                       $newrows[$k] = $rows[$k];
+               }
+       }
+       $rows = $newrows;
+}
+
+/**
+ *
+ */
+function rcCountLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) {
+       global $wgUser, $wgLang, $wgContLang;
+       $sk = $wgUser->getSkin();
+       $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ),
+         ($lim ? $wgLang->formatNum( "{$lim}" ) : wfMsg( 'recentchangesall' ) ), "{$more}" .
+         ($d ? "days={$d}&" : '') . 'limit='.$lim, '', '',
+         $active ? 'style="font-weight: bold;"' : '' );
+       return $s;
+}
+
+/**
+ *
+ */
+function rcDaysLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) {
+       global $wgUser, $wgLang, $wgContLang;
+       $sk = $wgUser->getSkin();
+       $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ),
+         ($d ? $wgLang->formatNum( "{$d}" ) : wfMsg( 'recentchangesall' ) ), $more.'days='.$d .
+         ($lim ? '&limit='.$lim : ''), '', '',
+         $active ? 'style="font-weight: bold;"' : '' );
+       return $s;
+}
+
+/**
+ * Used by Recentchangeslinked
+ */
+function rcDayLimitLinks( $days, $limit, $page='Recentchanges', $more='', $doall = false, $minorLink = '',
+       $botLink = '', $liuLink = '', $patrLink = '', $myselfLink = '' ) {
+       global $wgRCLinkLimits, $wgRCLinkDays;
+       if ($more != '') $more .= '&';
+       
+       # Sort data for display and make sure it's unique after we've added user data.
+       # FIXME: why does this piss around with globals like this? Why is $limit added on globally?
+       $wgRCLinkLimits[] = $limit;
+       $wgRCLinkDays[] = $days;
+       sort($wgRCLinkLimits);
+       sort($wgRCLinkDays);
+       $wgRCLinkLimits = array_unique($wgRCLinkLimits);
+       $wgRCLinkDays = array_unique($wgRCLinkDays);
+       
+       $cl = array();
+       foreach( $wgRCLinkLimits as $countLink ) {
+               $cl[] = rcCountLink( $countLink, $days, $page, $more, $countLink == $limit );
+       }
+       if( $doall ) $cl[] = rcCountLink( 0, $days, $page, $more );
+       $cl = implode( ' | ', $cl);
+       
+       $dl = array();
+       foreach( $wgRCLinkDays as $daysLink ) {
+               $dl[] = rcDaysLink( $limit, $daysLink, $page, $more, $daysLink == $days );
+       }
+       if( $doall ) $dl[] = rcDaysLink( $limit, 0, $page, $more );
+       $dl = implode( ' | ', $dl);
+       
+       $linkParts = array( 'minorLink' => 'minor', 'botLink' => 'bots', 'liuLink' => 'liu', 'patrLink' => 'patr', 'myselfLink' => 'mine' );
+       foreach( $linkParts as $linkVar => $linkMsg ) {
+               if( $$linkVar != '' )
+                       $links[] = wfMsgHtml( 'rcshowhide' . $linkMsg, $$linkVar );
+       }
+
+       $shm = implode( ' | ', $links );
+       $note = wfMsg( 'rclinks', $cl, $dl, $shm );
+       return $note;
+}
+
+
+/**
+ * Makes change an option link which carries all the other options
+ * @param $title see Title
+ * @param $override
+ * @param $options
+ */
+function makeOptionsLink( $title, $override, $options, $active = false ) {
+       global $wgUser, $wgContLang;
+       $sk = $wgUser->getSkin();
+       return $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchanges' ),
+               htmlspecialchars( $title ), wfArrayToCGI( $override, $options ), '', '',
+               $active ? 'style="font-weight: bold;"' : '' );
+}
+
+/**
+ * Creates the options panel.
+ * @param $defaults
+ * @param $nondefaults
+ */
+function rcOptionsPanel( $defaults, $nondefaults ) {
+       global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays;
+
+       $options = $nondefaults + $defaults;
+
+       if( $options['from'] )
+               $note = wfMsgExt( 'rcnotefrom', array( 'parseinline' ),
+                       $wgLang->formatNum( $options['limit'] ),
+                       $wgLang->timeanddate( $options['from'], true ) );
+       else
+               $note = wfMsgExt( 'rcnote', array( 'parseinline' ),
+                       $wgLang->formatNum( $options['limit'] ),
+                       $wgLang->formatNum( $options['days'] ),
+                       $wgLang->timeAndDate( wfTimestampNow(), true ) );
+
+       # Sort data for display and make sure it's unique after we've added user data.
+       $wgRCLinkLimits[] = $options['limit'];
+       $wgRCLinkDays[] = $options['days'];
+       sort($wgRCLinkLimits);
+       sort($wgRCLinkDays);
+       $wgRCLinkLimits = array_unique($wgRCLinkLimits);
+       $wgRCLinkDays = array_unique($wgRCLinkDays);
+       
+       // limit links
+       foreach( $wgRCLinkLimits as $value ) {
+               $cl[] = makeOptionsLink( $wgLang->formatNum( $value ),
+                       array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ;
+       }
+       $cl = implode( ' | ', $cl);
+
+       // day links, reset 'from' to none
+       foreach( $wgRCLinkDays as $value ) {
+               $dl[] = makeOptionsLink( $wgLang->formatNum( $value ),
+                       array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ;
+       }
+       $dl = implode( ' | ', $dl);
+
+
+       // show/hide links
+       $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' ));
+       $minorLink = makeOptionsLink( $showhide[1-$options['hideminor']],
+               array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults);
+       $botLink = makeOptionsLink( $showhide[1-$options['hidebots']],
+               array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults);
+       $anonsLink = makeOptionsLink( $showhide[ 1 - $options['hideanons'] ],
+               array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults );
+       $liuLink   = makeOptionsLink( $showhide[1-$options['hideliu']],
+               array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults);
+       $patrLink  = makeOptionsLink( $showhide[1-$options['hidepatrolled']],
+               array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults);
+       $myselfLink = makeOptionsLink( $showhide[1-$options['hidemyself']],
+               array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults);
+
+       $links[] = wfMsgHtml( 'rcshowhideminor', $minorLink );
+       $links[] = wfMsgHtml( 'rcshowhidebots', $botLink );
+       $links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink );
+       $links[] = wfMsgHtml( 'rcshowhideliu', $liuLink );
+       if( $wgUser->useRCPatrol() )
+               $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink );
+       $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink );
+       $hl = implode( ' | ', $links );
+
+       // show from this onward link
+       $now = $wgLang->timeanddate( wfTimestampNow(), true );
+       $tl =  makeOptionsLink( $now, array( 'from' => wfTimestampNow()), $nondefaults );
+
+       $rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter'),
+               $cl, $dl, $hl );
+       $rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter'), $tl );
+       return "$note<br />$rclinks<br />$rclistfrom";
+
+}
\ No newline at end of file
diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php
new file mode 100644 (file)
index 0000000..73d46f8
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+/**
+ * This is to display changes made to all articles linked in an article.
+ * @file
+ * @ingroup SpecialPage
+ */
+
+require_once( 'Recentchanges.php' );
+
+/**
+ * Entrypoint
+ * @param string $par parent page we will look at
+ */
+function wfSpecialRecentchangeslinked( $par = NULL ) {
+       global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest, $wgTitle, $wgScript;
+
+       $days = $wgRequest->getInt( 'days' );
+       $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
+       $hideminor = $wgRequest->getBool( 'hideminor' ) ? 1 : 0;
+       $showlinkedto = $wgRequest->getBool( 'showlinkedto' ) ? 1 : 0;
+
+       $title = Title::newFromURL( $target );
+       $target = $title ? $title->getPrefixedText() : '';
+
+       $wgOut->setPagetitle( wfMsg( 'recentchangeslinked' ) );
+       $sk = $wgUser->getSkin();
+
+       $wgOut->addHTML(
+               Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
+               Xml::openElement( 'fieldset' ) .
+               Xml::element( 'legend', array(), wfMsg( 'recentchangeslinked' ) ) . "\n" .
+               Xml::inputLabel( wfMsg( 'recentchangeslinked-page' ), 'target', 'recentchangeslinked-target', 40, $target ) .
+               "&nbsp;&nbsp;&nbsp;<span style='white-space: nowrap'>" .
+               Xml::check( 'showlinkedto', $showlinkedto, array('id' => 'showlinkedto') ) . ' ' .
+               Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) .
+               "</span><br/>\n" .
+               Xml::hidden( 'title', $wgTitle->getPrefixedText() ). "\n" .
+               Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+               Xml::closeElement( 'fieldset' ) .
+               Xml::closeElement( 'form' ) . "\n"
+       );
+
+       if ( !$target ) {
+               return;
+       }
+       $nt = Title::newFromURL( $target );
+       if( !$nt ) {
+               $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
+               return;
+       }
+       $id = $nt->getArticleId();
+
+       $wgOut->setPageTitle( wfMsg( 'recentchangeslinked-title', $nt->getPrefixedText() ) );
+       $wgOut->setSyndicated();
+       $wgOut->setFeedAppendQuery( "target=" . urlencode( $target ) );
+
+       if ( !$days ) {
+               $days = (int)$wgUser->getOption( 'rcdays', 7 );
+       }
+       list( $limit, /* offset */ ) = wfCheckLimits( 100, 'rclimit' );
+
+       $dbr = wfGetDB( DB_SLAVE,'recentchangeslinked' );
+       $cutoff = $dbr->timestamp( time() - ( $days * 86400 ) );
+
+       $hideminor = ($hideminor ? 1 : 0);
+       if ( $hideminor ) {
+               $mlink = $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchangeslinked' ),
+                 wfMsg( 'show' ), 'target=' . htmlspecialchars( $nt->getPrefixedURL() ) .
+                 "&days={$days}&limit={$limit}&hideminor=0&showlinkedto={$showlinkedto}" );
+       } else {
+               $mlink = $sk->makeKnownLink( $wgContLang->specialPage( "Recentchangeslinked" ),
+                 wfMsg( "hide" ), "target=" . htmlspecialchars( $nt->getPrefixedURL() ) .
+                 "&days={$days}&limit={$limit}&hideminor=1&showlinkedto={$showlinkedto}" );
+       }
+       if ( $hideminor ) {
+               $cmq = 'AND rc_minor=0';
+       } else { $cmq = ''; }
+
+       list($recentchanges, $categorylinks, $pagelinks, $watchlist) =
+           $dbr->tableNamesN( 'recentchanges', 'categorylinks', 'pagelinks', "watchlist" );
+
+       $uid = $wgUser->getId();
+       // The fields we are selecting
+       $fields = "rc_cur_id,rc_namespace,rc_title,
+               rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_log_type,rc_log_action,rc_params,rc_deleted,
+               rc_new, rc_id, rc_this_oldid, rc_last_oldid, rc_bot, rc_patrolled, rc_type, rc_old_len, rc_new_len";
+       $fields .= $uid ? ",wl_user" : "";
+
+       // Check if this should be a feed
+
+       $feed = false;
+       global $wgFeedLimit;
+       $feedFormat = $wgRequest->getVal( 'feed' );
+       if( $feedFormat ) {
+               $feed = new ChangesFeed( $feedFormat, false );
+               # Sanity check
+               if( $limit > $wgFeedLimit ) {
+                       $limit = $wgFeedLimit;
+               }
+       }
+
+       // If target is a Category, use categorylinks and invert from and to
+       if( $nt->getNamespace() == NS_CATEGORY ) {
+               $catkey = $dbr->addQuotes( $nt->getDBkey() );
+               # The table clauses
+               $tables = "$categorylinks, $recentchanges";
+               $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "";
+
+               $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables 
+                       WHERE rc_timestamp > '{$cutoff}' {$cmq} 
+                       AND cl_from=rc_cur_id 
+                       AND cl_to=$catkey 
+                       GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently
+       } else {
+               if( $showlinkedto ) {
+                       $ns = $dbr->addQuotes( $nt->getNamespace() );
+                       $dbkey = $dbr->addQuotes( $nt->getDBkey() );
+                       $joinConds = "AND pl_namespace={$ns} AND pl_title={$dbkey} AND pl_from=rc_cur_id";
+               } else {
+                       $joinConds = "AND pl_namespace=rc_namespace AND pl_title=rc_title AND pl_from=$id";
+               }
+               # The table clauses
+               $tables = "$pagelinks, $recentchanges";
+               $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "";
+
+               $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables
+                       WHERE rc_timestamp > '{$cutoff}' {$cmq}
+                       {$joinConds}
+                       GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently
+       }
+       # Actually do the query
+       $res = $dbr->query( $sql, __METHOD__ );
+       $count = $dbr->numRows( $res );
+       $rchanges = array();
+       # Output feeds now and be done with it!
+       if( $feed ) {
+               if( $count ) {
+                       $counter = 1;
+                       while ( $limit ) {
+                               if ( 0 == $count ) { break; }
+                               $obj = $dbr->fetchObject( $res );
+                               --$count;
+                               $rc = RecentChange::newFromRow( $obj );
+                               $rc->counter = $counter++;
+                               --$limit;
+                               $rchanges[] = $obj;
+                       }
+               }
+               $wgOut->disable();
+
+               $feedObj = $feed->getFeedObject(
+                       wfMsgForContent( 'recentchangeslinked-title', $nt->getPrefixedText() ),
+                       wfMsgForContent( 'recentchangeslinked' )
+               );
+               ChangesFeed::generateFeed( $rchanges, $feedObj );
+               return;
+       }
+       
+       # Otherwise, carry on with regular output...
+       $wgOut->addHTML("&lt; ".$sk->makeLinkObj($nt, "", "redirect=no" )."<br />\n");
+       $note = wfMsgExt( "rcnote", array ( 'parseinline' ), $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) );
+       $wgOut->addHTML( "<hr />\n{$note}\n<br />" );
+
+       $note = rcDayLimitlinks( $days, $limit, "Recentchangeslinked",
+                                "target=" . $nt->getPrefixedURL() . "&hideminor={$hideminor}&showlinkedto={$showlinkedto}",
+                                false, $mlink );
+
+       $wgOut->addHTML( $note."\n" );
+
+       $list = ChangesList::newFromUser( $wgUser );
+       $s = $list->beginRecentChangesList();
+
+       if ( $count ) {
+               $counter = 1;
+               while ( $limit ) {
+                       if ( 0 == $count ) { break; }
+                       $obj = $dbr->fetchObject( $res );
+                       --$count;
+                       $rc = RecentChange::newFromRow( $obj );
+                       $rc->counter = $counter++;
+                       $s .= $list->recentChangesLine( $rc , !empty( $obj->wl_user) );
+                       --$limit;
+               }
+       } else {
+               $wgOut->addWikiMsg('recentchangeslinked-noresult');
+       }
+       $s .= $list->endRecentChangesList();
+
+       $dbr->freeResult( $res );
+       $wgOut->addHTML( $s );
+}
diff --git a/includes/specials/SpecialResetpass.php b/includes/specials/SpecialResetpass.php
new file mode 100644 (file)
index 0000000..707b941
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/** Constructor */
+function wfSpecialResetpass( $par ) {
+       $form = new PasswordResetForm();
+       $form->execute( $par );
+}
+
+/**
+ * Let users recover their password.
+ * @ingroup SpecialPage
+ */
+class PasswordResetForm extends SpecialPage {
+       function __construct( $name=null, $reset=null ) {
+               if( $name !== null ) {
+                       $this->mName = $name;
+                       $this->mTemporaryPassword = $reset;
+               } else {
+                       global $wgRequest;
+                       $this->mName = $wgRequest->getVal( 'wpName' );
+                       $this->mTemporaryPassword = $wgRequest->getVal( 'wpPassword' );
+               }
+       }
+
+       /**
+        * Main execution point
+        */
+       function execute( $par ) {
+               global $wgUser, $wgAuth, $wgOut, $wgRequest;
+
+               if( !$wgAuth->allowPasswordChange() ) {
+                       $this->error( wfMsg( 'resetpass_forbidden' ) );
+                       return;
+               }
+
+               if( $this->mName === null && !$wgRequest->wasPosted() ) {
+                       $this->error( wfMsg( 'resetpass_missing' ) );
+                       return;
+               }
+
+               if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'token' ) ) ) {
+                       $newpass = $wgRequest->getVal( 'wpNewPassword' );
+                       $retype = $wgRequest->getVal( 'wpRetype' );
+                       try {
+                               $this->attemptReset( $newpass, $retype );
+                               $wgOut->addWikiMsg( 'resetpass_success' );
+
+                               $data = array(
+                                       'action' => 'submitlogin',
+                                       'wpName' => $this->mName,
+                                       'wpPassword' => $newpass,
+                                       'returnto' => $wgRequest->getVal( 'returnto' ),
+                               );
+                               if( $wgRequest->getCheck( 'wpRemember' ) ) {
+                                       $data['wpRemember'] = 1;
+                               }
+                               $login = new LoginForm( new FauxRequest( $data, true ) );
+                               $login->execute();
+
+                               return;
+                       } catch( PasswordError $e ) {
+                               $this->error( $e->getMessage() );
+                       }
+               }
+               $this->showForm();
+       }
+
+       function error( $msg ) {
+               global $wgOut;
+               $wgOut->addHtml( '<div class="errorbox">' .
+                       htmlspecialchars( $msg ) .
+                       '</div>' );
+       }
+
+       function showForm() {
+               global $wgOut, $wgUser, $wgRequest;
+
+               $wgOut->disallowUserJs();
+
+               $self = SpecialPage::getTitleFor( 'Resetpass' );
+               $form  =
+                       '<div id="userloginForm">' .
+                       wfOpenElement( 'form',
+                               array(
+                                       'method' => 'post',
+                                       'action' => $self->getLocalUrl() ) ) .
+                       '<h2>' . wfMsgHtml( 'resetpass_header' ) . '</h2>' .
+                       '<div id="userloginprompt">' .
+                       wfMsgExt( 'resetpass_text', array( 'parse' ) ) .
+                       '</div>' .
+                       '<table>' .
+                       wfHidden( 'token', $wgUser->editToken() ) .
+                       wfHidden( 'wpName', $this->mName ) .
+                       wfHidden( 'wpPassword', $this->mTemporaryPassword ) .
+                       wfHidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) .
+                       $this->pretty( array(
+                               array( 'wpName', 'username', 'text', $this->mName ),
+                               array( 'wpNewPassword', 'newpassword', 'password', '' ),
+                               array( 'wpRetype', 'yourpasswordagain', 'password', '' ),
+                       ) ) .
+                       '<tr>' .
+                               '<td></td>' .
+                               '<td>' .
+                                       Xml::checkLabel( wfMsg( 'remembermypassword' ),
+                                               'wpRemember', 'wpRemember',
+                                               $wgRequest->getCheck( 'wpRemember' ) ) .
+                               '</td>' .
+                       '</tr>' .
+                       '<tr>' .
+                               '<td></td>' .
+                               '<td>' .
+                                       wfSubmitButton( wfMsgHtml( 'resetpass_submit' ) ) .
+                               '</td>' .
+                       '</tr>' .
+                       '</table>' .
+                       wfCloseElement( 'form' ) .
+                       '</div>';
+               $wgOut->addHtml( $form );
+       }
+
+       function pretty( $fields ) {
+               $out = '';
+               foreach( $fields as $list ) {
+                       list( $name, $label, $type, $value ) = $list;
+                       if( $type == 'text' ) {
+                               $field = '<tt>' . htmlspecialchars( $value ) . '</tt>';
+                       } else {
+                               $field = Xml::input( $name, 20, $value,
+                                       array( 'id' => $name, 'type' => $type ) );
+                       }
+                       $out .= '<tr>';
+                       $out .= '<td align="right">';
+                       $out .= Xml::label( wfMsg( $label ), $name );
+                       $out .= '</td>';
+                       $out .= '<td>';
+                       $out .= $field;
+                       $out .= '</td>';
+                       $out .= '</tr>';
+               }
+               return $out;
+       }
+
+       /**
+        * @throws PasswordError when cannot set the new password because requirements not met.
+        */
+       function attemptReset( $newpass, $retype ) {
+               $user = User::newFromName( $this->mName );
+               if( $user->isAnon() ) {
+                       throw new PasswordError( 'no such user' );
+               }
+
+               if( !$user->checkTemporaryPassword( $this->mTemporaryPassword ) ) {
+                       throw new PasswordError( wfMsg( 'resetpass_bad_temporary' ) );
+               }
+
+               if( $newpass !== $retype ) {
+                       throw new PasswordError( wfMsg( 'badretype' ) );
+               }
+
+               $user->setPassword( $newpass );
+               $user->saveSettings();
+       }
+}
diff --git a/includes/specials/SpecialRevisiondelete.php b/includes/specials/SpecialRevisiondelete.php
new file mode 100644 (file)
index 0000000..a3d4b7e
--- /dev/null
@@ -0,0 +1,1470 @@
+<?php
+/**
+ * Special page allowing users with the appropriate permissions to view
+ * and hide revisions. Log items can also be hidden.
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+function wfSpecialRevisiondelete( $par = null ) {
+       global $wgOut, $wgRequest, $wgUser;
+       # Handle our many different possible input types
+       $target = $wgRequest->getText( 'target' );
+       $oldid = $wgRequest->getArray( 'oldid' );
+       $artimestamp = $wgRequest->getArray( 'artimestamp' );
+       $logid = $wgRequest->getArray( 'logid' );
+       $img = $wgRequest->getArray( 'oldimage' );
+       $fileid = $wgRequest->getArray( 'fileid' );
+       # For reviewing deleted files...
+       $file = $wgRequest->getVal( 'file' );
+       # If this is a revision, then we need a target page
+       $page = Title::newFromUrl( $target );
+       if( is_null($page) ) {
+               $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
+               return;
+       }
+       # Only one target set at a time please!
+       $i = (bool)$file + (bool)$oldid + (bool)$logid + (bool)$artimestamp + (bool)$fileid + (bool)$img;
+       if( $i !== 1 ) {
+               $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+               return;
+       }
+       # Logs must have a type given
+       if( $logid && !strpos($page->getDBKey(),'/') ) {
+               $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+               return;
+       }
+       # Either submit or create our form
+       $form = new RevisionDeleteForm( $page, $oldid, $logid, $artimestamp, $fileid, $img, $file );
+       if( $wgRequest->wasPosted() ) {
+               $form->submit( $wgRequest );
+       } else if( $oldid || $artimestamp ) {
+               $form->showRevs();
+       } else if( $fileid || $img ) {
+               $form->showImages();
+       } else if( $logid ) {
+               $form->showLogItems();
+       }
+       # Show relevant lines from the deletion log. This will show even if said ID
+       # does not exist...might be helpful
+       $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+       LogEventsList::showLogExtract( $wgOut, 'delete', $page->getPrefixedText() );
+       if( $wgUser->isAllowed( 'suppressionlog' ) ){
+               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "</h2>\n" );
+               LogEventsList::showLogExtract( $wgOut, 'suppress', $page->getPrefixedText() );
+       }
+}
+
+/**
+ * Implements the GUI for Revision Deletion.
+ * @ingroup SpecialPage
+ */
+class RevisionDeleteForm {
+       /**
+        * @param Title $page
+        * @param array $oldids
+        * @param array $logids
+        * @param array $artimestamps
+        * @param array $fileids
+        * @param array $img
+        * @param string $file
+        */
+       function __construct( $page, $oldids, $logids, $artimestamps, $fileids, $img, $file ) {
+               global $wgUser, $wgOut;
+
+               $this->page = $page;
+               # For reviewing deleted files...
+               if( $file ) {
+                       $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $page, $file );
+                       $oimage->load();
+                       // Check if user is allowed to see this file
+                       if( !$oimage->userCan(File::DELETED_FILE) ) {
+                               $wgOut->permissionRequired( 'suppressrevision' );
+                       } else {
+                               $this->showFile( $file );
+                       }
+                       return;
+               }
+               $this->skin = $wgUser->getSkin();
+               # Give a link to the log for this page
+               if( !is_null($this->page) && $this->page->getNamespace() > -1 ) {
+                       $links = array();
+
+                       $logtitle = SpecialPage::getTitleFor( 'Log' );
+                       $links[] = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'viewpagelogs' ),
+                               wfArrayToCGI( array( 'page' => $this->page->getPrefixedUrl() ) ) );
+                       # Give a link to the page history
+                       $links[] = $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml( 'pagehist' ),
+                               wfArrayToCGI( array( 'action' => 'history' ) ) );
+                       # Link to deleted edits
+                       if( $wgUser->isAllowed('undelete') ) {
+                               $undelete = SpecialPage::getTitleFor( 'Undelete' );
+                               $links[] = $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml( 'deletedhist' ),
+                                       wfArrayToCGI( array( 'target' => $this->page->getPrefixedUrl() ) ) );
+                       }
+                       # Logs themselves don't have histories or archived revisions
+                       $wgOut->setSubtitle( '<p>'.implode($links,' / ').'</p>' );
+               }
+               // At this point, we should only have one of these
+               if( $oldids ) {
+                       $this->revisions = $oldids;
+                       $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT );
+                       $this->deleteKey='oldid';
+               } else if( $artimestamps ) {
+                       $this->archrevs = $artimestamps;
+                       $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT );
+                       $this->deleteKey='artimestamp';
+               } else if( $img ) {
+                       $this->ofiles = $img;
+                       $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE );
+                       $this->deleteKey='oldimage';
+               } else if( $fileids ) {
+                       $this->afiles = $fileids;
+                       $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE );
+                       $this->deleteKey='fileid';
+               } else if( $logids ) {
+                       $this->events = $logids;
+                       $hide_content_name = array( 'revdelete-hide-name', 'wpHideName', LogPage::DELETED_ACTION );
+                       $this->deleteKey='logid';
+               }
+               // Our checkbox messages depends one what we are doing,
+               // e.g. we don't hide "text" for logs or images
+               $this->checks = array(
+                       $hide_content_name,
+                       array( 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ),
+                       array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ) );
+               if( $wgUser->isAllowed('suppressrevision') ) {
+                       $this->checks[] = array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED );
+               }
+       }
+
+       /**
+        * Show a deleted file version requested by the visitor.
+        */
+       private function showFile( $key ) {
+               global $wgOut, $wgRequest;
+               $wgOut->disable();
+
+               # We mustn't allow the output to be Squid cached, otherwise
+               # if an admin previews a deleted image, and it's cached, then
+               # a user without appropriate permissions can toddle off and
+               # nab the image, and Squid will serve it
+               $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+               $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
+               $wgRequest->response()->header( 'Pragma: no-cache' );
+
+               $store = FileStore::get( 'deleted' );
+               $store->stream( $key );
+       }
+
+       /**
+        * This lets a user set restrictions for live and archived revisions
+        */
+       function showRevs() {
+               global $wgOut, $wgUser, $action;
+
+               $UserAllowed = true;
+
+               $count = ($this->deleteKey=='oldid') ?
+                       count($this->revisions) : count($this->archrevs);
+               $wgOut->addWikiMsg( 'revdelete-selected', $this->page->getPrefixedText(), $count );
+
+               $bitfields = 0;
+               $wgOut->addHtml( "<ul>" );
+
+               $where = $revObjs = array();
+               $dbr = wfGetDB( DB_SLAVE );
+               
+               $revisions = 0;
+               // Live revisions...
+               if( $this->deleteKey=='oldid' ) {
+                       // Run through and pull all our data in one query
+                       foreach( $this->revisions as $revid ) {
+                               $where[] = intval($revid);
+                       }
+                       $whereClause = 'rev_id IN(' . implode(',',$where) . ')';
+                       $result = $dbr->select( array('revision','page'), '*',
+                               array( 'rev_page' => $this->page->getArticleID(),
+                                       $whereClause, 'rev_page = page_id' ),
+                               __METHOD__ );
+                       while( $row = $dbr->fetchObject( $result ) ) {
+                               $revObjs[$row->rev_id] = new Revision( $row );
+                       }
+                       foreach( $this->revisions as $revid ) {
+                               // Hiding top revisison is bad
+                               if( !isset($revObjs[$revid]) || $revObjs[$revid]->isCurrent() ) {
+                                       continue;
+                               } else if( !$revObjs[$revid]->userCan(Revision::DELETED_RESTRICTED) ) {
+                               // If a rev is hidden from sysops
+                                       if( $action != 'submit') {
+                                               $wgOut->permissionRequired( 'suppressrevision' );
+                                               return;
+                                       }
+                                       $UserAllowed = false;
+                               }
+                               $revisions++;
+                               $wgOut->addHtml( $this->historyLine( $revObjs[$revid] ) );
+                               $bitfields |= $revObjs[$revid]->mDeleted;
+                       }
+               // The archives...
+               } else {
+                       // Run through and pull all our data in one query
+                       foreach( $this->archrevs as $timestamp ) {
+                               $where[] = $dbr->addQuotes( $timestamp );
+                       }
+                       $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')';
+                       $result = $dbr->select( 'archive', '*',
+                               array( 'ar_namespace' => $this->page->getNamespace(),
+                                       'ar_title' => $this->page->getDBKey(),
+                                               $whereClause ),
+                               __METHOD__ );
+                       while( $row = $dbr->fetchObject( $result ) ) {
+                               $revObjs[$row->ar_timestamp] = new Revision( array(
+                               'page'       => $this->page->getArticleId(),
+                               'id'         => $row->ar_rev_id,
+                               'text'       => $row->ar_text_id,
+                               'comment'    => $row->ar_comment,
+                               'user'       => $row->ar_user,
+                               'user_text'  => $row->ar_user_text,
+                               'timestamp'  => $row->ar_timestamp,
+                               'minor_edit' => $row->ar_minor_edit,
+                               'text_id'    => $row->ar_text_id,
+                               'deleted'    => $row->ar_deleted,
+                               'len'        => $row->ar_len) );
+                       }
+                       foreach( $this->archrevs as $timestamp ) {
+                               if( !isset($revObjs[$timestamp]) ) {
+                                       continue;
+                               } else if( !$revObjs[$timestamp]->userCan(Revision::DELETED_RESTRICTED) ) {
+                               // If a rev is hidden from sysops
+                                       if( $action != 'submit') {
+                                               $wgOut->permissionRequired( 'suppressrevision' );
+                                               return;
+                                       }
+                                       $UserAllowed = false;
+                               }
+                               $revisions++;
+                               $wgOut->addHtml( $this->historyLine( $revObjs[$timestamp] ) );
+                               $bitfields |= $revObjs[$timestamp]->mDeleted;
+                       }
+               }
+               if( !$revisions ) {
+                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+                       return;
+               }
+               
+               $wgOut->addHtml( "</ul>" );
+
+               $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
+
+               // Normal sysops can always see what they did, but can't always change it
+               if( !$UserAllowed ) return;
+
+               $items = array(
+                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+                       Xml::submitButton( wfMsg( 'revdelete-submit' ) )
+               );
+               $hidden = array(
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
+                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
+                       Xml::hidden( 'type', $this->deleteKey )
+               );
+               if( $this->deleteKey=='oldid' ) {
+                       foreach( $revObjs as $rev )
+                               $hidden[] = wfHidden( 'oldid[]', $rev->getId() );
+               } else {
+                       foreach( $revObjs as $rev )
+                               $hidden[] = wfHidden( 'artimestamp[]', $rev->getTimestamp() );
+               }
+               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
+               $wgOut->addHtml(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
+                               'id' => 'mw-revdel-form-revisions' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
+               );
+               // FIXME: all items checked for just one rev are checked, even if not set for the others
+               foreach( $this->checks as $item ) {
+                       list( $message, $name, $field ) = $item;
+                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
+               }
+               foreach( $items as $item ) {
+                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
+               }
+               foreach( $hidden as $item ) {
+                       $wgOut->addHtml( $item );
+               }
+               $wgOut->addHtml(
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+
+       }
+
+       /**
+        * This lets a user set restrictions for archived images
+        */
+       function showImages() {
+               global $wgOut, $wgUser, $action;
+
+               $UserAllowed = true;
+
+               $count = ($this->deleteKey=='oldimage') ? count($this->ofiles) : count($this->afiles);
+               $wgOut->addWikiText( wfMsgExt( 'revdelete-selected', array('parsemag'),
+                       $this->page->getPrefixedText(), $count ) );
+
+               $bitfields = 0;
+               $wgOut->addHtml( "<ul>" );
+
+               $where = $filesObjs = array();
+               $dbr = wfGetDB( DB_SLAVE );
+               // Live old revisions...
+               $revisions = 0;
+               if( $this->deleteKey=='oldimage' ) {
+                       // Run through and pull all our data in one query
+                       foreach( $this->ofiles as $timestamp ) {
+                               $where[] = $dbr->addQuotes( $timestamp.'!'.$this->page->getDbKey() );
+                       }
+                       $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')';
+                       $result = $dbr->select( 'oldimage', '*',
+                               array( 'oi_name' => $this->page->getDbKey(),
+                                       $whereClause ),
+                               __METHOD__ );
+                       while( $row = $dbr->fetchObject( $result ) ) {
+                               $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
+                               $filesObjs[$row->oi_archive_name]->user = $row->oi_user;
+                               $filesObjs[$row->oi_archive_name]->user_text = $row->oi_user_text;
+                       }
+                       // Check through our images
+                       foreach( $this->ofiles as $timestamp ) {
+                               $archivename = $timestamp.'!'.$this->page->getDbKey();
+                               if( !isset($filesObjs[$archivename]) ) {
+                                       continue;
+                               } else if( !$filesObjs[$archivename]->userCan(File::DELETED_RESTRICTED) ) {
+                                       // If a rev is hidden from sysops
+                                       if( $action != 'submit' ) {
+                                               $wgOut->permissionRequired( 'suppressrevision' );
+                                               return;
+                                       }
+                                       $UserAllowed = false;
+                               }
+                               $revisions++;
+                               // Inject history info
+                               $wgOut->addHtml( $this->fileLine( $filesObjs[$archivename] ) );
+                               $bitfields |= $filesObjs[$archivename]->deleted;
+                       }
+               // Archived files...
+               } else {
+                       // Run through and pull all our data in one query
+                       foreach( $this->afiles as $id ) {
+                               $where[] = intval($id);
+                       }
+                       $whereClause = 'fa_id IN(' . implode(',',$where) . ')';
+                       $result = $dbr->select( 'filearchive', '*',
+                               array( 'fa_name' => $this->page->getDbKey(),
+                                       $whereClause ),
+                               __METHOD__ );
+                       while( $row = $dbr->fetchObject( $result ) ) {
+                               $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row );
+                       }
+
+                       foreach( $this->afiles as $fileid ) {
+                               if( !isset($filesObjs[$fileid]) ) {
+                                       continue;
+                               } else if( !$filesObjs[$fileid]->userCan(File::DELETED_RESTRICTED) ) {
+                                       // If a rev is hidden from sysops
+                                       if( $action != 'submit' ) {
+                                               $wgOut->permissionRequired( 'suppressrevision' );
+                                               return;
+                                       }
+                                       $UserAllowed = false;
+                               }
+                               $revisions++;
+                               // Inject history info
+                               $wgOut->addHtml( $this->archivedfileLine( $filesObjs[$fileid] ) );
+                               $bitfields |= $filesObjs[$fileid]->deleted;
+                       }
+               }
+               if( !$revisions ) {
+                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+                       return;
+               }
+               
+               $wgOut->addHtml( "</ul>" );
+
+               $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
+               //Normal sysops can always see what they did, but can't always change it
+               if( !$UserAllowed ) return;
+
+               $items = array(
+                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+                       Xml::submitButton( wfMsg( 'revdelete-submit' ) )
+               );
+               $hidden = array(
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
+                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
+                       Xml::hidden( 'type', $this->deleteKey )
+               );
+               if( $this->deleteKey=='oldimage' ) {
+                       foreach( $this->ofiles as $filename )
+                               $hidden[] = wfHidden( 'oldimage[]', $filename );
+               } else {
+                       foreach( $this->afiles as $fileid )
+                               $hidden[] = wfHidden( 'fileid[]', $fileid );
+               }
+               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
+               $wgOut->addHtml(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
+                               'id' => 'mw-revdel-form-filerevisions' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
+               );
+               // FIXME: all items checked for just one file are checked, even if not set for the others
+               foreach( $this->checks as $item ) {
+                       list( $message, $name, $field ) = $item;
+                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
+               }
+               foreach( $items as $item ) {
+                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
+               }
+               foreach( $hidden as $item ) {
+                       $wgOut->addHtml( $item );
+               }
+
+               $wgOut->addHtml(
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+       }
+
+       /**
+        * This lets a user set restrictions for log items
+        */
+       function showLogItems() {
+               global $wgOut, $wgUser, $action, $wgMessageCache;
+
+               $UserAllowed = true;
+               $wgOut->addWikiText( wfMsgExt( 'logdelete-selected', array('parsemag'), count($this->events) ) );
+
+               $bitfields = 0;
+               $wgOut->addHtml( "<ul>" );
+
+               $where = $logRows = array();
+               $dbr = wfGetDB( DB_SLAVE );
+               // Run through and pull all our data in one query
+               $logItems = 0;
+               foreach( $this->events as $logid ) {
+                       $where[] = intval($logid);
+               }
+               list($log,$logtype) = explode( '/',$this->page->getDBKey(), 2 );
+               $whereClause = "log_type = '$logtype' AND log_id IN(" . implode(',',$where) . ")";
+               $result = $dbr->select( 'logging', '*',
+                       array( $whereClause ),
+                       __METHOD__ );
+               while( $row = $dbr->fetchObject( $result ) ) {
+                       $logRows[$row->log_id] = $row;
+               }
+               $wgMessageCache->loadAllMessages();
+               foreach( $this->events as $logid ) {
+                       // Don't hide from oversight log!!!
+                       if( !isset( $logRows[$logid] ) || $logRows[$logid]->log_type=='suppress' ) {
+                               continue;
+                       } else if( !LogEventsList::userCan( $logRows[$logid],Revision::DELETED_RESTRICTED) ) {
+                       // If an event is hidden from sysops
+                               if( $action != 'submit') {
+                                       $wgOut->permissionRequired( 'suppressrevision' );
+                                       return;
+                               }
+                               $UserAllowed = false;
+                       }
+                       $logItems++;
+                       $wgOut->addHtml( $this->logLine( $logRows[$logid] ) );
+                       $bitfields |= $logRows[$logid]->log_deleted;
+               }
+               if( !$logItems ) {
+                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+                       return;
+               }
+               
+               $wgOut->addHtml( "</ul>" );
+
+               $wgOut->addWikiMsg( 'revdelete-text' );
+               // Normal sysops can always see what they did, but can't always change it
+               if( !$UserAllowed ) return;
+
+               $items = array(
+                       Xml::inputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+                       Xml::submitButton( wfMsg( 'revdelete-submit' ) ) );
+               $hidden = array(
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
+                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
+                       Xml::hidden( 'type', $this->deleteKey ) );
+               foreach( $this->events as $logid ) {
+                       $hidden[] = Xml::hidden( 'logid[]', $logid );
+               }
+
+               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
+               $wgOut->addHtml(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $special->getLocalUrl( 'action=submit' ), 
+                               'id' => 'mw-revdel-form-logs' ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       xml::element( 'legend', null,  wfMsg( 'revdelete-legend' ) )
+               );
+               // FIXME: all items checked for just on event are checked, even if not set for the others
+               foreach( $this->checks as $item ) {
+                       list( $message, $name, $field ) = $item;
+                       $wgOut->addHtml( Xml::tags( 'div', null, Xml::checkLabel( wfMsg( $message ), $name, $name, $bitfields & $field ) ) );
+               }
+               foreach( $items as $item ) {
+                       $wgOut->addHtml( Xml::tags( 'p', null, $item ) );
+               }
+               foreach( $hidden as $item ) {
+                       $wgOut->addHtml( $item );
+               }
+
+               $wgOut->addHtml(
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+       }
+
+       /**
+        * @param Revision $rev
+        * @returns string
+        */
+       private function historyLine( $rev ) {
+               global $wgContLang;
+
+               $date = $wgContLang->timeanddate( $rev->getTimestamp() );
+               $difflink = $del = '';
+               // Live revisions
+               if( $this->deleteKey=='oldid' ) {
+                       $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() );
+                       $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
+                               'diff=' . $rev->getId() . '&oldid=prev' ) . ')';
+               // Archived revisions
+               } else {
+                       $undelete = SpecialPage::getTitleFor( 'Undelete' );
+                       $target = $this->page->getPrefixedText();
+                       $revlink = $this->skin->makeLinkObj( $undelete, $date,
+                               "target=$target&timestamp=" . $rev->getTimestamp() );
+                       $difflink = '(' . $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml('diff'),
+                               "target=$target&diff=prev&timestamp=" . $rev->getTimestamp() ) . ')';
+               }
+
+               if( $rev->isDeleted(Revision::DELETED_TEXT) ) {
+                       $revlink = '<span class="history-deleted">'.$revlink.'</span>';
+                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+                       if( !$rev->userCan(Revision::DELETED_TEXT) ) {
+                               $revlink = '<span class="history-deleted">'.$date.'</span>';
+                               $difflink = '(' . wfMsgHtml('diff') . ')';
+                       }
+               }
+
+               return "<li> $difflink $revlink ".$this->skin->revUserLink( $rev )." ".$this->skin->revComment( $rev )."$del</li>";
+       }
+
+       /**
+        * @param File $file
+        * @returns string
+        */
+       private function fileLine( $file ) {
+               global $wgContLang, $wgTitle;
+
+               $target = $this->page->getPrefixedText();
+               $date = $wgContLang->timeanddate( $file->getTimestamp(), true  );
+
+               $del = '';
+               # Hidden files...
+               if( $file->isDeleted(File::DELETED_FILE) ) {
+                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+                       if( !$file->userCan(File::DELETED_FILE) ) {
+                               $pageLink = $date;
+                       } else {
+                               $pageLink = $this->skin->makeKnownLinkObj( $wgTitle, $date,
+                                       "target=$target&file=$file->sha1.".$file->getExtension() );
+                       }
+                       $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
+               # Regular files...
+               } else {
+                       $url = $file->getUrlRel();
+                       $pageLink = "<a href=\"{$url}\">{$date}</a>";
+               }
+
+               $data = wfMsgHtml( 'widthheight',
+                                       $wgContLang->formatNum( $file->getWidth() ),
+                                       $wgContLang->formatNum( $file->getHeight() ) ) .
+                       ' (' . wfMsgHtml( 'nbytes', $wgContLang->formatNum( $file->getSize() ) ) . ')';
+
+               return "<li>$pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
+       }
+
+       /**
+        * @param ArchivedFile $file
+        * @returns string
+        */
+       private function archivedfileLine( $file ) {
+               global $wgContLang, $wgTitle;
+
+               $target = $this->page->getPrefixedText();
+               $date = $wgContLang->timeanddate( $file->getTimestamp(), true  );
+
+               $undelete = SpecialPage::getTitleFor( 'Undelete' );
+               $pageLink = $this->skin->makeKnownLinkObj( $undelete, $date, "target=$target&file={$file->getKey()}" );
+
+               $del = '';
+               if( $file->isDeleted(File::DELETED_FILE) ) {
+                       $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+               }
+
+               $data = wfMsgHtml( 'widthheight',
+                                       $wgContLang->formatNum( $file->getWidth() ),
+                                       $wgContLang->formatNum( $file->getHeight() ) ) .
+                       ' (' . wfMsgHtml( 'nbytes', $wgContLang->formatNum( $file->getSize() ) ) . ')';
+
+               return "<li> $pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
+       }
+
+       /**
+        * @param Array $row row
+        * @returns string
+        */
+       private function logLine( $row ) {
+               global $wgContLang;
+
+               $date = $wgContLang->timeanddate( $row->log_timestamp );
+               $paramArray = LogPage::extractParams( $row->log_params );
+               $title = Title::makeTitle( $row->log_namespace, $row->log_title );
+
+               $logtitle = SpecialPage::getTitleFor( 'Log' );
+               $loglink = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'log' ),
+                       wfArrayToCGI( array( 'page' => $title->getPrefixedUrl() ) ) );
+               // Action text
+               if( !LogEventsList::userCan($row,LogPage::DELETED_ACTION) ) {
+                       $action = '<span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+               } else {
+                       $action = LogPage::actionText( $row->log_type, $row->log_action, $title,
+                               $this->skin, $paramArray, true, true );
+                       if( $row->log_deleted & LogPage::DELETED_ACTION )
+                               $action = '<span class="history-deleted">' . $action . '</span>';
+               }
+               // User links
+               $userLink = $this->skin->userLink( $row->log_user, User::WhoIs($row->log_user) );
+               if( LogEventsList::isDeleted($row,LogPage::DELETED_USER) ) {
+                       $userLink = '<span class="history-deleted">' . $userLink . '</span>';
+               }
+               // Comment
+               $comment = $wgContLang->getDirMark() . $this->skin->commentBlock( $row->log_comment );
+               if( LogEventsList::isDeleted($row,LogPage::DELETED_COMMENT) ) {
+                       $comment = '<span class="history-deleted">' . $comment . '</span>';
+               }
+               return "<li>($loglink) $date $userLink $action $comment</li>";
+       }
+
+       /**
+        * Generate a user tool link cluster if the current user is allowed to view it
+        * @param ArchivedFile $file
+        * @return string HTML
+        */
+       private function fileUserTools( $file ) {
+               if( $file->userCan( Revision::DELETED_USER ) ) {
+                       $link = $this->skin->userLink( $file->user, $file->user_text ) .
+                               $this->skin->userToolLinks( $file->user, $file->user_text );
+               } else {
+                       $link = wfMsgHtml( 'rev-deleted-user' );
+               }
+               if( $file->isDeleted( Revision::DELETED_USER ) ) {
+                       return '<span class="history-deleted">' . $link . '</span>';
+               }
+               return $link;
+       }
+
+       /**
+        * Wrap and format the given file's comment block, if the current
+        * user is allowed to view it.
+        *
+        * @param ArchivedFile $file
+        * @return string HTML
+        */
+       private function fileComment( $file ) {
+               if( $file->userCan( File::DELETED_COMMENT ) ) {
+                       $block = $this->skin->commentBlock( $file->description );
+               } else {
+                       $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
+               }
+               if( $file->isDeleted( File::DELETED_COMMENT ) ) {
+                       return "<span class=\"history-deleted\">$block</span>";
+               }
+               return $block;
+       }
+
+       /**
+        * @param WebRequest $request
+        */
+       function submit( $request ) {
+               global $wgUser, $wgOut;
+
+               $bitfield = $this->extractBitfield( $request );
+               $comment = $request->getText( 'wpReason' );
+               # Can the user set this field?
+               if( $bitfield & Revision::DELETED_RESTRICTED && !$wgUser->isAllowed('suppressrevision') ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+               # If the save went through, go to success message. Otherwise
+               # bounce back to form...
+               if( $this->save( $bitfield, $comment, $this->page ) ) {
+                       $this->success();
+               } else if( $request->getCheck( 'oldid' ) || $request->getCheck( 'artimestamp' ) ) {
+                       return $this->showRevs();
+               } else if( $request->getCheck( 'logid' ) ) {
+                       return $this->showLogs();
+               } else if( $request->getCheck( 'oldimage' ) || $request->getCheck( 'fileid' ) ) {
+                       return $this->showImages();
+               }
+       }
+
+       private function success() {
+               global $wgOut;
+
+               $wgOut->setPagetitle( wfMsgHtml( 'actioncomplete' ) );
+
+               if( $this->deleteKey=='logid' ) {
+                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'logdelete-success' ) ), false );
+                       $this->showLogItems();
+               } else if( $this->deleteKey=='oldid' || $this->deleteKey=='artimestamp' ) {
+                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
+                       $this->showRevs();
+               } else if( $this->deleteKey=='fileid' ) {
+                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
+                       $this->showImages();
+               } else if( $this->deleteKey=='oldimage' ) {
+                       $wgOut->addWikiText( Xml::element( 'span', array( 'class' => 'success' ), wfMsg( 'revdelete-success' ) ), false );
+                       $this->showImages();
+               }
+       }
+
+       /**
+        * Put together a rev_deleted bitfield from the submitted checkboxes
+        * @param WebRequest $request
+        * @return int
+        */
+       private function extractBitfield( $request ) {
+               $bitfield = 0;
+               foreach( $this->checks as $item ) {
+                       list( /* message */ , $name, $field ) = $item;
+                       if( $request->getCheck( $name ) ) {
+                               $bitfield |= $field;
+                       }
+               }
+               return $bitfield;
+       }
+
+       private function save( $bitfield, $reason, $title ) {
+               $dbw = wfGetDB( DB_MASTER );
+               // Don't allow simply locking the interface for no reason
+               if( $bitfield == Revision::DELETED_RESTRICTED ) {
+                       $bitfield = 0;
+               }
+               $deleter = new RevisionDeleter( $dbw );
+               // By this point, only one of the below should be set
+               if( isset($this->revisions) ) {
+                       return $deleter->setRevVisibility( $title, $this->revisions, $bitfield, $reason );
+               } else if( isset($this->archrevs) ) {
+                       return $deleter->setArchiveVisibility( $title, $this->archrevs, $bitfield, $reason );
+               } else if( isset($this->ofiles) ) {
+                       return $deleter->setOldImgVisibility( $title, $this->ofiles, $bitfield, $reason );
+               } else if( isset($this->afiles) ) {
+                       return $deleter->setArchFileVisibility( $title, $this->afiles, $bitfield, $reason );
+               } else if( isset($this->events) ) {
+                       return $deleter->setEventVisibility( $title, $this->events, $bitfield, $reason );
+               }
+       }
+}
+
+/**
+ * Implements the actions for Revision Deletion.
+ * @ingroup SpecialPage
+ */
+class RevisionDeleter {
+       function __construct( $db ) {
+               $this->dbw = $db;
+       }
+
+       /**
+        * @param $title, the page these events apply to
+        * @param array $items list of revision ID numbers
+        * @param int $bitfield new rev_deleted value
+        * @param string $comment Comment for log records
+        */
+       function setRevVisibility( $title, $items, $bitfield, $comment ) {
+               global $wgOut;
+
+               $userAllowedAll = $success = true;
+               $revIDs = array();
+               $revCount = 0;
+               // Run through and pull all our data in one query
+               foreach( $items as $revid ) {
+                       $where[] = intval($revid);
+               }
+               $whereClause = 'rev_id IN(' . implode(',',$where) . ')';
+               $result = $this->dbw->select( 'revision', '*',
+                       array( 'rev_page' => $title->getArticleID(),
+                               $whereClause ),
+                       __METHOD__ );
+               while( $row = $this->dbw->fetchObject( $result ) ) {
+                       $revObjs[$row->rev_id] = new Revision( $row );
+               }
+               // To work!
+               foreach( $items as $revid ) {
+                       if( !isset($revObjs[$revid]) || $revObjs[$revid]->isCurrent() ) {
+                               $success = false;
+                               continue; // Must exist
+                       } else if( !$revObjs[$revid]->userCan(Revision::DELETED_RESTRICTED) ) {
+                       $userAllowedAll=false;
+                               continue;
+                       }
+                       // For logging, maintain a count of revisions
+                       if( $revObjs[$revid]->mDeleted != $bitfield ) {
+                               $revCount++;
+                               $revIDs[]=$revid;
+
+                               $this->updateRevision( $revObjs[$revid], $bitfield );
+                               $this->updateRecentChangesEdits( $revObjs[$revid], $bitfield, false );
+                       }
+               }
+               // Clear caches...
+               // Don't log or touch if nothing changed
+               if( $revCount > 0 ) {
+                       $this->updateLog( $title, $revCount, $bitfield, $revObjs[$revid]->mDeleted,
+                               $comment, $title, 'oldid', $revIDs );
+                       $this->updatePage( $title );
+               }
+               // Where all revs allowed to be set?
+               if( !$userAllowedAll ) {
+                       //FIXME: still might be confusing???
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+
+               return $success;
+       }
+
+        /**
+        * @param $title, the page these events apply to
+        * @param array $items list of revision ID numbers
+        * @param int $bitfield new rev_deleted value
+        * @param string $comment Comment for log records
+        */
+       function setArchiveVisibility( $title, $items, $bitfield, $comment ) {
+               global $wgOut;
+
+               $userAllowedAll = $success = true;
+               $count = 0;
+               $Id_set = array();
+               // Run through and pull all our data in one query
+               foreach( $items as $timestamp ) {
+                       $where[] = $this->dbw->addQuotes( $timestamp );
+               }
+               $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')';
+               $result = $this->dbw->select( 'archive', '*',
+                       array( 'ar_namespace' => $title->getNamespace(),
+                               'ar_title' => $title->getDBKey(),
+                                       $whereClause ),
+                       __METHOD__ );
+               while( $row = $this->dbw->fetchObject( $result ) ) {
+                       $revObjs[$row->ar_timestamp] = new Revision( array(
+                       'page'       => $title->getArticleId(),
+                       'id'         => $row->ar_rev_id,
+                       'text'       => $row->ar_text_id,
+                       'comment'    => $row->ar_comment,
+                       'user'       => $row->ar_user,
+                       'user_text'  => $row->ar_user_text,
+                       'timestamp'  => $row->ar_timestamp,
+                       'minor_edit' => $row->ar_minor_edit,
+                       'text_id'    => $row->ar_text_id,
+                       'deleted'    => $row->ar_deleted,
+                       'len'        => $row->ar_len) );
+               }
+               // To work!
+               foreach( $items as $timestamp ) {
+                       // This will only select the first revision with this timestamp.
+                       // Since they are all selected/deleted at once, we can just check the
+                       // permissions of one. UPDATE is done via timestamp, so all revs are set.
+                       if( !is_object($revObjs[$timestamp]) ) {
+                               $success = false;
+                               continue; // Must exist
+                       } else if( !$revObjs[$timestamp]->userCan(Revision::DELETED_RESTRICTED) ) {
+                       $userAllowedAll=false;
+                               continue;
+                       }
+                       // Which revisions did we change anything about?
+                       if( $revObjs[$timestamp]->mDeleted != $bitfield ) {
+                          $Id_set[]=$timestamp;
+                          $count++;
+
+                          $this->updateArchive( $revObjs[$timestamp], $title, $bitfield );
+                       }
+               }
+               // For logging, maintain a count of revisions
+               if( $count > 0 ) {
+                       $this->updateLog( $title, $count, $bitfield, $revObjs[$timestamp]->mDeleted,
+                               $comment, $title, 'artimestamp', $Id_set );
+               }
+               // Where all revs allowed to be set?
+               if( !$userAllowedAll ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+
+               return $success;
+       }
+
+        /**
+        * @param $title, the page these events apply to
+        * @param array $items list of revision ID numbers
+        * @param int $bitfield new rev_deleted value
+        * @param string $comment Comment for log records
+        */
+       function setOldImgVisibility( $title, $items, $bitfield, $comment ) {
+               global $wgOut;
+
+               $userAllowedAll = $success = true;
+               $count = 0;
+               $set = array();
+               // Run through and pull all our data in one query
+               foreach( $items as $timestamp ) {
+                       $where[] = $this->dbw->addQuotes( $timestamp.'!'.$title->getDbKey() );
+               }
+               $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')';
+               $result = $this->dbw->select( 'oldimage', '*',
+                       array( 'oi_name' => $title->getDbKey(),
+                               $whereClause ),
+                       __METHOD__ );
+               while( $row = $this->dbw->fetchObject( $result ) ) {
+                       $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
+                       $filesObjs[$row->oi_archive_name]->user = $row->oi_user;
+                       $filesObjs[$row->oi_archive_name]->user_text = $row->oi_user_text;
+               }
+               // To work!
+               foreach( $items as $timestamp ) {
+                       $archivename = $timestamp.'!'.$title->getDbKey();
+                       if( !isset($filesObjs[$archivename]) ) {
+                               $success = false;
+                               continue; // Must exist
+                       } else if( !$filesObjs[$archivename]->userCan(File::DELETED_RESTRICTED) ) {
+                       $userAllowedAll=false;
+                               continue;
+                       }
+
+                       $transaction = true;
+                       // Which revisions did we change anything about?
+                       if( $filesObjs[$archivename]->deleted != $bitfield ) {
+                               $count++;
+
+                               $this->dbw->begin();
+                               $this->updateOldFiles( $filesObjs[$archivename], $bitfield );
+                               // If this image is currently hidden...
+                               if( $filesObjs[$archivename]->deleted & File::DELETED_FILE ) {
+                                       if( $bitfield & File::DELETED_FILE ) {
+                                               # Leave it alone if we are not changing this...
+                                               $set[]=$archivename;
+                                               $transaction = true;
+                                       } else {
+                                               # We are moving this out
+                                               $transaction = $this->makeOldImagePublic( $filesObjs[$archivename] );
+                                               $set[]=$transaction;
+                                       }
+                               // Is it just now becoming hidden?
+                               } else if( $bitfield & File::DELETED_FILE ) {
+                                       $transaction = $this->makeOldImagePrivate( $filesObjs[$archivename] );
+                                       $set[]=$transaction;
+                               } else {
+                                       $set[]=$timestamp;
+                               }
+                               // If our file operations fail, then revert back the db
+                               if( $transaction==false ) {
+                                       $this->dbw->rollback();
+                                       return false;
+                               }
+                               $this->dbw->commit();
+                       }
+               }
+
+               // Log if something was changed
+               if( $count > 0 ) {
+                       $this->updateLog( $title, $count, $bitfield, $filesObjs[$archivename]->deleted,
+                               $comment, $title, 'oldimage', $set );
+                       # Purge page/history
+                       $file = wfLocalFile( $title );
+                       $file->purgeCache();
+                       $file->purgeHistory();
+                       # Invalidate cache for all pages using this file
+                       $update = new HTMLCacheUpdate( $title, 'imagelinks' );
+                       $update->doUpdate();
+               }
+               // Where all revs allowed to be set?
+               if( !$userAllowedAll ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+
+               return $success;
+       }
+
+        /**
+        * @param $title, the page these events apply to
+        * @param array $items list of revision ID numbers
+        * @param int $bitfield new rev_deleted value
+        * @param string $comment Comment for log records
+        */
+       function setArchFileVisibility( $title, $items, $bitfield, $comment ) {
+               global $wgOut;
+
+               $userAllowedAll = $success = true;
+               $count = 0;
+               $Id_set = array();
+
+               // Run through and pull all our data in one query
+               foreach( $items as $id ) {
+                       $where[] = intval($id);
+               }
+               $whereClause = 'fa_id IN(' . implode(',',$where) . ')';
+               $result = $this->dbw->select( 'filearchive', '*',
+                       array( 'fa_name' => $title->getDbKey(),
+                               $whereClause ),
+                       __METHOD__ );
+               while( $row = $this->dbw->fetchObject( $result ) ) {
+                       $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row );
+               }
+               // To work!
+               foreach( $items as $fileid ) {
+                       if( !isset($filesObjs[$fileid]) ) {
+                               $success = false;
+                               continue; // Must exist
+                       } else if( !$filesObjs[$fileid]->userCan(File::DELETED_RESTRICTED) ) {
+                       $userAllowedAll=false;
+                               continue;
+                       }
+                       // Which revisions did we change anything about?
+                       if( $filesObjs[$fileid]->deleted != $bitfield ) {
+                          $Id_set[]=$fileid;
+                          $count++;
+
+                          $this->updateArchFiles( $filesObjs[$fileid], $bitfield );
+                       }
+               }
+               // Log if something was changed
+               if( $count > 0 ) {
+                       $this->updateLog( $title, $count, $bitfield, $comment,
+                               $filesObjs[$fileid]->deleted, $title, 'fileid', $Id_set );
+               }
+               // Where all revs allowed to be set?
+               if( !$userAllowedAll ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+
+               return $success;
+       }
+
+       /**
+        * @param $title, the log page these events apply to
+        * @param array $items list of log ID numbers
+        * @param int $bitfield new log_deleted value
+        * @param string $comment Comment for log records
+        */
+       function setEventVisibility( $title, $items, $bitfield, $comment ) {
+               global $wgOut;
+
+               $userAllowedAll = $success = true;
+               $count = 0;
+               $log_Ids = array();
+
+               // Run through and pull all our data in one query
+               foreach( $items as $logid ) {
+                       $where[] = intval($logid);
+               }
+               list($log,$logtype) = explode( '/',$title->getDBKey(), 2 );
+               $whereClause = "log_type ='$logtype' AND log_id IN(" . implode(',',$where) . ")";
+               $result = $this->dbw->select( 'logging', '*',
+                       array( $whereClause ),
+                       __METHOD__ );
+               while( $row = $this->dbw->fetchObject( $result ) ) {
+                       $logRows[$row->log_id] = $row;
+               }
+               // To work!
+               foreach( $items as $logid ) {
+                       if( !isset($logRows[$logid]) ) {
+                               $success = false;
+                               continue; // Must exist
+                       } else if( !LogEventsList::userCan($logRows[$logid], LogPage::DELETED_RESTRICTED)
+                                || $logRows[$logid]->log_type == 'suppress' ) {
+                       // Don't hide from oversight log!!!
+                       $userAllowedAll=false;
+                       continue;
+                       }
+                       // Which logs did we change anything about?
+                       if( $logRows[$logid]->log_deleted != $bitfield ) {
+                               $log_Ids[]=$logid;
+                               $count++;
+
+                               $this->updateLogs( $logRows[$logid], $bitfield );
+                               $this->updateRecentChangesLog( $logRows[$logid], $bitfield, true );
+                       }
+               }
+               // Don't log or touch if nothing changed
+               if( $count > 0 ) {
+                       $this->updateLog( $title, $count, $bitfield, $logRows[$logid]->log_deleted,
+                               $comment, $title, 'logid', $log_Ids );
+               }
+               // Were all revs allowed to be set?
+               if( !$userAllowedAll ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+                       return false;
+               }
+
+               return $success;
+       }
+
+       /**
+        * Moves an image to a safe private location
+        * Caller is responsible for clearing caches
+        * @param File $oimage
+        * @returns mixed, timestamp string on success, false on failure
+        */
+       function makeOldImagePrivate( $oimage ) {
+               $transaction = new FSTransaction();
+               if( !FileStore::lock() ) {
+                       wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
+                       return false;
+               }
+               $oldpath = $oimage->getArchivePath() . DIRECTORY_SEPARATOR . $oimage->archive_name;
+               // Dupe the file into the file store
+               if( file_exists( $oldpath ) ) {
+                       // Is our directory configured?
+                       if( $store = FileStore::get( 'deleted' ) ) {
+                               if( !$oimage->sha1 ) {
+                                       $oimage->upgradeRow(); // sha1 may be missing
+                               }
+                               $key = $oimage->sha1 . '.' . $oimage->getExtension();
+                               $transaction->add( $store->insert( $key, $oldpath, FileStore::DELETE_ORIGINAL ) );
+                       } else {
+                               $group = null;
+                               $key = null;
+                               $transaction = false; // Return an error and do nothing
+                       }
+               } else {
+                       wfDebug( __METHOD__." deleting already-missing '$oldpath'; moving on to database\n" );
+                       $group = null;
+                       $key = '';
+                       $transaction = new FSTransaction(); // empty
+               }
+
+               if( $transaction === false ) {
+                       // Fail to restore?
+                       wfDebug( __METHOD__.": import to file store failed, aborting\n" );
+                       throw new MWException( "Could not archive and delete file $oldpath" );
+                       return false;
+               }
+
+               wfDebug( __METHOD__.": set db items, applying file transactions\n" );
+               $transaction->commit();
+               FileStore::unlock();
+
+               $m = explode('!',$oimage->archive_name,2);
+               $timestamp = $m[0];
+
+               return $timestamp;
+       }
+
+       /**
+        * Moves an image from a safe private location
+        * Caller is responsible for clearing caches
+        * @param File $oimage
+        * @returns mixed, string timestamp on success, false on failure
+        */
+       function makeOldImagePublic( $oimage ) {
+               $transaction = new FSTransaction();
+               if( !FileStore::lock() ) {
+                       wfDebug( __METHOD__." could not acquire filestore lock\n" );
+                       return false;
+               }
+
+               $store = FileStore::get( 'deleted' );
+               if( !$store ) {
+                       wfDebug( __METHOD__.": skipping row with no file.\n" );
+                       return false;
+               }
+
+               $key = $oimage->sha1.'.'.$oimage->getExtension();
+               $destDir = $oimage->getArchivePath();
+               if( !is_dir( $destDir ) ) {
+                       wfMkdirParents( $destDir );
+               }
+               $destPath = $destDir . DIRECTORY_SEPARATOR . $oimage->archive_name;
+               // Check if any other stored revisions use this file;
+               // if so, we shouldn't remove the file from the hidden
+               // archives so they will still work. Check hidden files first.
+               $useCount = $this->dbw->selectField( 'oldimage', '1',
+                       array( 'oi_sha1' => $oimage->sha1,
+                               'oi_deleted & '.File::DELETED_FILE => File::DELETED_FILE ),
+                       __METHOD__, array( 'FOR UPDATE' ) );
+               // Check the rest of the deleted archives too.
+               // (these are the ones that don't show in the image history)
+               if( !$useCount ) {
+                       $useCount = $this->dbw->selectField( 'filearchive', '1',
+                               array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $key ),
+                               __METHOD__, array( 'FOR UPDATE' ) );
+               }
+
+               if( $useCount == 0 ) {
+                       wfDebug( __METHOD__.": nothing else using {$oimage->sha1}, will deleting after\n" );
+                       $flags = FileStore::DELETE_ORIGINAL;
+               } else {
+                       $flags = 0;
+               }
+               $transaction->add( $store->export( $key, $destPath, $flags ) );
+
+               wfDebug( __METHOD__.": set db items, applying file transactions\n" );
+               $transaction->commit();
+               FileStore::unlock();
+
+               $m = explode('!',$oimage->archive_name,2);
+               $timestamp = $m[0];
+
+               return $timestamp;
+       }
+
+       /**
+        * Update the revision's rev_deleted field
+        * @param Revision $rev
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateRevision( $rev, $bitfield ) {
+               $this->dbw->update( 'revision',
+                       array( 'rev_deleted' => $bitfield ),
+                       array( 'rev_id' => $rev->getId(),
+                               'rev_page' => $rev->getPage() ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the revision's rev_deleted field
+        * @param Revision $rev
+        * @param Title $title
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateArchive( $rev, $title, $bitfield ) {
+               $this->dbw->update( 'archive',
+                       array( 'ar_deleted' => $bitfield ),
+                       array( 'ar_namespace' => $title->getNamespace(),
+                               'ar_title'     => $title->getDBKey(),
+                               'ar_timestamp' => $this->dbw->timestamp( $rev->getTimestamp() ),
+                               'ar_rev_id' => $rev->getId() ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the images's oi_deleted field
+        * @param File $file
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateOldFiles( $file, $bitfield ) {
+               $this->dbw->update( 'oldimage',
+                       array( 'oi_deleted' => $bitfield ),
+                       array( 'oi_name' => $file->getName(),
+                               'oi_timestamp' => $this->dbw->timestamp( $file->getTimestamp() ) ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the images's fa_deleted field
+        * @param ArchivedFile $file
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateArchFiles( $file, $bitfield ) {
+               $this->dbw->update( 'filearchive',
+                       array( 'fa_deleted' => $bitfield ),
+                       array( 'fa_id' => $file->getId() ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the logging log_deleted field
+        * @param Row $row
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateLogs( $row, $bitfield ) {
+               $this->dbw->update( 'logging',
+                       array( 'log_deleted' => $bitfield ),
+                       array( 'log_id' => $row->log_id ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the revision's recentchanges record if fields have been hidden
+        * @param Revision $rev
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateRecentChangesEdits( $rev, $bitfield ) {
+               $this->dbw->update( 'recentchanges',
+                       array( 'rc_deleted' => $bitfield,
+                                  'rc_patrolled' => 1 ),
+                       array( 'rc_this_oldid' => $rev->getId(),
+                               'rc_timestamp' => $this->dbw->timestamp( $rev->getTimestamp() ) ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Update the revision's recentchanges record if fields have been hidden
+        * @param Row $row
+        * @param int $bitfield new rev_deleted bitfield value
+        */
+       function updateRecentChangesLog( $row, $bitfield ) {
+               $this->dbw->update( 'recentchanges',
+                       array( 'rc_deleted' => $bitfield,
+                                  'rc_patrolled' => 1 ),
+                       array( 'rc_logid' => $row->log_id,
+                               'rc_timestamp' => $row->log_timestamp ),
+                       __METHOD__ );
+       }
+
+       /**
+        * Touch the page's cache invalidation timestamp; this forces cached
+        * history views to refresh, so any newly hidden or shown fields will
+        * update properly.
+        * @param Title $title
+        */
+       function updatePage( $title ) {
+               $title->invalidateCache();
+               $title->purgeSquid();
+
+               // Extensions that require referencing previous revisions may need this
+               wfRunHooks( 'ArticleRevisionVisiblitySet', array( &$title ) );
+       }
+
+       /**
+        * Checks for a change in the bitfield for a certain option and updates the
+        * provided array accordingly.
+        *
+        * @param String $desc Description to add to the array if the option was
+        * enabled / disabled.
+        * @param int $field The bitmask describing the single option.
+        * @param int $diff The xor of the old and new bitfields.
+        * @param array $arr The array to update.
+        */
+       function checkItem ( $desc, $field, $diff, $new, &$arr ) {
+               if ( $diff & $field ) {
+                       $arr [ ( $new & $field ) ? 0 : 1 ][] = $desc;
+               }
+       }
+
+       /**
+        * Gets an array describing the changes made to the visibilit of the revision.
+        * If the resulting array is $arr, then $arr[0] will contain an array of strings
+        * describing the items that were hidden, $arr[2] will contain an array of strings
+        * describing the items that were unhidden, and $arr[3] will contain an array with
+        * a single string, which can be one of "applied restrictions to sysops",
+        * "removed restrictions from sysops", or null.
+        *
+        * @param int $n The new bitfield.
+        * @param int $o The old bitfield.
+        * @return An array as described above.
+        */
+       function getChanges ( $n, $o ) {
+               $diff = $n ^ $o;
+               $ret = array ( 0 => array(), 1 => array(), 2 => array() );
+
+               $this->checkItem ( wfMsgForContent ( 'revdelete-content' ),
+                               Revision::DELETED_TEXT, $diff, $n, $ret );
+               $this->checkItem ( wfMsgForContent ( 'revdelete-summary' ),
+                               Revision::DELETED_COMMENT, $diff, $n, $ret );
+               $this->checkItem ( wfMsgForContent ( 'revdelete-uname' ),
+                               Revision::DELETED_USER, $diff, $n, $ret );
+
+               // Restriction application to sysops
+               if ( $diff & Revision::DELETED_RESTRICTED ) {
+                       if ( $n & Revision::DELETED_RESTRICTED )
+                               $ret[2][] = wfMsgForContent ( 'revdelete-restricted' );
+                       else
+                               $ret[2][] = wfMsgForContent ( 'revdelete-unrestricted' );
+               }
+
+               return $ret;
+       }
+
+       /**
+        * Gets a log message to describe the given revision visibility change. This
+        * message will be of the form "[hid {content, edit summary, username}];
+        * [unhid {...}][applied restrictions to sysops] for $count revisions: $comment".
+        *
+        * @param int $count The number of effected revisions.
+        * @param int $nbitfield The new bitfield for the revision.
+        * @param int $obitfield The old bitfield for the revision.
+        * @param string $comment The comment associated with the change.
+        * @param bool $isForLog
+        */
+       function getLogMessage ( $count, $nbitfield, $obitfield, $comment, $isForLog = false ) {
+               global $wgContLang;
+
+               $s = '';
+               $changes = $this->getChanges( $nbitfield, $obitfield );
+
+               if ( count ( $changes[0] ) ) {
+                       $s .= wfMsgForContent ( 'revdelete-hid', implode ( ', ', $changes[0] ) );
+               }
+
+               if ( count ( $changes[1] ) ) {
+                       if ($s) $s .= '; ';
+
+                       $s .= wfMsgForContent ( 'revdelete-unhid', implode ( ', ', $changes[1] ) );
+               }
+
+               if ( count ( $changes[2] )) {
+                       if ($s)
+                               $s .= ' (' . $changes[2][0] . ')';
+                       else
+                               $s = $changes[2][0];
+               }
+
+               $msg = $isForLog ? 'logdelete-log-message' : 'revdelete-log-message';
+               $ret = wfMsgExt ( $msg, array( 'parsemag', 'content' ),
+                       $s, $wgContLang->formatNum( $count ) );
+
+               if ( $comment )
+                       $ret .= ": $comment";
+
+               return $ret;
+
+       }
+
+       /**
+        * Record a log entry on the action
+        * @param Title $title, page where item was removed from
+        * @param int $count the number of revisions altered for this page
+        * @param int $nbitfield the new _deleted value
+        * @param int $obitfield the old _deleted value
+        * @param string $comment
+        * @param Title $target, the relevant page
+        * @param string $param, URL param
+        * @param Array $items
+        */
+       function updateLog( $title, $count, $nbitfield, $obitfield, $comment, $target, $param, $items = array() ) {
+               // Put things hidden from sysops in the oversight log
+               $logtype = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ? 'suppress' : 'delete';
+               $log = new LogPage( $logtype );
+
+               $reason = $this->getLogMessage ( $count, $nbitfield, $obitfield, $comment, $param == 'logid' );
+
+               if( $param == 'logid' ) {
+                       $params = array( implode( ',', $items) );
+                       $log->addEntry( 'event', $title, $reason, $params );
+               } else {
+                       // Add params for effected page and ids
+                       $params = array( $param, implode( ',', $items) );
+                       $log->addEntry( 'revision', $title, $reason, $params );
+               }
+       }
+}
diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php
new file mode 100644 (file)
index 0000000..0a483af
--- /dev/null
@@ -0,0 +1,651 @@
+<?php
+# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * Run text & title search and display the output
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Entry point
+ *
+ * @param $par String: (default '')
+ */
+function wfSpecialSearch( $par = '' ) {
+       global $wgRequest, $wgUser;
+
+       $search = str_replace( "\n", " ", $wgRequest->getText( 'search', $par ) );
+       $searchPage = new SpecialSearch( $wgRequest, $wgUser );
+       if( $wgRequest->getVal( 'fulltext' ) 
+               || !is_null( $wgRequest->getVal( 'offset' )) 
+               || !is_null( $wgRequest->getVal( 'searchx' ))) {
+               $searchPage->showResults( $search, 'search' );
+       } else {
+               $searchPage->goResult( $search );
+       }
+}
+
+/**
+ * implements Special:Search - Run text & title search and display the output
+ * @ingroup SpecialPage
+ */
+class SpecialSearch {
+
+       /**
+        * Set up basic search parameters from the request and user settings.
+        * Typically you'll pass $wgRequest and $wgUser.
+        *
+        * @param WebRequest $request
+        * @param User $user
+        * @public
+        */
+       function SpecialSearch( &$request, &$user ) {
+               list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' );
+
+               $this->namespaces = $this->powerSearch( $request );
+               if( empty( $this->namespaces ) ) {
+                       $this->namespaces = SearchEngine::userNamespaces( $user );
+               }
+
+               $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false;
+       }
+
+       /**
+        * If an exact title match can be found, jump straight ahead to it.
+        * @param string $term
+        * @public
+        */
+       function goResult( $term ) {
+               global $wgOut;
+               global $wgGoToEdit;
+
+               $this->setupPage( $term );
+
+               # Try to go to page as entered.
+               $t = Title::newFromText( $term );
+
+               # If the string cannot be used to create a title
+               if( is_null( $t ) ){
+                       return $this->showResults( $term );
+               }
+
+               # If there's an exact or very near match, jump right there.
+               $t = SearchEngine::getNearMatch( $term );
+               if( !is_null( $t ) ) {
+                       $wgOut->redirect( $t->getFullURL() );
+                       return;
+               }
+
+               # No match, generate an edit URL
+               $t = Title::newFromText( $term );
+               if( ! is_null( $t ) ) {
+                       wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) );
+                       # If the feature is enabled, go straight to the edit page
+                       if ( $wgGoToEdit ) {
+                               $wgOut->redirect( $t->getFullURL( 'action=edit' ) );
+                               return;
+                       }
+               }
+
+               $wgOut->wrapWikiMsg( "==$1==\n", 'notitlematches' );
+               if( $t->quickUserCan( 'create' ) && $t->quickUserCan( 'edit' ) ) {
+                       $wgOut->addWikiMsg( 'noexactmatch', wfEscapeWikiText( $term ) );
+               } else {
+                       $wgOut->addWikiMsg( 'noexactmatch-nocreate', wfEscapeWikiText( $term ) );
+               }
+
+               return $this->showResults( $term );
+       }
+
+       /**
+        * @param string $term
+        * @public
+        */
+       function showResults( $term ) {
+               $fname = 'SpecialSearch::showResults';
+               wfProfileIn( $fname );
+               global $wgOut, $wgUser;
+               $sk = $wgUser->getSkin();
+
+               $this->setupPage( $term );
+
+               $wgOut->addWikiMsg( 'searchresulttext' );
+
+               if( '' === trim( $term ) ) {
+                       // Empty query -- straight view of search form
+                       $wgOut->setSubtitle( '' );
+                       $wgOut->addHTML( $this->powerSearchBox( $term ) );
+                       $wgOut->addHTML( $this->powerSearchFocus() );
+                       wfProfileOut( $fname );
+                       return;
+               }
+
+               global $wgDisableTextSearch;
+               if ( $wgDisableTextSearch ) {
+                       global $wgForwardSearchUrl;
+                       if( $wgForwardSearchUrl ) {
+                               $url = str_replace( '$1', urlencode( $term ), $wgForwardSearchUrl );
+                               $wgOut->redirect( $url );
+                               return;
+                       }
+                       global $wgInputEncoding;
+                       $wgOut->addHTML(
+                               Xml::openElement( 'fieldset' ) .
+                               Xml::element( 'legend', null, wfMsg( 'search-external' ) ) .
+                               Xml::element( 'p', array( 'class' => 'mw-searchdisabled' ), wfMsg( 'searchdisabled' ) ) .
+                               wfMsg( 'googlesearch',
+                                       htmlspecialchars( $term ),
+                                       htmlspecialchars( $wgInputEncoding ),
+                                       htmlspecialchars( wfMsg( 'searchbutton' ) )
+                               ) .
+                               Xml::closeElement( 'fieldset' )
+                       );
+                       wfProfileOut( $fname );
+                       return;
+               }
+
+               $wgOut->addHTML( $this->shortDialog( $term ) );
+
+               $search = SearchEngine::create();
+               $search->setLimitOffset( $this->limit, $this->offset );
+               $search->setNamespaces( $this->namespaces );
+               $search->showRedirects = $this->searchRedirects;
+               $rewritten = $search->replacePrefixes($term);
+
+               $titleMatches = $search->searchTitle( $rewritten );
+
+               // Sometimes the search engine knows there are too many hits
+               if ($titleMatches instanceof SearchResultTooMany) {
+                       $wgOut->addWikiText( '==' . wfMsg( 'toomanymatches' ) . "==\n" );
+                       $wgOut->addHTML( $this->powerSearchBox( $term ) );
+                       $wgOut->addHTML( $this->powerSearchFocus() );
+                       wfProfileOut( $fname );
+                       return;
+               }
+               
+               $textMatches = $search->searchText( $rewritten );
+
+               // did you mean... suggestions
+               if($textMatches && $textMatches->hasSuggestion()){
+                       $st = SpecialPage::getTitleFor( 'Search' );                     
+                       $stParams = wfArrayToCGI( array( 
+                                       'search'        => $textMatches->getSuggestionQuery(), 
+                                       'fulltext'      => wfMsg('search')),
+                                       $this->powerSearchOptions());
+                                       
+                       $suggestLink = '<a href="'.$st->escapeLocalURL($stParams).'">'.
+                                       $textMatches->getSuggestionSnippet().'</a>';
+                                       
+                       $wgOut->addHTML('<div class="searchdidyoumean">'.wfMsg('search-suggest',$suggestLink).'</div>');
+               }
+
+               // show number of results
+               $num = ( $titleMatches ? $titleMatches->numRows() : 0 )
+                       + ( $textMatches ? $textMatches->numRows() : 0);
+               $totalNum = 0;
+               if($titleMatches && !is_null($titleMatches->getTotalHits()))
+                       $totalNum += $titleMatches->getTotalHits();
+               if($textMatches && !is_null($textMatches->getTotalHits()))
+                       $totalNum += $textMatches->getTotalHits();
+               if ( $num > 0 ) {
+                       if ( $totalNum > 0 ){
+                               $top = wfMsgExt('showingresultstotal', array( 'parseinline' ), 
+                                       $this->offset+1, $this->offset+$num, $totalNum );
+                       } elseif ( $num >= $this->limit ) {
+                               $top = wfShowingResults( $this->offset, $this->limit );
+                       } else {
+                               $top = wfShowingResultsNum( $this->offset, $this->limit, $num );
+                       }
+                       $wgOut->addHTML( "<p class='mw-search-numberresults'>{$top}</p>\n" );
+               }
+
+               // prev/next links
+               if( $num || $this->offset ) {
+                       $prevnext = wfViewPrevNext( $this->offset, $this->limit,
+                               SpecialPage::getTitleFor( 'Search' ),
+                               wfArrayToCGI(
+                                       $this->powerSearchOptions(),
+                                       array( 'search' => $term ) ),
+                                       ($num < $this->limit) );
+                       $wgOut->addHTML( "<p class='mw-search-pager-top'>{$prevnext}</p>\n" );
+                       wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) );
+               } else {
+                       wfRunHooks( 'SpecialSearchNoResults', array( $term ) );
+               }
+
+               if( $titleMatches ) {
+                       if( $titleMatches->numRows() ) {
+                               $wgOut->wrapWikiMsg( "==$1==\n", 'titlematches' );
+                               $wgOut->addHTML( $this->showMatches( $titleMatches ) );
+                       }
+                       $titleMatches->free();
+               }
+
+               if( $textMatches ) {
+                       // output appropriate heading
+                       if( $textMatches->numRows() ) {
+                               if($titleMatches)
+                                       $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' );
+                               else // if no title matches the heading is redundant
+                                       $wgOut->addHTML("<hr/>");                                                               
+                       } elseif( $num == 0 ) {
+                               # Don't show the 'no text matches' if we received title matches
+                               $wgOut->wrapWikiMsg( "==$1==\n", 'notextmatches' );
+                       }
+                       // show interwiki results if any
+                       if( $textMatches->hasInterwikiResults() )
+                               $wgOut->addHtml( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ));
+                       // show results
+                       if( $textMatches->numRows() )
+                               $wgOut->addHTML( $this->showMatches( $textMatches ) );
+
+                       $textMatches->free();
+               }
+
+               if ( $num == 0 ) {
+                       $wgOut->addWikiMsg( 'nonefound' );
+               }
+               if( $num || $this->offset ) {
+                       $wgOut->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" );
+               }
+               $wgOut->addHTML( $this->powerSearchBox( $term ) );
+               wfProfileOut( $fname );
+       }
+
+       #------------------------------------------------------------------
+       # Private methods below this line
+       
+       /**
+        *
+        */
+       function setupPage( $term ) {
+               global $wgOut;
+               if( !empty( $term ) )
+                       $wgOut->setPageTitle( wfMsg( 'searchresults' ) );                       
+               $subtitlemsg = ( Title::newFromText( $term ) ? 'searchsubtitle' : 'searchsubtitleinvalid' );
+               $wgOut->setSubtitle( $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ) );
+               $wgOut->setArticleRelated( false );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+       }
+
+       /**
+        * Extract "power search" namespace settings from the request object,
+        * returning a list of index numbers to search.
+        *
+        * @param WebRequest $request
+        * @return array
+        * @private
+        */
+       function powerSearch( &$request ) {
+               $arr = array();
+               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
+                       if( $request->getCheck( 'ns' . $ns ) ) {
+                               $arr[] = $ns;
+                       }
+               }
+               return $arr;
+       }
+
+       /**
+        * Reconstruct the 'power search' options for links
+        * @return array
+        * @private
+        */
+       function powerSearchOptions() {
+               $opt = array();
+               foreach( $this->namespaces as $n ) {
+                       $opt['ns' . $n] = 1;
+               }
+               $opt['redirs'] = $this->searchRedirects ? 1 : 0;
+               return $opt;
+       }
+
+       /**
+        * Show whole set of results 
+        * 
+        * @param SearchResultSet $matches
+        */
+       function showMatches( &$matches ) {
+               $fname = 'SpecialSearch::showMatches';
+               wfProfileIn( $fname );
+
+               global $wgContLang;
+               $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
+
+               $out = "";
+               
+               $infoLine = $matches->getInfo();
+               if( !is_null($infoLine) )
+                       $out .= "\n<!-- {$infoLine} -->\n";
+                       
+               
+               $off = $this->offset + 1;
+               $out .= "<ul class='mw-search-results'>\n";
+
+               while( $result = $matches->next() ) {
+                       $out .= $this->showHit( $result, $terms );
+               }
+               $out .= "</ul>\n";
+
+               // convert the whole thing to desired language variant
+               global $wgContLang;
+               $out = $wgContLang->convert( $out );
+               wfProfileOut( $fname );
+               return $out;
+       }
+
+       /**
+        * Format a single hit result
+        * @param SearchResult $result
+        * @param array $terms terms to highlight
+        */
+       function showHit( $result, $terms ) {
+               $fname = 'SpecialSearch::showHit';
+               wfProfileIn( $fname );
+               global $wgUser, $wgContLang, $wgLang;
+               
+               if( $result->isBrokenTitle() ) {
+                       wfProfileOut( $fname );
+                       return "<!-- Broken link in search result -->\n";
+               }
+               
+               $t = $result->getTitle();
+               $sk = $wgUser->getSkin();
+
+               $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms));
+
+               //If page content is not readable, just return the title.
+               //This is not quite safe, but better than showing excerpts from non-readable pages
+               //Note that hiding the entry entirely would screw up paging.
+               if (!$t->userCanRead()) {
+                       wfProfileOut( $fname );
+                       return "<li>{$link}</li>\n";
+               }
+
+               // If the page doesn't *exist*... our search index is out of date.
+               // The least confusing at this point is to drop the result.
+               // You may get less results, but... oh well. :P
+               if( $result->isMissingRevision() ) {
+                       wfProfileOut( $fname );
+                       return "<!-- missing page " .
+                               htmlspecialchars( $t->getPrefixedText() ) . "-->\n";
+               }
+
+               // format redirects / relevant sections
+               $redirectTitle = $result->getRedirectTitle();
+               $redirectText = $result->getRedirectSnippet($terms);
+               $sectionTitle = $result->getSectionTitle();
+               $sectionText = $result->getSectionSnippet($terms);
+               $redirect = '';
+               if( !is_null($redirectTitle) )
+                       $redirect = "<span class='searchalttitle'>"
+                               .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText))
+                               ."</span>";
+               $section = '';
+               if( !is_null($sectionTitle) )
+                       $section = "<span class='searchalttitle'>" 
+                               .wfMsg('search-section', $sk->makeKnownLinkObj( $sectionTitle, $sectionText))
+                               ."</span>";
+
+               // format text extract
+               $extract = "<div class='searchresult'>".$result->getTextSnippet($terms)."</div>";
+               
+               // format score
+               if( is_null( $result->getScore() ) ) {
+                       // Search engine doesn't report scoring info
+                       $score = '';
+               } else {
+                       $percent = sprintf( '%2.1f', $result->getScore() * 100 );
+                       $score = wfMsg( 'search-result-score', $wgLang->formatNum( $percent ) )
+                               . ' - ';
+               }
+
+               // format description
+               $byteSize = $result->getByteSize();
+               $wordCount = $result->getWordCount();
+               $timestamp = $result->getTimestamp();
+               $size = wfMsgExt( 'search-result-size', array( 'parsemag', 'escape' ),
+                       $sk->formatSize( $byteSize ),
+                       $wordCount );
+               $date = $wgLang->timeanddate( $timestamp );
+
+               // link to related articles if supported
+               $related = '';
+               if( $result->hasRelated() ){
+                       $st = SpecialPage::getTitleFor( 'Search' );
+                       $stParams = wfArrayToCGI( $this->powerSearchOptions(),
+                               array('search'    => wfMsgForContent('searchrelated').':'.$t->getPrefixedText(),
+                                     'fulltext'  => wfMsg('search') ));
+                       
+                       $related = ' -- <a href="'.$st->escapeLocalURL($stParams).'">'. 
+                               wfMsg('search-relatedarticle').'</a>';
+               }
+                               
+               // Include a thumbnail for media files...
+               if( $t->getNamespace() == NS_IMAGE ) {
+                       $img = wfFindFile( $t );
+                       if( $img ) {
+                               $thumb = $img->getThumbnail( 120, 120 );
+                               if( $thumb ) {
+                                       $desc = $img->getShortDesc();
+                                       wfProfileOut( $fname );
+                                       // Ugly table. :D
+                                       // Float doesn't seem to interact well with the bullets.
+                                       // Table messes up vertical alignment of the bullet, but I'm
+                                       // not sure what more I can do about that. :(
+                                       return "<li>" .
+                                               '<table class="searchResultImage">' .
+                                               '<tr>' .
+                                               '<td width="120" align="center">' .
+                                               $thumb->toHtml( array( 'desc-link' => true ) ) .
+                                               '</td>' .
+                                               '<td valign="top">' .
+                                               $link .
+                                               $extract .
+                                               "<div class='mw-search-result-data'>{$score}{$desc} - {$date}{$related}</div>" .
+                                               '</td>' .
+                                               '</tr>' .
+                                               '</table>' .
+                                               "</li>\n";
+                               }
+                       }
+               }
+
+               wfProfileOut( $fname );
+               return "<li>{$link} {$redirect} {$section} {$extract}\n" .
+                       "<div class='mw-search-result-data'>{$score}{$size} - {$date}{$related}</div>" .
+                       "</li>\n";
+
+       }
+
+       /**
+        * Show results from other wikis
+        * 
+        * @param SearchResultSet $matches
+        */
+       function showInterwiki( &$matches, $query ) {
+               $fname = 'SpecialSearch::showInterwiki';
+               wfProfileIn( $fname );
+
+               global $wgContLang;
+               $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
+
+               $out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>".wfMsg('search-interwiki-caption')."</div>\n";             
+               $off = $this->offset + 1;
+               $out .= "<ul start='{$off}' class='mw-search-iwresults'>\n";
+
+               // work out custom project captions
+               $customCaptions = array();
+               $customLines = explode("\n",wfMsg('search-interwiki-custom')); // format per line <iwprefix>:<caption>
+               foreach($customLines as $line){
+                       $parts = explode(":",$line,2);
+                       if(count($parts) == 2) // validate line
+                               $customCaptions[$parts[0]] = $parts[1]; 
+               }
+               
+               
+               $prev = null;
+               while( $result = $matches->next() ) {
+                       $out .= $this->showInterwikiHit( $result, $prev, $terms, $query, $customCaptions );
+                       $prev = $result->getInterwikiPrefix();
+               }
+               // FIXME: should support paging in a non-confusing way (not sure how though, maybe via ajax)..
+               $out .= "</ul></div>\n";
+
+               // convert the whole thing to desired language variant
+               global $wgContLang;
+               $out = $wgContLang->convert( $out );
+               wfProfileOut( $fname );
+               return $out;
+       }
+       
+       /**
+        * Show single interwiki link
+        *
+        * @param SearchResult $result
+        * @param string $lastInterwiki
+        * @param array $terms
+        * @param string $query 
+        * @param array $customCaptions iw prefix -> caption
+        */
+       function showInterwikiHit( $result, $lastInterwiki, $terms, $query, $customCaptions){
+               $fname = 'SpecialSearch::showInterwikiHit';
+               wfProfileIn( $fname );
+               global $wgUser, $wgContLang, $wgLang;
+               
+               if( $result->isBrokenTitle() ) {
+                       wfProfileOut( $fname );
+                       return "<!-- Broken link in search result -->\n";
+               }
+               
+               $t = $result->getTitle();
+               $sk = $wgUser->getSkin();
+               
+               $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms));
+                               
+               // format redirect if any
+               $redirectTitle = $result->getRedirectTitle();
+               $redirectText = $result->getRedirectSnippet($terms);
+               $redirect = '';
+               if( !is_null($redirectTitle) )
+                       $redirect = "<span class='searchalttitle'>"
+                               .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText))
+                               ."</span>";
+
+               $out = "";
+               // display project name 
+               if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()){
+                       if( key_exists($t->getInterwiki(),$customCaptions) )
+                               // captions from 'search-interwiki-custom'
+                               $caption = $customCaptions[$t->getInterwiki()];
+                       else{
+                               // default is to show the hostname of the other wiki which might suck 
+                               // if there are many wikis on one hostname
+                               $parsed = parse_url($t->getFullURL());
+                               $caption = wfMsg('search-interwiki-default', $parsed['host']); 
+                       }               
+                       // "more results" link (special page stuff could be localized, but we might not know target lang)
+                       $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search");                        
+                       $searchLink = $sk->makeKnownLinkObj( $searchTitle, wfMsg('search-interwiki-more'),
+                               wfArrayToCGI(array('search' => $query, 'fulltext' => 'Search'))); 
+                       $out .= "</ul><div class='mw-search-interwiki-project'><span class='mw-search-interwiki-more'>{$searchLink}</span>{$caption}</div>\n<ul>";
+               }
+
+               $out .= "<li>{$link} {$redirect}</li>\n"; 
+               wfProfileOut( $fname );
+               return $out;
+       }
+       
+
+       /**
+        * Generates the power search box at bottom of [[Special:Search]]
+        * @param $term string: search term
+        * @return $out string: HTML form
+        */
+       function powerSearchBox( $term ) {
+               global $wgScript;
+
+               $namespaces = '';
+               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
+                       $name = str_replace( '_', ' ', $name );
+                       if( '' == $name ) {
+                               $name = wfMsg( 'blanknamespace' );
+                       }
+                       $namespaces .= Xml::openElement( 'span', array( 'style' => 'white-space: nowrap' ) ) .
+                                       Xml::checkLabel( $name, "ns{$ns}", "mw-search-ns{$ns}", in_array( $ns, $this->namespaces ) ) .
+                                       Xml::closeElement( 'span' ) . "\n";
+               }
+
+               $redirect = Xml::check( 'redirs', $this->searchRedirects, array( 'value' => '1', 'id' => 'redirs' ) );
+               $redirectLabel = Xml::label( wfMsg( 'powersearch-redir' ), 'redirs' );
+               $searchField = Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'powerSearchText' ) );
+               $searchButton = Xml::submitButton( wfMsg( 'powersearch' ), array( 'name' => 'fulltext' ) ) . "\n";
+
+               $out = Xml::openElement( 'form', array( 'id' => 'powersearch', 'method' => 'get', 'action' => $wgScript ) ) .
+                       Xml::fieldset( wfMsg( 'powersearch-legend' ),
+                               Xml::hidden( 'title', 'Special:Search' ) .
+                               "<p>" .
+                               wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) .
+                               "<br />" .
+                               $namespaces .
+                               "</p>" .
+                               "<p>" .
+                               $redirect . " " . $redirectLabel .
+                               "</p>" .
+                               wfMsgExt( 'powersearch-field', array( 'parseinline' ) ) .
+                               "&nbsp;" .
+                               $searchField .
+                               "&nbsp;" .
+                               $searchButton ) .
+                       "</form>";
+
+               return $out;
+       }
+
+       function powerSearchFocus() {
+               global $wgJsMimeType;
+               return "<script type=\"$wgJsMimeType\">" .
+                       "hookEvent(\"load\", function(){" .
+                               "document.getElementById('powerSearchText').focus();" .
+                       "});" .
+                       "</script>";
+       }
+
+       function shortDialog($term) {
+               global $wgScript;
+
+               $out  = Xml::openElement( 'form', array(
+                       'id' => 'search',
+                       'method' => 'get',
+                       'action' => $wgScript
+               ));
+               $out .= Xml::hidden( 'title', 'Special:Search' );
+               $out .= Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'searchText' ) ) . ' ';
+               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
+                       if( in_array( $ns, $this->namespaces ) ) {
+                               $out .= Xml::hidden( "ns{$ns}", '1' );
+                       }
+               }
+               $out .= Xml::submitButton( wfMsg( 'searchbutton' ), array( 'name' => 'fulltext' ) );
+               $out .= Xml::closeElement( 'form' );
+
+               return $out;
+       }
+}
diff --git a/includes/specials/SpecialShortpages.php b/includes/specials/SpecialShortpages.php
new file mode 100644 (file)
index 0000000..2e7d24a
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * SpecialShortpages extends QueryPage. It is used to return the shortest
+ * pages in the database.
+ * @ingroup SpecialPage
+ */
+class ShortPagesPage extends QueryPage {
+
+       function getName() {
+               return 'Shortpages';
+       }
+
+       /**
+        * This query is indexed as of 1.5
+        */
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() {
+               return false;
+       }
+
+       function getSQL() {
+               global $wgContentNamespaces;
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $page = $dbr->tableName( 'page' );
+               $name = $dbr->addQuotes( $this->getName() );
+
+               $forceindex = $dbr->useIndexClause("page_len");
+
+               if ($wgContentNamespaces)
+                       $nsclause = "page_namespace IN (" . $dbr->makeList($wgContentNamespaces) . ")";
+               else
+                       $nsclause = "page_namespace = " . NS_MAIN;
+
+               return
+                       "SELECT $name as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               page_len AS value
+                       FROM $page $forceindex
+                       WHERE $nsclause AND page_is_redirect=0";
+       }
+
+       function preprocessResults( $db, $res ) {
+               # There's no point doing a batch check if we aren't caching results;
+               # the page must exist for it to have been pulled out of the table
+               if( $this->isCached() ) {
+                       $batch = new LinkBatch();
+                       while( $row = $db->fetchObject( $res ) )
+                               $batch->add( $row->namespace, $row->title );
+                       $batch->execute();
+                       if( $db->numRows( $res ) > 0 )
+                               $db->dataSeek( $res, 0 );
+               }
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+               $dm = $wgContLang->getDirMark();
+
+               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               if ( !$title ) {
+                       return '<!-- Invalid title ' .  htmlspecialchars( "{$result->namespace}:{$result->title}" ). '-->';
+               }
+               $hlink = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
+               $plink = $this->isCached()
+                                       ? $skin->makeLinkObj( $title )
+                                       : $skin->makeKnownLinkObj( $title );
+               $size = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ), $wgLang->formatNum( htmlspecialchars( $result->value ) ) );
+
+               return $title->exists()
+                               ? "({$hlink}) {$dm}{$plink} {$dm}[{$size}]"
+                               : "<s>({$hlink}) {$dm}{$plink} {$dm}[{$size}]</s>";
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialShortpages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $spp = new ShortPagesPage();
+
+       return $spp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialSpecialpages.php b/includes/specials/SpecialSpecialpages.php
new file mode 100644 (file)
index 0000000..ca91ad5
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ */
+function wfSpecialSpecialpages() {
+       global $wgOut, $wgUser, $wgMessageCache, $wgSortSpecialPages;
+
+       $wgMessageCache->loadAllMessages();
+
+       $wgOut->setRobotpolicy( 'noindex,nofollow' );  # Is this really needed?
+       $sk = $wgUser->getSkin();
+
+       $pages = SpecialPage::getUsablePages();
+
+       if( count( $pages ) == 0 ) {
+               # Yeah, that was pointless. Thanks for coming.
+               return;
+       }
+
+       /** Put them into a sortable array */
+       $groups = array();
+       foreach ( $pages as $page ) {
+               if ( $page->isListed() ) {
+                       $group = SpecialPage::getGroup( $page );
+                       if( !isset($groups[$group]) ) {
+                               $groups[$group] = array();
+                       }
+                       $groups[$group][$page->getDescription()] = array( $page->getTitle(), $page->isRestricted() );
+               }
+       }
+
+       /** Sort */
+       if ( $wgSortSpecialPages ) {
+               foreach( $groups as $group => $sortedPages ) {
+                       ksort( $groups[$group] );
+               }
+       }
+
+       /** Always move "other" to end */
+       if( array_key_exists('other',$groups) ) {
+               $other = $groups['other'];
+               unset( $groups['other'] );
+               $groups['other'] = $other;
+       }
+
+       /** Now output the HTML */
+       foreach ( $groups as $group => $sortedPages ) {
+               $middle = ceil( count($sortedPages)/2 );
+               $total = count($sortedPages);
+               $count = 0;
+
+               $wgOut->addHTML( "<h4 class='mw-specialpagesgroup'>".wfMsgHtml("specialpages-group-$group")."</h4>\n" );
+               $wgOut->addHTML( "<table style='width: 100%;' class='mw-specialpages-table'><tr>" );
+               $wgOut->addHTML( "<td width='30%' valign='top'><ul>\n" );
+               foreach( $sortedPages as $desc => $specialpage ) {
+                       list( $title, $restricted ) = $specialpage;
+                       $link = $sk->makeKnownLinkObj( $title , htmlspecialchars( $desc ) );
+                       if( $restricted ) {
+                               $wgOut->addHTML( "<li class='mw-specialpages-page mw-specialpagerestricted'>{$link}</li>\n" );
+                       } else {
+                               $wgOut->addHTML( "<li>{$link}</li>\n" );
+                       }
+
+                       # Split up the larger groups
+                       $count++;
+                       if( $total > 3 && $count == $middle ) {
+                               $wgOut->addHTML( "</ul></td><td width='10%'></td><td width='30%' valign='top'><ul>" );
+                       }
+               }
+               $wgOut->addHTML( "</ul></td><td width='30%' valign='top'></td></tr></table>\n" );
+       }
+       $wgOut->addHTML(
+               Xml::openElement('div', array( 'class' => 'mw-specialpages-notes' )).
+               wfMsgWikiHtml('specialpages-note').
+               Xml::closeElement('div')
+       );
+}
diff --git a/includes/specials/SpecialStatistics.php b/includes/specials/SpecialStatistics.php
new file mode 100644 (file)
index 0000000..570a21c
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Special page lists various statistics, including the contents of
+ * `site_stats`, plus page view details if enabled
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Show the special page
+ *
+ * @param mixed $par (not used)
+ */
+function wfSpecialStatistics( $par = '' ) {
+       global $wgOut, $wgLang, $wgRequest;
+       $dbr = wfGetDB( DB_SLAVE );
+
+       $views = SiteStats::views();
+       $edits = SiteStats::edits();
+       $good = SiteStats::articles();
+       $images = SiteStats::images();
+       $total = SiteStats::pages();
+       $users = SiteStats::users();
+       $admins = SiteStats::admins();
+       $numJobs = SiteStats::jobs();
+
+       if( $wgRequest->getVal( 'action' ) == 'raw' ) {
+               $wgOut->disable();
+               header( 'Pragma: nocache' );
+               echo "total=$total;good=$good;views=$views;edits=$edits;users=$users;admins=$admins;images=$images;jobs=$numJobs\n";
+               return;
+       } else {
+               $text = "__NOTOC__\n";
+               $text .= '==' . wfMsgNoTrans( 'sitestats' ) . "==\n";
+               $text .= wfMsgExt( 'sitestatstext', array( 'parsemag' ),
+                       $wgLang->formatNum( $total ),
+                       $wgLang->formatNum( $good ),
+                       $wgLang->formatNum( $views ),
+                       $wgLang->formatNum( $edits ),
+                       $wgLang->formatNum( sprintf( '%.2f', $total ? $edits / $total : 0 ) ),
+                       $wgLang->formatNum( sprintf( '%.2f', $edits ? $views / $edits : 0 ) ),
+                       $wgLang->formatNum( $numJobs ),
+                       $wgLang->formatNum( $images )
+               )."\n";
+
+               $text .= "==" . wfMsgNoTrans( 'userstats' ) . "==\n";
+               $text .= wfMsgExt( 'userstatstext', array ( 'parsemag' ),
+                       $wgLang->formatNum( $users ),
+                       $wgLang->formatNum( $admins ),
+                       '[[' . wfMsgForContent( 'grouppage-sysop' ) . ']]', # TODO somehow remove, kept for backwards compatibility
+                       $wgLang->formatNum( @sprintf( '%.2f', $admins / $users * 100 ) ),
+                       User::makeGroupLinkWiki( 'sysop' )
+               )."\n";
+
+               global $wgDisableCounters, $wgMiserMode, $wgUser, $wgLang, $wgContLang;
+               if( !$wgDisableCounters && !$wgMiserMode ) {
+                       $res = $dbr->select(
+                               'page',
+                               array(
+                                       'page_namespace',
+                                       'page_title',
+                                       'page_counter',
+                               ),
+                               array(
+                                       'page_is_redirect' => 0,
+                                       'page_counter > 0',
+                               ),
+                               __METHOD__,
+                               array(
+                                       'ORDER BY' => 'page_counter DESC',
+                                       'LIMIT' => 10,
+                               )
+                       );
+                       if( $res->numRows() > 0 ) {
+                               $text .= "==" . wfMsgNoTrans( 'statistics-mostpopular' ) . "==\n";
+                               while( $row = $res->fetchObject() ) {
+                                       $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
+                                       if( $title instanceof Title )
+                                               $text .= '* [[:' . $title->getPrefixedText() . ']] (' . $wgLang->formatNum( $row->page_counter ) . ")\n";
+                               }
+                               $res->free();
+                       }
+               }
+
+               $footer = wfMsgNoTrans( 'statistics-footer' );
+               if( !wfEmptyMsg( 'statistics-footer', $footer ) && $footer != '' )
+                       $text .= "\n" . $footer;
+
+               $wgOut->addWikiText( $text );
+       }
+}
diff --git a/includes/specials/SpecialUncategorizedcategories.php b/includes/specials/SpecialUncategorizedcategories.php
new file mode 100644 (file)
index 0000000..f23e89c
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * implements Special:Uncategorizedcategories
+ * @ingroup SpecialPage
+ */
+class UncategorizedCategoriesPage extends UncategorizedPagesPage {
+       function UncategorizedCategoriesPage() {
+               $this->requestedNamespace = NS_CATEGORY;
+       }
+
+       function getName() {
+               return "Uncategorizedcategories";
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialUncategorizedcategories() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $lpp = new UncategorizedCategoriesPage();
+
+       return $lpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUncategorizedimages.php b/includes/specials/SpecialUncategorizedimages.php
new file mode 100644 (file)
index 0000000..986ec96
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Special page lists images which haven't been categorised
+ *
+ * @file
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class UncategorizedImagesPage extends ImageQueryPage {
+
+       function getName() {
+               return 'Uncategorizedimages';
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() {
+               return false;
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
+               $ns = NS_IMAGE;
+
+               return "SELECT 'Uncategorizedimages' AS type, page_namespace AS namespace,
+                               page_title AS title, page_title AS value
+                               FROM {$page} LEFT JOIN {$categorylinks} ON page_id = cl_from
+                               WHERE cl_from IS NULL AND page_namespace = {$ns} AND page_is_redirect = 0";
+       }
+
+}
+
+function wfSpecialUncategorizedimages() {
+       $uip = new UncategorizedImagesPage();
+       list( $limit, $offset ) = wfCheckLimits();
+       return $uip->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUncategorizedpages.php b/includes/specials/SpecialUncategorizedpages.php
new file mode 100644 (file)
index 0000000..e7f0aac
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page looking for page without any category.
+ * @ingroup SpecialPage
+ */
+class UncategorizedPagesPage extends PageQueryPage {
+       var $requestedNamespace = NS_MAIN;
+
+       function getName() {
+               return "Uncategorizedpages";
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function isExpensive() {
+               return true;
+       }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
+               $name = $dbr->addQuotes( $this->getName() );
+
+               return
+                       "
+                       SELECT
+                               $name as type,
+                               page_namespace AS namespace,
+                               page_title AS title,
+                               page_title AS value
+                       FROM $page
+                       LEFT JOIN $categorylinks ON page_id=cl_from
+                       WHERE cl_from IS NULL AND page_namespace={$this->requestedNamespace} AND page_is_redirect=0
+                       ";
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialUncategorizedpages() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $lpp = new UncategorizedPagesPage();
+
+       return $lpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUncategorizedtemplates.php b/includes/specials/SpecialUncategorizedtemplates.php
new file mode 100644 (file)
index 0000000..cb2a6d4
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page lists all uncategorised pages in the
+ * template namespace
+ *
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class UncategorizedTemplatesPage extends UncategorizedPagesPage {
+
+       var $requestedNamespace = NS_TEMPLATE;
+
+       public function getName() {
+               return 'Uncategorizedtemplates';
+       }
+
+}
+
+/**
+ * Main execution point
+ *
+ * @param mixed $par Parameter passed to the page
+ */
+function wfSpecialUncategorizedtemplates() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $utp = new UncategorizedTemplatesPage();
+       $utp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php
new file mode 100644 (file)
index 0000000..33d9476
--- /dev/null
@@ -0,0 +1,1278 @@
+<?php
+
+/**
+ * Special page allowing users with the appropriate permissions to view
+ * and restore deleted content
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Constructor
+ */
+function wfSpecialUndelete( $par ) {
+       global $wgRequest;
+
+       $form = new UndeleteForm( $wgRequest, $par );
+       $form->execute();
+}
+
+/**
+ * Used to show archived pages and eventually restore them.
+ * @ingroup SpecialPage
+ */
+class PageArchive {
+       protected $title;
+       var $fileStatus;
+
+       function __construct( $title ) {
+               if( is_null( $title ) ) {
+                       throw new MWException( 'Archiver() given a null title.');
+               }
+               $this->title = $title;
+       }
+
+       /**
+        * List all deleted pages recorded in the archive table. Returns result
+        * wrapper with (ar_namespace, ar_title, count) fields, ordered by page
+        * namespace/title.
+        *
+        * @return ResultWrapper
+        */
+       public static function listAllPages() {
+               $dbr = wfGetDB( DB_SLAVE );
+               return self::listPages( $dbr, '' );
+       }
+
+       /**
+        * List deleted pages recorded in the archive table matching the
+        * given title prefix.
+        * Returns result wrapper with (ar_namespace, ar_title, count) fields.
+        *
+        * @return ResultWrapper
+        */
+       public static function listPagesByPrefix( $prefix ) {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               $title = Title::newFromText( $prefix );
+               if( $title ) {
+                       $ns = $title->getNamespace();
+                       $encPrefix = $dbr->escapeLike( $title->getDBkey() );
+               } else {
+                       // Prolly won't work too good
+                       // @todo handle bare namespace names cleanly?
+                       $ns = 0;
+                       $encPrefix = $dbr->escapeLike( $prefix );
+               }
+               $conds = array(
+                       'ar_namespace' => $ns,
+                       "ar_title LIKE '$encPrefix%'",
+               );
+               return self::listPages( $dbr, $conds );
+       }
+
+       protected static function listPages( $dbr, $condition ) {
+               return $dbr->resultObject(
+                       $dbr->select(
+                               array( 'archive' ),
+                               array(
+                                       'ar_namespace',
+                                       'ar_title',
+                                       'COUNT(*) AS count'
+                               ),
+                               $condition,
+                               __METHOD__,
+                               array(
+                                       'GROUP BY' => 'ar_namespace,ar_title',
+                                       'ORDER BY' => 'ar_namespace,ar_title',
+                                       'LIMIT' => 100,
+                               )
+                       )
+               );
+       }
+
+       /**
+        * List the revisions of the given page. Returns result wrapper with
+        * (ar_minor_edit, ar_timestamp, ar_user, ar_user_text, ar_comment) fields.
+        *
+        * @return ResultWrapper
+        */
+       function listRevisions() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'archive',
+                       array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len', 'ar_deleted' ),
+                       array( 'ar_namespace' => $this->title->getNamespace(),
+                              'ar_title' => $this->title->getDBkey() ),
+                       'PageArchive::listRevisions',
+                       array( 'ORDER BY' => 'ar_timestamp DESC' ) );
+               $ret = $dbr->resultObject( $res );
+               return $ret;
+       }
+
+       /**
+        * List the deleted file revisions for this page, if it's a file page.
+        * Returns a result wrapper with various filearchive fields, or null
+        * if not a file page.
+        *
+        * @return ResultWrapper
+        * @todo Does this belong in Image for fuller encapsulation?
+        */
+       function listFiles() {
+               if( $this->title->getNamespace() == NS_IMAGE ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $res = $dbr->select( 'filearchive',
+                               array(
+                                       'fa_id',
+                                       'fa_name',
+                                       'fa_archive_name',
+                                       'fa_storage_key',
+                                       'fa_storage_group',
+                                       'fa_size',
+                                       'fa_width',
+                                       'fa_height',
+                                       'fa_bits',
+                                       'fa_metadata',
+                                       'fa_media_type',
+                                       'fa_major_mime',
+                                       'fa_minor_mime',
+                                       'fa_description',
+                                       'fa_user',
+                                       'fa_user_text',
+                                       'fa_timestamp',
+                                       'fa_deleted' ),
+                               array( 'fa_name' => $this->title->getDBkey() ),
+                               __METHOD__,
+                               array( 'ORDER BY' => 'fa_timestamp DESC' ) );
+                       $ret = $dbr->resultObject( $res );
+                       return $ret;
+               }
+               return null;
+       }
+
+       /**
+        * Fetch (and decompress if necessary) the stored text for the deleted
+        * revision of the page with the given timestamp.
+        *
+        * @return string
+        * @deprecated Use getRevision() for more flexible information
+        */
+       function getRevisionText( $timestamp ) {
+               $rev = $this->getRevision( $timestamp );
+               return $rev ? $rev->getText() : null;
+       }
+
+       /**
+        * Return a Revision object containing data for the deleted revision.
+        * Note that the result *may* or *may not* have a null page ID.
+        * @param string $timestamp
+        * @return Revision
+        */
+       function getRevision( $timestamp ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $row = $dbr->selectRow( 'archive',
+                       array(
+                               'ar_rev_id',
+                               'ar_text',
+                               'ar_comment',
+                               'ar_user',
+                               'ar_user_text',
+                               'ar_timestamp',
+                               'ar_minor_edit',
+                               'ar_flags',
+                               'ar_text_id',
+                               'ar_deleted',
+                               'ar_len' ),
+                       array( 'ar_namespace' => $this->title->getNamespace(),
+                              'ar_title' => $this->title->getDBkey(),
+                              'ar_timestamp' => $dbr->timestamp( $timestamp ) ),
+                       __METHOD__ );
+               if( $row ) {
+                       return new Revision( array(
+                               'page'       => $this->title->getArticleId(),
+                               'id'         => $row->ar_rev_id,
+                               'text'       => ($row->ar_text_id
+                                       ? null
+                                       : Revision::getRevisionText( $row, 'ar_' ) ),
+                               'comment'    => $row->ar_comment,
+                               'user'       => $row->ar_user,
+                               'user_text'  => $row->ar_user_text,
+                               'timestamp'  => $row->ar_timestamp,
+                               'minor_edit' => $row->ar_minor_edit,
+                               'text_id'    => $row->ar_text_id,
+                               'deleted'    => $row->ar_deleted,
+                               'len'        => $row->ar_len) );
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Return the most-previous revision, either live or deleted, against
+        * the deleted revision given by timestamp.
+        *
+        * May produce unexpected results in case of history merges or other
+        * unusual time issues.
+        *
+        * @param string $timestamp
+        * @return Revision or null
+        */
+       function getPreviousRevision( $timestamp ) {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               // Check the previous deleted revision...
+               $row = $dbr->selectRow( 'archive',
+                       'ar_timestamp',
+                       array( 'ar_namespace' => $this->title->getNamespace(),
+                              'ar_title' => $this->title->getDBkey(),
+                              'ar_timestamp < ' .
+                                               $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ),
+                       __METHOD__,
+                       array(
+                               'ORDER BY' => 'ar_timestamp DESC',
+                               'LIMIT' => 1 ) );
+               $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
+
+               $row = $dbr->selectRow( array( 'page', 'revision' ),
+                       array( 'rev_id', 'rev_timestamp' ),
+                       array(
+                               'page_namespace' => $this->title->getNamespace(),
+                               'page_title' => $this->title->getDBkey(),
+                               'page_id = rev_page',
+                               'rev_timestamp < ' .
+                                               $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ),
+                       __METHOD__,
+                       array(
+                               'ORDER BY' => 'rev_timestamp DESC',
+                               'LIMIT' => 1 ) );
+               $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
+               $prevLiveId = $row ? intval( $row->rev_id ) : null;
+
+               if( $prevLive && $prevLive > $prevDeleted ) {
+                       // Most prior revision was live
+                       return Revision::newFromId( $prevLiveId );
+               } elseif( $prevDeleted ) {
+                       // Most prior revision was deleted
+                       return $this->getRevision( $prevDeleted );
+               } else {
+                       // No prior revision on this page.
+                       return null;
+               }
+       }
+
+       /**
+        * Get the text from an archive row containing ar_text, ar_flags and ar_text_id
+        */
+       function getTextFromRow( $row ) {
+               if( is_null( $row->ar_text_id ) ) {
+                       // An old row from MediaWiki 1.4 or previous.
+                       // Text is embedded in this row in classic compression format.
+                       return Revision::getRevisionText( $row, "ar_" );
+               } else {
+                       // New-style: keyed to the text storage backend.
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $text = $dbr->selectRow( 'text',
+                               array( 'old_text', 'old_flags' ),
+                               array( 'old_id' => $row->ar_text_id ),
+                               __METHOD__ );
+                       return Revision::getRevisionText( $text );
+               }
+       }
+
+
+       /**
+        * Fetch (and decompress if necessary) the stored text of the most
+        * recently edited deleted revision of the page.
+        *
+        * If there are no archived revisions for the page, returns NULL.
+        *
+        * @return string
+        */
+       function getLastRevisionText() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $row = $dbr->selectRow( 'archive',
+                       array( 'ar_text', 'ar_flags', 'ar_text_id' ),
+                       array( 'ar_namespace' => $this->title->getNamespace(),
+                              'ar_title' => $this->title->getDBkey() ),
+                       'PageArchive::getLastRevisionText',
+                       array( 'ORDER BY' => 'ar_timestamp DESC' ) );
+               if( $row ) {
+                       return $this->getTextFromRow( $row );
+               } else {
+                       return NULL;
+               }
+       }
+
+       /**
+        * Quick check if any archived revisions are present for the page.
+        * @return bool
+        */
+       function isDeleted() {
+               $dbr = wfGetDB( DB_SLAVE );
+               $n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
+                       array( 'ar_namespace' => $this->title->getNamespace(),
+                              'ar_title' => $this->title->getDBkey() ) );
+               return ($n > 0);
+       }
+
+       /**
+        * Restore the given (or all) text and file revisions for the page.
+        * Once restored, the items will be removed from the archive tables.
+        * The deletion log will be updated with an undeletion notice.
+        *
+        * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete.
+        * @param string $comment
+        * @param array $fileVersions
+        * @param bool $unsuppress
+        *
+        * @return array(number of file revisions restored, number of image revisions restored, log message)
+        * on success, false on failure
+        */
+       function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false ) {
+               // If both the set of text revisions and file revisions are empty,
+               // restore everything. Otherwise, just restore the requested items.
+               $restoreAll = empty( $timestamps ) && empty( $fileVersions );
+
+               $restoreText = $restoreAll || !empty( $timestamps );
+               $restoreFiles = $restoreAll || !empty( $fileVersions );
+
+               if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
+                       $img = wfLocalFile( $this->title );
+                       $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
+                       $filesRestored = $this->fileStatus->successCount;
+               } else {
+                       $filesRestored = 0;
+               }
+
+               if( $restoreText ) {
+                       $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress );
+                       if($textRestored === false) // It must be one of UNDELETE_*
+                               return false;
+               } else {
+                       $textRestored = 0;
+               }
+
+               // Touch the log!
+               global $wgContLang;
+               $log = new LogPage( 'delete' );
+
+               if( $textRestored && $filesRestored ) {
+                       $reason = wfMsgExt( 'undeletedrevisions-files', array( 'content', 'parsemag' ),
+                               $wgContLang->formatNum( $textRestored ),
+                               $wgContLang->formatNum( $filesRestored ) );
+               } elseif( $textRestored ) {
+                       $reason = wfMsgExt( 'undeletedrevisions', array( 'content', 'parsemag' ),
+                               $wgContLang->formatNum( $textRestored ) );
+               } elseif( $filesRestored ) {
+                       $reason = wfMsgExt( 'undeletedfiles', array( 'content', 'parsemag' ),
+                               $wgContLang->formatNum( $filesRestored ) );
+               } else {
+                       wfDebug( "Undelete: nothing undeleted...\n" );
+                       return false;
+               }
+
+               if( trim( $comment ) != '' )
+                       $reason .= ": {$comment}";
+               $log->addEntry( 'restore', $this->title, $reason );
+
+               return array($textRestored, $filesRestored, $reason);
+       }
+
+       /**
+        * This is the meaty bit -- restores archived revisions of the given page
+        * to the cur/old tables. If the page currently exists, all revisions will
+        * be stuffed into old, otherwise the most recent will go into cur.
+        *
+        * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete.
+        * @param string $comment
+        * @param array $fileVersions
+        * @param bool $unsuppress, remove all ar_deleted/fa_deleted restrictions of seletected revs
+        *
+        * @return mixed number of revisions restored or false on failure
+        */
+       private function undeleteRevisions( $timestamps, $unsuppress = false ) {
+               if ( wfReadOnly() )
+                       return false;
+               $restoreAll = empty( $timestamps );
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               # Does this page already exist? We'll have to update it...
+               $article = new Article( $this->title );
+               $options = 'FOR UPDATE';
+               $page = $dbw->selectRow( 'page',
+                       array( 'page_id', 'page_latest' ),
+                       array( 'page_namespace' => $this->title->getNamespace(),
+                              'page_title'     => $this->title->getDBkey() ),
+                       __METHOD__,
+                       $options );
+               if( $page ) {
+                       $makepage = false;
+                       # Page already exists. Import the history, and if necessary
+                       # we'll update the latest revision field in the record.
+                       $newid             = 0;
+                       $pageId            = $page->page_id;
+                       $previousRevId     = $page->page_latest;
+                       # Get the time span of this page
+                       $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
+                               array( 'rev_id' => $previousRevId ),
+                               __METHOD__ );
+                       if( $previousTimestamp === false ) {
+                               wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" );
+                               return 0;
+                       }
+               } else {
+                       # Have to create a new article...
+                       $makepage = true;
+                       $previousRevId = 0;
+                       $previousTimestamp = 0;
+               }
+
+               if( $restoreAll ) {
+                       $oldones = '1 = 1'; # All revisions...
+               } else {
+                       $oldts = implode( ',',
+                               array_map( array( &$dbw, 'addQuotes' ),
+                                       array_map( array( &$dbw, 'timestamp' ),
+                                               $timestamps ) ) );
+
+                       $oldones = "ar_timestamp IN ( {$oldts} )";
+               }
+
+               /**
+                * Select each archived revision...
+                */
+               $result = $dbw->select( 'archive',
+                       /* fields */ array(
+                               'ar_rev_id',
+                               'ar_text',
+                               'ar_comment',
+                               'ar_user',
+                               'ar_user_text',
+                               'ar_timestamp',
+                               'ar_minor_edit',
+                               'ar_flags',
+                               'ar_text_id',
+                               'ar_deleted',
+                               'ar_page_id',
+                               'ar_len' ),
+                       /* WHERE */ array(
+                               'ar_namespace' => $this->title->getNamespace(),
+                               'ar_title'     => $this->title->getDBkey(),
+                               $oldones ),
+                       __METHOD__,
+                       /* options */ array(
+                               'ORDER BY' => 'ar_timestamp' )
+                       );
+               $ret = $dbw->resultObject( $result );
+
+               $rev_count = $dbw->numRows( $result );
+               if( $rev_count ) {
+                       # We need to seek around as just using DESC in the ORDER BY
+                       # would leave the revisions inserted in the wrong order
+                       $first = $ret->fetchObject();
+                       $ret->seek( $rev_count - 1 );
+                       $last = $ret->fetchObject();
+                       // We don't handle well changing the top revision's settings
+                       if( !$unsuppress && $last->ar_deleted && $last->ar_timestamp > $previousTimestamp ) {
+                               wfDebug( __METHOD__.": restoration would result in a deleted top revision\n" );
+                               return false;
+                       }
+                       $ret->seek( 0 );
+               }
+
+               if( $makepage ) {
+                       $newid  = $article->insertOn( $dbw );
+                       $pageId = $newid;
+               }
+
+               $revision = null;
+               $restored = 0;
+
+               while( $row = $ret->fetchObject() ) {
+                       if( $row->ar_text_id ) {
+                               // Revision was deleted in 1.5+; text is in
+                               // the regular text table, use the reference.
+                               // Specify null here so the so the text is
+                               // dereferenced for page length info if needed.
+                               $revText = null;
+                       } else {
+                               // Revision was deleted in 1.4 or earlier.
+                               // Text is squashed into the archive row, and
+                               // a new text table entry will be created for it.
+                               $revText = Revision::getRevisionText( $row, 'ar_' );
+                       }
+                       $revision = new Revision( array(
+                               'page'       => $pageId,
+                               'id'         => $row->ar_rev_id,
+                               'text'       => $revText,
+                               'comment'    => $row->ar_comment,
+                               'user'       => $row->ar_user,
+                               'user_text'  => $row->ar_user_text,
+                               'timestamp'  => $row->ar_timestamp,
+                               'minor_edit' => $row->ar_minor_edit,
+                               'text_id'    => $row->ar_text_id,
+                               'deleted'        => $unsuppress ? 0 : $row->ar_deleted,
+                               'len'        => $row->ar_len
+                               ) );
+                       $revision->insertOn( $dbw );
+                       $restored++;
+
+                       wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) );
+               }
+               // Was anything restored at all?
+               if($restored == 0)
+                       return 0;
+
+               if( $revision ) {
+                       // Attach the latest revision to the page...
+                       $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId );
+
+                       if( $newid || $wasnew ) {
+                               // Update site stats, link tables, etc
+                               $article->createUpdates( $revision );
+                       }
+
+                       if( $newid ) {
+                               wfRunHooks( 'ArticleUndelete', array( &$this->title, true ) );
+                               Article::onArticleCreate( $this->title );
+                       } else {
+                               wfRunHooks( 'ArticleUndelete', array( &$this->title, false ) );
+                               Article::onArticleEdit( $this->title );
+                       }
+
+                       if( $this->title->getNamespace() == NS_IMAGE ) {
+                               $update = new HTMLCacheUpdate( $this->title, 'imagelinks' );
+                               $update->doUpdate();
+                       }
+               } else {
+                       // Revision couldn't be created. This is very weird
+                       return self::UNDELETE_UNKNOWNERR;
+               }
+
+               # Now that it's safely stored, take it out of the archive
+               $dbw->delete( 'archive',
+                       /* WHERE */ array(
+                               'ar_namespace' => $this->title->getNamespace(),
+                               'ar_title' => $this->title->getDBkey(),
+                               $oldones ),
+                       __METHOD__ );
+
+               return $restored;
+       }
+
+       function getFileStatus() { return $this->fileStatus; }
+}
+
+/**
+ * The HTML form for Special:Undelete, which allows users with the appropriate
+ * permissions to view and restore deleted content.
+ * @ingroup SpecialPage
+ */
+class UndeleteForm {
+       var $mAction, $mTarget, $mTimestamp, $mRestore, $mTargetObj;
+       var $mTargetTimestamp, $mAllowed, $mComment;
+
+       function UndeleteForm( $request, $par = "" ) {
+               global $wgUser;
+               $this->mAction = $request->getVal( 'action' );
+               $this->mTarget = $request->getVal( 'target' );
+               $this->mSearchPrefix = $request->getText( 'prefix' );
+               $time = $request->getVal( 'timestamp' );
+               $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : '';
+               $this->mFile = $request->getVal( 'file' );
+
+               $posted = $request->wasPosted() &&
+                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
+               $this->mRestore = $request->getCheck( 'restore' ) && $posted;
+               $this->mPreview = $request->getCheck( 'preview' ) && $posted;
+               $this->mDiff = $request->getCheck( 'diff' );
+               $this->mComment = $request->getText( 'wpComment' );
+               $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
+
+               if( $par != "" ) {
+                       $this->mTarget = $par;
+               }
+               if ( $wgUser->isAllowed( 'undelete' ) && !$wgUser->isBlocked() ) {
+                       $this->mAllowed = true;
+               } else {
+                       $this->mAllowed = false;
+                       $this->mTimestamp = '';
+                       $this->mRestore = false;
+               }
+               if ( $this->mTarget !== "" ) {
+                       $this->mTargetObj = Title::newFromURL( $this->mTarget );
+               } else {
+                       $this->mTargetObj = NULL;
+               }
+               if( $this->mRestore ) {
+                       $timestamps = array();
+                       $this->mFileVersions = array();
+                       foreach( $_REQUEST as $key => $val ) {
+                               $matches = array();
+                               if( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) {
+                                       array_push( $timestamps, $matches[1] );
+                               }
+
+                               if( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
+                                       $this->mFileVersions[] = intval( $matches[1] );
+                               }
+                       }
+                       rsort( $timestamps );
+                       $this->mTargetTimestamp = $timestamps;
+               }
+       }
+
+       function execute() {
+               global $wgOut, $wgUser;
+               if ( $this->mAllowed ) {
+                       $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+               } else {
+                       $wgOut->setPagetitle( wfMsg( "viewdeletedpage" ) );
+               }
+
+               if( is_null( $this->mTargetObj ) ) {
+               # Not all users can just browse every deleted page from the list
+                       if( $wgUser->isAllowed( 'browsearchive' ) ) {
+                               $this->showSearchForm();
+
+                               # List undeletable articles
+                               if( $this->mSearchPrefix ) {
+                                       $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
+                                       $this->showList( $result );
+                               }
+                       } else {
+                               $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
+                       }
+                       return;
+               }
+               if( $this->mTimestamp !== '' ) {
+                       return $this->showRevision( $this->mTimestamp );
+               }
+               if( $this->mFile !== null ) {
+                       $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile );
+                       // Check if user is allowed to see this file
+                       if( !$file->userCan( File::DELETED_FILE ) ) {
+                               $wgOut->permissionRequired( 'suppressrevision' );
+                               return false;
+                       } else {
+                               return $this->showFile( $this->mFile );
+                       }
+               }
+               if( $this->mRestore && $this->mAction == "submit" ) {
+                       return $this->undelete();
+               }
+               return $this->showHistory();
+       }
+
+       function showSearchForm() {
+               global $wgOut, $wgScript;
+               $wgOut->addWikiMsg( 'undelete-header' );
+
+               $wgOut->addHtml(
+                       Xml::openElement( 'form', array(
+                               'method' => 'get',
+                               'action' => $wgScript ) ) .
+                       '<fieldset>' .
+                       Xml::element( 'legend', array(),
+                               wfMsg( 'undelete-search-box' ) ) .
+                       Xml::hidden( 'title',
+                               SpecialPage::getTitleFor( 'Undelete' )->getPrefixedDbKey() ) .
+                       Xml::inputLabel( wfMsg( 'undelete-search-prefix' ),
+                               'prefix', 'prefix', 20,
+                               $this->mSearchPrefix ) .
+                       Xml::submitButton( wfMsg( 'undelete-search-submit' ) ) .
+                       '</fieldset>' .
+                       '</form>' );
+       }
+
+       // Generic list of deleted pages
+       private function showList( $result ) {
+               global $wgLang, $wgContLang, $wgUser, $wgOut;
+
+               if( $result->numRows() == 0 ) {
+                       $wgOut->addWikiMsg( 'undelete-no-results' );
+                       return;
+               }
+
+               $wgOut->addWikiMsg( "undeletepagetext" );
+
+               $sk = $wgUser->getSkin();
+               $undelete = SpecialPage::getTitleFor( 'Undelete' );
+               $wgOut->addHTML( "<ul>\n" );
+               while( $row = $result->fetchObject() ) {
+                       $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
+                       $link = $sk->makeKnownLinkObj( $undelete, htmlspecialchars( $title->getPrefixedText() ),
+                               'target=' . $title->getPrefixedUrl() );
+                       #$revs = wfMsgHtml( 'undeleterevisions', $wgLang->formatNum( $row->count ) );
+                       $revs = wfMsgExt( 'undeleterevisions',
+                               array( 'parseinline' ),
+                               $wgLang->formatNum( $row->count ) );
+                       $wgOut->addHtml( "<li>{$link} ({$revs})</li>\n" );
+               }
+               $result->free();
+               $wgOut->addHTML( "</ul>\n" );
+
+               return true;
+       }
+
+       private function showRevision( $timestamp ) {
+               global $wgLang, $wgUser, $wgOut;
+               $self = SpecialPage::getTitleFor( 'Undelete' );
+               $skin = $wgUser->getSkin();
+
+               if(!preg_match("/[0-9]{14}/",$timestamp)) return 0;
+
+               $archive = new PageArchive( $this->mTargetObj );
+               $rev = $archive->getRevision( $timestamp );
+
+               if( !$rev ) {
+                       $wgOut->addWikiMsg( 'undeleterevision-missing' );
+                       return;
+               }
+
+               if( $rev->isDeleted(Revision::DELETED_TEXT) ) {
+                       if( !$rev->userCan(Revision::DELETED_TEXT) ) {
+                               $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+                               return;
+                       } else {
+                               $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+                               $wgOut->addHTML( '<br/>' );
+                               // and we are allowed to see...
+                       }
+               }
+
+               $wgOut->setPageTitle( wfMsg( 'undeletepage' ) );
+
+               $link = $skin->makeKnownLinkObj(
+                       SpecialPage::getTitleFor( 'Undelete', $this->mTargetObj->getPrefixedDBkey() ),
+                       htmlspecialchars( $this->mTargetObj->getPrefixedText() )
+               );
+               $time = htmlspecialchars( $wgLang->timeAndDate( $timestamp, true ) );
+               $user = $skin->revUserTools( $rev );
+
+               if( $this->mDiff ) {
+                       $previousRev = $archive->getPreviousRevision( $timestamp );
+                       if( $previousRev ) {
+                               $this->showDiff( $previousRev, $rev );
+                               if( $wgUser->getOption( 'diffonly' ) ) {
+                                       return;
+                               } else {
+                                       $wgOut->addHtml( '<hr />' );
+                               }
+                       } else {
+                               $wgOut->addHtml( wfMsgHtml( 'undelete-nodiff' ) );
+                       }
+               }
+
+               $wgOut->addHtml( '<p>' . wfMsgHtml( 'undelete-revision', $link, $time, $user ) . '</p>' );
+
+               wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) );
+
+               if( $this->mPreview ) {
+                       $wgOut->addHtml( "<hr />\n" );
+
+                       //Hide [edit]s
+                       $popts = $wgOut->parserOptions();
+                       $popts->setEditSection( false );
+                       $wgOut->parserOptions( $popts );
+                       $wgOut->addWikiTextTitleTidy( $rev->revText(), $this->mTargetObj, true );
+               }
+
+               $wgOut->addHtml(
+                       wfElement( 'textarea', array(
+                                       'readonly' => 'readonly',
+                                       'cols' => intval( $wgUser->getOption( 'cols' ) ),
+                                       'rows' => intval( $wgUser->getOption( 'rows' ) ) ),
+                               $rev->revText() . "\n" ) .
+                       wfOpenElement( 'div' ) .
+                       wfOpenElement( 'form', array(
+                               'method' => 'post',
+                               'action' => $self->getLocalURL( "action=submit" ) ) ) .
+                       wfElement( 'input', array(
+                               'type' => 'hidden',
+                               'name' => 'target',
+                               'value' => $this->mTargetObj->getPrefixedDbKey() ) ) .
+                       wfElement( 'input', array(
+                               'type' => 'hidden',
+                               'name' => 'timestamp',
+                               'value' => $timestamp ) ) .
+                       wfElement( 'input', array(
+                               'type' => 'hidden',
+                               'name' => 'wpEditToken',
+                               'value' => $wgUser->editToken() ) ) .
+                       wfElement( 'input', array(
+                               'type' => 'submit',
+                               'name' => 'preview',
+                               'value' => wfMsg( 'showpreview' ) ) ) .
+                       wfElement( 'input', array(
+                               'name' => 'diff',
+                               'type' => 'submit',
+                               'value' => wfMsg( 'showdiff' ) ) ) .
+                       wfCloseElement( 'form' ) .
+                       wfCloseElement( 'div' ) );
+       }
+
+       /**
+        * Build a diff display between this and the previous either deleted
+        * or non-deleted edit.
+        * @param Revision $previousRev
+        * @param Revision $currentRev
+        * @return string HTML
+        */
+       function showDiff( $previousRev, $currentRev ) {
+               global $wgOut, $wgUser;
+
+               $diffEngine = new DifferenceEngine();
+               $diffEngine->showDiffStyle();
+               $wgOut->addHtml(
+                       "<div>" .
+                       "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" .
+                       "<col class='diff-marker' />" .
+                       "<col class='diff-content' />" .
+                       "<col class='diff-marker' />" .
+                       "<col class='diff-content' />" .
+                       "<tr>" .
+                               "<td colspan='2' width='50%' align='center' class='diff-otitle'>" .
+                               $this->diffHeader( $previousRev ) .
+                               "</td>" .
+                               "<td colspan='2' width='50%' align='center' class='diff-ntitle'>" .
+                               $this->diffHeader( $currentRev ) .
+                               "</td>" .
+                       "</tr>" .
+                       $diffEngine->generateDiffBody(
+                               $previousRev->getText(), $currentRev->getText() ) .
+                       "</table>" .
+                       "</div>\n" );
+
+       }
+
+       private function diffHeader( $rev ) {
+               global $wgUser, $wgLang, $wgLang;
+               $sk = $wgUser->getSkin();
+               $isDeleted = !( $rev->getId() && $rev->getTitle() );
+               if( $isDeleted ) {
+                       /// @fixme $rev->getTitle() is null for deleted revs...?
+                       $targetPage = SpecialPage::getTitleFor( 'Undelete' );
+                       $targetQuery = 'target=' .
+                               $this->mTargetObj->getPrefixedUrl() .
+                               '&timestamp=' .
+                               wfTimestamp( TS_MW, $rev->getTimestamp() );
+               } else {
+                       /// @fixme getId() may return non-zero for deleted revs...
+                       $targetPage = $rev->getTitle();
+                       $targetQuery = 'oldid=' . $rev->getId();
+               }
+               return
+                       '<div id="mw-diff-otitle1"><strong>' .
+                               $sk->makeLinkObj( $targetPage,
+                                       wfMsgHtml( 'revisionasof',
+                                               $wgLang->timeanddate( $rev->getTimestamp(), true ) ),
+                                       $targetQuery ) .
+                               ( $isDeleted ? ' ' . wfMsgHtml( 'deletedrev' ) : '' ) .
+                       '</strong></div>' .
+                       '<div id="mw-diff-otitle2">' .
+                               $sk->revUserTools( $rev ) . '<br/>' .
+                       '</div>' .
+                       '<div id="mw-diff-otitle3">' .
+                               $sk->revComment( $rev ) . '<br/>' .
+                       '</div>';
+       }
+
+       /**
+        * Show a deleted file version requested by the visitor.
+        */
+       private function showFile( $key ) {
+               global $wgOut, $wgRequest;
+               $wgOut->disable();
+
+               # We mustn't allow the output to be Squid cached, otherwise
+               # if an admin previews a deleted image, and it's cached, then
+               # a user without appropriate permissions can toddle off and
+               # nab the image, and Squid will serve it
+               $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+               $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
+               $wgRequest->response()->header( 'Pragma: no-cache' );
+
+               $store = FileStore::get( 'deleted' );
+               $store->stream( $key );
+       }
+
+       private function showHistory() {
+               global $wgLang, $wgUser, $wgOut;
+
+               $sk = $wgUser->getSkin();
+               if( $this->mAllowed ) {
+                       $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+               } else {
+                       $wgOut->setPagetitle( wfMsg( 'viewdeletedpage' ) );
+               }
+
+               $wgOut->addWikiText( wfMsgHtml( 'undeletepagetitle', $this->mTargetObj->getPrefixedText()) );
+
+               $archive = new PageArchive( $this->mTargetObj );
+               /*
+               $text = $archive->getLastRevisionText();
+               if( is_null( $text ) ) {
+                       $wgOut->addWikiMsg( "nohistory" );
+                       return;
+               }
+               */
+               if ( $this->mAllowed ) {
+                       $wgOut->addWikiMsg( "undeletehistory" );
+                       $wgOut->addWikiMsg( "undeleterevdel" );
+               } else {
+                       $wgOut->addWikiMsg( "undeletehistorynoadmin" );
+               }
+
+               # List all stored revisions
+               $revisions = $archive->listRevisions();
+               $files = $archive->listFiles();
+
+               $haveRevisions = $revisions && $revisions->numRows() > 0;
+               $haveFiles = $files && $files->numRows() > 0;
+
+               # Batch existence check on user and talk pages
+               if( $haveRevisions ) {
+                       $batch = new LinkBatch();
+                       while( $row = $revisions->fetchObject() ) {
+                               $batch->addObj( Title::makeTitleSafe( NS_USER, $row->ar_user_text ) );
+                               $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ar_user_text ) );
+                       }
+                       $batch->execute();
+                       $revisions->seek( 0 );
+               }
+               if( $haveFiles ) {
+                       $batch = new LinkBatch();
+                       while( $row = $files->fetchObject() ) {
+                               $batch->addObj( Title::makeTitleSafe( NS_USER, $row->fa_user_text ) );
+                               $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->fa_user_text ) );
+                       }
+                       $batch->execute();
+                       $files->seek( 0 );
+               }
+
+               if ( $this->mAllowed ) {
+                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
+                       $action = $titleObj->getLocalURL( "action=submit" );
+                       # Start the form here
+                       $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) );
+                       $wgOut->addHtml( $top );
+               }
+
+               # Show relevant lines from the deletion log:
+               $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) . "\n" );
+               LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTargetObj->getPrefixedText() );
+
+               if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
+                       # Format the user-visible controls (comment field, submission button)
+                       # in a nice little table
+                       if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+                               $unsuppressBox =
+                                       "<tr>
+                                               <td>&nbsp;</td>
+                                               <td class='mw-input'>" .
+                                                       Xml::checkLabel( wfMsg('revdelete-unsuppress'), 'wpUnsuppress',
+                                                               'mw-undelete-unsuppress', $this->mUnsuppress ).
+                                               "</td>
+                                       </tr>";
+                       } else {
+                               $unsuppressBox = "";
+                       }
+                       $table =
+                               Xml::openElement( 'fieldset' ) .
+                               Xml::element( 'legend', null, wfMsg( 'undelete') ).
+                               Xml::openElement( 'table', array( 'id' => 'mw-undelete-table' ) ) .
+                                       "<tr>
+                                               <td colspan='2'>" .
+                                                       wfMsgWikiHtml( 'undeleteextrahelp' ) .
+                                               "</td>
+                                       </tr>
+                                       <tr>
+                                               <td class='mw-label'>" .
+                                                       Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) .
+                                               "</td>
+                                               <td class='mw-input'>" .
+                                                       Xml::input( 'wpComment', 50, $this->mComment, array( 'id' =>  'wpComment' ) ) .
+                                               "</td>
+                                       </tr>
+                                       <tr>
+                                               <td>&nbsp;</td>
+                                               <td class='mw-submit'>" .
+                                                       Xml::submitButton( wfMsg( 'undeletebtn' ), array( 'name' => 'restore', 'id' => 'mw-undelete-submit' ) ) .
+                                                       Xml::element( 'input', array( 'type' => 'reset', 'value' => wfMsg( 'undeletereset' ), 'id' => 'mw-undelete-reset' ) ) .
+                                               "</td>
+                                       </tr>" .
+                                       $unsuppressBox .
+                               Xml::closeElement( 'table' ) .
+                               Xml::closeElement( 'fieldset' );
+
+                       $wgOut->addHtml( $table );
+               }
+
+               $wgOut->addHTML( Xml::element( 'h2', null, wfMsg( 'history' ) ) . "\n" );
+
+               if( $haveRevisions ) {
+                       # The page's stored (deleted) history:
+                       $wgOut->addHTML("<ul>");
+                       $target = urlencode( $this->mTarget );
+                       $remaining = $revisions->numRows();
+                       $earliestLiveTime = $this->getEarliestTime( $this->mTargetObj );
+
+                       while( $row = $revisions->fetchObject() ) {
+                               $remaining--;
+                               $wgOut->addHTML( $this->formatRevisionRow( $row, $earliestLiveTime, $remaining, $sk ) );
+                       }
+                       $revisions->free();
+                       $wgOut->addHTML("</ul>");
+               } else {
+                       $wgOut->addWikiMsg( "nohistory" );
+               }
+
+               if( $haveFiles ) {
+                       $wgOut->addHtml( Xml::element( 'h2', null, wfMsg( 'filehist' ) ) . "\n" );
+                       $wgOut->addHtml( "<ul>" );
+                       while( $row = $files->fetchObject() ) {
+                               $wgOut->addHTML( $this->formatFileRow( $row, $sk ) );
+                       }
+                       $files->free();
+                       $wgOut->addHTML( "</ul>" );
+               }
+
+               if ( $this->mAllowed ) {
+                       # Slip in the hidden controls here
+                       $misc  = Xml::hidden( 'target', $this->mTarget );
+                       $misc .= Xml::hidden( 'wpEditToken', $wgUser->editToken() );
+                       $misc .= Xml::closeElement( 'form' );
+                       $wgOut->addHtml( $misc );
+               }
+
+               return true;
+       }
+
+       private function formatRevisionRow( $row, $earliestLiveTime, $remaining, $sk ) {
+               global $wgUser, $wgLang;
+
+               $rev = new Revision( array(
+                               'page'       => $this->mTargetObj->getArticleId(),
+                               'comment'    => $row->ar_comment,
+                               'user'       => $row->ar_user,
+                               'user_text'  => $row->ar_user_text,
+                               'timestamp'  => $row->ar_timestamp,
+                               'minor_edit' => $row->ar_minor_edit,
+                               'deleted'    => $row->ar_deleted,
+                               'len'        => $row->ar_len ) );
+
+               $stxt = '';
+               $ts = wfTimestamp( TS_MW, $row->ar_timestamp );
+               if( $this->mAllowed ) {
+                       $checkBox = Xml::check( "ts$ts" );
+                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
+                       $pageLink = $this->getPageLink( $rev, $titleObj, $ts, $sk );
+                       # Last link
+                       if( !$rev->userCan( Revision::DELETED_TEXT ) ) {
+                               $last = wfMsgHtml('diff');
+                       } else if( $remaining > 0 || ($earliestLiveTime && $ts > $earliestLiveTime) ) {
+                               $last = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml('diff'),
+                                       "target=" . $this->mTargetObj->getPrefixedUrl() . "&timestamp=$ts&diff=prev" );
+                       } else {
+                               $last = wfMsgHtml('diff');
+                       }
+               } else {
+                       $checkBox = '';
+                       $pageLink = $wgLang->timeanddate( $ts, true );
+                       $last = wfMsgHtml('diff');
+               }
+               $userLink = $sk->revUserTools( $rev );
+
+               if(!is_null($size = $row->ar_len)) {
+                       if($size == 0)
+                               $stxt = wfMsgHtml('historyempty');
+                       else
+                               $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
+               }
+               $comment = $sk->revComment( $rev );
+               $revdlink = '';
+               if( $wgUser->isAllowed( 'deleterevision' ) ) {
+                       $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+                       if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
+                       // If revision was hidden from sysops
+                               $del = wfMsgHtml('rev-delundel');
+                       } else {
+                               $ts = wfTimestamp( TS_MW, $row->ar_timestamp );
+                               $del = $sk->makeKnownLinkObj( $revdel,
+                                       wfMsgHtml('rev-delundel'),
+                                       'target=' . $this->mTargetObj->getPrefixedUrl() . "&artimestamp=$ts" );
+                               // Bolden oversighted content
+                               if( $rev->isDeleted( Revision::DELETED_RESTRICTED ) )
+                                       $del = "<strong>$del</strong>";
+                       }
+                       $revdlink = "<tt>(<small>$del</small>)</tt>";
+               }
+
+               return "<li>$checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment</li>";
+       }
+
+       private function formatFileRow( $row, $sk ) {
+               global $wgUser, $wgLang;
+
+               $file = ArchivedFile::newFromRow( $row );
+
+               $ts = wfTimestamp( TS_MW, $row->fa_timestamp );
+               if( $this->mAllowed && $row->fa_storage_key ) {
+                       $checkBox = Xml::check( "fileid" . $row->fa_id );
+                       $key = urlencode( $row->fa_storage_key );
+                       $target = urlencode( $this->mTarget );
+                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
+                       $pageLink = $this->getFileLink( $file, $titleObj, $ts, $key, $sk );
+               } else {
+                       $checkBox = '';
+                       $pageLink = $wgLang->timeanddate( $ts, true );
+               }
+               $userLink = $this->getFileUser( $file, $sk );
+               $data =
+                       wfMsgHtml( 'widthheight',
+                               $wgLang->formatNum( $row->fa_width ),
+                               $wgLang->formatNum( $row->fa_height ) ) .
+                       ' (' .
+                       wfMsgHtml( 'nbytes', $wgLang->formatNum( $row->fa_size ) ) .
+                       ')';
+               $comment = $this->getFileComment( $file, $sk );
+               $revdlink = '';
+               if( $wgUser->isAllowed( 'deleterevision' ) ) {
+                       $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+                       if( !$file->userCan(File::DELETED_RESTRICTED ) ) {
+                       // If revision was hidden from sysops
+                               $del = wfMsgHtml('rev-delundel');
+                       } else {
+                               $del = $sk->makeKnownLinkObj( $revdel,
+                                       wfMsgHtml('rev-delundel'),
+                                       'target=' . $this->mTargetObj->getPrefixedUrl() .
+                                       '&fileid=' . $row->fa_id );
+                               // Bolden oversighted content
+                               if( $file->isDeleted( File::DELETED_RESTRICTED ) )
+                                       $del = "<strong>$del</strong>";
+                       }
+                       $revdlink = "<tt>(<small>$del</small>)</tt>";
+               }
+               return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
+       }
+
+       private function getEarliestTime( $title ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               if( $title->exists() ) {
+                       $min = $dbr->selectField( 'revision',
+                               'MIN(rev_timestamp)',
+                               array( 'rev_page' => $title->getArticleId() ),
+                               __METHOD__ );
+                       return wfTimestampOrNull( TS_MW, $min );
+               }
+               return null;
+       }
+
+       /**
+        * Fetch revision text link if it's available to all users
+        * @return string
+        */
+       function getPageLink( $rev, $titleObj, $ts, $sk ) {
+               global $wgLang;
+
+               if( !$rev->userCan(Revision::DELETED_TEXT) ) {
+                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
+               } else {
+                       $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ),
+                               "target=".$this->mTargetObj->getPrefixedUrl()."&timestamp=$ts" );
+                       if( $rev->isDeleted(Revision::DELETED_TEXT) )
+                               $link = '<span class="history-deleted">' . $link . '</span>';
+                       return $link;
+               }
+       }
+
+       /**
+        * Fetch image view link if it's available to all users
+        * @return string
+        */
+       function getFileLink( $file, $titleObj, $ts, $key, $sk ) {
+               global $wgLang;
+
+               if( !$file->userCan(File::DELETED_FILE) ) {
+                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
+               } else {
+                       $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ),
+                               "target=".$this->mTargetObj->getPrefixedUrl()."&file=$key" );
+                       if( $file->isDeleted(File::DELETED_FILE) )
+                               $link = '<span class="history-deleted">' . $link . '</span>';
+                       return $link;
+               }
+       }
+
+       /**
+        * Fetch file's user id if it's available to this user
+        * @return string
+        */
+       function getFileUser( $file, $sk ) {
+               if( !$file->userCan(File::DELETED_USER) ) {
+                       return '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+               } else {
+                       $link = $sk->userLink( $file->getRawUser(), $file->getRawUserText() ) .
+                               $sk->userToolLinks( $file->getRawUser(), $file->getRawUserText() );
+                       if( $file->isDeleted(File::DELETED_USER) )
+                               $link = '<span class="history-deleted">' . $link . '</span>';
+                       return $link;
+               }
+       }
+
+       /**
+        * Fetch file upload comment if it's available to this user
+        * @return string
+        */
+       function getFileComment( $file, $sk ) {
+               if( !$file->userCan(File::DELETED_COMMENT) ) {
+                       return '<span class="history-deleted"><span class="comment">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></span>';
+               } else {
+                       $link = $sk->commentBlock( $file->getRawDescription() );
+                       if( $file->isDeleted(File::DELETED_COMMENT) )
+                               $link = '<span class="history-deleted">' . $link . '</span>';
+                       return $link;
+               }
+       }
+
+       function undelete() {
+               global $wgOut, $wgUser;
+               if ( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+               if( !is_null( $this->mTargetObj ) ) {
+                       $archive = new PageArchive( $this->mTargetObj );
+                       $ok = $archive->undelete(
+                               $this->mTargetTimestamp,
+                               $this->mComment,
+                               $this->mFileVersions,
+                               $this->mUnsuppress );
+
+                       if( is_array($ok) ) {
+                               if ( $ok[1] ) // Undeleted file count
+                                       wfRunHooks( 'FileUndeleteComplete', array(
+                                               $this->mTargetObj, $this->mFileVersions,
+                                               $wgUser, $this->mComment) );
+
+                               $skin = $wgUser->getSkin();
+                               $link = $skin->makeKnownLinkObj( $this->mTargetObj );
+                               $wgOut->addHtml( wfMsgWikiHtml( 'undeletedpage', $link ) );
+                       } else {
+                               $wgOut->showFatalError( wfMsg( "cannotundelete" ) );
+                               $wgOut->addHtml( '<p>' . wfMsgHtml( "undeleterevdel" ) . '</p>' );
+                       }
+
+                       // Show file deletion warnings and errors
+                       $status = $archive->getFileStatus();
+                       if( $status && !$status->isGood() ) {
+                               $wgOut->addWikiText( $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) );
+                       }
+               } else {
+                       $wgOut->showFatalError( wfMsg( "cannotundelete" ) );
+               }
+               return false;
+       }
+}
diff --git a/includes/specials/SpecialUnlockdb.php b/includes/specials/SpecialUnlockdb.php
new file mode 100644 (file)
index 0000000..0bf7e5a
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ */
+function wfSpecialUnlockdb() {
+       global $wgUser, $wgOut, $wgRequest;
+
+       if( !$wgUser->isAllowed( 'siteadmin' ) ) {
+               $wgOut->permissionRequired( 'siteadmin' );
+               return;
+       }
+
+       $action = $wgRequest->getVal( 'action' );
+       $f = new DBUnlockForm();
+
+       if ( "success" == $action ) {
+               $f->showSuccess();
+       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( "" );
+       }
+}
+
+/**
+ * @ingroup SpecialPage
+ */
+class DBUnlockForm {
+       function showForm( $err )
+       {
+               global $wgOut, $wgUser;
+
+               global $wgReadOnlyFile;
+               if( !file_exists( $wgReadOnlyFile ) ) {
+                       $wgOut->addWikiMsg( 'databasenotlocked' );
+                       return;
+               }
+
+               $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
+               $wgOut->addWikiMsg( "unlockdbtext" );
+
+               if ( "" != $err ) {
+                       $wgOut->setSubtitle( wfMsg( "formerror" ) );
+                       $wgOut->addHTML( '<p class="error">' . htmlspecialchars( $err ) . "</p>\n" );
+               }
+               $lc = htmlspecialchars( wfMsg( "unlockconfirm" ) );
+               $lb = htmlspecialchars( wfMsg( "unlockbtn" ) );
+               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
+               $action = $titleObj->escapeLocalURL( "action=submit" );
+               $token = htmlspecialchars( $wgUser->editToken() );
+
+               $wgOut->addHTML( <<<END
+
+<form id="unlockdb" method="post" action="{$action}">
+<table border="0">
+       <tr>
+               <td align="right">
+                       <input type="checkbox" name="wpLockConfirm" />
+               </td>
+               <td align="left">{$lc}</td>
+       </tr>
+       <tr>
+               <td>&nbsp;</td>
+               <td align="left">
+                       <input type="submit" name="wpLock" value="{$lb}" />
+               </td>
+       </tr>
+</table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
+</form>
+END
+);
+
+       }
+
+       function doSubmit() {
+               global $wgOut, $wgRequest, $wgReadOnlyFile;
+
+               $wpLockConfirm = $wgRequest->getCheck( 'wpLockConfirm' );
+               if ( ! $wpLockConfirm ) {
+                       $this->showForm( wfMsg( "locknoconfirm" ) );
+                       return;
+               }
+               if ( @! unlink( $wgReadOnlyFile ) ) {
+                       $wgOut->showFileDeleteError( $wgReadOnlyFile );
+                       return;
+               }
+               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
+               $success = $titleObj->getFullURL( "action=success" );
+               $wgOut->redirect( $success );
+       }
+
+       function showSuccess() {
+               global $wgOut;
+               global $ip;
+
+               $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
+               $wgOut->setSubtitle( wfMsg( "unlockdbsuccesssub" ) );
+               $wgOut->addWikiMsg( "unlockdbsuccesstext", $ip );
+       }
+}
diff --git a/includes/specials/SpecialUnusedcategories.php b/includes/specials/SpecialUnusedcategories.php
new file mode 100644 (file)
index 0000000..406f794
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class UnusedCategoriesPage extends QueryPage {
+
+       function isExpensive() { return true; }
+
+       function getName() {
+               return 'Unusedcategories';
+       }
+
+       function getPageHeader() {
+               return wfMsgExt( 'unusedcategoriestext', array( 'parse' ) );
+       }
+
+       function getSQL() {
+               $NScat = NS_CATEGORY;
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
+               return "SELECT 'Unusedcategories' as type,
+                               {$NScat} as namespace, page_title as title, page_title as value
+                               FROM $page
+                               LEFT JOIN $categorylinks ON page_title=cl_to
+                               WHERE cl_from IS NULL
+                               AND page_namespace = {$NScat}
+                               AND page_is_redirect = 0";
+       }
+
+       function formatResult( $skin, $result ) {
+               $title = Title::makeTitle( NS_CATEGORY, $result->title );
+               return $skin->makeLinkObj( $title, $title->getText() );
+       }
+}
+
+/** constructor */
+function wfSpecialUnusedCategories() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $uc = new UnusedCategoriesPage();
+       return $uc->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUnusedimages.php b/includes/specials/SpecialUnusedimages.php
new file mode 100644 (file)
index 0000000..d71b638
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * implements Special:Unusedimages
+ * @ingroup SpecialPage
+ */
+class UnusedimagesPage extends ImageQueryPage {
+
+       function isExpensive() { return true; }
+
+       function getName() {
+               return 'Unusedimages';
+       }
+
+       function sortDescending() {
+               return false;
+       }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               global $wgCountCategorizedImagesAsUsed;
+               $dbr = wfGetDB( DB_SLAVE );
+
+               if ( $wgCountCategorizedImagesAsUsed ) {
+                       list( $page, $image, $imagelinks, $categorylinks ) = $dbr->tableNamesN( 'page', 'image', 'imagelinks', 'categorylinks' );
+
+                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, img_timestamp as value,
+                                               img_user, img_user_text,  img_description
+                                       FROM ((($page AS I LEFT JOIN $categorylinks AS L ON I.page_id = L.cl_from)
+                                               LEFT JOIN $imagelinks AS P ON I.page_title = P.il_to)
+                                               INNER JOIN $image AS G ON I.page_title = G.img_name)
+                                       WHERE I.page_namespace = ".NS_IMAGE." AND L.cl_from IS NULL AND P.il_to IS NULL";
+               } else {
+                       list( $image, $imagelinks ) = $dbr->tableNamesN( 'image','imagelinks' );
+
+                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, img_timestamp as value,
+                               img_user, img_user_text,  img_description
+                               FROM $image LEFT JOIN $imagelinks ON img_name=il_to WHERE il_to IS NULL ";
+               }
+       }
+
+       function getPageHeader() {
+               return wfMsgExt( 'unusedimagestext', array( 'parse') );
+       }
+
+}
+
+/**
+ * Entry point
+ */
+function wfSpecialUnusedimages() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $uip = new UnusedimagesPage();
+
+       return $uip->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUnusedtemplates.php b/includes/specials/SpecialUnusedtemplates.php
new file mode 100644 (file)
index 0000000..89acd09
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * implements Special:Unusedtemplates
+ * @author Rob Church <robchur@gmail.com>
+ * @copyright Â© 2006 Rob Church
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @ingroup SpecialPage
+ */
+class UnusedtemplatesPage extends QueryPage {
+
+       function getName() { return( 'Unusedtemplates' ); }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+       function sortDescending() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $templatelinks) = $dbr->tableNamesN( 'page', 'templatelinks' );
+               $sql = "SELECT 'Unusedtemplates' AS type, page_title AS title,
+                       page_namespace AS namespace, 0 AS value
+                       FROM $page
+                       LEFT JOIN $templatelinks
+                       ON page_namespace = tl_namespace AND page_title = tl_title
+                       WHERE page_namespace = 10 AND tl_from IS NULL
+                       AND page_is_redirect = 0";
+               return $sql;
+       }
+
+       function formatResult( $skin, $result ) {
+               $title = Title::makeTitle( NS_TEMPLATE, $result->title );
+               $pageLink = $skin->makeKnownLinkObj( $title, '', 'redirect=no' );
+               $wlhLink = $skin->makeKnownLinkObj(
+                       SpecialPage::getTitleFor( 'Whatlinkshere' ),
+                       wfMsgHtml( 'unusedtemplateswlh' ),
+                       'target=' . $title->getPrefixedUrl() );
+               return wfSpecialList( $pageLink, $wlhLink );
+       }
+
+       function getPageHeader() {
+               return wfMsgExt( 'unusedtemplatestext', array( 'parse' ) );
+       }
+
+}
+
+function wfSpecialUnusedtemplates() {
+       list( $limit, $offset ) = wfCheckLimits();
+       $utp = new UnusedtemplatesPage();
+       $utp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUnwatchedpages.php b/includes/specials/SpecialUnwatchedpages.php
new file mode 100644 (file)
index 0000000..64ab372
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page that displays a list of pages that are not on anyones watchlist.
+ * Implements Special:Unwatchedpages
+ *
+ * @ingroup SpecialPage
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+class UnwatchedpagesPage extends QueryPage {
+
+       function getName() { return 'Unwatchedpages'; }
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $watchlist ) = $dbr->tableNamesN( 'page', 'watchlist' );
+               $mwns = NS_MEDIAWIKI;
+               return
+                       "
+                       SELECT
+                               'Unwatchedpages' as type,
+                               page_namespace as namespace,
+                               page_title as title,
+                               page_namespace as value
+                       FROM $page
+                       LEFT JOIN $watchlist ON wl_namespace = page_namespace AND page_title = wl_title
+                       WHERE wl_title IS NULL AND page_is_redirect = 0 AND page_namespace<>$mwns
+                       ";
+       }
+
+       function sortDescending() { return false; }
+
+       function formatResult( $skin, $result ) {
+               global $wgContLang;
+
+               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getPrefixedText() );
+
+               $plink = $skin->makeKnownLinkObj( $nt, htmlspecialchars( $text ) );
+               $wlink = $skin->makeKnownLinkObj( $nt, wfMsgHtml( 'watch' ), 'action=watch' );
+
+               return wfSpecialList( $plink, $wlink );
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialUnwatchedpages() {
+       global $wgUser, $wgOut;
+
+       if ( ! $wgUser->isAllowed( 'unwatchedpages' ) )
+               return $wgOut->permissionRequired( 'unwatchedpages' );
+
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new UnwatchedpagesPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php
new file mode 100644 (file)
index 0000000..0f37f50
--- /dev/null
@@ -0,0 +1,1755 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+
+/**
+ * Entry point
+ */
+function wfSpecialUpload() {
+       global $wgRequest;
+       $form = new UploadForm( $wgRequest );
+       $form->execute();
+}
+
+/**
+ * implements Special:Upload
+ * @ingroup SpecialPage
+ */
+class UploadForm {
+       const SUCCESS = 0;
+       const BEFORE_PROCESSING = 1;
+       const LARGE_FILE_SERVER = 2;
+       const EMPTY_FILE = 3;
+       const MIN_LENGHT_PARTNAME = 4;
+       const ILLEGAL_FILENAME = 5;
+       const PROTECTED_PAGE = 6;
+       const OVERWRITE_EXISTING_FILE = 7;
+       const FILETYPE_MISSING = 8;
+       const FILETYPE_BADTYPE = 9;
+       const VERIFICATION_ERROR = 10;
+       const UPLOAD_VERIFICATION_ERROR = 11;
+       const UPLOAD_WARNING = 12;
+       const INTERNAL_ERROR = 13;
+
+       /**#@+
+        * @access private
+        */
+       var $mComment, $mLicense, $mIgnoreWarning, $mCurlError;
+       var $mDestName, $mTempPath, $mFileSize, $mFileProps;
+       var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked;
+       var $mSrcName, $mSessionKey, $mStashed, $mDesiredDestName, $mRemoveTempFile, $mSourceType;
+       var $mDestWarningAck, $mCurlDestHandle;
+       var $mLocalFile;
+
+       # Placeholders for text injection by hooks (must be HTML)
+       # extensions should take care to _append_ to the present value
+       var $uploadFormTextTop;
+       var $uploadFormTextAfterSummary;
+
+       const SESSION_VERSION = 1;
+       /**#@-*/
+
+       /**
+        * Constructor : initialise object
+        * Get data POSTed through the form and assign them to the object
+        * @param $request Data posted.
+        */
+       function UploadForm( &$request ) {
+               global $wgAllowCopyUploads;
+               $this->mDesiredDestName   = $request->getText( 'wpDestFile' );
+               $this->mIgnoreWarning     = $request->getCheck( 'wpIgnoreWarning' );
+               $this->mComment           = $request->getText( 'wpUploadDescription' );
+
+               if( !$request->wasPosted() ) {
+                       # GET requests just give the main form; no data except destination
+                       # filename and description
+                       return;
+               }
+
+               # Placeholders for text injection by hooks (empty per default)
+               $this->uploadFormTextTop = "";
+               $this->uploadFormTextAfterSummary = "";
+
+               $this->mReUpload          = $request->getCheck( 'wpReUpload' );
+               $this->mUploadClicked     = $request->getCheck( 'wpUpload' );
+
+               $this->mLicense           = $request->getText( 'wpLicense' );
+               $this->mCopyrightStatus   = $request->getText( 'wpUploadCopyStatus' );
+               $this->mCopyrightSource   = $request->getText( 'wpUploadSource' );
+               $this->mWatchthis         = $request->getBool( 'wpWatchthis' );
+               $this->mSourceType        = $request->getText( 'wpSourceType' );
+               $this->mDestWarningAck    = $request->getText( 'wpDestFileWarningAck' );
+
+               $this->mAction            = $request->getVal( 'action' );
+
+               $this->mSessionKey        = $request->getInt( 'wpSessionKey' );
+               if( !empty( $this->mSessionKey ) &&
+                       isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) &&
+                       $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) {
+                       /**
+                        * Confirming a temporarily stashed upload.
+                        * We don't want path names to be forged, so we keep
+                        * them in the session on the server and just give
+                        * an opaque key to the user agent.
+                        */
+                       $data = $_SESSION['wsUploadData'][$this->mSessionKey];
+                       $this->mTempPath         = $data['mTempPath'];
+                       $this->mFileSize         = $data['mFileSize'];
+                       $this->mSrcName          = $data['mSrcName'];
+                       $this->mFileProps        = $data['mFileProps'];
+                       $this->mCurlError        = 0/*UPLOAD_ERR_OK*/;
+                       $this->mStashed          = true;
+                       $this->mRemoveTempFile   = false;
+               } else {
+                       /**
+                        *Check for a newly uploaded file.
+                        */
+                       if( $wgAllowCopyUploads && $this->mSourceType == 'web' ) {
+                               $this->initializeFromUrl( $request );
+                       } else {
+                               $this->initializeFromUpload( $request );
+                       }
+               }
+       }
+
+       /**
+        * Initialize the uploaded file from PHP data
+        * @access private
+        */
+       function initializeFromUpload( $request ) {
+               $this->mTempPath       = $request->getFileTempName( 'wpUploadFile' );
+               $this->mFileSize       = $request->getFileSize( 'wpUploadFile' );
+               $this->mSrcName        = $request->getFileName( 'wpUploadFile' );
+               $this->mCurlError      = $request->getUploadError( 'wpUploadFile' );
+               $this->mSessionKey     = false;
+               $this->mStashed        = false;
+               $this->mRemoveTempFile = false; // PHP will handle this
+       }
+
+       /**
+        * Copy a web file to a temporary file
+        * @access private
+        */
+       function initializeFromUrl( $request ) {
+               global $wgTmpDirectory;
+               $url = $request->getText( 'wpUploadFileURL' );
+               $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
+
+               $this->mTempPath       = $local_file;
+               $this->mFileSize       = 0; # Will be set by curlCopy
+               $this->mCurlError      = $this->curlCopy( $url, $local_file );
+               $pathParts             = explode( '/', $url );
+               $this->mSrcName        = array_pop( $pathParts );
+               $this->mSessionKey     = false;
+               $this->mStashed        = false;
+
+               // PHP won't auto-cleanup the file
+               $this->mRemoveTempFile = file_exists( $local_file );
+       }
+
+       /**
+        * Safe copy from URL
+        * Returns true if there was an error, false otherwise
+        */
+       private function curlCopy( $url, $dest ) {
+               global $wgUser, $wgOut;
+
+               if( !$wgUser->isAllowed( 'upload_by_url' ) ) {
+                       $wgOut->permissionRequired( 'upload_by_url' );
+                       return true;
+               }
+
+               # Maybe remove some pasting blanks :-)
+               $url =  trim( $url );
+               if( stripos($url, 'http://') !== 0 && stripos($url, 'ftp://') !== 0 ) {
+                       # Only HTTP or FTP URLs
+                       $wgOut->showErrorPage( 'upload-proto-error', 'upload-proto-error-text' );
+                       return true;
+               }
+
+               # Open temporary file
+               $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
+               if( $this->mCurlDestHandle === false ) {
+                       # Could not open temporary file to write in
+                       $wgOut->showErrorPage( 'upload-file-error', 'upload-file-error-text');
+                       return true;
+               }
+
+               $ch = curl_init();
+               curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug
+               curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout
+               curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed
+               curl_setopt( $ch, CURLOPT_URL, $url);
+               curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
+               curl_exec( $ch );
+               $error = curl_errno( $ch ) ? true : false;
+               $errornum =  curl_errno( $ch );
+               // if ( $error ) print curl_error ( $ch ) ; # Debugging output
+               curl_close( $ch );
+
+               fclose( $this->mCurlDestHandle );
+               unset( $this->mCurlDestHandle );
+               if( $error ) {
+                       unlink( $dest );
+                       if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) )
+                               $wgOut->showErrorPage( 'upload-misc-error', 'upload-misc-error-text' );
+                       else
+                               $wgOut->showErrorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" );
+               }
+
+               return $error;
+       }
+
+       /**
+        * Callback function for CURL-based web transfer
+        * Write data to file unless we've passed the length limit;
+        * if so, abort immediately.
+        * @access private
+        */
+       function uploadCurlCallback( $ch, $data ) {
+               global $wgMaxUploadSize;
+               $length = strlen( $data );
+               $this->mFileSize += $length;
+               if( $this->mFileSize > $wgMaxUploadSize ) {
+                       return 0;
+               }
+               fwrite( $this->mCurlDestHandle, $data );
+               return $length;
+       }
+
+       /**
+        * Start doing stuff
+        * @access public
+        */
+       function execute() {
+               global $wgUser, $wgOut;
+               global $wgEnableUploads;
+
+               # Check uploading enabled
+               if( !$wgEnableUploads ) {
+                       $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) );
+                       return;
+               }
+
+               # Check permissions
+               if( !$wgUser->isAllowed( 'upload' ) ) {
+                       if( !$wgUser->isLoggedIn() ) {
+                               $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+                       } else {
+                               $wgOut->permissionRequired( 'upload' );
+                       }
+                       return;
+               }
+
+               # Check blocks
+               if( $wgUser->isBlocked() ) {
+                       $wgOut->blockedPage();
+                       return;
+               }
+
+               if( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+
+               if( $this->mReUpload ) {
+                       if( !$this->unsaveUploadedFile() ) {
+                               return;
+                       }
+                       # Because it is probably checked and shouldn't be
+                       $this->mIgnoreWarning = false;
+                       
+                       $this->mainUploadForm();
+               } else if( 'submit' == $this->mAction || $this->mUploadClicked ) {
+                       $this->processUpload();
+               } else {
+                       $this->mainUploadForm();
+               }
+
+               $this->cleanupTempFile();
+       }
+
+       /**
+        * Do the upload
+        * Checks are made in SpecialUpload::execute()
+        *
+        * @access private
+        */
+       function processUpload(){
+               global $wgUser, $wgOut, $wgFileExtensions;
+               $details = null;
+               $value = null;
+               $value = $this->internalProcessUpload( $details );
+
+               switch($value) {
+                       case self::SUCCESS:
+                               $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() );
+                               break;
+
+                       case self::BEFORE_PROCESSING:
+                               break;
+
+                       case self::LARGE_FILE_SERVER:
+                               $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) );
+                               break;
+
+                       case self::EMPTY_FILE:
+                               $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) );
+                               break;
+
+                       case self::MIN_LENGHT_PARTNAME:
+                               $this->mainUploadForm( wfMsgHtml( 'minlength1' ) );
+                               break;
+
+                       case self::ILLEGAL_FILENAME:
+                               $filtered = $details['filtered'];
+                               $this->uploadError( wfMsgWikiHtml( 'illegalfilename', htmlspecialchars( $filtered ) ) );
+                               break;
+
+                       case self::PROTECTED_PAGE:
+                               $wgOut->showPermissionsErrorPage( $details['permissionserrors'] );
+                               break;
+
+                       case self::OVERWRITE_EXISTING_FILE:
+                               $errorText = $details['overwrite'];
+                               $overwrite = new WikiError( $wgOut->parse( $errorText ) );
+                               $this->uploadError( $overwrite->toString() );
+                               break;
+
+                       case self::FILETYPE_MISSING:
+                               $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) );
+                               break;
+
+                       case self::FILETYPE_BADTYPE:
+                               $finalExt = $details['finalExt'];
+                               $this->uploadError(
+                                       wfMsgExt( 'filetype-banned-type',
+                                               array( 'parseinline' ),
+                                               htmlspecialchars( $finalExt ),
+                                               implode(
+                                                       wfMsgExt( 'comma-separator', array( 'escapenoentities' ) ),
+                                                       $wgFileExtensions
+                                               )
+                                       )
+                               );
+                               break;
+
+                       case self::VERIFICATION_ERROR:
+                               $veri = $details['veri'];
+                               $this->uploadError( $veri->toString() );
+                               break;
+
+                       case self::UPLOAD_VERIFICATION_ERROR:
+                               $error = $details['error'];
+                               $this->uploadError( $error );
+                               break;
+
+                       case self::UPLOAD_WARNING:
+                               $warning = $details['warning'];
+                               $this->uploadWarning( $warning );
+                               break;
+
+                       case self::INTERNAL_ERROR:
+                               $internal = $details['internal'];
+                               $this->showError( $internal );
+                               break;
+
+                       default:
+                               throw new MWException( __METHOD__ . ": Unknown value `{$value}`" );
+               }
+       }
+
+       /**
+        * Really do the upload
+        * Checks are made in SpecialUpload::execute()
+        *
+        * @param array $resultDetails contains result-specific dict of additional values
+        *
+        * @access private
+        */
+       function internalProcessUpload( &$resultDetails ) {
+               global $wgUser;
+
+               if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) )
+               {
+                       wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." );
+                       return self::BEFORE_PROCESSING;
+               }
+
+               /**
+                * If there was no filename or a zero size given, give up quick.
+                */
+               if( trim( $this->mSrcName ) == '' || empty( $this->mFileSize ) ) {
+                       return self::EMPTY_FILE;
+               }
+
+               /* Check for curl error */
+               if( $this->mCurlError ) {
+                       return self::BEFORE_PROCESSING;
+               }
+
+               # Chop off any directories in the given filename
+               if( $this->mDesiredDestName ) {
+                       $basename = $this->mDesiredDestName;
+               } else {
+                       $basename = $this->mSrcName;
+               }
+               $filtered = wfBaseName( $basename );
+
+               /**
+                * We'll want to blacklist against *any* 'extension', and use
+                * only the final one for the whitelist.
+                */
+               list( $partname, $ext ) = $this->splitExtensions( $filtered );
+
+               if( count( $ext ) ) {
+                       $finalExt = $ext[count( $ext ) - 1];
+               } else {
+                       $finalExt = '';
+               }
+
+               # If there was more than one "extension", reassemble the base
+               # filename to prevent bogus complaints about length
+               if( count( $ext ) > 1 ) {
+                       for( $i = 0; $i < count( $ext ) - 1; $i++ )
+                               $partname .= '.' . $ext[$i];
+               }
+
+               if( strlen( $partname ) < 1 ) {
+                       return self::MIN_LENGHT_PARTNAME;
+               }
+
+               /**
+                * Filter out illegal characters, and try to make a legible name
+                * out of it. We'll strip some silently that Title would die on.
+                */
+               $filtered = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $filtered );
+               $nt = Title::makeTitleSafe( NS_IMAGE, $filtered );
+               if( is_null( $nt ) ) {
+                       $resultDetails = array( 'filtered' => $filtered );
+                       return self::ILLEGAL_FILENAME;
+               }
+               $this->mLocalFile = wfLocalFile( $nt );
+               $this->mDestName = $this->mLocalFile->getName();
+
+               /**
+                * If the image is protected, non-sysop users won't be able
+                * to modify it by uploading a new revision.
+                */
+               $permErrors = $nt->getUserPermissionsErrors( 'edit', $wgUser );
+               $permErrorsUpload = $nt->getUserPermissionsErrors( 'upload', $wgUser );
+               $permErrorsCreate = ( $nt->exists() ? array() : $nt->getUserPermissionsErrors( 'create', $wgUser ) );
+
+               if( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
+                       // merge all the problems into one list, avoiding duplicates
+                       $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
+                       $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
+                       $resultDetails = array( 'permissionserrors' => $permErrors );
+                       return self::PROTECTED_PAGE;
+               }
+
+               /**
+                * In some cases we may forbid overwriting of existing files.
+                */
+               $overwrite = $this->checkOverwrite( $this->mDestName );
+               if( $overwrite !== true ) {
+                       $resultDetails = array( 'overwrite' => $overwrite );
+                       return self::OVERWRITE_EXISTING_FILE;
+               }
+
+               /* Don't allow users to override the blacklist (check file extension) */
+               global $wgCheckFileExtensions, $wgStrictFileExtensions;
+               global $wgFileExtensions, $wgFileBlacklist;
+               if ($finalExt == '') {
+                       return self::FILETYPE_MISSING;
+               } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
+                               ($wgCheckFileExtensions && $wgStrictFileExtensions &&
+                                       !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
+                       $resultDetails = array( 'finalExt' => $finalExt );
+                       return self::FILETYPE_BADTYPE;
+               }
+
+               /**
+                * Look at the contents of the file; if we can recognize the
+                * type but it's corrupt or data of the wrong type, we should
+                * probably not accept it.
+                */
+               if( !$this->mStashed ) {
+                       $this->mFileProps = File::getPropsFromPath( $this->mTempPath, $finalExt );
+                       $this->checkMacBinary();
+                       $veri = $this->verify( $this->mTempPath, $finalExt );
+
+                       if( $veri !== true ) { //it's a wiki error...
+                               $resultDetails = array( 'veri' => $veri );
+                               return self::VERIFICATION_ERROR;
+                       }
+
+                       /**
+                        * Provide an opportunity for extensions to add further checks
+                        */
+                       $error = '';
+                       if( !wfRunHooks( 'UploadVerification',
+                                       array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
+                               $resultDetails = array( 'error' => $error );
+                               return self::UPLOAD_VERIFICATION_ERROR;
+                       }
+               }
+
+
+               /**
+                * Check for non-fatal conditions
+                */
+               if ( ! $this->mIgnoreWarning ) {
+                       $warning = '';
+
+                       global $wgCapitalLinks;
+                       if( $wgCapitalLinks ) {
+                               $filtered = ucfirst( $filtered );
+                       }
+                       if( $basename != $filtered ) {
+                               $warning .=  '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ).'</li>';
+                       }
+
+                       global $wgCheckFileExtensions;
+                       if ( $wgCheckFileExtensions ) {
+                               if ( !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) {
+                                       $warning .= '<li>' .
+                                       wfMsgExt( 'filetype-unwanted-type',
+                                               array( 'parseinline' ),
+                                               htmlspecialchars( $finalExt ),
+                                               implode(
+                                                       wfMsgExt( 'comma-separator', array( 'escapenoentities' ) ),
+                                                       $wgFileExtensions
+                                               )
+                                       ) . '</li>';
+                               }
+                       }
+
+                       global $wgUploadSizeWarning;
+                       if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) {
+                               $skin = $wgUser->getSkin();
+                               $wsize = $skin->formatSize( $wgUploadSizeWarning );
+                               $asize = $skin->formatSize( $this->mFileSize );
+                               $warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>';
+                       }
+                       if ( $this->mFileSize == 0 ) {
+                               $warning .= '<li>'.wfMsgHtml( 'emptyfile' ).'</li>';
+                       }
+
+                       if ( !$this->mDestWarningAck ) {
+                               $warning .= self::getExistsWarning( $this->mLocalFile );
+                       }
+                       
+                       $warning .= $this->getDupeWarning( $this->mTempPath );
+                       
+                       if( $warning != '' ) {
+                               /**
+                                * Stash the file in a temporary location; the user can choose
+                                * to let it through and we'll complete the upload then.
+                                */
+                               $resultDetails = array( 'warning' => $warning );
+                               return self::UPLOAD_WARNING;
+                       }
+               }
+
+               /**
+                * Try actually saving the thing...
+                * It will show an error form on failure.
+                */
+               $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
+                       $this->mCopyrightStatus, $this->mCopyrightSource );
+
+               $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
+                       File::DELETE_SOURCE, $this->mFileProps );
+               if ( !$status->isGood() ) {
+                       $resultDetails = array( 'internal' => $status->getWikiText() );
+                       return self::INTERNAL_ERROR;
+               } else {
+                       if ( $this->mWatchthis ) {
+                               global $wgUser;
+                               $wgUser->addWatch( $this->mLocalFile->getTitle() );
+                       }
+                       // Success, redirect to description page
+                       $img = null; // @todo: added to avoid passing a ref to null - should this be defined somewhere?
+                       wfRunHooks( 'UploadComplete', array( &$this ) );
+                       return self::SUCCESS;
+               }
+       }
+
+       /**
+        * Do existence checks on a file and produce a warning
+        * This check is static and can be done pre-upload via AJAX
+        * Returns an HTML fragment consisting of one or more LI elements if there is a warning
+        * Returns an empty string if there is no warning
+        */
+       static function getExistsWarning( $file ) {
+               global $wgUser, $wgContLang;
+               // Check for uppercase extension. We allow these filenames but check if an image
+               // with lowercase extension exists already
+               $warning = '';
+               $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+               if( strpos( $file->getName(), '.' ) == false ) {
+                       $partname = $file->getName();
+                       $rawExtension = '';
+               } else {
+                       $n = strrpos( $file->getName(), '.' );
+                       $rawExtension = substr( $file->getName(), $n + 1 );
+                       $partname = substr( $file->getName(), 0, $n );
+               }
+
+               $sk = $wgUser->getSkin();
+
+               if ( $rawExtension != $file->getExtension() ) {
+                       // We're not using the normalized form of the extension.
+                       // Normal form is lowercase, using most common of alternate
+                       // extensions (eg 'jpg' rather than 'JPEG').
+                       //
+                       // Check for another file using the normalized form...
+                       $nt_lc = Title::makeTitle( NS_IMAGE, $partname . '.' . $file->getExtension() );
+                       $file_lc = wfLocalFile( $nt_lc );
+               } else {
+                       $file_lc = false;
+               }
+
+               if( $file->exists() ) {
+                       $dlink = $sk->makeKnownLinkObj( $file->getTitle() );
+                       if ( $file->allowInlineDisplay() ) {
+                               $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline' ),
+                                       $file->getName(), $align, array(), false, true );
+                       } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) {
+                               $icon = $file->iconThumb();
+                               $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
+                                       $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>';
+                       } else {
+                               $dlink2 = '';
+                       }
+
+                       $warning .= '<li>' . wfMsgExt( 'fileexists', array('parseinline','replaceafter'), $dlink ) . '</li>' . $dlink2;
+
+               } elseif( $file->getTitle()->getArticleID() ) {
+                       $lnk = $sk->makeKnownLinkObj( $file->getTitle(), '', 'redirect=no' );
+                       $warning .= '<li>' . wfMsgExt( 'filepageexists', array( 'parseinline', 'replaceafter' ), $lnk ) . '</li>';
+               } elseif ( $file_lc && $file_lc->exists() ) {
+                       # Check if image with lowercase extension exists.
+                       # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension
+                       $dlink = $sk->makeKnownLinkObj( $nt_lc );
+                       if ( $file_lc->allowInlineDisplay() ) {
+                               $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline' ),
+                                       $nt_lc->getText(), $align, array(), false, true );
+                       } elseif ( !$file_lc->allowInlineDisplay() && $file_lc->isSafeFile() ) {
+                               $icon = $file_lc->iconThumb();
+                               $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
+                                       $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>';
+                       } else {
+                               $dlink2 = '';
+                       }
+
+                       $warning .= '<li>' .
+                               wfMsgExt( 'fileexists-extension', 'parsemag',
+                                       $file->getTitle()->getPrefixedText(), $dlink ) .
+                               '</li>' . $dlink2;
+
+               } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' )
+                       && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) )
+               {
+                       # Check for filenames like 50px- or 180px-, these are mostly thumbnails
+                       $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $rawExtension );
+                       $file_thb = wfLocalFile( $nt_thb );
+                       if ($file_thb->exists() ) {
+                               # Check if an image without leading '180px-' (or similiar) exists
+                               $dlink = $sk->makeKnownLinkObj( $nt_thb);
+                               if ( $file_thb->allowInlineDisplay() ) {
+                                       $dlink2 = $sk->makeImageLinkObj( $nt_thb,
+                                               wfMsgExt( 'fileexists-thumb', 'parseinline' ),
+                                               $nt_thb->getText(), $align, array(), false, true );
+                               } elseif ( !$file_thb->allowInlineDisplay() && $file_thb->isSafeFile() ) {
+                                       $icon = $file_thb->iconThumb();
+                                       $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
+                                               $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' .
+                                               $dlink . '</div>';
+                               } else {
+                                       $dlink2 = '';
+                               }
+
+                               $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) .
+                                       '</li>' . $dlink2;
+                       } else {
+                               # Image w/o '180px-' does not exists, but we do not like these filenames
+                               $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' ,
+                                       substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>';
+                       }
+               }
+
+               $filenamePrefixBlacklist = self::getFilenamePrefixBlacklist();
+               # Do the match
+               foreach( $filenamePrefixBlacklist as $prefix ) {
+                       if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
+                               $warning .= '<li>' . wfMsgExt( 'filename-bad-prefix', 'parseinline', $prefix ) . '</li>';
+                               break;
+                       }
+               }
+
+               if ( $file->wasDeleted() && !$file->exists() ) {
+                       # If the file existed before and was deleted, warn the user of this
+                       # Don't bother doing so if the file exists now, however
+                       $ltitle = SpecialPage::getTitleFor( 'Log' );
+                       $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ),
+                               'type=delete&page=' . $file->getTitle()->getPrefixedUrl() );
+                       $warning .= '<li>' . wfMsgWikiHtml( 'filewasdeleted', $llink ) . '</li>';
+               }
+               return $warning;
+       }
+
+       /**
+        * Get a list of warnings
+        *
+        * @param string local filename, e.g. 'file exists', 'non-descriptive filename'
+        * @return array list of warning messages
+        */
+       static function ajaxGetExistsWarning( $filename ) {
+               $file = wfFindFile( $filename );
+               if( !$file ) {
+                       // Force local file so we have an object to do further checks against
+                       // if there isn't an exact match...
+                       $file = wfLocalFile( $filename );
+               }
+               $s = '&nbsp;';
+               if ( $file ) {
+                       $warning = self::getExistsWarning( $file );
+                       if ( $warning !== '' ) {
+                               $s = "<ul>$warning</ul>";
+                       }
+               }
+               return $s;
+       }
+
+       /**
+        * Render a preview of a given license for the AJAX preview on upload
+        *
+        * @param string $license
+        * @return string
+        */
+       public static function ajaxGetLicensePreview( $license ) {
+               global $wgParser, $wgUser;
+               $text = '{{' . $license . '}}';
+               $title = Title::makeTitle( NS_IMAGE, 'Sample.jpg' );
+               $options = ParserOptions::newFromUser( $wgUser );
+
+               // Expand subst: first, then live templates...
+               $text = $wgParser->preSaveTransform( $text, $title, $wgUser, $options );
+               $output = $wgParser->parse( $text, $title, $options );
+
+               return $output->getText();
+       }
+       
+       /**
+        * Check for duplicate files and throw up a warning before the upload
+        * completes.
+        */
+       function getDupeWarning( $tempfile ) {
+               $hash = File::sha1Base36( $tempfile );
+               $dupes = RepoGroup::singleton()->findBySha1( $hash );
+               if( $dupes ) {
+                       global $wgOut;
+                       $msg = "<gallery>";
+                       foreach( $dupes as $file ) {
+                               $title = $file->getTitle();
+                               $msg .= $title->getPrefixedText() .
+                                       "|" . $title->getText() . "\n";
+                       }
+                       $msg .= "</gallery>";
+                       return "<li>" .
+                               wfMsgExt( "file-exists-duplicate", array( "parse" ), count( $dupes ) ) .
+                               $wgOut->parse( $msg ) .
+                               "</li>\n";
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * Get a list of blacklisted filename prefixes from [[MediaWiki:filename-prefix-blacklist]]
+        *
+        * @return array list of prefixes
+        */
+       public static function getFilenamePrefixBlacklist() {
+               $blacklist = array();
+               $message = wfMsgForContent( 'filename-prefix-blacklist' );
+               if( $message && !( wfEmptyMsg( 'filename-prefix-blacklist', $message ) || $message == '-' ) ) {
+                       $lines = explode( "\n", $message );
+                       foreach( $lines as $line ) {
+                               // Remove comment lines
+                               $comment = substr( trim( $line ), 0, 1 );
+                               if ( $comment == '#' || $comment == '' ) {
+                                       continue;
+                               }
+                               // Remove additional comments after a prefix
+                               $comment = strpos( $line, '#' );
+                               if ( $comment > 0 ) {
+                                       $line = substr( $line, 0, $comment-1 );
+                               }
+                               $blacklist[] = trim( $line );
+                       }
+               }
+               return $blacklist;
+       }
+
+       /**
+        * Stash a file in a temporary directory for later processing
+        * after the user has confirmed it.
+        *
+        * If the user doesn't explicitly cancel or accept, these files
+        * can accumulate in the temp directory.
+        *
+        * @param string $saveName - the destination filename
+        * @param string $tempName - the source temporary file to save
+        * @return string - full path the stashed file, or false on failure
+        * @access private
+        */
+       function saveTempUploadedFile( $saveName, $tempName ) {
+               global $wgOut;
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $status = $repo->storeTemp( $saveName, $tempName );
+               if ( !$status->isGood() ) {
+                       $this->showError( $status->getWikiText() );
+                       return false;
+               } else {
+                       return $status->value;
+               }
+       }
+
+       /**
+        * Stash a file in a temporary directory for later processing,
+        * and save the necessary descriptive info into the session.
+        * Returns a key value which will be passed through a form
+        * to pick up the path info on a later invocation.
+        *
+        * @return int
+        * @access private
+        */
+       function stashSession() {
+               $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
+
+               if( !$stash ) {
+                       # Couldn't save the file.
+                       return false;
+               }
+
+               $key = mt_rand( 0, 0x7fffffff );
+               $_SESSION['wsUploadData'][$key] = array(
+                       'mTempPath'       => $stash,
+                       'mFileSize'       => $this->mFileSize,
+                       'mSrcName'        => $this->mSrcName,
+                       'mFileProps'      => $this->mFileProps,
+                       'version'         => self::SESSION_VERSION,
+               );
+               return $key;
+       }
+
+       /**
+        * Remove a temporarily kept file stashed by saveTempUploadedFile().
+        * @access private
+        * @return success
+        */
+       function unsaveUploadedFile() {
+               global $wgOut;
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $success = $repo->freeTemp( $this->mTempPath );
+               if ( ! $success ) {
+                       $wgOut->showFileDeleteError( $this->mTempPath );
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+
+       /* -------------------------------------------------------------- */
+
+       /**
+        * @param string $error as HTML
+        * @access private
+        */
+       function uploadError( $error ) {
+               global $wgOut;
+               $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" );
+               $wgOut->addHTML( '<span class="error">' . $error . '</span>' );
+       }
+
+       /**
+        * There's something wrong with this file, not enough to reject it
+        * totally but we require manual intervention to save it for real.
+        * Stash it away, then present a form asking to confirm or cancel.
+        *
+        * @param string $warning as HTML
+        * @access private
+        */
+       function uploadWarning( $warning ) {
+               global $wgOut;
+               global $wgUseCopyrightUpload;
+
+               $this->mSessionKey = $this->stashSession();
+               if( !$this->mSessionKey ) {
+                       # Couldn't save file; an error has been displayed so let's go.
+                       return;
+               }
+
+               $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" );
+               $wgOut->addHTML( '<ul class="warning">' . $warning . "</ul>\n" );
+
+               $titleObj = SpecialPage::getTitleFor( 'Upload' );
+
+               if ( $wgUseCopyrightUpload ) {
+                       $copyright = Xml::hidden( 'wpUploadCopyStatus', $this->mCopyrightStatus ) . "\n" .
+                                       Xml::hidden( 'wpUploadSource', $this->mCopyrightSource ) . "\n";
+               } else {
+                       $copyright = '';
+               }
+
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ),
+                                'enctype' => 'multipart/form-data', 'id' => 'uploadwarning' ) ) . "\n" .
+                       Xml::hidden( 'wpIgnoreWarning', '1' ) . "\n" .
+                       Xml::hidden( 'wpSessionKey', $this->mSessionKey ) . "\n" .
+                       Xml::hidden( 'wpUploadDescription', $this->mComment ) . "\n" .
+                       Xml::hidden( 'wpLicense', $this->mLicense ) . "\n" .
+                       Xml::hidden( 'wpDestFile', $this->mDesiredDestName ) . "\n" .
+                       Xml::hidden( 'wpWatchthis', $this->mWatchthis ) . "\n" .
+                       "{$copyright}<br />" .
+                       Xml::submitButton( wfMsg( 'ignorewarning' ), array ( 'name' => 'wpUpload', 'id' => 'wpUpload', 'checked' => 'checked' ) ) . ' ' .
+                       Xml::submitButton( wfMsg( 'reuploaddesc' ), array ( 'name' => 'wpReUpload', 'id' => 'wpReUpload' ) ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+       }
+
+       /**
+        * Displays the main upload form, optionally with a highlighted
+        * error message up at the top.
+        *
+        * @param string $msg as HTML
+        * @access private
+        */
+       function mainUploadForm( $msg='' ) {
+               global $wgOut, $wgUser, $wgLang, $wgMaxUploadSize;
+               global $wgUseCopyrightUpload, $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview;
+               global $wgRequest, $wgAllowCopyUploads;
+               global $wgStylePath, $wgStyleVersion;
+
+               $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck;
+               $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview;
+
+               $adc = wfBoolToStr( $useAjaxDestCheck );
+               $alp = wfBoolToStr( $useAjaxLicensePreview );
+               $autofill = wfBoolToStr( $this->mDesiredDestName == '' );
+
+               $wgOut->addScript( "<script type=\"text/javascript\">
+wgAjaxUploadDestCheck = {$adc};
+wgAjaxLicensePreview = {$alp};
+wgUploadAutoFill = {$autofill};
+</script>" );
+               $wgOut->addScriptFile( 'upload.js' );
+               $wgOut->addScriptFile( 'edit.js' ); // For <charinsert> support
+
+               if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) )
+               {
+                       wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" );
+                       return false;
+               }
+
+               if( $this->mDesiredDestName ) {
+                       $title = Title::makeTitleSafe( NS_IMAGE, $this->mDesiredDestName );
+                       // Show a subtitle link to deleted revisions (to sysops et al only)
+                       if( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) {
+                               $link = wfMsgExt(
+                                       $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted',
+                                       array( 'parse', 'replaceafter' ),
+                                       $wgUser->getSkin()->makeKnownLinkObj(
+                                               SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ),
+                                               wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count )
+                                       )
+                               );
+                               $wgOut->addHtml( "<div id=\"contentSub2\">{$link}</div>" );
+                       }
+
+                       // Show the relevant lines from deletion log (for still deleted files only)
+                       if( $title instanceof Title && $title->isDeleted() > 0 && !$title->exists() ) {
+                               $this->showDeletionLog( $wgOut, $title->getPrefixedText() );
+                       }
+               }
+
+               $cols = intval($wgUser->getOption( 'cols' ));
+
+               if( $wgUser->getOption( 'editwidth' ) ) {
+                       $width = " style=\"width:100%\"";
+               } else {
+                       $width = '';
+               }
+
+               if ( '' != $msg ) {
+                       $sub = wfMsgHtml( 'uploaderror' );
+                       $wgOut->addHTML( "<h2>{$sub}</h2>\n" .
+                         "<span class='error'>{$msg}</span>\n" );
+               }
+               $wgOut->addHTML( '<div id="uploadtext">' );
+               $wgOut->addWikiMsg( 'uploadtext', $this->mDesiredDestName );
+               $wgOut->addHTML( "</div>\n" );
+
+               # Print a list of allowed file extensions, if so configured.  We ignore
+               # MIME type here, it's incomprehensible to most people and too long.
+               global $wgCheckFileExtensions, $wgStrictFileExtensions,
+               $wgFileExtensions, $wgFileBlacklist;
+
+               $allowedExtensions = '';
+               if( $wgCheckFileExtensions ) {
+                       $delim = wfMsgExt( 'comma-separator', array( 'escapenoentities' ) );
+                       if( $wgStrictFileExtensions ) {
+                               # Everything not permitted is banned
+                               $extensionsList =
+                                       '<div id="mw-upload-permitted">' .
+                                       wfMsgWikiHtml( 'upload-permitted', implode( $wgFileExtensions, $delim ) ) .
+                                       "</div>\n";
+                       } else {
+                               # We have to list both preferred and prohibited
+                               $extensionsList =
+                                       '<div id="mw-upload-preferred">' .
+                                       wfMsgWikiHtml( 'upload-preferred', implode( $wgFileExtensions, $delim ) ) .
+                                       "</div>\n" .
+                                       '<div id="mw-upload-prohibited">' .
+                                       wfMsgWikiHtml( 'upload-prohibited', implode( $wgFileBlacklist, $delim ) ) .
+                                       "</div>\n";
+                       }
+               } else {
+                       # Everything is permitted.
+                       $extensionsList = '';
+               }
+
+               # Get the maximum file size from php.ini as $wgMaxUploadSize works for uploads from URL via CURL only
+               # See http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize for possible values of upload_max_filesize
+               $val = trim( ini_get( 'upload_max_filesize' ) );
+               $last = strtoupper( ( substr( $val, -1 ) ) );
+               switch( $last ) {
+                       case 'G':
+                               $val2 = substr( $val, 0, -1 ) * 1024 * 1024 * 1024;
+                               break;
+                       case 'M':
+                               $val2 = substr( $val, 0, -1 ) * 1024 * 1024;
+                               break;
+                       case 'K':
+                               $val2 = substr( $val, 0, -1 ) * 1024;
+                               break;
+                       default:
+                               $val2 = $val;
+               }
+               $val2 = $wgAllowCopyUploads ? min( $wgMaxUploadSize, $val2 ) : $val2;
+               $maxUploadSize = '<div id="mw-upload-maxfilesize">' . 
+                       wfMsgExt( 'upload-maxfilesize', array( 'parseinline', 'escapenoentities' ), 
+                               $wgLang->formatSize( $val2 ) ) .
+                               "</div>\n";
+
+               $sourcefilename = wfMsgExt( 'sourcefilename', array( 'parseinline', 'escapenoentities' ) );
+        $destfilename = wfMsgExt( 'destfilename', array( 'parseinline', 'escapenoentities' ) ); 
+               
+               $summary = wfMsgExt( 'fileuploadsummary', 'parseinline' );
+
+               $licenses = new Licenses();
+               $license = wfMsgExt( 'license', array( 'parseinline' ) );
+               $nolicense = wfMsgHtml( 'nolicense' );
+               $licenseshtml = $licenses->getHtml();
+
+               $ulb = wfMsgHtml( 'uploadbtn' );
+
+
+               $titleObj = SpecialPage::getTitleFor( 'Upload' );
+
+               $encDestName = htmlspecialchars( $this->mDesiredDestName );
+
+               $watchChecked = $this->watchCheck()
+                       ? 'checked="checked"'
+                       : '';
+               $warningChecked = $this->mIgnoreWarning ? 'checked' : '';
+
+               // Prepare form for upload or upload/copy
+               if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) {
+                       $filename_form =
+                               "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " .
+                                  "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked='checked' />" .
+                                "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
+                                  "onfocus='" .
+                                    "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" .
+                                    "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")' " .
+                                    "onchange='fillDestFilename(\"wpUploadFile\")' size='60' />" .
+                               wfMsgHTML( 'upload_source_file' ) . "<br/>" .
+                               "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' " .
+                                 "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" .
+                               "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " .
+                                 "onfocus='" .
+                                   "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" .
+                                   "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")' " .
+                                   "onchange='fillDestFilename(\"wpUploadFileURL\")' size='60' disabled='disabled' />" .
+                               wfMsgHtml( 'upload_source_url' ) ;
+               } else {
+                       $filename_form =
+                               "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
+                               ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
+                               "size='60' />" .
+                               "<input type='hidden' name='wpSourceType' value='file' />" ;
+               }
+               if ( $useAjaxDestCheck ) {
+                       $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'>&nbsp;</td></tr>";
+                       $destOnkeyup = 'onkeyup="wgUploadWarningObj.keypress();"';
+               } else {
+                       $warningRow = '';
+                       $destOnkeyup = '';
+               }
+
+               $encComment = htmlspecialchars( $this->mComment );
+
+               $wgOut->addHTML(
+                        Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL(),
+                                'enctype' => 'multipart/form-data', 'id' => 'mw-upload-form' ) ) .
+                        Xml::openElement( 'fieldset' ) .
+                        Xml::element( 'legend', null, wfMsg( 'upload' ) ) .
+                        Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-upload-table' ) ) .
+                        "<tr>
+                               {$this->uploadFormTextTop}
+                               <td class='mw-label'>
+                                       <label for='wpUploadFile'>{$sourcefilename}</label>
+                               </td>
+                               <td class='mw-input'>
+                                       {$filename_form}
+                               </td>
+                       </tr>
+                       <tr>
+                               <td></td>
+                               <td>
+                                       {$maxUploadSize}
+                                       {$extensionsList}
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>
+                                       <label for='wpDestFile'>{$destfilename}</label>
+                               </td>
+                               <td class='mw-input'>
+                                       <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='60'
+                                               value=\"{$encDestName}\" onchange='toggleFilenameFiller()' $destOnkeyup />
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class='mw-label'>
+                                       <label for='wpUploadDescription'>{$summary}</label>
+                               </td>
+                               <td class='mw-input'>
+                                       <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6'
+                                               cols='{$cols}'{$width}>$encComment</textarea>
+                                       {$this->uploadFormTextAfterSummary}
+                               </td>
+                       </tr>
+                       <tr>"
+               );
+
+               if ( $licenseshtml != '' ) {
+                       global $wgStylePath;
+                       $wgOut->addHTML( "
+                                       <td class='mw-label'>
+                                               <label for='wpLicense'>$license</label>
+                                       </td>
+                                       <td class='mw-input'>
+                                               <select name='wpLicense' id='wpLicense' tabindex='4'
+                                                       onchange='licenseSelectorCheck()'>
+                                                       <option value=''>$nolicense</option>
+                                                       $licenseshtml
+                                               </select>
+                                       </td>
+                               </tr>
+                               <tr>"
+                       );
+                       if( $useAjaxLicensePreview ) {
+                               $wgOut->addHtml( "
+                                               <td></td>
+                                               <td id=\"mw-license-preview\"></td>
+                                       </tr>
+                                       <tr>"
+                               );
+                       }
+               }
+
+               if ( $wgUseCopyrightUpload ) {
+                       $filestatus = wfMsgExt( 'filestatus', 'escapenoentities' );
+                       $copystatus =  htmlspecialchars( $this->mCopyrightStatus );
+                       $filesource = wfMsgExt( 'filesource', 'escapenoentities' );
+                       $uploadsource = htmlspecialchars( $this->mCopyrightSource );
+
+                       $wgOut->addHTML( "
+                                       <td class='mw-label' style='white-space: nowrap;'>
+                                               <label for='wpUploadCopyStatus'>$filestatus</label></td>
+                                       <td class='mw-input'>
+                                               <input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus'
+                                                       value=\"$copystatus\" size='60' />
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td class='mw-label'>
+                                               <label for='wpUploadCopyStatus'>$filesource</label>
+                                       </td>
+                                       <td class='mw-input'>
+                                               <input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus'
+                                                       value=\"$uploadsource\" size='60' />
+                                       </td>
+                               </tr>
+                               <tr>"
+                       );
+               }
+
+               $wgOut->addHtml( "
+                               <td></td>
+                               <td>
+                                       <input tabindex='7' type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' />
+                                       <label for='wpWatchthis'>" . wfMsgHtml( 'watchthisupload' ) . "</label>
+                                       <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' $warningChecked/>
+                                       <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label>
+                               </td>
+                       </tr>
+                       $warningRow
+                       <tr>
+                               <td></td>
+                                       <td class='mw-input'>
+                                               <input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\"" . $wgUser->getSkin()->tooltipAndAccesskey( 'upload' ) . " />
+                                       </td>
+                       </tr>
+                       <tr>
+                               <td></td>
+                               <td class='mw-input'>"
+               );
+               $wgOut->addWikiText( wfMsgForContent( 'edittools' ) );
+               $wgOut->addHTML( "
+                               </td>
+                       </tr>" .
+                       Xml::closeElement( 'table' ) .
+                       Xml::hidden( 'wpDestFileWarningAck', '', array( 'id' => 'wpDestFileWarningAck' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' )
+               );
+               $uploadfooter = wfMsgNoTrans( 'uploadfooter' );
+               if( $uploadfooter != '-' && !wfEmptyMsg( 'uploadfooter', $uploadfooter ) ){
+                       $wgOut->addWikiText( '<div id="mw-upload-footer-message">' . $uploadfooter . '</div>' );
+               }
+       }
+
+       /* -------------------------------------------------------------- */
+       
+       /**
+        * See if we should check the 'watch this page' checkbox on the form
+        * based on the user's preferences and whether we're being asked
+        * to create a new file or update an existing one.
+        *
+        * In the case where 'watch edits' is off but 'watch creations' is on,
+        * we'll leave the box unchecked.
+        *
+        * Note that the page target can be changed *on the form*, so our check
+        * state can get out of sync.
+        */
+       function watchCheck() {
+               global $wgUser;
+               if( $wgUser->getOption( 'watchdefault' ) ) {
+                       // Watch all edits!
+                       return true;
+               }
+               
+               $local = wfLocalFile( $this->mDesiredDestName );
+               if( $local && $local->exists() ) {
+                       // We're uploading a new version of an existing file.
+                       // No creation, so don't watch it if we're not already.
+                       return $local->getTitle()->userIsWatching();
+               } else {
+                       // New page should get watched if that's our option.
+                       return $wgUser->getOption( 'watchcreations' );
+               }
+       }
+
+       /**
+        * Split a file into a base name and all dot-delimited 'extensions'
+        * on the end. Some web server configurations will fall back to
+        * earlier pseudo-'extensions' to determine type and execute
+        * scripts, so the blacklist needs to check them all.
+        *
+        * @return array
+        */
+       function splitExtensions( $filename ) {
+               $bits = explode( '.', $filename );
+               $basename = array_shift( $bits );
+               return array( $basename, $bits );
+       }
+
+       /**
+        * Perform case-insensitive match against a list of file extensions.
+        * Returns true if the extension is in the list.
+        *
+        * @param string $ext
+        * @param array $list
+        * @return bool
+        */
+       function checkFileExtension( $ext, $list ) {
+               return in_array( strtolower( $ext ), $list );
+       }
+
+       /**
+        * Perform case-insensitive match against a list of file extensions.
+        * Returns true if any of the extensions are in the list.
+        *
+        * @param array $ext
+        * @param array $list
+        * @return bool
+        */
+       function checkFileExtensionList( $ext, $list ) {
+               foreach( $ext as $e ) {
+                       if( in_array( strtolower( $e ), $list ) ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Verifies that it's ok to include the uploaded file
+        *
+        * @param string $tmpfile the full path of the temporary file to verify
+        * @param string $extension The filename extension that the file is to be served with
+        * @return mixed true of the file is verified, a WikiError object otherwise.
+        */
+       function verify( $tmpfile, $extension ) {
+               #magically determine mime type
+               $magic = MimeMagic::singleton();
+               $mime = $magic->guessMimeType($tmpfile,false);
+
+               #check mime type, if desired
+               global $wgVerifyMimeType;
+               if ($wgVerifyMimeType) {
+
+                 wfDebug ( "\n\nmime: <$mime> extension: <$extension>\n\n");
+                       #check mime type against file extension
+                       if( !$this->verifyExtension( $mime, $extension ) ) {
+                               return new WikiErrorMsg( 'uploadcorrupt' );
+                       }
+
+                       #check mime type blacklist
+                       global $wgMimeTypeBlacklist;
+                       if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
+                               && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
+                               return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) );
+                       }
+               }
+
+               #check for htmlish code and javascript
+               if( $this->detectScript ( $tmpfile, $mime, $extension ) ) {
+                       return new WikiErrorMsg( 'uploadscripted' );
+               }
+
+               /**
+               * Scan the uploaded file for viruses
+               */
+               $virus= $this->detectVirus($tmpfile);
+               if ( $virus ) {
+                       return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) );
+               }
+
+               wfDebug( __METHOD__.": all clear; passing.\n" );
+               return true;
+       }
+
+       /**
+        * Checks if the mime type of the uploaded file matches the file extension.
+        *
+        * @param string $mime the mime type of the uploaded file
+        * @param string $extension The filename extension that the file is to be served with
+        * @return bool
+        */
+       function verifyExtension( $mime, $extension ) {
+               $magic = MimeMagic::singleton();
+
+               if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' )
+                       if ( ! $magic->isRecognizableExtension( $extension ) ) {
+                               wfDebug( __METHOD__.": passing file with unknown detected mime type; " .
+                                       "unrecognized extension '$extension', can't verify\n" );
+                               return true;
+                       } else {
+                               wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ".
+                                       "recognized extension '$extension', so probably invalid file\n" );
+                               return false;
+                       }
+
+               $match= $magic->isMatchingExtension($extension,$mime);
+
+               if ($match===NULL) {
+                       wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" );
+                       return true;
+               } elseif ($match===true) {
+                       wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" );
+
+                       #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
+                       return true;
+
+               } else {
+                       wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" );
+                       return false;
+               }
+       }
+
+       /**
+        * Heuristic for detecting files that *could* contain JavaScript instructions or
+        * things that may look like HTML to a browser and are thus
+        * potentially harmful. The present implementation will produce false positives in some situations.
+        *
+        * @param string $file Pathname to the temporary upload file
+        * @param string $mime The mime type of the file
+        * @param string $extension The extension of the file
+        * @return bool true if the file contains something looking like embedded scripts
+        */
+       function detectScript($file, $mime, $extension) {
+               global $wgAllowTitlesInSVG;
+
+               #ugly hack: for text files, always look at the entire file.
+               #For binarie field, just check the first K.
+
+               if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file );
+               else {
+                       $fp = fopen( $file, 'rb' );
+                       $chunk = fread( $fp, 1024 );
+                       fclose( $fp );
+               }
+
+               $chunk= strtolower( $chunk );
+
+               if (!$chunk) return false;
+
+               #decode from UTF-16 if needed (could be used for obfuscation).
+               if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE";
+               elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE";
+               else $enc= NULL;
+
+               if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk);
+
+               $chunk= trim($chunk);
+
+               #FIXME: convert from UTF-16 if necessarry!
+
+               wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n");
+
+               #check for HTML doctype
+               if (eregi("<!DOCTYPE *X?HTML",$chunk)) return true;
+
+               /**
+               * Internet Explorer for Windows performs some really stupid file type
+               * autodetection which can cause it to interpret valid image files as HTML
+               * and potentially execute JavaScript, creating a cross-site scripting
+               * attack vectors.
+               *
+               * Apple's Safari browser also performs some unsafe file type autodetection
+               * which can cause legitimate files to be interpreted as HTML if the
+               * web server is not correctly configured to send the right content-type
+               * (or if you're really uploading plain text and octet streams!)
+               *
+               * Returns true if IE is likely to mistake the given file for HTML.
+               * Also returns true if Safari would mistake the given file for HTML
+               * when served with a generic content-type.
+               */
+
+               $tags = array(
+                       '<body',
+                       '<head',
+                       '<html',   #also in safari
+                       '<img',
+                       '<pre',
+                       '<script', #also in safari
+                       '<table'
+                       );
+               if( ! $wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
+                       $tags[] = '<title';
+               }
+
+               foreach( $tags as $tag ) {
+                       if( false !== strpos( $chunk, $tag ) ) {
+                               return true;
+                       }
+               }
+
+               /*
+               * look for javascript
+               */
+
+               #resolve entity-refs to look at attributes. may be harsh on big files... cache result?
+               $chunk = Sanitizer::decodeCharReferences( $chunk );
+
+               #look for script-types
+               if (preg_match('!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim',$chunk)) return true;
+
+               #look for html-style script-urls
+               if (preg_match('!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
+
+               #look for css-style script-urls
+               if (preg_match('!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
+
+               wfDebug("SpecialUpload::detectScript: no scripts found\n");
+               return false;
+       }
+
+       /**
+        * Generic wrapper function for a virus scanner program.
+        * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
+        * $wgAntivirusRequired may be used to deny upload if the scan fails.
+        *
+        * @param string $file Pathname to the temporary upload file
+        * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
+        *         or a string containing feedback from the virus scanner if a virus was found.
+        *         If textual feedback is missing but a virus was found, this function returns true.
+        */
+       function detectVirus($file) {
+               global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
+
+               if ( !$wgAntivirus ) {
+                       wfDebug( __METHOD__.": virus scanner disabled\n");
+                       return NULL;
+               }
+
+               if ( !$wgAntivirusSetup[$wgAntivirus] ) {
+                       wfDebug( __METHOD__.": unknown virus scanner: $wgAntivirus\n" );
+                       # @TODO: localise
+                       $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" );
+                       return "unknown antivirus: $wgAntivirus";
+               }
+
+               # look up scanner configuration
+               $command = $wgAntivirusSetup[$wgAntivirus]["command"];
+               $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"];
+               $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ?
+                       $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null;
+
+               if ( strpos( $command,"%f" ) === false ) {
+                       # simple pattern: append file to scan
+                       $command .= " " . wfEscapeShellArg( $file );
+               } else {
+                       # complex pattern: replace "%f" with file to scan
+                       $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
+               }
+
+               wfDebug( __METHOD__.": running virus scan: $command \n" );
+
+               # execute virus scanner
+               $exitCode = false;
+
+               #NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
+               #      that does not seem to be worth the pain.
+               #      Ask me (Duesentrieb) about it if it's ever needed.
+               $output = array();
+               if ( wfIsWindows() ) {
+                       exec( "$command", $output, $exitCode );
+               } else {
+                       exec( "$command 2>&1", $output, $exitCode );
+               }
+
+               # map exit code to AV_xxx constants.
+               $mappedCode = $exitCode;
+               if ( $exitCodeMap ) {
+                       if ( isset( $exitCodeMap[$exitCode] ) ) {
+                               $mappedCode = $exitCodeMap[$exitCode];
+                       } elseif ( isset( $exitCodeMap["*"] ) ) {
+                               $mappedCode = $exitCodeMap["*"];
+                       }
+               }
+
+               if ( $mappedCode === AV_SCAN_FAILED ) {
+                       # scan failed (code was mapped to false by $exitCodeMap)
+                       wfDebug( __METHOD__.": failed to scan $file (code $exitCode).\n" );
+
+                       if ( $wgAntivirusRequired ) {
+                               return "scan failed (code $exitCode)";
+                       } else {
+                               return NULL;
+                       }
+               } else if ( $mappedCode === AV_SCAN_ABORTED ) {
+                       # scan failed because filetype is unknown (probably imune)
+                       wfDebug( __METHOD__.": unsupported file type $file (code $exitCode).\n" );
+                       return NULL;
+               } else if ( $mappedCode === AV_NO_VIRUS ) {
+                       # no virus found
+                       wfDebug( __METHOD__.": file passed virus scan.\n" );
+                       return false;
+               } else {
+                       $output = join( "\n", $output );
+                       $output = trim( $output );
+
+                       if ( !$output ) {
+                               $output = true; #if there's no output, return true
+                       } elseif ( $msgPattern ) {
+                               $groups = array();
+                               if ( preg_match( $msgPattern, $output, $groups ) ) {
+                                       if ( $groups[1] ) {
+                                               $output = $groups[1];
+                                       }
+                               }
+                       }
+
+                       wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" );
+                       return $output;
+               }
+       }
+
+       /**
+        * Check if the temporary file is MacBinary-encoded, as some uploads
+        * from Internet Explorer on Mac OS Classic and Mac OS X will be.
+        * If so, the data fork will be extracted to a second temporary file,
+        * which will then be checked for validity and either kept or discarded.
+        *
+        * @access private
+        */
+       function checkMacBinary() {
+               $macbin = new MacBinary( $this->mTempPath );
+               if( $macbin->isValid() ) {
+                       $dataFile = tempnam( wfTempDir(), "WikiMacBinary" );
+                       $dataHandle = fopen( $dataFile, 'wb' );
+
+                       wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" );
+                       $macbin->extractData( $dataHandle );
+
+                       $this->mTempPath = $dataFile;
+                       $this->mFileSize = $macbin->dataForkLength();
+
+                       // We'll have to manually remove the new file if it's not kept.
+                       $this->mRemoveTempFile = true;
+               }
+               $macbin->close();
+       }
+
+       /**
+        * If we've modified the upload file we need to manually remove it
+        * on exit to clean up.
+        * @access private
+        */
+       function cleanupTempFile() {
+               if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) {
+                       wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" );
+                       unlink( $this->mTempPath );
+               }
+       }
+
+       /**
+        * Check if there's an overwrite conflict and, if so, if restrictions
+        * forbid this user from performing the upload.
+        *
+        * @return mixed true on success, WikiError on failure
+        * @access private
+        */
+       function checkOverwrite( $name ) {
+               $img = wfFindFile( $name );
+
+               $error = '';
+               if( $img ) {
+                       global $wgUser, $wgOut;
+                       if( $img->isLocal() ) {
+                               if( !self::userCanReUpload( $wgUser, $img->name ) ) {
+                                       $error = 'fileexists-forbidden';
+                               }
+                       } else {
+                               if( !$wgUser->isAllowed( 'reupload' ) ||
+                                   !$wgUser->isAllowed( 'reupload-shared' ) ) {
+                                       $error = "fileexists-shared-forbidden";
+                               }
+                       }
+               }
+
+               if( $error ) {
+                       $errorText = wfMsg( $error, wfEscapeWikiText( $img->getName() ) );
+                       return $errorText;
+               }
+
+               // Rockin', go ahead and upload
+               return true;
+       }
+
+        /**
+        * Check if a user is the last uploader
+        *
+        * @param User $user
+        * @param string $img, image name
+        * @return bool
+        */
+       public static function userCanReUpload( User $user, $img ) {
+               if( $user->isAllowed( 'reupload' ) )
+                       return true; // non-conditional
+               if( !$user->isAllowed( 'reupload-own' ) )
+                       return false;
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $row = $dbr->selectRow('image',
+               /* SELECT */ 'img_user',
+               /* WHERE */ array( 'img_name' => $img )
+               );
+               if ( !$row )
+                       return false;
+
+               return $user->getId() == $row->img_user;
+       }
+
+       /**
+        * Display an error with a wikitext description
+        */
+       function showError( $description ) {
+               global $wgOut;
+               $wgOut->setPageTitle( wfMsg( "internalerror" ) );
+               $wgOut->setRobotpolicy( "noindex,nofollow" );
+               $wgOut->setArticleRelated( false );
+               $wgOut->enableClientCache( false );
+               $wgOut->addWikiText( $description );
+       }
+
+       /**
+        * Get the initial image page text based on a comment and optional file status information
+        */
+       static function getInitialPageText( $comment, $license, $copyStatus, $source ) {
+               global $wgUseCopyrightUpload;
+               if ( $wgUseCopyrightUpload ) {
+                       if ( $license != '' ) {
+                               $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
+                       }
+                       $pageText = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n" .
+                         '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
+                         "$licensetxt" .
+                         '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
+               } else {
+                       if ( $license != '' ) {
+                               $filedesc = $comment == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n";
+                                $pageText = $filedesc .
+                                        '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
+                       } else {
+                               $pageText = $comment;
+                       }
+               }
+               return $pageText;
+       }
+
+       /**
+        * If there are rows in the deletion log for this file, show them,
+        * along with a nice little note for the user
+        *
+        * @param OutputPage $out
+        * @param string filename
+        */
+       private function showDeletionLog( $out, $filename ) {
+               global $wgUser;
+               $loglist = new LogEventsList( $wgUser->getSkin(), $out );
+               $pager = new LogPager( $loglist, 'delete', false, $filename );
+               if( $pager->getNumRows() > 0 ) {
+                       $out->addHtml( '<div id="mw-upload-deleted-warn">' );
+                       $out->addWikiMsg( 'upload-wasdeleted' );
+                       $out->addHTML(
+                               $loglist->beginLogEventsList() .
+                               $pager->getBody() .
+                               $loglist->endLogEventsList()
+                       );
+                       $out->addHtml( '</div>' );
+               }
+       }
+}
diff --git a/includes/specials/SpecialUploadMogile.php b/includes/specials/SpecialUploadMogile.php
new file mode 100644 (file)
index 0000000..7ff8fda
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * You will need the extension MogileClient to use this special page.
+ */
+require_once( 'MogileFS.php' );
+
+/**
+ * Entry point
+ */
+function wfSpecialUploadMogile() {
+       global $wgRequest;
+       $form = new UploadFormMogile( $wgRequest );
+       $form->execute();
+}
+
+/**
+ * Extends Special:Upload with MogileFS.
+ * @ingroup SpecialPage
+ */
+class UploadFormMogile extends UploadForm {
+       /**
+        * Move the uploaded file from its temporary location to the final
+        * destination. If a previous version of the file exists, move
+        * it into the archive subdirectory.
+        *
+        * @todo If the later save fails, we may have disappeared the original file.
+        *
+        * @param string $saveName
+        * @param string $tempName full path to the temporary file
+        * @param bool $useRename  Not used in this implementation
+        */
+       function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
+               global $wgOut;
+               $mfs = MogileFS::NewMogileFS();
+
+               $this->mSavedFile = "image!{$saveName}";
+
+               if( $mfs->getPaths( $this->mSavedFile )) {
+                       $this->mUploadOldVersion = gmdate( 'YmdHis' ) . "!{$saveName}";
+                       if( !$mfs->rename( $this->mSavedFile, "archive!{$this->mUploadOldVersion}" ) ) {
+                               $wgOut->showFileRenameError( $this->mSavedFile,
+                                 "archive!{$this->mUploadOldVersion}" );
+                               return false;
+                       }
+               } else {
+                       $this->mUploadOldVersion = '';
+               }
+
+               if ( $this->mStashed ) {
+                       if (!$mfs->rename($tempName,$this->mSavedFile)) {
+                               $wgOut->showFileRenameError($tempName, $this->mSavedFile );
+                               return false;
+                       }
+               } else {
+                       if ( !$mfs->saveFile($this->mSavedFile,'normal',$tempName )) {
+                               $wgOut->showFileCopyError( $tempName, $this->mSavedFile );
+                               return false;
+                       }
+                       unlink($tempName);
+               }
+               return true;
+       }
+
+       /**
+        * Stash a file in a temporary directory for later processing
+        * after the user has confirmed it.
+        *
+        * If the user doesn't explicitly cancel or accept, these files
+        * can accumulate in the temp directory.
+        *
+        * @param string $saveName - the destination filename
+        * @param string $tempName - the source temporary file to save
+        * @return string - full path the stashed file, or false on failure
+        * @access private
+        */
+       function saveTempUploadedFile( $saveName, $tempName ) {
+               global $wgOut;
+
+               $stash = 'stash!' . gmdate( "YmdHis" ) . '!' . $saveName;
+               $mfs = MogileFS::NewMogileFS();
+               if ( !$mfs->saveFile( $stash, 'normal', $tempName ) ) {
+                       $wgOut->showFileCopyError( $tempName, $stash );
+                       return false;
+               }
+               unlink($tempName);
+               return $stash;
+       }
+
+       /**
+        * Stash a file in a temporary directory for later processing,
+        * and save the necessary descriptive info into the session.
+        * Returns a key value which will be passed through a form
+        * to pick up the path info on a later invocation.
+        *
+        * @return int
+        * @access private
+        */
+       function stashSession() {
+               $stash = $this->saveTempUploadedFile(
+                       $this->mUploadSaveName, $this->mUploadTempName );
+
+               if( !$stash ) {
+                       # Couldn't save the file.
+                       return false;
+               }
+
+               $key = mt_rand( 0, 0x7fffffff );
+               $_SESSION['wsUploadData'][$key] = array(
+                       'mUploadTempName' => $stash,
+                       'mUploadSize'     => $this->mUploadSize,
+                       'mOname'          => $this->mOname );
+               return $key;
+       }
+
+       /**
+        * Remove a temporarily kept file stashed by saveTempUploadedFile().
+        * @access private
+        * @return success
+        */
+       function unsaveUploadedFile() {
+               global $wgOut;
+               $mfs = MogileFS::NewMogileFS();
+               if ( ! $mfs->delete( $this->mUploadTempName ) ) {
+                       $wgOut->showFileDeleteError( $this->mUploadTempName );
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+}
diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php
new file mode 100644 (file)
index 0000000..179ef3f
--- /dev/null
@@ -0,0 +1,928 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * constructor
+ */
+function wfSpecialUserlogin( $par = '' ) {
+       global $wgRequest;
+       if( session_id() == '' ) {
+               wfSetupSession();
+       }
+
+       $form = new LoginForm( $wgRequest, $par );
+       $form->execute();
+}
+
+/**
+ * implements Special:Login
+ * @ingroup SpecialPage
+ */
+class LoginForm {
+
+       const SUCCESS = 0;
+       const NO_NAME = 1;
+       const ILLEGAL = 2;
+       const WRONG_PLUGIN_PASS = 3;
+       const NOT_EXISTS = 4;
+       const WRONG_PASS = 5;
+       const EMPTY_PASS = 6;
+       const RESET_PASS = 7;
+       const ABORTED = 8;
+       const CREATE_BLOCKED = 9;
+
+       var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
+       var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
+       var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage, $mSkipCookieCheck;
+
+       /**
+        * Constructor
+        * @param WebRequest $request A WebRequest object passed by reference
+        */
+       function LoginForm( &$request, $par = '' ) {
+               global $wgLang, $wgAllowRealName, $wgEnableEmail;
+               global $wgAuth;
+
+               $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]]
+               $this->mName = $request->getText( 'wpName' );
+               $this->mPassword = $request->getText( 'wpPassword' );
+               $this->mRetype = $request->getText( 'wpRetype' );
+               $this->mDomain = $request->getText( 'wpDomain' );
+               $this->mReturnTo = $request->getVal( 'returnto' );
+               $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
+               $this->mPosted = $request->wasPosted();
+               $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' );
+               $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
+                                           && $wgEnableEmail;
+               $this->mMailmypassword = $request->getCheck( 'wpMailmypassword' )
+                                        && $wgEnableEmail;
+               $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
+               $this->mAction = $request->getVal( 'action' );
+               $this->mRemember = $request->getCheck( 'wpRemember' );
+               $this->mLanguage = $request->getText( 'uselang' );
+               $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
+
+               if( $wgEnableEmail ) {
+                       $this->mEmail = $request->getText( 'wpEmail' );
+               } else {
+                       $this->mEmail = '';
+               }
+               if( $wgAllowRealName ) {
+                   $this->mRealName = $request->getText( 'wpRealName' );
+               } else {
+                   $this->mRealName = '';
+               }
+
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mDomain = 'invaliddomain';
+               }
+               $wgAuth->setDomain( $this->mDomain );
+
+               # When switching accounts, it sucks to get automatically logged out
+               if( $this->mReturnTo == $wgLang->specialPage( 'Userlogout' ) ) {
+                       $this->mReturnTo = '';
+               }
+       }
+
+       function execute() {
+               if ( !is_null( $this->mCookieCheck ) ) {
+                       $this->onCookieRedirectCheck( $this->mCookieCheck );
+                       return;
+               } else if( $this->mPosted ) {
+                       if( $this->mCreateaccount ) {
+                               return $this->addNewAccount();
+                       } else if ( $this->mCreateaccountMail ) {
+                               return $this->addNewAccountMailPassword();
+                       } else if ( $this->mMailmypassword ) {
+                               return $this->mailPassword();
+                       } else if ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
+                               return $this->processLogin();
+                       }
+               }
+               $this->mainLoginForm( '' );
+       }
+
+       /**
+        * @private
+        */
+       function addNewAccountMailPassword() {
+               global $wgOut;
+
+               if ('' == $this->mEmail) {
+                       $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
+                       return;
+               }
+
+               $u = $this->addNewaccountInternal();
+
+               if ($u == NULL) {
+                       return;
+               }
+
+               // Wipe the initial password and mail a temporary one
+               $u->setPassword( null );
+               $u->saveSettings();
+               $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
+
+               wfRunHooks( 'AddNewAccount', array( $u, true ) );
+
+               $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+
+               if( WikiError::isError( $result ) ) {
+                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
+               } else {
+                       $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
+                       $wgOut->returnToMain( false );
+               }
+               $u = 0;
+       }
+
+
+       /**
+        * @private
+        */
+       function addNewAccount() {
+               global $wgUser, $wgEmailAuthentication;
+
+               # Create the account and abort if there's a problem doing so
+               $u = $this->addNewAccountInternal();
+               if( $u == NULL )
+                       return;
+
+               # If we showed up language selection links, and one was in use, be
+               # smart (and sensible) and save that language as the user's preference
+               global $wgLoginLanguageSelector;
+               if( $wgLoginLanguageSelector && $this->mLanguage )
+                       $u->setOption( 'language', $this->mLanguage );
+
+               # Send out an email authentication message if needed
+               if( $wgEmailAuthentication && User::isValidEmailAddr( $u->getEmail() ) ) {
+                       global $wgOut;
+                       $error = $u->sendConfirmationMail();
+                       if( WikiError::isError( $error ) ) {
+                               $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
+                       } else {
+                               $wgOut->addWikiMsg( 'confirmemail_oncreate' );
+                       }
+               }
+
+               # Save settings (including confirmation token)
+               $u->saveSettings();
+
+               # If not logged in, assume the new account as the current one and set session cookies
+               # then show a "welcome" message or a "need cookies" message as needed
+               if( $wgUser->isAnon() ) {
+                       $wgUser = $u;
+                       $wgUser->setCookies();
+                       wfRunHooks( 'AddNewAccount', array( $wgUser ) );
+                       if( $this->hasSessionCookie() ) {
+                               return $this->successfulLogin( wfMsg( 'welcomecreation', $wgUser->getName() ), false );
+                       } else {
+                               return $this->cookieRedirectCheck( 'new' );
+                       }
+               } else {
+                       # Confirm that the account was created
+                       global $wgOut;
+                       $self = SpecialPage::getTitleFor( 'Userlogin' );
+                       $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
+                       $wgOut->setArticleRelated( false );
+                       $wgOut->setRobotPolicy( 'noindex,nofollow' );
+                       $wgOut->addHtml( wfMsgWikiHtml( 'accountcreatedtext', $u->getName() ) );
+                       $wgOut->returnToMain( false, $self );
+                       wfRunHooks( 'AddNewAccount', array( $u ) );
+                       return true;
+               }
+       }
+
+       /**
+        * @private
+        */
+       function addNewAccountInternal() {
+               global $wgUser, $wgOut;
+               global $wgEnableSorbs, $wgProxyWhitelist;
+               global $wgMemc, $wgAccountCreationThrottle;
+               global $wgAuth, $wgMinimalPasswordLength;
+               global $wgEmailConfirmToEdit;
+
+               // If the user passes an invalid domain, something is fishy
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
+                       return false;
+               }
+
+               // If we are not allowing users to login locally, we should
+               // be checking to see if the user is actually able to
+               // authenticate to the authentication server before they
+               // create an account (otherwise, they can create a local account
+               // and login as any domain user). We only need to check this for
+               // domains that aren't local.
+               if( 'local' != $this->mDomain && '' != $this->mDomain ) {
+                       if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
+                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
+                               return false;
+                       }
+               }
+
+               if ( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return false;
+               }
+
+               # Check permissions
+               if ( !$wgUser->isAllowed( 'createaccount' ) ) {
+                       $this->userNotPrivilegedMessage();
+                       return false;
+               } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
+                       $this->userBlockedMessage();
+                       return false;
+               }
+
+               $ip = wfGetIP();
+               if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
+                 $wgUser->inSorbsBlacklist( $ip ) )
+               {
+                       $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
+                       return;
+               }
+
+               # Now create a dummy user ($u) and check if it is valid
+               $name = trim( $this->mName );
+               $u = User::newFromName( $name, 'creatable' );
+               if ( is_null( $u ) ) {
+                       $this->mainLoginForm( wfMsg( 'noname' ) );
+                       return false;
+               }
+
+               if ( 0 != $u->idForName() ) {
+                       $this->mainLoginForm( wfMsg( 'userexists' ) );
+                       return false;
+               }
+
+               if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
+                       $this->mainLoginForm( wfMsg( 'badretype' ) );
+                       return false;
+               }
+
+               # check for minimal password length
+               if ( !$u->isValidPassword( $this->mPassword ) ) {
+                       if ( !$this->mCreateaccountMail ) {
+                               $this->mainLoginForm( wfMsgExt( 'passwordtooshort', array( 'parsemag' ), $wgMinimalPasswordLength ) );
+                               return false;
+                       } else {
+                               # do not force a password for account creation by email
+                               # set invalid password, it will be replaced later by a random generated password
+                               $this->mPassword = null;
+                       }
+               }
+
+               # if you need a confirmed email address to edit, then obviously you need an email address.
+               if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
+                       $this->mainLoginForm( wfMsg( 'noemailtitle' ) );
+                       return false;
+               }
+
+               if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
+                       $this->mainLoginForm( wfMsg( 'invalidemailaddress' ) );
+                       return false;
+               }
+
+               # Set some additional data so the AbortNewAccount hook can be
+               # used for more than just username validation
+               $u->setEmail( $this->mEmail );
+               $u->setRealName( $this->mRealName );
+
+               $abortError = '';
+               if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
+                       // Hook point to add extra creation throttles and blocks
+                       wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
+                       $this->mainLoginForm( $abortError );
+                       return false;
+               }
+
+               if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
+                       $key = wfMemcKey( 'acctcreate', 'ip', $ip );
+                       $value = $wgMemc->incr( $key );
+                       if ( !$value ) {
+                               $wgMemc->set( $key, 1, 86400 );
+                       }
+                       if ( $value > $wgAccountCreationThrottle ) {
+                               $this->throttleHit( $wgAccountCreationThrottle );
+                               return false;
+                       }
+               }
+
+               if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
+                       $this->mainLoginForm( wfMsg( 'externaldberror' ) );
+                       return false;
+               }
+
+               return $this->initUser( $u, false );
+       }
+
+       /**
+        * Actually add a user to the database.
+        * Give it a User object that has been initialised with a name.
+        *
+        * @param $u User object.
+        * @param $autocreate boolean -- true if this is an autocreation via auth plugin
+        * @return User object.
+        * @private
+        */
+       function initUser( $u, $autocreate ) {
+               global $wgAuth;
+
+               $u->addToDatabase();
+
+               if ( $wgAuth->allowPasswordChange() ) {
+                       $u->setPassword( $this->mPassword );
+               }
+
+               $u->setEmail( $this->mEmail );
+               $u->setRealName( $this->mRealName );
+               $u->setToken();
+
+               $wgAuth->initUser( $u, $autocreate );
+
+               $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
+               $u->saveSettings();
+
+               # Update user count
+               $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
+               $ssUpdate->doUpdate();
+
+               return $u;
+       }
+
+       /**
+        * Internally authenticate the login request.
+        *
+        * This may create a local account as a side effect if the
+        * authentication plugin allows transparent local account
+        * creation.
+        *
+        * @public
+        */
+       function authenticateUserData() {
+               global $wgUser, $wgAuth;
+               if ( '' == $this->mName ) {
+                       return self::NO_NAME;
+               }
+
+               // Load $wgUser now, and check to see if we're logging in as the same name. 
+               // This is necessary because loading $wgUser (say by calling getName()) calls
+               // the UserLoadFromSession hook, which potentially creates the user in the 
+               // database. Until we load $wgUser, checking for user existence using 
+               // User::newFromName($name)->getId() below will effectively be using stale data.
+               if ( $wgUser->getName() === $this->mName ) {
+                       wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
+                       return self::SUCCESS;
+               }
+               $u = User::newFromName( $this->mName );
+               if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
+                       return self::ILLEGAL;
+               }
+
+               $isAutoCreated = false;
+               if ( 0 == $u->getID() ) {
+                       $status = $this->attemptAutoCreate( $u );
+                       if ( $status !== self::SUCCESS ) {
+                               return $status;
+                       } else {
+                               $isAutoCreated = true;
+                       }
+               } else {
+                       $u->load();
+               }
+
+               // Give general extensions, such as a captcha, a chance to abort logins
+               $abort = self::ABORTED;
+               if( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort ) ) ) {
+                       return $abort;
+               }
+
+               if (!$u->checkPassword( $this->mPassword )) {
+                       if( $u->checkTemporaryPassword( $this->mPassword ) ) {
+                               // The e-mailed temporary password should not be used
+                               // for actual logins; that's a very sloppy habit,
+                               // and insecure if an attacker has a few seconds to
+                               // click "search" on someone's open mail reader.
+                               //
+                               // Allow it to be used only to reset the password
+                               // a single time to a new value, which won't be in
+                               // the user's e-mail archives.
+                               //
+                               // For backwards compatibility, we'll still recognize
+                               // it at the login form to minimize surprises for
+                               // people who have been logging in with a temporary
+                               // password for some time.
+                               //
+                               // As a side-effect, we can authenticate the user's
+                               // e-mail address if it's not already done, since
+                               // the temporary password was sent via e-mail.
+                               //
+                               if( !$u->isEmailConfirmed() ) {
+                                       $u->confirmEmail();
+                                       $u->saveSettings();
+                               }
+
+                               // At this point we just return an appropriate code
+                               // indicating that the UI should show a password
+                               // reset form; bot interfaces etc will probably just
+                               // fail cleanly here.
+                               //
+                               $retval = self::RESET_PASS;
+                       } else {
+                               $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
+                       }
+               } else {
+                       $wgAuth->updateUser( $u );
+                       $wgUser = $u;
+
+                       if ( $isAutoCreated ) {
+                               // Must be run after $wgUser is set, for correct new user log
+                               wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
+                       }
+
+                       $retval = self::SUCCESS;
+               }
+               wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
+               return $retval;
+       }
+
+       /**
+        * Attempt to automatically create a user on login.
+        * Only succeeds if there is an external authentication method which allows it.
+        * @return integer Status code
+        */
+       function attemptAutoCreate( $user ) {
+               global $wgAuth, $wgUser;
+               /**
+                * If the external authentication plugin allows it,
+                * automatically create a new account for users that
+                * are externally defined but have not yet logged in.
+                */
+               if ( !$wgAuth->autoCreate() ) {
+                       return self::NOT_EXISTS;
+               }
+               if ( !$wgAuth->userExists( $user->getName() ) ) {
+                       wfDebug( __METHOD__.": user does not exist\n" );
+                       return self::NOT_EXISTS;
+               }
+               if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
+                       wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
+                       return self::WRONG_PLUGIN_PASS;
+               }
+               if ( $wgUser->isBlockedFromCreateAccount() ) {
+                       wfDebug( __METHOD__.": user is blocked from account creation\n" );
+                       return self::CREATE_BLOCKED;
+               }
+
+               wfDebug( __METHOD__.": creating account\n" );
+               $user = $this->initUser( $user, true );
+               return self::SUCCESS;
+       }
+
+       function processLogin() {
+               global $wgUser, $wgAuth;
+
+               switch ($this->authenticateUserData())
+               {
+                       case self::SUCCESS:
+                               # We've verified now, update the real record
+                               if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
+                                       $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
+                                       $wgUser->saveSettings();
+                               } else {
+                                       $wgUser->invalidateCache();
+                               }
+                               $wgUser->setCookies();
+
+                               if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
+                                       /* Replace the language object to provide user interface in correct
+                                        * language immediately on this first page load.
+                                        */
+                                       global $wgLang, $wgRequest;
+                                       $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
+                                       $wgLang = Language::factory( $code );
+                                       return $this->successfulLogin( wfMsg( 'loginsuccess', $wgUser->getName() ) );
+                               } else {
+                                       return $this->cookieRedirectCheck( 'login' );
+                               }
+                               break;
+
+                       case self::NO_NAME:
+                       case self::ILLEGAL:
+                               $this->mainLoginForm( wfMsg( 'noname' ) );
+                               break;
+                       case self::WRONG_PLUGIN_PASS:
+                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
+                               break;
+                       case self::NOT_EXISTS:
+                               if( $wgUser->isAllowed( 'createaccount' ) ){
+                                       $this->mainLoginForm( wfMsg( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
+                               } else {
+                                       $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
+                               }
+                               break;
+                       case self::WRONG_PASS:
+                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
+                               break;
+                       case self::EMPTY_PASS:
+                               $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
+                               break;
+                       case self::RESET_PASS:
+                               $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
+                               break;
+                       case self::CREATE_BLOCKED:
+                               $this->userBlockedMessage();
+                               break;
+                       default:
+                               throw new MWException( "Unhandled case value" );
+               }
+       }
+
+       function resetLoginForm( $error ) {
+               global $wgOut;
+               $wgOut->addWikiText( "<div class=\"errorbox\">$error</div>" );
+               $reset = new PasswordResetForm( $this->mName, $this->mPassword );
+               $reset->execute( null );
+       }
+
+       /**
+        * @private
+        */
+       function mailPassword() {
+               global $wgUser, $wgOut, $wgAuth;
+
+               if( !$wgAuth->allowPasswordChange() ) {
+                       $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
+                       return;
+               }
+
+               # Check against blocked IPs
+               # fixme -- should we not?
+               if( $wgUser->isBlocked() ) {
+                       $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
+                       return;
+               }
+
+               # Check against the rate limiter
+               if( $wgUser->pingLimiter( 'mailpassword' ) ) {
+                       $wgOut->rateLimited();
+                       return;
+               }
+
+               if ( '' == $this->mName ) {
+                       $this->mainLoginForm( wfMsg( 'noname' ) );
+                       return;
+               }
+               $u = User::newFromName( $this->mName );
+               if( is_null( $u ) ) {
+                       $this->mainLoginForm( wfMsg( 'noname' ) );
+                       return;
+               }
+               if ( 0 == $u->getID() ) {
+                       $this->mainLoginForm( wfMsg( 'nosuchuser', $u->getName() ) );
+                       return;
+               }
+
+               # Check against password throttle
+               if ( $u->isPasswordReminderThrottled() ) {
+                       global $wgPasswordReminderResendTime;
+                       # Round the time in hours to 3 d.p., in case someone is specifying minutes or seconds.
+                       $this->mainLoginForm( wfMsgExt( 'throttled-mailpassword', array( 'parsemag' ),
+                               round( $wgPasswordReminderResendTime, 3 ) ) );
+                       return;
+               }
+
+               $result = $this->mailPasswordInternal( $u, true, 'passwordremindertitle', 'passwordremindertext' );
+               if( WikiError::isError( $result ) ) {
+                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
+               } else {
+                       $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' );
+               }
+       }
+
+
+       /**
+        * @param object user
+        * @param bool throttle
+        * @param string message name of email title
+        * @param string message name of email text
+        * @return mixed true on success, WikiError on failure
+        * @private
+        */
+       function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
+               global $wgCookiePath, $wgCookieDomain, $wgCookiePrefix, $wgCookieSecure;
+               global $wgServer, $wgScript;
+
+               if ( '' == $u->getEmail() ) {
+                       return new WikiError( wfMsg( 'noemail', $u->getName() ) );
+               }
+
+               $np = $u->randomPassword();
+               $u->setNewpassword( $np, $throttle );
+               $u->saveSettings();
+
+               $ip = wfGetIP();
+               if ( '' == $ip ) { $ip = '(Unknown)'; }
+
+               $m = wfMsg( $emailText, $ip, $u->getName(), $np, $wgServer . $wgScript );
+               $result = $u->sendMail( wfMsg( $emailTitle ), $m );
+
+               return $result;
+       }
+
+
+       /**
+        * @param string $msg Message that will be shown on success
+        * @param bool $auto Toggle auto-redirect to main page; default true
+        * @private
+        */
+       function successfulLogin( $msg, $auto = true ) {
+               global $wgUser;
+               global $wgOut;
+
+               # Run any hooks; ignore results
+
+               $injected_html = '';
+               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
+
+               $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+               $wgOut->addWikiText( $msg );
+               $wgOut->addHtml( $injected_html );
+               if ( !empty( $this->mReturnTo ) ) {
+                       $wgOut->returnToMain( $auto, $this->mReturnTo );
+               } else {
+                       $wgOut->returnToMain( $auto );
+               }
+       }
+
+       /** */
+       function userNotPrivilegedMessage($errors) {
+               global $wgOut;
+
+               $wgOut->setPageTitle( wfMsg( 'permissionserrors' ) );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+
+               $wgOut->addWikitext( $wgOut->formatPermissionsErrorMessage( $errors, 'createaccount' ) );
+               // Stuff that might want to be added at the end. For example, instructions if blocked.
+               $wgOut->addWikiMsg( 'cantcreateaccount-nonblock-text' );
+
+               $wgOut->returnToMain( false );
+       }
+
+       /** */
+       function userBlockedMessage() {
+               global $wgOut, $wgUser;
+
+               # Let's be nice about this, it's likely that this feature will be used
+               # for blocking large numbers of innocent people, e.g. range blocks on
+               # schools. Don't blame it on the user. There's a small chance that it
+               # really is the user's fault, i.e. the username is blocked and they
+               # haven't bothered to log out before trying to create an account to
+               # evade it, but we'll leave that to their guilty conscience to figure
+               # out.
+
+               $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+
+               $ip = wfGetIP();
+               $blocker = User::whoIs( $wgUser->mBlock->mBy );
+               $block_reason = $wgUser->mBlock->mReason;
+
+               if ( strval( $block_reason ) === '' ) {
+                       $block_reason = wfMsg( 'blockednoreason' );
+               }
+               $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
+               $wgOut->returnToMain( false );
+       }
+
+       /**
+        * @private
+        */
+       function mainLoginForm( $msg, $msgtype = 'error' ) {
+               global $wgUser, $wgOut, $wgAllowRealName, $wgEnableEmail;
+               global $wgCookiePrefix, $wgAuth, $wgLoginLanguageSelector;
+               global $wgAuth, $wgEmailConfirmToEdit;
+               
+               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
+               
+               if ( $this->mType == 'signup' ) {
+                       // Block signup here if in readonly. Keeps user from 
+                       // going through the process (filling out data, etc) 
+                       // and being informed later.
+                       if ( wfReadOnly() ) {
+                               $wgOut->readOnlyPage();
+                               return;
+                       } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
+                               $this->userBlockedMessage();
+                               return;
+                       } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
+                               $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
+                               return;
+                       }
+               }
+
+               if ( '' == $this->mName ) {
+                       if ( $wgUser->isLoggedIn() ) {
+                               $this->mName = $wgUser->getName();
+                       } else {
+                               $this->mName = isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ? $_COOKIE[$wgCookiePrefix.'UserName'] : null;
+                       }
+               }
+
+               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
+
+               if ( $this->mType == 'signup' ) {
+                       $template = new UsercreateTemplate();
+                       $q = 'action=submitlogin&type=signup';
+                       $linkq = 'type=login';
+                       $linkmsg = 'gotaccount';
+               } else {
+                       $template = new UserloginTemplate();
+                       $q = 'action=submitlogin&type=login';
+                       $linkq = 'type=signup';
+                       $linkmsg = 'nologin';
+               }
+
+               if ( !empty( $this->mReturnTo ) ) {
+                       $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
+                       $q .= $returnto;
+                       $linkq .= $returnto;
+               }
+
+               # Pass any language selection on to the mode switch link
+               if( $wgLoginLanguageSelector && $this->mLanguage )
+                       $linkq .= '&uselang=' . $this->mLanguage;
+
+               $link = '<a href="' . htmlspecialchars ( $titleObj->getLocalUrl( $linkq ) ) . '">';
+               $link .= wfMsgHtml( $linkmsg . 'link' ); # Calling either 'gotaccountlink' or 'nologinlink'
+               $link .= '</a>';
+
+               # Don't show a "create account" link if the user can't
+               if( $this->showCreateOrLoginLink( $wgUser ) )
+                       $template->set( 'link', wfMsgHtml( $linkmsg, $link ) );
+               else
+                       $template->set( 'link', '' );
+
+               $template->set( 'header', '' );
+               $template->set( 'name', $this->mName );
+               $template->set( 'password', $this->mPassword );
+               $template->set( 'retype', $this->mRetype );
+               $template->set( 'email', $this->mEmail );
+               $template->set( 'realname', $this->mRealName );
+               $template->set( 'domain', $this->mDomain );
+
+               $template->set( 'action', $titleObj->getLocalUrl( $q ) );
+               $template->set( 'message', $msg );
+               $template->set( 'messagetype', $msgtype );
+               $template->set( 'createemail', $wgEnableEmail && $wgUser->isLoggedIn() );
+               $template->set( 'userealname', $wgAllowRealName );
+               $template->set( 'useemail', $wgEnableEmail );
+               $template->set( 'emailrequired', $wgEmailConfirmToEdit );
+               $template->set( 'canreset', $wgAuth->allowPasswordChange() );
+               $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember  );
+
+               # Prepare language selection links as needed
+               if( $wgLoginLanguageSelector ) {
+                       $template->set( 'languages', $this->makeLanguageSelector() );
+                       if( $this->mLanguage )
+                               $template->set( 'uselang', $this->mLanguage );
+               }
+
+               // Give authentication and captcha plugins a chance to modify the form
+               $wgAuth->modifyUITemplate( $template );
+               if ( $this->mType == 'signup' ) {
+                       wfRunHooks( 'UserCreateForm', array( &$template ) );
+               } else {
+                       wfRunHooks( 'UserLoginForm', array( &$template ) );
+               }
+
+               $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
+               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+               $wgOut->disallowUserJs();  // just in case...
+               $wgOut->addTemplate( $template );
+       }
+
+       /**
+        * @private
+        */
+       function showCreateOrLoginLink( &$user ) {
+               if( $this->mType == 'signup' ) {
+                       return( true );
+               } elseif( $user->isAllowed( 'createaccount' ) ) {
+                       return( true );
+               } else {
+                       return( false );
+               }
+       }
+
+       /**
+        * Check if a session cookie is present.
+        *
+        * This will not pick up a cookie set during _this_ request, but is
+        * meant to ensure that the client is returning the cookie which was
+        * set on a previous pass through the system.
+        *
+        * @private
+        */
+       function hasSessionCookie() {
+               global $wgDisableCookieCheck, $wgRequest;
+               return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
+       }
+
+       /**
+        * @private
+        */
+       function cookieRedirectCheck( $type ) {
+               global $wgOut;
+
+               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
+               $check = $titleObj->getFullURL( 'wpCookieCheck='.$type );
+
+               return $wgOut->redirect( $check );
+       }
+
+       /**
+        * @private
+        */
+       function onCookieRedirectCheck( $type ) {
+               global $wgUser;
+
+               if ( !$this->hasSessionCookie() ) {
+                       if ( $type == 'new' ) {
+                               return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
+                       } else if ( $type == 'login' ) {
+                               return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
+                       } else {
+                               # shouldn't happen
+                               return $this->mainLoginForm( wfMsg( 'error' ) );
+                       }
+               } else {
+                       return $this->successfulLogin( wfMsgExt( 'loginsuccess', array( 'parseinline' ), $wgUser->getName() ) );
+               }
+       }
+
+       /**
+        * @private
+        */
+       function throttleHit( $limit ) {
+               global $wgOut;
+
+               $wgOut->addWikiMsg( 'acct_creation_throttle_hit', $limit );
+       }
+
+       /**
+        * Produce a bar of links which allow the user to select another language
+        * during login/registration but retain "returnto"
+        *
+        * @return string
+        */
+       function makeLanguageSelector() {
+               $msg = wfMsgForContent( 'loginlanguagelinks' );
+               if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
+                       $langs = explode( "\n", $msg );
+                       $links = array();
+                       foreach( $langs as $lang ) {
+                               $lang = trim( $lang, '* ' );
+                               $parts = explode( '|', $lang );
+                               if (count($parts) >= 2) {
+                                       $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
+                               }
+                       }
+                       return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', implode( ' | ', $links ) ) : '';
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * Create a language selector link for a particular language
+        * Links back to this page preserving type and returnto
+        *
+        * @param $text Link text
+        * @param $lang Language code
+        */
+       function makeLanguageSelectorLink( $text, $lang ) {
+               global $wgUser;
+               $self = SpecialPage::getTitleFor( 'Userlogin' );
+               $attr[] = 'uselang=' . $lang;
+               if( $this->mType == 'signup' )
+                       $attr[] = 'type=signup';
+               if( $this->mReturnTo )
+                       $attr[] = 'returnto=' . $this->mReturnTo;
+               $skin = $wgUser->getSkin();
+               return $skin->makeKnownLinkObj( $self, htmlspecialchars( $text ), implode( '&', $attr ) );
+       }
+}
diff --git a/includes/specials/SpecialUserlogout.php b/includes/specials/SpecialUserlogout.php
new file mode 100644 (file)
index 0000000..137eadb
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * constructor
+ */
+function wfSpecialUserlogout() {
+       global $wgUser, $wgOut;
+
+       $oldName = $wgUser->getName();
+       $wgUser->logout();
+       $wgOut->setRobotpolicy( 'noindex,nofollow' );
+
+       // Hook.
+       $injected_html = '';
+       wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName) );
+
+       $wgOut->addHTML( wfMsgExt( 'logouttext', array( 'parse' ) ) . $injected_html );
+       $wgOut->returnToMain();
+}
diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php
new file mode 100644 (file)
index 0000000..bf4d440
--- /dev/null
@@ -0,0 +1,576 @@
+<?php
+/**
+ * Special page to allow managing user group membership
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A class to manage user levels rights.
+ * @ingroup SpecialPage
+ */
+class UserrightsPage extends SpecialPage {
+       # The target of the local right-adjuster's interest.  Can be gotten from
+       # either a GET parameter or a subpage-style parameter, so have a member
+       # variable for it.
+       protected $mTarget;
+       protected $isself = false;
+
+       public function __construct() {
+               parent::__construct( 'Userrights' );
+       }
+
+       public function isRestricted() {
+               return true;
+       }
+
+       public function userCanExecute( $user ) {
+               $available = $this->changeableGroups();
+               return !empty( $available['add'] )
+                       or !empty( $available['remove'] )
+                       or ($this->isself and
+                               (!empty( $available['add-self'] )
+                                or !empty( $available['remove-self'] )));
+       }
+
+       /**
+        * Manage forms to be shown according to posted data.
+        * Depending on the submit button used, call a form or a save function.
+        *
+        * @param $par Mixed: string if any subpage provided, else null
+        */
+       function execute( $par ) {
+               // If the visitor doesn't have permissions to assign or remove
+               // any groups, it's a bit silly to give them the user search prompt.
+               global $wgUser, $wgRequest;
+
+               if( $par ) {
+                       $this->mTarget = $par;
+               } else {
+                       $this->mTarget = $wgRequest->getVal( 'user' );
+               }
+
+               if (!$this->mTarget) {
+                       /*
+                        * If the user specified no target, and they can only
+                        * edit their own groups, automatically set them as the
+                        * target.
+                        */
+                       $available = $this->changeableGroups();
+                       if (empty($available['add']) && empty($available['remove']))
+                               $this->mTarget = $wgUser->getName();
+               }
+
+               if ($this->mTarget == $wgUser->getName())
+                       $this->isself = true;
+
+               if( !$this->userCanExecute( $wgUser ) ) {
+                       // fixme... there may be intermediate groups we can mention.
+                       global $wgOut;
+                       $wgOut->showPermissionsErrorPage( array(
+                               $wgUser->isAnon()
+                                       ? 'userrights-nologin'
+                                       : 'userrights-notallowed' ) );
+                       return;
+               }
+
+               if ( wfReadOnly() ) {
+                       global $wgOut;
+                       $wgOut->readOnlyPage();
+                       return;
+               }
+
+               $this->outputHeader();
+
+               $this->setHeaders();
+
+               // show the general form
+               $this->switchForm();
+
+               if( $wgRequest->wasPosted() ) {
+                       // save settings
+                       if( $wgRequest->getCheck( 'saveusergroups' ) ) {
+                               $reason = $wgRequest->getVal( 'user-reason' );
+                               if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $this->mTarget ) ) {
+                                       $this->saveUserGroups(
+                                               $this->mTarget,
+                                               $reason
+                                       );
+                               }
+                       }
+               }
+
+               // show some more forms
+               if( $this->mTarget ) {
+                       $this->editUserGroupsForm( $this->mTarget );
+               }
+       }
+
+       /**
+        * Save user groups changes in the database.
+        * Data comes from the editUserGroupsForm() form function
+        *
+        * @param $username String: username to apply changes to.
+        * @param $reason String: reason for group change
+        * @return null
+        */
+       function saveUserGroups( $username, $reason = '') {
+               global $wgRequest, $wgUser, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
+
+               $user = $this->fetchUser( $username );
+               if( !$user ) {
+                       return;
+               }
+
+               $allgroups = $this->getAllGroups();
+               $addgroup = array();
+               $removegroup = array();
+
+               // This could possibly create a highly unlikely race condition if permissions are changed between
+               //  when the form is loaded and when the form is saved. Ignoring it for the moment.
+               foreach ($allgroups as $group) {
+                       // We'll tell it to remove all unchecked groups, and add all checked groups.
+                       // Later on, this gets filtered for what can actually be removed
+                       if ($wgRequest->getCheck( "wpGroup-$group" )) {
+                               $addgroup[] = $group;
+                       } else {
+                               $removegroup[] = $group;
+                       }
+               }
+
+               // Validate input set...
+               $changeable = $this->changeableGroups();
+               if ($wgUser->getId() != 0 && $wgUser->getId() == $user->getId()) {
+                       $addable = array_merge($changeable['add'], $wgGroupsAddToSelf);
+                       $removable = array_merge($changeable['remove'], $wgGroupsRemoveFromSelf);
+               } else {
+                       $addable = $changeable['add'];
+                       $removable = $changeable['remove'];
+               }
+
+               $removegroup = array_unique(
+                       array_intersect( (array)$removegroup, $removable ) );
+               $addgroup = array_unique(
+                       array_intersect( (array)$addgroup, $addable ) );
+
+               $oldGroups = $user->getGroups();
+               $newGroups = $oldGroups;
+               // remove then add groups
+               if( $removegroup ) {
+                       $newGroups = array_diff($newGroups, $removegroup);
+                       foreach( $removegroup as $group ) {
+                               $user->removeGroup( $group );
+                       }
+               }
+               if( $addgroup ) {
+                       $newGroups = array_merge($newGroups, $addgroup);
+                       foreach( $addgroup as $group ) {
+                               $user->addGroup( $group );
+                       }
+               }
+               $newGroups = array_unique( $newGroups );
+
+               // Ensure that caches are cleared
+               $user->invalidateCache();
+
+               wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) );
+               wfDebug( 'newGroups: ' . print_r( $newGroups, true ) );
+               if( $user instanceof User ) {
+                       // hmmm
+                       wfRunHooks( 'UserRights', array( &$user, $addgroup, $removegroup ) );
+               }
+
+               if( $newGroups != $oldGroups ) {
+                       $this->addLogEntry( $user, $oldGroups, $newGroups );
+               }
+       }
+       
+       /**
+        * Add a rights log entry for an action.
+        */
+       function addLogEntry( $user, $oldGroups, $newGroups ) {
+               global $wgRequest;
+               $log = new LogPage( 'rights' );
+
+               $log->addEntry( 'rights',
+                       $user->getUserPage(),
+                       $wgRequest->getText( 'user-reason' ),
+                       array(
+                               $this->makeGroupNameList( $oldGroups ),
+                               $this->makeGroupNameList( $newGroups )
+                       )
+               );
+       }
+
+       /**
+        * Edit user groups membership
+        * @param $username String: name of the user.
+        */
+       function editUserGroupsForm( $username ) {
+               global $wgOut;
+
+               $user = $this->fetchUser( $username );
+               if( !$user ) {
+                       return;
+               }
+
+               $groups = $user->getGroups();
+
+               $this->showEditUserGroupsForm( $user, $groups );
+
+               // This isn't really ideal logging behavior, but let's not hide the
+               // interwiki logs if we're using them as is.
+               $this->showLogFragment( $user, $wgOut );
+       }
+
+       /**
+        * Normalize the input username, which may be local or remote, and
+        * return a user (or proxy) object for manipulating it.
+        *
+        * Side effects: error output for invalid access
+        * @return mixed User, UserRightsProxy, or null
+        */
+       function fetchUser( $username ) {
+               global $wgOut, $wgUser;
+
+               $parts = explode( '@', $username );
+               if( count( $parts ) < 2 ) {
+                       $name = trim( $username );
+                       $database = '';
+               } else {
+                       list( $name, $database ) = array_map( 'trim', $parts );
+
+                       if( !$wgUser->isAllowed( 'userrights-interwiki' ) ) {
+                               $wgOut->addWikiMsg( 'userrights-no-interwiki' );
+                               return null;
+                       }
+                       if( !UserRightsProxy::validDatabase( $database ) ) {
+                               $wgOut->addWikiMsg( 'userrights-nodatabase', $database );
+                               return null;
+                       }
+               }
+
+               if( $name == '' ) {
+                       $wgOut->addWikiMsg( 'nouserspecified' );
+                       return false;
+               }
+
+               if( $name{0} == '#' ) {
+                       // Numeric ID can be specified...
+                       // We'll do a lookup for the name internally.
+                       $id = intval( substr( $name, 1 ) );
+
+                       if( $database == '' ) {
+                               $name = User::whoIs( $id );
+                       } else {
+                               $name = UserRightsProxy::whoIs( $database, $id );
+                       }
+
+                       if( !$name ) {
+                               $wgOut->addWikiMsg( 'noname' );
+                               return null;
+                       }
+               }
+
+               if( $database == '' ) {
+                       $user = User::newFromName( $name );
+               } else {
+                       $user = UserRightsProxy::newFromName( $database, $name );
+               }
+
+               if( !$user || $user->isAnon() ) {
+                       $wgOut->addWikiMsg( 'nosuchusershort', $username );
+                       return null;
+               }
+
+               return $user;
+       }
+
+       function makeGroupNameList( $ids ) {
+               return implode( ', ', $ids );
+       }
+
+       /**
+        * Output a form to allow searching for a user
+        */
+       function switchForm() {
+               global $wgOut, $wgScript;
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'name' => 'uluser', 'id' => 'mw-userrights-form1' ) ) .
+                       Xml::hidden( 'title',  $this->getTitle()->getPrefixedText() ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', array(), wfMsg( 'userrights-lookup-user' ) ) .
+                       Xml::inputLabel( wfMsg( 'userrights-user-editname' ), 'user', 'username', 30, $this->mTarget ) . ' ' .
+                       Xml::submitButton( wfMsg( 'editusergroup' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+       }
+
+       /**
+        * Go through used and available groups and return the ones that this
+        * form will be able to manipulate based on the current user's system
+        * permissions.
+        *
+        * @param $groups Array: list of groups the given user is in
+        * @return Array:  Tuple of addable, then removable groups
+        */
+       protected function splitGroups( $groups ) {
+               global $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
+               list($addable, $removable) = array_values( $this->changeableGroups() );
+
+               $removable = array_intersect(
+                               array_merge($this->isself ? $wgGroupsRemoveFromSelf : array(), $removable),
+                               $groups ); // Can't remove groups the user doesn't have
+               $addable   = array_diff(
+                               array_merge($this->isself ? $wgGroupsAddToSelf : array(), $addable),
+                               $groups ); // Can't add groups the user does have
+
+               return array( $addable, $removable );
+       }
+
+       /**
+        * Show the form to edit group memberships.
+        *
+        * @param $user      User or UserRightsProxy you're editing
+        * @param $groups    Array:  Array of groups the user is in
+        */
+       protected function showEditUserGroupsForm( $user, $groups ) {
+               global $wgOut, $wgUser;
+
+               list( $addable, $removable ) = $this->splitGroups( $groups );
+
+               $list = array();
+               foreach( $user->getGroups() as $group )
+                       $list[] = self::buildGroupLink( $group );
+
+               $grouplist = '';
+               if( count( $list ) > 0 ) {
+                       $grouplist = Xml::tags( 'p', null, wfMsgHtml( 'userrights-groupsmember' ) . ' ' . implode( ', ', $list ) );
+               }
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL(), 'name' => 'editGroup', 'id' => 'mw-userrights-form2' ) ) .
+                       Xml::hidden( 'user', $this->mTarget ) .
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->mTarget ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) .
+                       wfMsgExt( 'editinguser', array( 'parse' ), wfEscapeWikiText( $user->getName() ) ) .
+                       wfMsgExt( 'userrights-groups-help', array( 'parse' ) ) .
+                       $grouplist .
+                       Xml::tags( 'p', null, $this->groupCheckboxes( $groups ) ) .
+                       Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-userrights-table-outer' ) ) .
+                               "<tr>
+                                       <td class='mw-label'>" .
+                                               Xml::label( wfMsg( 'userrights-reason' ), 'wpReason' ) .
+                                       "</td>
+                                       <td class='mw-input'>" .
+                                               Xml::input( 'user-reason', 60, false, array( 'id' => 'wpReason', 'maxlength' => 255 ) ) .
+                                       "</td>
+                               </tr>
+                               <tr>
+                                       <td></td>
+                                       <td class='mw-submit'>" .
+                                               Xml::submitButton( wfMsg( 'saveusergroups' ), array( 'name' => 'saveusergroups' ) ) .
+                                       "</td>
+                               </tr>" .
+                       Xml::closeElement( 'table' ) . "\n" .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n"
+               );
+       }
+
+       /**
+        * Format a link to a group description page
+        *
+        * @param $group string
+        * @return string
+        */
+       private static function buildGroupLink( $group ) {
+               static $cache = array();
+               if( !isset( $cache[$group] ) )
+                       $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupName( $group ) );
+               return $cache[$group];
+       }
+       
+       /**
+        * Returns an array of all groups that may be edited
+        * @return array Array of groups that may be edited.
+        */
+        protected static function getAllGroups() {
+               return User::getAllGroups();
+        }
+
+       /**
+        * Adds a table with checkboxes where you can select what groups to add/remove
+        *
+        * @param $usergroups Array: groups the user belongs to
+        * @return string XHTML table element with checkboxes
+        */
+       private function groupCheckboxes( $usergroups ) {
+               $allgroups = $this->getAllGroups();
+               $ret = '';
+
+               $column = 1;
+               $settable_col = '';
+               $unsettable_col = '';
+
+               foreach ($allgroups as $group) {
+                       $set = in_array( $group, $usergroups );
+                       # Should the checkbox be disabled?
+                       $disabled = !(
+                               ( $set && $this->canRemove( $group ) ) ||
+                               ( !$set && $this->canAdd( $group ) ) );
+                       # Do we need to point out that this action is irreversible?
+                       $irreversible = !$disabled && (
+                               ($set && !$this->canAdd( $group )) ||
+                               (!$set && !$this->canRemove( $group ) ) );
+
+                       $attr = $disabled ? array( 'disabled' => 'disabled' ) : array();
+                       $text = $irreversible
+                               ? wfMsgHtml( 'userrights-irreversible-marker', User::getGroupMember( $group ) )
+                               : User::getGroupMember( $group );
+                       $checkbox = Xml::checkLabel( $text, "wpGroup-$group",
+                               "wpGroup-$group", $set, $attr );
+                       $checkbox = $disabled ? Xml::tags( 'span', array( 'class' => 'mw-userrights-disabled' ), $checkbox ) : $checkbox;
+
+                       if ($disabled) {
+                               $unsettable_col .= "$checkbox<br />\n";
+                       } else {
+                               $settable_col .= "$checkbox<br />\n";
+                       }
+               }
+
+               if ($column) {
+                       $ret .= Xml::openElement( 'table', array( 'border' => '0', 'class' => 'mw-userrights-groups' ) ) .
+                               "<tr>
+";
+                       if( $settable_col !== '' ) {
+                               $ret .= xml::element( 'th', null, wfMsg( 'userrights-changeable-col' ) );
+                       }
+                       if( $unsettable_col !== '' ) {
+                               $ret .= xml::element( 'th', null, wfMsg( 'userrights-unchangeable-col' ) );
+                       }
+                       $ret.= "</tr>
+                               <tr>
+";
+                       if( $settable_col !== '' ) {
+                               $ret .=
+"                                      <td style='vertical-align:top;'>
+                                               $settable_col
+                                       </td>
+";
+                       }
+                       if( $unsettable_col !== '' ) {
+                               $ret .=
+"                                      <td style='vertical-align:top;'>
+                                               $unsettable_col
+                                       </td>
+";
+                       }
+                       $ret .= Xml::closeElement( 'tr' ) . Xml::closeElement( 'table' );
+               }
+
+               return $ret;
+       }
+
+       /**
+        * @param  $group String: the name of the group to check
+        * @return bool Can we remove the group?
+        */
+       private function canRemove( $group ) {
+               // $this->changeableGroups()['remove'] doesn't work, of course. Thanks,
+               // PHP.
+               $groups = $this->changeableGroups();
+               return in_array( $group, $groups['remove'] ) || ($this->isself && in_array( $group, $groups['remove-self'] ));
+       }
+
+       /**
+        * @param $group string: the name of the group to check
+        * @return bool Can we add the group?
+        */
+       private function canAdd( $group ) {
+               $groups = $this->changeableGroups();
+               return in_array( $group, $groups['add'] ) || ($this->isself && in_array( $group, $groups['add-self'] ));
+       }
+
+       /**
+        * Returns an array of the groups that the user can add/remove.
+        *
+        * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) )
+        */
+       function changeableGroups() {
+               global $wgUser, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
+
+               if( $wgUser->isAllowed( 'userrights' ) ) {
+                       // This group gives the right to modify everything (reverse-
+                       // compatibility with old "userrights lets you change
+                       // everything")
+                       // Using array_merge to make the groups reindexed
+                       $all = array_merge( User::getAllGroups() );
+                       return array(
+                               'add' => $all,
+                               'remove' => $all,
+                               'add-self' => array(),
+                               'remove-self' => array()
+                       );
+               }
+
+               // Okay, it's not so simple, we will have to go through the arrays
+               $groups = array(
+                               'add' => array(),
+                               'remove' => array(),
+                               'add-self' => $wgGroupsAddToSelf,
+                               'remove-self' => $wgGroupsRemoveFromSelf);
+               $addergroups = $wgUser->getEffectiveGroups();
+
+               foreach ($addergroups as $addergroup) {
+                       $groups = array_merge_recursive(
+                               $groups, $this->changeableByGroup($addergroup)
+                       );
+                       $groups['add']    = array_unique( $groups['add'] );
+                       $groups['remove'] = array_unique( $groups['remove'] );
+               }
+               return $groups;
+       }
+
+       /**
+        * Returns an array of the groups that a particular group can add/remove.
+        *
+        * @param $group String: the group to check for whether it can add/remove
+        * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) )
+        */
+       private function changeableByGroup( $group ) {
+               global $wgAddGroups, $wgRemoveGroups;
+
+               $groups = array( 'add' => array(), 'remove' => array() );
+               if( empty($wgAddGroups[$group]) ) {
+                       // Don't add anything to $groups
+               } elseif( $wgAddGroups[$group] === true ) {
+                       // You get everything
+                       $groups['add'] = User::getAllGroups();
+               } elseif( is_array($wgAddGroups[$group]) ) {
+                       $groups['add'] = $wgAddGroups[$group];
+               }
+
+               // Same thing for remove
+               if( empty($wgRemoveGroups[$group]) ) {
+               } elseif($wgRemoveGroups[$group] === true ) {
+                       $groups['remove'] = User::getAllGroups();
+               } elseif( is_array($wgRemoveGroups[$group]) ) {
+                       $groups['remove'] = $wgRemoveGroups[$group];
+               }
+               return $groups;
+       }
+
+       /**
+        * Show a rights log fragment for the specified user
+        *
+        * @param $user User to show log for
+        * @param $output OutputPage to use
+        */
+       protected function showLogFragment( $user, $output ) {
+               $output->addHtml( Xml::element( 'h2', null, LogPage::logName( 'rights' ) . "\n" ) );
+               LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage()->getPrefixedText() );
+       }
+}
diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php
new file mode 100644 (file)
index 0000000..8771fa1
--- /dev/null
@@ -0,0 +1,390 @@
+<?php
+/**#@+
+ * Give information about the version of MediaWiki, PHP, the DB and extensions
+ *
+ * @file
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * constructor
+ */
+function wfSpecialVersion() {
+       $version = new SpecialVersion;
+       $version->execute();
+}
+
+/**
+ * @ingroup SpecialPage
+ */
+class SpecialVersion {
+       private $firstExtOpened = true;
+
+       /**
+        * main()
+        */
+       function execute() {
+               global $wgOut, $wgMessageCache, $wgSpecialVersionShowHooks;
+               $wgMessageCache->loadAllMessages();
+
+               $wgOut->addHTML( '<div dir="ltr">' );
+               $text = 
+                       $this->MediaWikiCredits() .
+                       $this->softwareInformation() .
+                       $this->extensionCredits();
+               if  ( $wgSpecialVersionShowHooks ) {
+                       $text .= $this->wgHooks();
+               }
+               $wgOut->addWikiText( $text );
+               $wgOut->addHTML( $this->IPInfo() );
+               $wgOut->addHTML( '</div>' );
+       }
+
+       /**#@+
+        * @private
+        */
+
+       /**
+        * @return wiki text showing the license information
+        */
+       static function MediaWikiCredits() {
+               $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) ) .
+               "__NOTOC__
+               This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
+               copyright (C) 2001-2008 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
+               Tim Starling, Erik Möller, Gabriel Wicke, Ã†var Arnfjörð Bjarmason,
+               Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan and others.
+
+               MediaWiki is free software; you can redistribute it and/or modify
+               it under the terms of the GNU General Public License as published by
+               the Free Software Foundation; either version 2 of the License, or
+               (at your option) any later version.
+
+               MediaWiki is distributed in the hope that it will be useful,
+               but WITHOUT ANY WARRANTY; without even the implied warranty of
+               MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+               GNU General Public License for more details.
+
+               You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
+               along with this program; if not, write to the Free Software
+               Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+               or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
+               ";
+
+               return str_replace( "\t\t", '', $ret ) . "\n";
+       }
+
+       /**
+        * @return wiki text showing the third party software versions (apache, php, mysql).
+        */
+       static function softwareInformation() {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               return Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
+                       Xml::openElement( 'table', array( 'id' => 'sv-software' ) ) .
+                               "<tr>
+                                       <th>" . wfMsg( 'version-software-product' ) . "</th>
+                                       <th>" . wfMsg( 'version-software-version' ) . "</th>
+                               </tr>\n
+                               <tr>
+                                       <td>[http://www.mediawiki.org/ MediaWiki]</td>
+                                       <td>" . self::getVersionLinked() . "</td>
+                               </tr>\n
+                               <tr>
+                                       <td>[http://www.php.net/ PHP]</td>
+                                       <td>" . phpversion() . " (" . php_sapi_name() . ")</td>
+                               </tr>\n
+                               <tr>
+                                       <td>" . $dbr->getSoftwareLink() . "</td>
+                                       <td>" . $dbr->getServerVersion() . "</td>
+                               </tr>\n" .
+                       Xml::closeElement( 'table' );
+       }
+
+       /**
+        * Return a string of the MediaWiki version with SVN revision if available
+        *
+        * @return mixed
+        */
+       public static function getVersion() {
+               global $wgVersion, $IP;
+               wfProfileIn( __METHOD__ );
+               $svn = self::getSvnRevision( $IP );
+               $version = $svn ? "$wgVersion (r$svn)" : $wgVersion;
+               wfProfileOut( __METHOD__ );
+               return $version;
+       }
+       
+       /**
+        * Return a string of the MediaWiki version with a link to SVN revision if
+        * available
+        *
+        * @return mixed
+        */
+       public static function getVersionLinked() {
+               global $wgVersion, $IP;
+               wfProfileIn( __METHOD__ );
+               $svn = self::getSvnRevision( $IP );
+               $viewvc = 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/?pathrev=';
+               $version = $svn ? "$wgVersion ([{$viewvc}{$svn} r$svn])" : $wgVersion;
+               wfProfileOut( __METHOD__ );
+               return $version;
+       }
+
+       /** Generate wikitext showing extensions name, URL, author and description */
+       function extensionCredits() {
+               global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
+
+               if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
+                       return '';
+
+               $extensionTypes = array(
+                       'specialpage' => wfMsg( 'version-specialpages' ),
+                       'parserhook' => wfMsg( 'version-parserhooks' ),
+                       'variable' => wfMsg( 'version-variables' ),
+                       'media' => wfMsg( 'version-mediahandlers' ),
+                       'other' => wfMsg( 'version-other' ),
+               );
+               wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
+
+               $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
+                       Xml::openElement( 'table', array( 'id' => 'sv-ext' ) );
+
+               foreach ( $extensionTypes as $type => $text ) {
+                       if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
+                               $out .= $this->openExtType( $text );
+
+                               usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
+
+                               foreach ( $wgExtensionCredits[$type] as $extension ) {
+                                       if ( isset( $extension['version'] ) ) {
+                                               $version = $extension['version'];
+                                       } elseif ( isset( $extension['svn-revision'] ) && 
+                                               preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/', 
+                                                       $extension['svn-revision'], $m ) ) 
+                                       {
+                                               $version = 'r' . $m[1];
+                                       } else {
+                                               $version = null;
+                                       }
+
+                                       $out .= $this->formatCredits(
+                                               isset ( $extension['name'] )           ? $extension['name']        : '',
+                                               $version,
+                                               isset ( $extension['author'] )         ? $extension['author']      : '',
+                                               isset ( $extension['url'] )            ? $extension['url']         : null,
+                                               isset ( $extension['description'] )    ? $extension['description'] : '',
+                                               isset ( $extension['descriptionmsg'] ) ? $extension['descriptionmsg'] : ''
+                                       );
+                               }
+                       }
+               }
+
+               if ( count( $wgExtensionFunctions ) ) {
+                       $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
+                       $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
+               }
+
+               if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
+                       for ( $i = 0; $i < $cnt; ++$i )
+                               $tags[$i] = "&lt;{$tags[$i]}&gt;";
+                       $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
+                       $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
+               }
+
+               if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
+                       $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
+                       $out .= '<tr><td colspan="3">' . $this->listToText( $fhooks ) . "</td></tr>\n";
+               }
+
+               if ( count( $wgSkinExtensionFunctions ) ) {
+                       $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
+                       $out .= '<tr><td colspan="3">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
+               }
+               $out .= Xml::closeElement( 'table' );
+               return $out;
+       }
+
+       /** Callback to sort extensions by type */
+       function compare( $a, $b ) {
+               global $wgLang;
+               if( $a['name'] === $b['name'] ) {
+                       return 0;
+               } else {
+                       return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
+                               ? 1
+                               : -1;
+               }
+       }
+
+       function formatCredits( $name, $version = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
+               $extension = isset( $url ) ? "[$url $name]" : $name;
+               $version = isset( $version ) ? "(" . wfMsg( 'version-version' ) . " $version)" : '';
+
+               # Look for a localized description
+               if( isset( $descriptionMsg ) ) {
+                       $msg = wfMsg( $descriptionMsg );
+                       if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
+                               $description = $msg;
+                       }
+               }
+
+               return "<tr>
+                               <td><em>$extension $version</em></td>
+                               <td>$description</td>
+                               <td>" . $this->listToText( (array)$author ) . "</td>
+                       </tr>\n";
+       }
+
+       /**
+        * @return string
+        */
+       function wgHooks() {
+               global $wgHooks;
+
+               if ( count( $wgHooks ) ) {
+                       $myWgHooks = $wgHooks;
+                       ksort( $myWgHooks );
+
+                       $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
+                               Xml::openElement( 'table', array( 'id' => 'sv-hooks' ) ) .
+                               "<tr>
+                                       <th>" . wfMsg( 'version-hook-name' ) . "</th>
+                                       <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
+                               </tr>\n";
+
+                       foreach ( $myWgHooks as $hook => $hooks )
+                               $ret .= "<tr>
+                                               <td>$hook</td>
+                                               <td>" . $this->listToText( $hooks ) . "</td>
+                                       </tr>\n";
+
+                       $ret .= Xml::closeElement( 'table' );
+                       return $ret;
+               } else
+                       return '';
+       }
+
+       private function openExtType($text, $name = null) {
+               $opt = array( 'colspan' => 3 );
+               $out = '';
+
+               if(!$this->firstExtOpened) {
+                       // Insert a spacing line
+                       $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
+               }
+               $this->firstExtOpened = false;
+
+               if($name) { $opt['id'] = "sv-$name"; }
+
+               $out .= "<tr>" . Xml::element( 'th', $opt, $text) . "</tr>\n";
+               return $out;
+       }
+
+       /**
+        * @static
+        *
+        * @return string
+        */
+       function IPInfo() {
+               $ip =  str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
+               return "<!-- visited from $ip -->\n" .
+                       "<span style='display:none'>visited from $ip</span>";
+       }
+
+       /**
+        * @param array $list
+        * @return string
+        */
+       function listToText( $list ) {
+               $cnt = count( $list );
+
+               if ( $cnt == 1 ) {
+                       // Enforce always returning a string
+                       return (string)$this->arrayToString( $list[0] );
+               } elseif ( $cnt == 0 ) {
+                       return '';
+               } else {
+                       sort( $list );
+                       $t = array_slice( $list, 0, $cnt - 1 );
+                       $one = array_map( array( &$this, 'arrayToString' ), $t );
+                       $two = $this->arrayToString( $list[$cnt - 1] );
+                       $and = wfMsg( 'and' );
+
+                       return implode( ', ', $one ) . " $and $two";
+               }
+       }
+
+       /**
+        * @static
+        *
+        * @param mixed $list Will convert an array to string if given and return
+        *                    the paramater unaltered otherwise
+        * @return mixed
+        */
+       function arrayToString( $list ) {
+               if( is_object( $list ) ) {
+                       $class = get_class( $list );
+                       return "($class)";
+               } elseif ( ! is_array( $list ) ) {
+                       return $list;
+               } else {
+                       $class = get_class( $list[0] );
+                       return "($class, {$list[1]})";
+               }
+       }
+
+       /**
+        * Retrieve the revision number of a Subversion working directory.
+        *
+        * @param string $dir
+        * @return mixed revision number as int, or false if not a SVN checkout
+        */
+       public static function getSvnRevision( $dir ) {
+               // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
+               $entries = $dir . '/.svn/entries';
+
+               if( !file_exists( $entries ) ) {
+                       return false;
+               }
+
+               $content = file( $entries );
+
+               // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
+               if( preg_match( '/^<\?xml/', $content[0] ) ) {
+                       // subversion is release <= 1.3
+                       if( !function_exists( 'simplexml_load_file' ) ) {
+                               // We could fall back to expat... YUCK
+                               return false;
+                       }
+
+                       // SimpleXml whines about the xmlns...
+                       wfSuppressWarnings();
+                       $xml = simplexml_load_file( $entries );
+                       wfRestoreWarnings();
+
+                       if( $xml ) {
+                               foreach( $xml->entry as $entry ) {
+                                       if( $xml->entry[0]['name'] == '' ) {
+                                               // The directory entry should always have a revision marker.
+                                               if( $entry['revision'] ) {
+                                                       return intval( $entry['revision'] );
+                                               }
+                                       }
+                               }
+                       }
+                       return false;
+               } else {
+                       // subversion is release 1.4
+                       return intval( $content[3] );
+               }
+       }
+
+       /**#@-*/
+}
+
+/**#@-*/
diff --git a/includes/specials/SpecialWantedcategories.php b/includes/specials/SpecialWantedcategories.php
new file mode 100644 (file)
index 0000000..969a8d8
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A querypage to list the most wanted categories - implements Special:Wantedcategories
+ *
+ * @ingroup SpecialPage
+ *
+ * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
+ * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+class WantedCategoriesPage extends QueryPage {
+
+       function getName() {
+               return 'Wantedcategories';
+       }
+
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() {
+               return false;
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
+               $name = $dbr->addQuotes( $this->getName() );
+               return
+                       "
+                       SELECT
+                               $name as type,
+                               " . NS_CATEGORY . " as namespace,
+                               cl_to as title,
+                               COUNT(*) as value
+                       FROM $categorylinks
+                       LEFT JOIN $page ON cl_to = page_title AND page_namespace = ". NS_CATEGORY ."
+                       WHERE page_title IS NULL
+                       GROUP BY 1,2,3
+                       ";
+       }
+
+       function sortDescending() { return true; }
+
+       /**
+        * Fetch user page links and cache their existence
+        */
+       function preprocessResults( $db, $res ) {
+               $batch = new LinkBatch;
+               while ( $row = $db->fetchObject( $res ) )
+                       $batch->add( $row->namespace, $row->title );
+               $batch->execute();
+
+               // Back to start for display
+               if ( $db->numRows( $res ) > 0 )
+                       // If there are no rows we get an error seeking.
+                       $db->dataSeek( $res, 0 );
+       }
+
+       function formatResult( $skin, $result ) {
+               global $wgLang, $wgContLang;
+
+               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $text = $wgContLang->convert( $nt->getText() );
+
+               $plink = $this->isCached() ?
+                       $skin->makeLinkObj( $nt, htmlspecialchars( $text ) ) :
+                       $skin->makeBrokenLinkObj( $nt, htmlspecialchars( $text ) );
+
+               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
+                       $wgLang->formatNum( $result->value ) );
+               return wfSpecialList($plink, $nlinks);
+       }
+}
+
+/**
+ * constructor
+ */
+function wfSpecialWantedCategories() {
+       list( $limit, $offset ) = wfCheckLimits();
+
+       $wpp = new WantedCategoriesPage();
+
+       $wpp->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/SpecialWantedpages.php b/includes/specials/SpecialWantedpages.php
new file mode 100644 (file)
index 0000000..650e04f
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * implements Special:Wantedpages
+ * @ingroup SpecialPage
+ */
+class WantedPagesPage extends QueryPage {
+       var $nlinks;
+
+       function WantedPagesPage( $inc = false, $nlinks = true ) {
+               $this->setListoutput( $inc );
+               $this->nlinks = $nlinks;
+       }
+
+       function getName() {
+               return 'Wantedpages';
+       }
+
+       function isExpensive() {
+               return true;
+       }
+       function isSyndicated() { return false; }
+
+       function getSQL() {
+               global $wgWantedPagesThreshold;
+               $count = $wgWantedPagesThreshold - 1;
+               $dbr = wfGetDB( DB_SLAVE );
+               $pagelinks = $dbr->tableName( 'pagelinks' );
+               $page      = $dbr->tableName( 'page' );
+               return
+                       "SELECT 'Wantedpages' AS type,
+                               pl_namespace AS namespace,
+                               pl_title AS title,
+                               COUNT(*) AS value
+                        FROM $pagelinks
+                        LEFT JOIN $page AS pg1
+                        ON pl_namespace = pg1.page_namespace AND pl_title = pg1.page_title
+                        LEFT JOIN $page AS pg2
+                        ON pl_from = pg2.page_id
+                        WHERE pg1.page_namespace IS NULL
+                        AND pl_namespace NOT IN ( 2, 3 )
+                        AND pg2.page_namespace != 8
+                        GROUP BY 1,2,3
+                        HAVING COUNT(*) > $count";
+       }
+
+       /**
+        * Cache page existence for performance
+        */
+       function preprocessResults( $db, $res ) {
+               $batch = new LinkBatch;
+               while ( $row = $db->fetchObject( $res ) )
+                       $batch->add( $row->namespace, $row->title );
+               $batch->execute();
+
+               // Back to start for display
+               if ( $db->numRows( $res ) > 0 )
+                       // If there are no rows we get an error seeking.
+                       $db->dataSeek( $res, 0 );
+       }
+
+       /**
+        * Format an individual result
+        *
+        * @param $skin Skin to use for UI elements
+        * @param $result Result row
+        * @return string
+        */
+       public function formatResult( $skin, $result ) {
+               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               if( $title instanceof Title ) {
+                       if( $this->isCached() ) {
+                               $pageLink = $title->exists()
+                                       ? '<s>' . $skin->makeLinkObj( $title ) . '</s>'
+                                       : $skin->makeBrokenLinkObj( $title );
+                       } else {
+                               $pageLink = $skin->makeBrokenLinkObj( $title );
+                       }
+                       return wfSpecialList( $pageLink, $this->makeWlhLink( $title, $skin, $result ) );
+               } else {
+                       $tsafe = htmlspecialchars( $result->title );
+                       return "Invalid title in result set; {$tsafe}";
+               }
+       }
+
+       /**
+        * Make a "what links here" link for a specified result if required
+        *
+        * @param $title Title to make the link for
+        * @param $skin Skin to use
+        * @param $result Result row
+        * @return string
+        */
+       private function makeWlhLink( $title, $skin, $result ) {
+               global $wgLang;
+               if( $this->nlinks ) {
+                       $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
+                       $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
+                               $wgLang->formatNum( $result->value ) );
+                       return $skin->makeKnownLinkObj( $wlh, $label, 'target=' . $title->getPrefixedUrl() );
+               } else {
+                       return null;
+               }
+       }
+
+}
+
+/**
+ * constructor
+ */
+function wfSpecialWantedpages( $par = null, $specialPage ) {
+       $inc = $specialPage->including();
+
+       if ( $inc ) {
+               @list( $limit, $nlinks ) = explode( '/', $par, 2 );
+               $limit = (int)$limit;
+               $nlinks = $nlinks === 'nlinks';
+               $offset = 0;
+       } else {
+               list( $limit, $offset ) = wfCheckLimits();
+               $nlinks = true;
+       }
+
+       $wpp = new WantedPagesPage( $inc, $nlinks );
+
+       $wpp->doQuery( $offset, $limit, !$inc );
+}
diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php
new file mode 100644 (file)
index 0000000..4af22c7
--- /dev/null
@@ -0,0 +1,372 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage Watchlist
+ */
+
+/**
+ * Constructor
+ *
+ * @param $par Parameter passed to the page
+ */
+function wfSpecialWatchlist( $par ) {
+       global $wgUser, $wgOut, $wgLang, $wgRequest;
+       global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
+       global $wgEnotifWatchlist;
+       $fname = 'wfSpecialWatchlist';
+
+       $skin = $wgUser->getSkin();
+       $specialTitle = SpecialPage::getTitleFor( 'Watchlist' );
+       $wgOut->setRobotPolicy( 'noindex,nofollow' );
+
+       # Anons don't get a watchlist
+       if( $wgUser->isAnon() ) {
+               $wgOut->setPageTitle( wfMsg( 'watchnologin' ) );
+               $llink = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Userlogin' ), wfMsgHtml( 'loginreqlink' ), 'returnto=' . $specialTitle->getPrefixedUrl() );
+               $wgOut->addHtml( wfMsgWikiHtml( 'watchlistanontext', $llink ) );
+               return;
+       }
+
+       $wgOut->setPageTitle( wfMsg( 'watchlist' ) );
+
+       $sub  = wfMsgExt( 'watchlistfor', 'parseinline', $wgUser->getName() );
+       $sub .= '<br />' . WatchlistEditor::buildTools( $wgUser->getSkin() );
+       $wgOut->setSubtitle( $sub );
+
+       if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) {
+               $editor = new WatchlistEditor();
+               $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
+               return;
+       }
+
+       $uid = $wgUser->getId();
+       if( ($wgEnotifWatchlist || $wgShowUpdatedMarker) && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
+               $wgUser->clearAllNotifications( $uid );
+               $wgOut->redirect( $specialTitle->getFullUrl() );
+               return;
+       }
+
+       $defaults = array(
+       /* float */ 'days' => floatval( $wgUser->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
+       /* bool  */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ),
+       /* bool  */ 'hideBots' => (int)$wgUser->getBoolOption( 'watchlisthidebots' ),
+       /* bool */ 'hideMinor' => (int)$wgUser->getBoolOption( 'watchlisthideminor' ),
+       /* ?     */ 'namespace' => 'all',
+       );
+
+       extract($defaults);
+
+       # Extract variables from the request, falling back to user preferences or
+       # other default values if these don't exist
+       $prefs['days'    ] = floatval( $wgUser->getOption( 'watchlistdays' ) );
+       $prefs['hideown' ] = $wgUser->getBoolOption( 'watchlisthideown' );
+       $prefs['hidebots'] = $wgUser->getBoolOption( 'watchlisthidebots' );
+       $prefs['hideminor'] = $wgUser->getBoolOption( 'watchlisthideminor' );
+
+       # Get query variables
+       $days     = $wgRequest->getVal(  'days', $prefs['days'] );
+       $hideOwn  = $wgRequest->getBool( 'hideOwn', $prefs['hideown'] );
+       $hideBots = $wgRequest->getBool( 'hideBots', $prefs['hidebots'] );
+       $hideMinor = $wgRequest->getBool( 'hideMinor', $prefs['hideminor'] );
+
+       # Get namespace value, if supplied, and prepare a WHERE fragment
+       $nameSpace = $wgRequest->getIntOrNull( 'namespace' );
+       if( !is_null( $nameSpace ) ) {
+               $nameSpace = intval( $nameSpace );
+               $nameSpaceClause = " AND rc_namespace = $nameSpace";
+       } else {
+               $nameSpace = '';
+               $nameSpaceClause = '';
+       }
+
+       $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
+       list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' );
+
+       $watchlistCount = $dbr->selectField( 'watchlist', 'COUNT(*)',
+               array( 'wl_user' => $uid ), __METHOD__ );
+       // Adjust for page X, talk:page X, which are both stored separately,
+       // but treated together
+       $nitems = floor($watchlistCount / 2);
+
+       if( is_null($days) || !is_numeric($days) ) {
+               $big = 1000; /* The magical big */
+               if($nitems > $big) {
+                       # Set default cutoff shorter
+                       $days = $defaults['days'] = (12.0 / 24.0); # 12 hours...
+               } else {
+                       $days = $defaults['days']; # default cutoff for shortlisters
+               }
+       } else {
+               $days = floatval($days);
+       }
+
+       // Dump everything here
+       $nondefaults = array();
+
+       wfAppendToArrayIfNotDefault('days'     , $days         , $defaults, $nondefaults);
+       wfAppendToArrayIfNotDefault('hideOwn'  , (int)$hideOwn , $defaults, $nondefaults);
+       wfAppendToArrayIfNotDefault('hideBots' , (int)$hideBots, $defaults, $nondefaults);
+       wfAppendToArrayIfNotDefault( 'hideMinor', (int)$hideMinor, $defaults, $nondefaults );
+       wfAppendToArrayIfNotDefault('namespace', $nameSpace    , $defaults, $nondefaults);
+
+       $hookSql = "";
+       if( ! wfRunHooks('BeforeWatchlist', array($nondefaults, $wgUser, &$hookSql)) ) {
+               return;
+       }
+
+       if($nitems == 0) {
+               $wgOut->addWikiMsg( 'nowatchlist' );
+               return;
+       }
+
+       if ( $days <= 0 ) {
+               $andcutoff = '';
+       } else {
+               $andcutoff = "AND rc_timestamp > '".$dbr->timestamp( time() - intval( $days * 86400 ) )."'";
+               /*
+               $sql = "SELECT COUNT(*) AS n FROM $page, $revision  WHERE rev_timestamp>'$cutoff' AND page_id=rev_page";
+               $res = $dbr->query( $sql, $fname );
+               $s = $dbr->fetchObject( $res );
+               $npages = $s->n;
+               */
+       }
+
+       # If the watchlist is relatively short, it's simplest to zip
+       # down its entirety and then sort the results.
+
+       # If it's relatively long, it may be worth our while to zip
+       # through the time-sorted page list checking for watched items.
+
+       # Up estimate of watched items by 15% to compensate for talk pages...
+
+       # Toggles
+       $andHideOwn = $hideOwn ? "AND (rc_user <> $uid)" : '';
+       $andHideBots = $hideBots ? "AND (rc_bot = 0)" : '';
+       $andHideMinor = $hideMinor ? 'AND rc_minor = 0' : '';
+
+       # Show watchlist header
+       $header = '';
+       if( $wgUser->getOption( 'enotifwatchlistpages' ) && $wgEnotifWatchlist) {
+               $header .= wfMsg( 'wlheader-enotif' ) . "\n";
+       }
+       if ( $wgShowUpdatedMarker ) {
+               $header .= wfMsg( 'wlheader-showupdated' ) . "\n";
+       }
+
+  # Toggle watchlist content (all recent edits or just the latest)
+       if( $wgUser->getOption( 'extendwatchlist' )) {
+               $andLatest='';
+               $limitWatchlist = 'LIMIT ' . intval( $wgUser->getOption( 'wllimit' ) );
+       } else {
+       # Top log Ids for a page are not stored
+               $andLatest = 'AND (rc_this_oldid=page_latest OR rc_type=' . RC_LOG . ') ';
+               $limitWatchlist = '';
+       }
+
+       $header .= wfMsgExt( 'watchlist-details', array( 'parsemag' ), $wgLang->formatNum( $nitems ) );
+       $wgOut->addWikiText( $header );
+
+       # Show a message about slave lag, if applicable
+       if( ( $lag = $dbr->getLag() ) > 0 )
+               $wgOut->showLagWarning( $lag );
+
+       if ( $wgShowUpdatedMarker ) {
+               $wgOut->addHTML( '<form action="' .
+                       $specialTitle->escapeLocalUrl() .
+                       '" method="post"><input type="submit" name="dummy" value="' .
+                       htmlspecialchars( wfMsg( 'enotif_reset' ) ) .
+                       '" /><input type="hidden" name="reset" value="all" /></form>' .
+                       "\n\n" );
+       }
+       if ( $wgShowUpdatedMarker ) {
+               $wltsfield = ", ${watchlist}.wl_notificationtimestamp ";
+       } else {
+               $wltsfield = '';
+       }
+       $sql = "SELECT ${recentchanges}.* ${wltsfield}
+         FROM $watchlist,$recentchanges
+         LEFT JOIN $page ON rc_cur_id=page_id
+         WHERE wl_user=$uid
+         AND wl_namespace=rc_namespace
+         AND wl_title=rc_title
+         $andcutoff
+         $andLatest
+         $andHideOwn
+         $andHideBots
+         $andHideMinor
+         $nameSpaceClause
+         $hookSql
+         ORDER BY rc_timestamp DESC
+         $limitWatchlist";
+
+       $res = $dbr->query( $sql, $fname );
+       $numRows = $dbr->numRows( $res );
+
+       /* Start bottom header */
+       $wgOut->addHTML( "<hr />\n" );
+
+       if($days >= 1) {
+               $wgOut->addWikiText( wfMsgExt( 'rcnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
+                       $wgLang->formatNum( $days ), $wgLang->timeAndDate( wfTimestampNow(), true ) ) . '<br />' , false );
+       } elseif($days > 0) {
+               $wgOut->addWikiText( wfMsgExt( 'wlnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
+                       $wgLang->formatNum( round($days*24) ) ) . '<br />' , false );
+       }
+
+       $wgOut->addHTML( "\n" . wlCutoffLinks( $days, 'Watchlist', $nondefaults ) . "<br />\n" );
+
+       # Spit out some control panel links
+       $thisTitle = SpecialPage::getTitleFor( 'Watchlist' );
+       $skin = $wgUser->getSkin();
+
+       # Hide/show bot edits
+       $label = $hideBots ? wfMsgHtml( 'watchlist-show-bots' ) : wfMsgHtml( 'watchlist-hide-bots' );
+       $linkBits = wfArrayToCGI( array( 'hideBots' => 1 - (int)$hideBots ), $nondefaults );
+       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
+
+       # Hide/show own edits
+       $label = $hideOwn ? wfMsgHtml( 'watchlist-show-own' ) : wfMsgHtml( 'watchlist-hide-own' );
+       $linkBits = wfArrayToCGI( array( 'hideOwn' => 1 - (int)$hideOwn ), $nondefaults );
+       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
+
+       # Hide/show minor edits
+       $label = $hideMinor ? wfMsgHtml( 'watchlist-show-minor' ) : wfMsgHtml( 'watchlist-hide-minor' );
+       $linkBits = wfArrayToCGI( array( 'hideMinor' => 1 - (int)$hideMinor ), $nondefaults );
+       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
+
+       $wgOut->addHTML( implode( ' | ', $links ) );
+
+       # Form for namespace filtering
+       $form  = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
+       $form .= '<p>';
+       $form .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;';
+       $form .= Xml::namespaceSelector( $nameSpace, '' ) . '&nbsp;';
+       $form .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . '</p>';
+       $form .= Xml::hidden( 'days', $days );
+       if( $hideOwn )
+               $form .= Xml::hidden( 'hideOwn', 1 );
+       if( $hideBots )
+               $form .= Xml::hidden( 'hideBots', 1 );
+       if( $hideMinor )
+               $form .= Xml::hidden( 'hideMinor', 1 );
+       $form .= Xml::closeElement( 'form' );
+       $wgOut->addHtml( $form );
+
+       # If there's nothing to show, stop here
+       if( $numRows == 0 ) {
+               $wgOut->addWikiMsg( 'watchnochange' );
+               return;
+       }
+
+       /* End bottom header */
+
+       /* Do link batch query */
+       $linkBatch = new LinkBatch;
+       while ( $row = $dbr->fetchObject( $res ) ) {
+               $userNameUnderscored = str_replace( ' ', '_', $row->rc_user_text );
+               if ( $row->rc_user != 0 ) {
+                       $linkBatch->add( NS_USER, $userNameUnderscored );
+               }
+               $linkBatch->add( NS_USER_TALK, $userNameUnderscored );
+       }
+       $linkBatch->execute();
+       $dbr->dataSeek( $res, 0 );
+
+       $list = ChangesList::newFromUser( $wgUser );
+
+       $s = $list->beginRecentChangesList();
+       $counter = 1;
+       while ( $obj = $dbr->fetchObject( $res ) ) {
+               # Make RC entry
+               $rc = RecentChange::newFromRow( $obj );
+               $rc->counter = $counter++;
+
+               if ( $wgShowUpdatedMarker ) {
+                       $updated = $obj->wl_notificationtimestamp;
+               } else {
+                       $updated = false;
+               }
+
+               if ($wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
+                       $rc->numberofWatchingusers = $dbr->selectField( 'watchlist',
+                               'COUNT(*)',
+                               array(
+                                       'wl_namespace' => $obj->rc_namespace,
+                                       'wl_title' => $obj->rc_title,
+                               ),
+                               __METHOD__ );
+               } else {
+                       $rc->numberofWatchingusers = 0;
+               }
+
+               $s .= $list->recentChangesLine( $rc, $updated );
+       }
+       $s .= $list->endRecentChangesList();
+
+       $dbr->freeResult( $res );
+       $wgOut->addHTML( $s );
+
+}
+
+function wlHoursLink( $h, $page, $options = array() ) {
+       global $wgUser, $wgLang, $wgContLang;
+       $sk = $wgUser->getSkin();
+       $s = $sk->makeKnownLink(
+         $wgContLang->specialPage( $page ),
+         $wgLang->formatNum( $h ),
+         wfArrayToCGI( array('days' => ($h / 24.0)), $options ) );
+       return $s;
+}
+
+function wlDaysLink( $d, $page, $options = array() ) {
+       global $wgUser, $wgLang, $wgContLang;
+       $sk = $wgUser->getSkin();
+       $s = $sk->makeKnownLink(
+         $wgContLang->specialPage( $page ),
+         ($d ? $wgLang->formatNum( $d ) : wfMsgHtml( 'watchlistall2' ) ),
+         wfArrayToCGI( array('days' => $d), $options ) );
+       return $s;
+}
+
+/**
+ * Returns html
+ */
+function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) {
+       $hours = array( 1, 2, 6, 12 );
+       $days = array( 1, 3, 7 );
+       $i = 0;
+       foreach( $hours as $h ) {
+               $hours[$i++] = wlHoursLink( $h, $page, $options );
+       }
+       $i = 0;
+       foreach( $days as $d ) {
+               $days[$i++] = wlDaysLink( $d, $page, $options );
+       }
+       return wfMsgExt('wlshowlast',
+               array('parseinline', 'replaceafter'),
+               implode(' | ', $hours),
+               implode(' | ', $days),
+               wlDaysLink( 0, $page, $options ) );
+}
+
+/**
+ * Count the number of items on a user's watchlist
+ *
+ * @param $talk Include talk pages
+ * @return integer
+ */
+function wlCountItems( &$user, $talk = true ) {
+       $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
+
+       # Fetch the raw count
+       $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->mId ), 'wlCountItems' );
+       $row = $dbr->fetchObject( $res );
+       $count = $row->count;
+       $dbr->freeResult( $res );
+
+       # Halve to remove talk pages if needed
+       if( !$talk )
+               $count = floor( $count / 2 );
+
+       return( $count );
+}
diff --git a/includes/specials/SpecialWhatlinkshere.php b/includes/specials/SpecialWhatlinkshere.php
new file mode 100644 (file)
index 0000000..a57df5e
--- /dev/null
@@ -0,0 +1,408 @@
+<?php
+/**
+ * @todo Use some variant of Pager or something; the pagination here is lousy.
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Entry point
+ * @param $par String: An article name ??
+ */
+function wfSpecialWhatlinkshere($par = NULL) {
+       global $wgRequest;
+       $page = new WhatLinksHerePage( $wgRequest, $par );
+       $page->execute();
+}
+
+/**
+ * implements Special:Whatlinkshere
+ * @ingroup SpecialPage
+ */
+class WhatLinksHerePage {
+       // Stored data
+       protected $par;
+
+       // Stored objects
+       protected $opts, $target, $selfTitle;
+
+       // Stored globals
+       protected $skin, $request;
+
+       protected $limits = array( 20, 50, 100, 250, 500 );
+
+       function WhatLinksHerePage( $request, $par = null ) {
+               global $wgUser;
+               $this->request = $request;
+               $this->skin = $wgUser->getSkin();
+               $this->par = $par;
+       }
+
+       function execute() {
+               global $wgOut;
+
+               $opts = new FormOptions();
+
+               $opts->add( 'target', '' );
+               $opts->add( 'namespace', '', FormOptions::INTNULL );
+               $opts->add( 'limit', 50 );
+               $opts->add( 'from', 0 );
+               $opts->add( 'back', 0 );
+               $opts->add( 'hideredirs', false );
+               $opts->add( 'hidetrans', false );
+               $opts->add( 'hidelinks', false );
+               $opts->add( 'hideimages', false );
+
+               $opts->fetchValuesFromRequest( $this->request );
+               $opts->validateIntBounds( 'limit', 0, 5000 );
+
+               // Give precedence to subpage syntax
+               if ( isset($this->par) ) {
+                       $opts->setValue( 'target', $this->par );
+               }
+
+               // Bind to member variable
+               $this->opts = $opts;
+
+               $this->target = Title::newFromURL( $opts->getValue( 'target' ) );
+               if( !$this->target ) {
+                       $wgOut->addHTML( $this->whatlinkshereForm() );
+                       return;
+               }
+
+               $this->selfTitle = SpecialPage::getTitleFor( 'Whatlinkshere', $this->target->getPrefixedDBkey() );
+
+               $wgOut->setPageTitle( wfMsgExt( 'whatlinkshere-title', 'escape', $this->target->getPrefixedText() ) );
+               $wgOut->setSubtitle( wfMsgHtml( 'linklistsub' ) );
+
+               $wgOut->addHTML( wfMsgExt( 'whatlinkshere-barrow', array( 'escapenoentities') ) . ' '  .$this->skin->makeLinkObj($this->target, '', 'redirect=no' )."<br />\n");
+
+               $this->showIndirectLinks( 0, $this->target, $opts->getValue( 'limit' ),
+                       $opts->getValue( 'from' ), $opts->getValue( 'back' ) );
+       }
+
+       /**
+        * @param $level  int     Recursion level
+        * @param $target Title   Target title
+        * @param $limit  int     Number of entries to display
+        * @param $from   Title   Display from this article ID
+        * @param $back   Title   Display from this article ID at backwards scrolling
+        * @private
+        */
+       function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
+               global $wgOut, $wgMaxRedirectLinksRetrieved;
+               $dbr = wfGetDB( DB_SLAVE );
+               $options = array();
+
+               $hidelinks = $this->opts->getValue( 'hidelinks' );
+               $hideredirs = $this->opts->getValue( 'hideredirs' );
+               $hidetrans = $this->opts->getValue( 'hidetrans' );
+               $hideimages = $target->getNamespace() != NS_IMAGE || $this->opts->getValue( 'hideimages' );
+
+               $fetchlinks = (!$hidelinks || !$hideredirs);
+
+               // Make the query
+               $plConds = array(
+                       'page_id=pl_from',
+                       'pl_namespace' => $target->getNamespace(),
+                       'pl_title' => $target->getDBkey(),
+               );
+               if( $hideredirs ) {
+                       $plConds['page_is_redirect'] = 0;
+               } elseif( $hidelinks ) {
+                       $plConds['page_is_redirect'] = 1;
+               }
+
+               $tlConds = array(
+                       'page_id=tl_from',
+                       'tl_namespace' => $target->getNamespace(),
+                       'tl_title' => $target->getDBkey(),
+               );
+
+               $ilConds = array(
+                       'page_id=il_from',
+                       'il_to' => $target->getDBkey(),
+               );
+
+               $namespace = $this->opts->getValue( 'namespace' );
+               if ( is_int($namespace) ) {
+                       $plConds['page_namespace'] = $namespace;
+                       $tlConds['page_namespace'] = $namespace;
+                       $ilConds['page_namespace'] = $namespace;
+               }
+
+               if ( $from ) {
+                       $tlConds[] = "tl_from >= $from";
+                       $plConds[] = "pl_from >= $from";
+                       $ilConds[] = "il_from >= $from";
+               }
+
+               // Read an extra row as an at-end check
+               $queryLimit = $limit + 1;
+
+               // Enforce join order, sometimes namespace selector may
+               // trigger filesorts which are far less efficient than scanning many entries
+               $options[] = 'STRAIGHT_JOIN';
+
+               $options['LIMIT'] = $queryLimit;
+               $fields = array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' );
+
+               if( $fetchlinks ) {
+                       $options['ORDER BY'] = 'pl_from';
+                       $plRes = $dbr->select( array( 'pagelinks', 'page' ), $fields,
+                               $plConds, __METHOD__, $options );
+               }
+
+               if( !$hidetrans ) {
+                       $options['ORDER BY'] = 'tl_from';
+                       $tlRes = $dbr->select( array( 'templatelinks', 'page' ), $fields,
+                               $tlConds, __METHOD__, $options );
+               }
+
+               if( !$hideimages ) {
+                       $options['ORDER BY'] = 'il_from';
+                       $ilRes = $dbr->select( array( 'imagelinks', 'page' ), $fields,
+                               $ilConds, __METHOD__, $options );
+               }
+
+               if( ( !$fetchlinks || !$dbr->numRows($plRes) ) && ( $hidetrans || !$dbr->numRows($tlRes) ) && ( $hideimages || !$dbr->numRows($ilRes) ) ) {
+                       if ( 0 == $level ) {
+                               $wgOut->addHTML( $this->whatlinkshereForm() );
+                               $errMsg = is_int($namespace) ? 'nolinkshere-ns' : 'nolinkshere';
+                               $wgOut->addWikiMsg( $errMsg, $this->target->getPrefixedText() );
+                               // Show filters only if there are links
+                               if( $hidelinks || $hidetrans || $hideredirs || $hideimages )
+                                       $wgOut->addHTML( $this->getFilterPanel() );
+                       }
+                       return;
+               }
+
+               // Read the rows into an array and remove duplicates
+               // templatelinks comes second so that the templatelinks row overwrites the
+               // pagelinks row, so we get (inclusion) rather than nothing
+               if( $fetchlinks ) {
+                       while ( $row = $dbr->fetchObject( $plRes ) ) {
+                               $row->is_template = 0;
+                               $row->is_image = 0;
+                               $rows[$row->page_id] = $row;
+                       }
+                       $dbr->freeResult( $plRes );
+
+               }
+               if( !$hidetrans ) {
+                       while ( $row = $dbr->fetchObject( $tlRes ) ) {
+                               $row->is_template = 1;
+                               $row->is_image = 0;
+                               $rows[$row->page_id] = $row;
+                       }
+                       $dbr->freeResult( $tlRes );
+               }
+               if( !$hideimages ) {
+                       while ( $row = $dbr->fetchObject( $ilRes ) ) {
+                               $row->is_template = 0;
+                               $row->is_image = 1;
+                               $rows[$row->page_id] = $row;
+                       }
+                       $dbr->freeResult( $ilRes );
+               }
+
+               // Sort by key and then change the keys to 0-based indices
+               ksort( $rows );
+               $rows = array_values( $rows );
+
+               $numRows = count( $rows );
+
+               // Work out the start and end IDs, for prev/next links
+               if ( $numRows > $limit ) {
+                       // More rows available after these ones
+                       // Get the ID from the last row in the result set
+                       $nextId = $rows[$limit]->page_id;
+                       // Remove undisplayed rows
+                       $rows = array_slice( $rows, 0, $limit );
+               } else {
+                       // No more rows after
+                       $nextId = false;
+               }
+               $prevId = $from;
+
+               if ( $level == 0 ) {
+                       $wgOut->addHTML( $this->whatlinkshereForm() );
+                       $wgOut->addHTML( $this->getFilterPanel() );
+                       $wgOut->addWikiMsg( 'linkshere', $this->target->getPrefixedText() );
+
+                       $prevnext = $this->getPrevNext( $prevId, $nextId );
+                       $wgOut->addHTML( $prevnext );
+               }
+
+               $wgOut->addHTML( $this->listStart() );
+               foreach ( $rows as $row ) {
+                       $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
+
+                       if ( $row->page_is_redirect && $level < 2 ) {
+                               $wgOut->addHTML( $this->listItem( $row, $nt, true ) );
+                               $this->showIndirectLinks( $level + 1, $nt, $wgMaxRedirectLinksRetrieved );
+                               $wgOut->addHTML( Xml::closeElement( 'li' ) );
+                       } else {
+                               $wgOut->addHTML( $this->listItem( $row, $nt ) );
+                       }
+               }
+
+               $wgOut->addHTML( $this->listEnd() );
+
+               if( $level == 0 ) {
+                       $wgOut->addHTML( $prevnext );
+               }
+       }
+
+       protected function listStart() {
+               return Xml::openElement( 'ul' );
+       }
+
+       protected function listItem( $row, $nt, $notClose = false ) {
+               # local message cache
+               static $msgcache = null;
+               if ( $msgcache === null ) {
+                       static $msgs = array( 'isredirect', 'istemplate', 'semicolon-separator',
+                               'whatlinkshere-links', 'isimage' );
+                       $msgcache = array();
+                       foreach ( $msgs as $msg ) {
+                               $msgcache[$msg] = wfMsgHtml( $msg );
+                       }
+               }
+
+               $suppressRedirect = $row->page_is_redirect ? 'redirect=no' : '';
+               $link = $this->skin->makeKnownLinkObj( $nt, '', $suppressRedirect );
+
+               // Display properties (redirect or template)
+               $propsText = '';
+               $props = array();
+               if ( $row->page_is_redirect )
+                       $props[] = $msgcache['isredirect'];
+               if ( $row->is_template )
+                       $props[] = $msgcache['istemplate'];
+               if( $row->is_image )
+                       $props[] = $msgcache['isimage'];
+
+               if ( count( $props ) ) {
+                       $propsText = '(' . implode( $msgcache['semicolon-separator'], $props ) . ')';
+               }
+
+               # Space for utilities links, with a what-links-here link provided
+               $wlhLink = $this->wlhLink( $nt, $msgcache['whatlinkshere-links'] );
+               $wlh = Xml::wrapClass( "($wlhLink)", 'mw-whatlinkshere-tools' );
+
+               return $notClose ?
+                       Xml::openElement( 'li' ) . "$link $propsText $wlh\n" :
+                       Xml::tags( 'li', null, "$link $propsText $wlh" ) . "\n";
+       }
+
+       protected function listEnd() {
+               return Xml::closeElement( 'ul' );
+       }
+
+       protected function wlhLink( Title $target, $text ) {
+               static $title = null;
+               if ( $title === null )
+                       $title = SpecialPage::getTitleFor( 'Whatlinkshere' );
+
+               $targetText = $target->getPrefixedUrl();
+               return $this->skin->makeKnownLinkObj( $title, $text, 'target=' . $targetText );
+       }
+
+       function makeSelfLink( $text, $query ) {
+               return $this->skin->makeKnownLinkObj( $this->selfTitle, $text, $query );
+       }
+
+       function getPrevNext( $prevId, $nextId ) {
+               global $wgLang;
+               $currentLimit = $this->opts->getValue( 'limit' );
+               $fmtLimit = $wgLang->formatNum( $currentLimit );
+               $prev = wfMsgExt( 'whatlinkshere-prev', array( 'parsemag', 'escape' ), $fmtLimit );
+               $next = wfMsgExt( 'whatlinkshere-next', array( 'parsemag', 'escape' ), $fmtLimit );
+
+               $changed = $this->opts->getChangedValues();
+               unset($changed['target']); // Already in the request title
+
+               if ( 0 != $prevId ) {
+                       $overrides = array( 'from' => $this->opts->getValue( 'back' ) );
+                       $prev = $this->makeSelfLink( $prev, wfArrayToCGI( $overrides, $changed ) );
+               }
+               if ( 0 != $nextId ) {
+                       $overrides = array( 'from' => $nextId, 'back' => $prevId );
+                       $next = $this->makeSelfLink( $next, wfArrayToCGI( $overrides, $changed ) );
+               }
+
+               $limitLinks = array();
+               foreach ( $this->limits as $limit ) {
+                       $prettyLimit = $wgLang->formatNum( $limit );
+                       $overrides = array( 'limit' => $limit );
+                       $limitLinks[] = $this->makeSelfLink( $prettyLimit, wfArrayToCGI( $overrides, $changed ) );
+               }
+
+               $nums = implode ( ' | ', $limitLinks );
+
+               return wfMsgHtml( 'viewprevnext', $prev, $next, $nums );
+       }
+
+       function whatlinkshereForm() {
+               global $wgScript, $wgTitle;
+
+               // We get nicer value from the title object
+               $this->opts->consumeValue( 'target' );
+               // Reset these for new requests
+               $this->opts->consumeValues( array( 'back', 'from' ) );
+
+               $target = $this->target ? $this->target->getPrefixedText() : '';
+               $namespace = $this->opts->consumeValue( 'namespace' );
+
+               # Build up the form
+               $f = Xml::openElement( 'form', array( 'action' => $wgScript ) );
+               
+               # Values that should not be forgotten
+               $f .= Xml::hidden( 'title', $wgTitle->getPrefixedText() );
+               foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
+                       $f .= Xml::hidden( $name, $value );
+               }
+
+               $f .= Xml::fieldset( wfMsg( 'whatlinkshere' ) );
+
+               # Target input
+               $f .= Xml::inputLabel( wfMsg( 'whatlinkshere-page' ), 'target',
+                               'mw-whatlinkshere-target', 40, $target );
+
+               $f .= ' ';
+
+               # Namespace selector
+               $f .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;' .
+                       Xml::namespaceSelector( $namespace, '' );
+
+               # Submit
+               $f .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
+
+               # Close
+               $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
+
+               return $f;
+       }
+
+       function getFilterPanel() {
+               $show = wfMsgHtml( 'show' );
+               $hide = wfMsgHtml( 'hide' );
+
+               $changed = $this->opts->getChangedValues();
+               unset($changed['target']); // Already in the request title
+
+               $links = array();
+               $types = array( 'hidetrans', 'hidelinks', 'hideredirs' );
+               if( $this->target->getNamespace() == NS_IMAGE )
+                       $types[] = 'hideimages';
+               foreach( $types as $type ) {
+                       $chosen = $this->opts->getValue( $type );
+                       $msg = wfMsgHtml( "whatlinkshere-{$type}", $chosen ? $show : $hide );
+                       $overrides = array( $type => !$chosen );
+                       $links[] = $this->makeSelfLink( $msg, wfArrayToCGI( $overrides, $changed ) );
+               }
+               return Xml::fieldset( wfMsg( 'whatlinkshere-filters' ), implode( '&nbsp;|&nbsp;', $links ) );
+       }
+}
diff --git a/includes/specials/SpecialWithoutinterwiki.php b/includes/specials/SpecialWithoutinterwiki.php
new file mode 100644 (file)
index 0000000..2092e43
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page lists pages without language links
+ *
+ * @ingroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class WithoutInterwikiPage extends PageQueryPage {
+       private $prefix = '';
+
+       function getName() {
+               return 'Withoutinterwiki';
+       }
+
+       function getPageHeader() {
+               global $wgScript, $wgMiserMode;
+
+               # Do not show useless input form if wiki is running in misermode
+               if( $wgMiserMode ) {
+                       return '';
+               }
+
+               $prefix = $this->prefix;
+               $t = SpecialPage::getTitleFor( $this->getName() );
+
+               return  Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'withoutinterwiki-legend' ) ) .
+                       Xml::hidden( 'title', $t->getPrefixedText() ) .
+                       Xml::inputLabel( wfMsg( 'allpagesprefix' ), 'prefix', 'wiprefix', 20, $prefix ) . ' ' .
+                       Xml::submitButton( wfMsg( 'withoutinterwiki-submit' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' );
+       }
+
+       function sortDescending() {
+               return false;
+       }
+
+       function isExpensive() {
+               return true;
+       }
+
+       function isSyndicated() {
+               return false;
+       }
+
+       function getSQL() {
+               $dbr = wfGetDB( DB_SLAVE );
+               list( $page, $langlinks ) = $dbr->tableNamesN( 'page', 'langlinks' );
+               $prefix = $this->prefix ? "AND page_title LIKE '" . $dbr->escapeLike( $this->prefix ) . "%'" : '';
+               return
+                 "SELECT 'Withoutinterwiki'  AS type,
+                         page_namespace AS namespace,
+                         page_title     AS title,
+                         page_title     AS value
+                    FROM $page
+               LEFT JOIN $langlinks
+                      ON ll_from = page_id
+                   WHERE ll_title IS NULL
+                     AND page_namespace=" . NS_MAIN . "
+                     AND page_is_redirect = 0
+                         {$prefix}";
+       }
+
+       function setPrefix( $prefix = '' ) {
+               $this->prefix = $prefix;
+       }
+
+}
+
+function wfSpecialWithoutinterwiki() {
+       global $wgRequest, $wgContLang, $wgCapitalLinks;
+       list( $limit, $offset ) = wfCheckLimits();
+       if( $wgCapitalLinks ) {
+               $prefix = $wgContLang->ucfirst( $wgRequest->getVal( 'prefix' ) );
+       } else {
+               $prefix = $wgRequest->getVal( 'prefix' );
+       }
+       $wip = new WithoutInterwikiPage();
+       $wip->setPrefix( $prefix );
+       $wip->doQuery( $offset, $limit );
+}
diff --git a/includes/specials/Specialpages.php b/includes/specials/Specialpages.php
deleted file mode 100644 (file)
index ca91ad5..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- *
- */
-function wfSpecialSpecialpages() {
-       global $wgOut, $wgUser, $wgMessageCache, $wgSortSpecialPages;
-
-       $wgMessageCache->loadAllMessages();
-
-       $wgOut->setRobotpolicy( 'noindex,nofollow' );  # Is this really needed?
-       $sk = $wgUser->getSkin();
-
-       $pages = SpecialPage::getUsablePages();
-
-       if( count( $pages ) == 0 ) {
-               # Yeah, that was pointless. Thanks for coming.
-               return;
-       }
-
-       /** Put them into a sortable array */
-       $groups = array();
-       foreach ( $pages as $page ) {
-               if ( $page->isListed() ) {
-                       $group = SpecialPage::getGroup( $page );
-                       if( !isset($groups[$group]) ) {
-                               $groups[$group] = array();
-                       }
-                       $groups[$group][$page->getDescription()] = array( $page->getTitle(), $page->isRestricted() );
-               }
-       }
-
-       /** Sort */
-       if ( $wgSortSpecialPages ) {
-               foreach( $groups as $group => $sortedPages ) {
-                       ksort( $groups[$group] );
-               }
-       }
-
-       /** Always move "other" to end */
-       if( array_key_exists('other',$groups) ) {
-               $other = $groups['other'];
-               unset( $groups['other'] );
-               $groups['other'] = $other;
-       }
-
-       /** Now output the HTML */
-       foreach ( $groups as $group => $sortedPages ) {
-               $middle = ceil( count($sortedPages)/2 );
-               $total = count($sortedPages);
-               $count = 0;
-
-               $wgOut->addHTML( "<h4 class='mw-specialpagesgroup'>".wfMsgHtml("specialpages-group-$group")."</h4>\n" );
-               $wgOut->addHTML( "<table style='width: 100%;' class='mw-specialpages-table'><tr>" );
-               $wgOut->addHTML( "<td width='30%' valign='top'><ul>\n" );
-               foreach( $sortedPages as $desc => $specialpage ) {
-                       list( $title, $restricted ) = $specialpage;
-                       $link = $sk->makeKnownLinkObj( $title , htmlspecialchars( $desc ) );
-                       if( $restricted ) {
-                               $wgOut->addHTML( "<li class='mw-specialpages-page mw-specialpagerestricted'>{$link}</li>\n" );
-                       } else {
-                               $wgOut->addHTML( "<li>{$link}</li>\n" );
-                       }
-
-                       # Split up the larger groups
-                       $count++;
-                       if( $total > 3 && $count == $middle ) {
-                               $wgOut->addHTML( "</ul></td><td width='10%'></td><td width='30%' valign='top'><ul>" );
-                       }
-               }
-               $wgOut->addHTML( "</ul></td><td width='30%' valign='top'></td></tr></table>\n" );
-       }
-       $wgOut->addHTML(
-               Xml::openElement('div', array( 'class' => 'mw-specialpages-notes' )).
-               wfMsgWikiHtml('specialpages-note').
-               Xml::closeElement('div')
-       );
-}
diff --git a/includes/specials/Statistics.php b/includes/specials/Statistics.php
deleted file mode 100644 (file)
index 570a21c..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-/**
- * Special page lists various statistics, including the contents of
- * `site_stats`, plus page view details if enabled
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Show the special page
- *
- * @param mixed $par (not used)
- */
-function wfSpecialStatistics( $par = '' ) {
-       global $wgOut, $wgLang, $wgRequest;
-       $dbr = wfGetDB( DB_SLAVE );
-
-       $views = SiteStats::views();
-       $edits = SiteStats::edits();
-       $good = SiteStats::articles();
-       $images = SiteStats::images();
-       $total = SiteStats::pages();
-       $users = SiteStats::users();
-       $admins = SiteStats::admins();
-       $numJobs = SiteStats::jobs();
-
-       if( $wgRequest->getVal( 'action' ) == 'raw' ) {
-               $wgOut->disable();
-               header( 'Pragma: nocache' );
-               echo "total=$total;good=$good;views=$views;edits=$edits;users=$users;admins=$admins;images=$images;jobs=$numJobs\n";
-               return;
-       } else {
-               $text = "__NOTOC__\n";
-               $text .= '==' . wfMsgNoTrans( 'sitestats' ) . "==\n";
-               $text .= wfMsgExt( 'sitestatstext', array( 'parsemag' ),
-                       $wgLang->formatNum( $total ),
-                       $wgLang->formatNum( $good ),
-                       $wgLang->formatNum( $views ),
-                       $wgLang->formatNum( $edits ),
-                       $wgLang->formatNum( sprintf( '%.2f', $total ? $edits / $total : 0 ) ),
-                       $wgLang->formatNum( sprintf( '%.2f', $edits ? $views / $edits : 0 ) ),
-                       $wgLang->formatNum( $numJobs ),
-                       $wgLang->formatNum( $images )
-               )."\n";
-
-               $text .= "==" . wfMsgNoTrans( 'userstats' ) . "==\n";
-               $text .= wfMsgExt( 'userstatstext', array ( 'parsemag' ),
-                       $wgLang->formatNum( $users ),
-                       $wgLang->formatNum( $admins ),
-                       '[[' . wfMsgForContent( 'grouppage-sysop' ) . ']]', # TODO somehow remove, kept for backwards compatibility
-                       $wgLang->formatNum( @sprintf( '%.2f', $admins / $users * 100 ) ),
-                       User::makeGroupLinkWiki( 'sysop' )
-               )."\n";
-
-               global $wgDisableCounters, $wgMiserMode, $wgUser, $wgLang, $wgContLang;
-               if( !$wgDisableCounters && !$wgMiserMode ) {
-                       $res = $dbr->select(
-                               'page',
-                               array(
-                                       'page_namespace',
-                                       'page_title',
-                                       'page_counter',
-                               ),
-                               array(
-                                       'page_is_redirect' => 0,
-                                       'page_counter > 0',
-                               ),
-                               __METHOD__,
-                               array(
-                                       'ORDER BY' => 'page_counter DESC',
-                                       'LIMIT' => 10,
-                               )
-                       );
-                       if( $res->numRows() > 0 ) {
-                               $text .= "==" . wfMsgNoTrans( 'statistics-mostpopular' ) . "==\n";
-                               while( $row = $res->fetchObject() ) {
-                                       $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
-                                       if( $title instanceof Title )
-                                               $text .= '* [[:' . $title->getPrefixedText() . ']] (' . $wgLang->formatNum( $row->page_counter ) . ")\n";
-                               }
-                               $res->free();
-                       }
-               }
-
-               $footer = wfMsgNoTrans( 'statistics-footer' );
-               if( !wfEmptyMsg( 'statistics-footer', $footer ) && $footer != '' )
-                       $text .= "\n" . $footer;
-
-               $wgOut->addWikiText( $text );
-       }
-}
diff --git a/includes/specials/Uncategorizedcategories.php b/includes/specials/Uncategorizedcategories.php
deleted file mode 100644 (file)
index f23e89c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * implements Special:Uncategorizedcategories
- * @ingroup SpecialPage
- */
-class UncategorizedCategoriesPage extends UncategorizedPagesPage {
-       function UncategorizedCategoriesPage() {
-               $this->requestedNamespace = NS_CATEGORY;
-       }
-
-       function getName() {
-               return "Uncategorizedcategories";
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialUncategorizedcategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new UncategorizedCategoriesPage();
-
-       return $lpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Uncategorizedimages.php b/includes/specials/Uncategorizedimages.php
deleted file mode 100644 (file)
index 986ec96..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-/**
- * Special page lists images which haven't been categorised
- *
- * @file
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>
- */
-
-/**
- * @ingroup SpecialPage
- */
-class UncategorizedImagesPage extends ImageQueryPage {
-
-       function getName() {
-               return 'Uncategorizedimages';
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() {
-               return false;
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
-               $ns = NS_IMAGE;
-
-               return "SELECT 'Uncategorizedimages' AS type, page_namespace AS namespace,
-                               page_title AS title, page_title AS value
-                               FROM {$page} LEFT JOIN {$categorylinks} ON page_id = cl_from
-                               WHERE cl_from IS NULL AND page_namespace = {$ns} AND page_is_redirect = 0";
-       }
-
-}
-
-function wfSpecialUncategorizedimages() {
-       $uip = new UncategorizedImagesPage();
-       list( $limit, $offset ) = wfCheckLimits();
-       return $uip->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Uncategorizedpages.php b/includes/specials/Uncategorizedpages.php
deleted file mode 100644 (file)
index e7f0aac..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page looking for page without any category.
- * @ingroup SpecialPage
- */
-class UncategorizedPagesPage extends PageQueryPage {
-       var $requestedNamespace = NS_MAIN;
-
-       function getName() {
-               return "Uncategorizedpages";
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function isExpensive() {
-               return true;
-       }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               page_namespace AS namespace,
-                               page_title AS title,
-                               page_title AS value
-                       FROM $page
-                       LEFT JOIN $categorylinks ON page_id=cl_from
-                       WHERE cl_from IS NULL AND page_namespace={$this->requestedNamespace} AND page_is_redirect=0
-                       ";
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialUncategorizedpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new UncategorizedPagesPage();
-
-       return $lpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Uncategorizedtemplates.php b/includes/specials/Uncategorizedtemplates.php
deleted file mode 100644 (file)
index cb2a6d4..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Special page lists all uncategorised pages in the
- * template namespace
- *
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>
- */
-class UncategorizedTemplatesPage extends UncategorizedPagesPage {
-
-       var $requestedNamespace = NS_TEMPLATE;
-
-       public function getName() {
-               return 'Uncategorizedtemplates';
-       }
-
-}
-
-/**
- * Main execution point
- *
- * @param mixed $par Parameter passed to the page
- */
-function wfSpecialUncategorizedtemplates() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $utp = new UncategorizedTemplatesPage();
-       $utp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Undelete.php b/includes/specials/Undelete.php
deleted file mode 100644 (file)
index 33d9476..0000000
+++ /dev/null
@@ -1,1278 +0,0 @@
-<?php
-
-/**
- * Special page allowing users with the appropriate permissions to view
- * and restore deleted content
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Constructor
- */
-function wfSpecialUndelete( $par ) {
-       global $wgRequest;
-
-       $form = new UndeleteForm( $wgRequest, $par );
-       $form->execute();
-}
-
-/**
- * Used to show archived pages and eventually restore them.
- * @ingroup SpecialPage
- */
-class PageArchive {
-       protected $title;
-       var $fileStatus;
-
-       function __construct( $title ) {
-               if( is_null( $title ) ) {
-                       throw new MWException( 'Archiver() given a null title.');
-               }
-               $this->title = $title;
-       }
-
-       /**
-        * List all deleted pages recorded in the archive table. Returns result
-        * wrapper with (ar_namespace, ar_title, count) fields, ordered by page
-        * namespace/title.
-        *
-        * @return ResultWrapper
-        */
-       public static function listAllPages() {
-               $dbr = wfGetDB( DB_SLAVE );
-               return self::listPages( $dbr, '' );
-       }
-
-       /**
-        * List deleted pages recorded in the archive table matching the
-        * given title prefix.
-        * Returns result wrapper with (ar_namespace, ar_title, count) fields.
-        *
-        * @return ResultWrapper
-        */
-       public static function listPagesByPrefix( $prefix ) {
-               $dbr = wfGetDB( DB_SLAVE );
-
-               $title = Title::newFromText( $prefix );
-               if( $title ) {
-                       $ns = $title->getNamespace();
-                       $encPrefix = $dbr->escapeLike( $title->getDBkey() );
-               } else {
-                       // Prolly won't work too good
-                       // @todo handle bare namespace names cleanly?
-                       $ns = 0;
-                       $encPrefix = $dbr->escapeLike( $prefix );
-               }
-               $conds = array(
-                       'ar_namespace' => $ns,
-                       "ar_title LIKE '$encPrefix%'",
-               );
-               return self::listPages( $dbr, $conds );
-       }
-
-       protected static function listPages( $dbr, $condition ) {
-               return $dbr->resultObject(
-                       $dbr->select(
-                               array( 'archive' ),
-                               array(
-                                       'ar_namespace',
-                                       'ar_title',
-                                       'COUNT(*) AS count'
-                               ),
-                               $condition,
-                               __METHOD__,
-                               array(
-                                       'GROUP BY' => 'ar_namespace,ar_title',
-                                       'ORDER BY' => 'ar_namespace,ar_title',
-                                       'LIMIT' => 100,
-                               )
-                       )
-               );
-       }
-
-       /**
-        * List the revisions of the given page. Returns result wrapper with
-        * (ar_minor_edit, ar_timestamp, ar_user, ar_user_text, ar_comment) fields.
-        *
-        * @return ResultWrapper
-        */
-       function listRevisions() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'archive',
-                       array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len', 'ar_deleted' ),
-                       array( 'ar_namespace' => $this->title->getNamespace(),
-                              'ar_title' => $this->title->getDBkey() ),
-                       'PageArchive::listRevisions',
-                       array( 'ORDER BY' => 'ar_timestamp DESC' ) );
-               $ret = $dbr->resultObject( $res );
-               return $ret;
-       }
-
-       /**
-        * List the deleted file revisions for this page, if it's a file page.
-        * Returns a result wrapper with various filearchive fields, or null
-        * if not a file page.
-        *
-        * @return ResultWrapper
-        * @todo Does this belong in Image for fuller encapsulation?
-        */
-       function listFiles() {
-               if( $this->title->getNamespace() == NS_IMAGE ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $res = $dbr->select( 'filearchive',
-                               array(
-                                       'fa_id',
-                                       'fa_name',
-                                       'fa_archive_name',
-                                       'fa_storage_key',
-                                       'fa_storage_group',
-                                       'fa_size',
-                                       'fa_width',
-                                       'fa_height',
-                                       'fa_bits',
-                                       'fa_metadata',
-                                       'fa_media_type',
-                                       'fa_major_mime',
-                                       'fa_minor_mime',
-                                       'fa_description',
-                                       'fa_user',
-                                       'fa_user_text',
-                                       'fa_timestamp',
-                                       'fa_deleted' ),
-                               array( 'fa_name' => $this->title->getDBkey() ),
-                               __METHOD__,
-                               array( 'ORDER BY' => 'fa_timestamp DESC' ) );
-                       $ret = $dbr->resultObject( $res );
-                       return $ret;
-               }
-               return null;
-       }
-
-       /**
-        * Fetch (and decompress if necessary) the stored text for the deleted
-        * revision of the page with the given timestamp.
-        *
-        * @return string
-        * @deprecated Use getRevision() for more flexible information
-        */
-       function getRevisionText( $timestamp ) {
-               $rev = $this->getRevision( $timestamp );
-               return $rev ? $rev->getText() : null;
-       }
-
-       /**
-        * Return a Revision object containing data for the deleted revision.
-        * Note that the result *may* or *may not* have a null page ID.
-        * @param string $timestamp
-        * @return Revision
-        */
-       function getRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $row = $dbr->selectRow( 'archive',
-                       array(
-                               'ar_rev_id',
-                               'ar_text',
-                               'ar_comment',
-                               'ar_user',
-                               'ar_user_text',
-                               'ar_timestamp',
-                               'ar_minor_edit',
-                               'ar_flags',
-                               'ar_text_id',
-                               'ar_deleted',
-                               'ar_len' ),
-                       array( 'ar_namespace' => $this->title->getNamespace(),
-                              'ar_title' => $this->title->getDBkey(),
-                              'ar_timestamp' => $dbr->timestamp( $timestamp ) ),
-                       __METHOD__ );
-               if( $row ) {
-                       return new Revision( array(
-                               'page'       => $this->title->getArticleId(),
-                               'id'         => $row->ar_rev_id,
-                               'text'       => ($row->ar_text_id
-                                       ? null
-                                       : Revision::getRevisionText( $row, 'ar_' ) ),
-                               'comment'    => $row->ar_comment,
-                               'user'       => $row->ar_user,
-                               'user_text'  => $row->ar_user_text,
-                               'timestamp'  => $row->ar_timestamp,
-                               'minor_edit' => $row->ar_minor_edit,
-                               'text_id'    => $row->ar_text_id,
-                               'deleted'    => $row->ar_deleted,
-                               'len'        => $row->ar_len) );
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * Return the most-previous revision, either live or deleted, against
-        * the deleted revision given by timestamp.
-        *
-        * May produce unexpected results in case of history merges or other
-        * unusual time issues.
-        *
-        * @param string $timestamp
-        * @return Revision or null
-        */
-       function getPreviousRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
-
-               // Check the previous deleted revision...
-               $row = $dbr->selectRow( 'archive',
-                       'ar_timestamp',
-                       array( 'ar_namespace' => $this->title->getNamespace(),
-                              'ar_title' => $this->title->getDBkey(),
-                              'ar_timestamp < ' .
-                                               $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ),
-                       __METHOD__,
-                       array(
-                               'ORDER BY' => 'ar_timestamp DESC',
-                               'LIMIT' => 1 ) );
-               $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
-
-               $row = $dbr->selectRow( array( 'page', 'revision' ),
-                       array( 'rev_id', 'rev_timestamp' ),
-                       array(
-                               'page_namespace' => $this->title->getNamespace(),
-                               'page_title' => $this->title->getDBkey(),
-                               'page_id = rev_page',
-                               'rev_timestamp < ' .
-                                               $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ),
-                       __METHOD__,
-                       array(
-                               'ORDER BY' => 'rev_timestamp DESC',
-                               'LIMIT' => 1 ) );
-               $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
-               $prevLiveId = $row ? intval( $row->rev_id ) : null;
-
-               if( $prevLive && $prevLive > $prevDeleted ) {
-                       // Most prior revision was live
-                       return Revision::newFromId( $prevLiveId );
-               } elseif( $prevDeleted ) {
-                       // Most prior revision was deleted
-                       return $this->getRevision( $prevDeleted );
-               } else {
-                       // No prior revision on this page.
-                       return null;
-               }
-       }
-
-       /**
-        * Get the text from an archive row containing ar_text, ar_flags and ar_text_id
-        */
-       function getTextFromRow( $row ) {
-               if( is_null( $row->ar_text_id ) ) {
-                       // An old row from MediaWiki 1.4 or previous.
-                       // Text is embedded in this row in classic compression format.
-                       return Revision::getRevisionText( $row, "ar_" );
-               } else {
-                       // New-style: keyed to the text storage backend.
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $text = $dbr->selectRow( 'text',
-                               array( 'old_text', 'old_flags' ),
-                               array( 'old_id' => $row->ar_text_id ),
-                               __METHOD__ );
-                       return Revision::getRevisionText( $text );
-               }
-       }
-
-
-       /**
-        * Fetch (and decompress if necessary) the stored text of the most
-        * recently edited deleted revision of the page.
-        *
-        * If there are no archived revisions for the page, returns NULL.
-        *
-        * @return string
-        */
-       function getLastRevisionText() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $row = $dbr->selectRow( 'archive',
-                       array( 'ar_text', 'ar_flags', 'ar_text_id' ),
-                       array( 'ar_namespace' => $this->title->getNamespace(),
-                              'ar_title' => $this->title->getDBkey() ),
-                       'PageArchive::getLastRevisionText',
-                       array( 'ORDER BY' => 'ar_timestamp DESC' ) );
-               if( $row ) {
-                       return $this->getTextFromRow( $row );
-               } else {
-                       return NULL;
-               }
-       }
-
-       /**
-        * Quick check if any archived revisions are present for the page.
-        * @return bool
-        */
-       function isDeleted() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
-                       array( 'ar_namespace' => $this->title->getNamespace(),
-                              'ar_title' => $this->title->getDBkey() ) );
-               return ($n > 0);
-       }
-
-       /**
-        * Restore the given (or all) text and file revisions for the page.
-        * Once restored, the items will be removed from the archive tables.
-        * The deletion log will be updated with an undeletion notice.
-        *
-        * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete.
-        * @param string $comment
-        * @param array $fileVersions
-        * @param bool $unsuppress
-        *
-        * @return array(number of file revisions restored, number of image revisions restored, log message)
-        * on success, false on failure
-        */
-       function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false ) {
-               // If both the set of text revisions and file revisions are empty,
-               // restore everything. Otherwise, just restore the requested items.
-               $restoreAll = empty( $timestamps ) && empty( $fileVersions );
-
-               $restoreText = $restoreAll || !empty( $timestamps );
-               $restoreFiles = $restoreAll || !empty( $fileVersions );
-
-               if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
-                       $img = wfLocalFile( $this->title );
-                       $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
-                       $filesRestored = $this->fileStatus->successCount;
-               } else {
-                       $filesRestored = 0;
-               }
-
-               if( $restoreText ) {
-                       $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress );
-                       if($textRestored === false) // It must be one of UNDELETE_*
-                               return false;
-               } else {
-                       $textRestored = 0;
-               }
-
-               // Touch the log!
-               global $wgContLang;
-               $log = new LogPage( 'delete' );
-
-               if( $textRestored && $filesRestored ) {
-                       $reason = wfMsgExt( 'undeletedrevisions-files', array( 'content', 'parsemag' ),
-                               $wgContLang->formatNum( $textRestored ),
-                               $wgContLang->formatNum( $filesRestored ) );
-               } elseif( $textRestored ) {
-                       $reason = wfMsgExt( 'undeletedrevisions', array( 'content', 'parsemag' ),
-                               $wgContLang->formatNum( $textRestored ) );
-               } elseif( $filesRestored ) {
-                       $reason = wfMsgExt( 'undeletedfiles', array( 'content', 'parsemag' ),
-                               $wgContLang->formatNum( $filesRestored ) );
-               } else {
-                       wfDebug( "Undelete: nothing undeleted...\n" );
-                       return false;
-               }
-
-               if( trim( $comment ) != '' )
-                       $reason .= ": {$comment}";
-               $log->addEntry( 'restore', $this->title, $reason );
-
-               return array($textRestored, $filesRestored, $reason);
-       }
-
-       /**
-        * This is the meaty bit -- restores archived revisions of the given page
-        * to the cur/old tables. If the page currently exists, all revisions will
-        * be stuffed into old, otherwise the most recent will go into cur.
-        *
-        * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete.
-        * @param string $comment
-        * @param array $fileVersions
-        * @param bool $unsuppress, remove all ar_deleted/fa_deleted restrictions of seletected revs
-        *
-        * @return mixed number of revisions restored or false on failure
-        */
-       private function undeleteRevisions( $timestamps, $unsuppress = false ) {
-               if ( wfReadOnly() )
-                       return false;
-               $restoreAll = empty( $timestamps );
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               # Does this page already exist? We'll have to update it...
-               $article = new Article( $this->title );
-               $options = 'FOR UPDATE';
-               $page = $dbw->selectRow( 'page',
-                       array( 'page_id', 'page_latest' ),
-                       array( 'page_namespace' => $this->title->getNamespace(),
-                              'page_title'     => $this->title->getDBkey() ),
-                       __METHOD__,
-                       $options );
-               if( $page ) {
-                       $makepage = false;
-                       # Page already exists. Import the history, and if necessary
-                       # we'll update the latest revision field in the record.
-                       $newid             = 0;
-                       $pageId            = $page->page_id;
-                       $previousRevId     = $page->page_latest;
-                       # Get the time span of this page
-                       $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
-                               array( 'rev_id' => $previousRevId ),
-                               __METHOD__ );
-                       if( $previousTimestamp === false ) {
-                               wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" );
-                               return 0;
-                       }
-               } else {
-                       # Have to create a new article...
-                       $makepage = true;
-                       $previousRevId = 0;
-                       $previousTimestamp = 0;
-               }
-
-               if( $restoreAll ) {
-                       $oldones = '1 = 1'; # All revisions...
-               } else {
-                       $oldts = implode( ',',
-                               array_map( array( &$dbw, 'addQuotes' ),
-                                       array_map( array( &$dbw, 'timestamp' ),
-                                               $timestamps ) ) );
-
-                       $oldones = "ar_timestamp IN ( {$oldts} )";
-               }
-
-               /**
-                * Select each archived revision...
-                */
-               $result = $dbw->select( 'archive',
-                       /* fields */ array(
-                               'ar_rev_id',
-                               'ar_text',
-                               'ar_comment',
-                               'ar_user',
-                               'ar_user_text',
-                               'ar_timestamp',
-                               'ar_minor_edit',
-                               'ar_flags',
-                               'ar_text_id',
-                               'ar_deleted',
-                               'ar_page_id',
-                               'ar_len' ),
-                       /* WHERE */ array(
-                               'ar_namespace' => $this->title->getNamespace(),
-                               'ar_title'     => $this->title->getDBkey(),
-                               $oldones ),
-                       __METHOD__,
-                       /* options */ array(
-                               'ORDER BY' => 'ar_timestamp' )
-                       );
-               $ret = $dbw->resultObject( $result );
-
-               $rev_count = $dbw->numRows( $result );
-               if( $rev_count ) {
-                       # We need to seek around as just using DESC in the ORDER BY
-                       # would leave the revisions inserted in the wrong order
-                       $first = $ret->fetchObject();
-                       $ret->seek( $rev_count - 1 );
-                       $last = $ret->fetchObject();
-                       // We don't handle well changing the top revision's settings
-                       if( !$unsuppress && $last->ar_deleted && $last->ar_timestamp > $previousTimestamp ) {
-                               wfDebug( __METHOD__.": restoration would result in a deleted top revision\n" );
-                               return false;
-                       }
-                       $ret->seek( 0 );
-               }
-
-               if( $makepage ) {
-                       $newid  = $article->insertOn( $dbw );
-                       $pageId = $newid;
-               }
-
-               $revision = null;
-               $restored = 0;
-
-               while( $row = $ret->fetchObject() ) {
-                       if( $row->ar_text_id ) {
-                               // Revision was deleted in 1.5+; text is in
-                               // the regular text table, use the reference.
-                               // Specify null here so the so the text is
-                               // dereferenced for page length info if needed.
-                               $revText = null;
-                       } else {
-                               // Revision was deleted in 1.4 or earlier.
-                               // Text is squashed into the archive row, and
-                               // a new text table entry will be created for it.
-                               $revText = Revision::getRevisionText( $row, 'ar_' );
-                       }
-                       $revision = new Revision( array(
-                               'page'       => $pageId,
-                               'id'         => $row->ar_rev_id,
-                               'text'       => $revText,
-                               'comment'    => $row->ar_comment,
-                               'user'       => $row->ar_user,
-                               'user_text'  => $row->ar_user_text,
-                               'timestamp'  => $row->ar_timestamp,
-                               'minor_edit' => $row->ar_minor_edit,
-                               'text_id'    => $row->ar_text_id,
-                               'deleted'        => $unsuppress ? 0 : $row->ar_deleted,
-                               'len'        => $row->ar_len
-                               ) );
-                       $revision->insertOn( $dbw );
-                       $restored++;
-
-                       wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) );
-               }
-               // Was anything restored at all?
-               if($restored == 0)
-                       return 0;
-
-               if( $revision ) {
-                       // Attach the latest revision to the page...
-                       $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId );
-
-                       if( $newid || $wasnew ) {
-                               // Update site stats, link tables, etc
-                               $article->createUpdates( $revision );
-                       }
-
-                       if( $newid ) {
-                               wfRunHooks( 'ArticleUndelete', array( &$this->title, true ) );
-                               Article::onArticleCreate( $this->title );
-                       } else {
-                               wfRunHooks( 'ArticleUndelete', array( &$this->title, false ) );
-                               Article::onArticleEdit( $this->title );
-                       }
-
-                       if( $this->title->getNamespace() == NS_IMAGE ) {
-                               $update = new HTMLCacheUpdate( $this->title, 'imagelinks' );
-                               $update->doUpdate();
-                       }
-               } else {
-                       // Revision couldn't be created. This is very weird
-                       return self::UNDELETE_UNKNOWNERR;
-               }
-
-               # Now that it's safely stored, take it out of the archive
-               $dbw->delete( 'archive',
-                       /* WHERE */ array(
-                               'ar_namespace' => $this->title->getNamespace(),
-                               'ar_title' => $this->title->getDBkey(),
-                               $oldones ),
-                       __METHOD__ );
-
-               return $restored;
-       }
-
-       function getFileStatus() { return $this->fileStatus; }
-}
-
-/**
- * The HTML form for Special:Undelete, which allows users with the appropriate
- * permissions to view and restore deleted content.
- * @ingroup SpecialPage
- */
-class UndeleteForm {
-       var $mAction, $mTarget, $mTimestamp, $mRestore, $mTargetObj;
-       var $mTargetTimestamp, $mAllowed, $mComment;
-
-       function UndeleteForm( $request, $par = "" ) {
-               global $wgUser;
-               $this->mAction = $request->getVal( 'action' );
-               $this->mTarget = $request->getVal( 'target' );
-               $this->mSearchPrefix = $request->getText( 'prefix' );
-               $time = $request->getVal( 'timestamp' );
-               $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : '';
-               $this->mFile = $request->getVal( 'file' );
-
-               $posted = $request->wasPosted() &&
-                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
-               $this->mRestore = $request->getCheck( 'restore' ) && $posted;
-               $this->mPreview = $request->getCheck( 'preview' ) && $posted;
-               $this->mDiff = $request->getCheck( 'diff' );
-               $this->mComment = $request->getText( 'wpComment' );
-               $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
-
-               if( $par != "" ) {
-                       $this->mTarget = $par;
-               }
-               if ( $wgUser->isAllowed( 'undelete' ) && !$wgUser->isBlocked() ) {
-                       $this->mAllowed = true;
-               } else {
-                       $this->mAllowed = false;
-                       $this->mTimestamp = '';
-                       $this->mRestore = false;
-               }
-               if ( $this->mTarget !== "" ) {
-                       $this->mTargetObj = Title::newFromURL( $this->mTarget );
-               } else {
-                       $this->mTargetObj = NULL;
-               }
-               if( $this->mRestore ) {
-                       $timestamps = array();
-                       $this->mFileVersions = array();
-                       foreach( $_REQUEST as $key => $val ) {
-                               $matches = array();
-                               if( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) {
-                                       array_push( $timestamps, $matches[1] );
-                               }
-
-                               if( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
-                                       $this->mFileVersions[] = intval( $matches[1] );
-                               }
-                       }
-                       rsort( $timestamps );
-                       $this->mTargetTimestamp = $timestamps;
-               }
-       }
-
-       function execute() {
-               global $wgOut, $wgUser;
-               if ( $this->mAllowed ) {
-                       $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
-               } else {
-                       $wgOut->setPagetitle( wfMsg( "viewdeletedpage" ) );
-               }
-
-               if( is_null( $this->mTargetObj ) ) {
-               # Not all users can just browse every deleted page from the list
-                       if( $wgUser->isAllowed( 'browsearchive' ) ) {
-                               $this->showSearchForm();
-
-                               # List undeletable articles
-                               if( $this->mSearchPrefix ) {
-                                       $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
-                                       $this->showList( $result );
-                               }
-                       } else {
-                               $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
-                       }
-                       return;
-               }
-               if( $this->mTimestamp !== '' ) {
-                       return $this->showRevision( $this->mTimestamp );
-               }
-               if( $this->mFile !== null ) {
-                       $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile );
-                       // Check if user is allowed to see this file
-                       if( !$file->userCan( File::DELETED_FILE ) ) {
-                               $wgOut->permissionRequired( 'suppressrevision' );
-                               return false;
-                       } else {
-                               return $this->showFile( $this->mFile );
-                       }
-               }
-               if( $this->mRestore && $this->mAction == "submit" ) {
-                       return $this->undelete();
-               }
-               return $this->showHistory();
-       }
-
-       function showSearchForm() {
-               global $wgOut, $wgScript;
-               $wgOut->addWikiMsg( 'undelete-header' );
-
-               $wgOut->addHtml(
-                       Xml::openElement( 'form', array(
-                               'method' => 'get',
-                               'action' => $wgScript ) ) .
-                       '<fieldset>' .
-                       Xml::element( 'legend', array(),
-                               wfMsg( 'undelete-search-box' ) ) .
-                       Xml::hidden( 'title',
-                               SpecialPage::getTitleFor( 'Undelete' )->getPrefixedDbKey() ) .
-                       Xml::inputLabel( wfMsg( 'undelete-search-prefix' ),
-                               'prefix', 'prefix', 20,
-                               $this->mSearchPrefix ) .
-                       Xml::submitButton( wfMsg( 'undelete-search-submit' ) ) .
-                       '</fieldset>' .
-                       '</form>' );
-       }
-
-       // Generic list of deleted pages
-       private function showList( $result ) {
-               global $wgLang, $wgContLang, $wgUser, $wgOut;
-
-               if( $result->numRows() == 0 ) {
-                       $wgOut->addWikiMsg( 'undelete-no-results' );
-                       return;
-               }
-
-               $wgOut->addWikiMsg( "undeletepagetext" );
-
-               $sk = $wgUser->getSkin();
-               $undelete = SpecialPage::getTitleFor( 'Undelete' );
-               $wgOut->addHTML( "<ul>\n" );
-               while( $row = $result->fetchObject() ) {
-                       $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
-                       $link = $sk->makeKnownLinkObj( $undelete, htmlspecialchars( $title->getPrefixedText() ),
-                               'target=' . $title->getPrefixedUrl() );
-                       #$revs = wfMsgHtml( 'undeleterevisions', $wgLang->formatNum( $row->count ) );
-                       $revs = wfMsgExt( 'undeleterevisions',
-                               array( 'parseinline' ),
-                               $wgLang->formatNum( $row->count ) );
-                       $wgOut->addHtml( "<li>{$link} ({$revs})</li>\n" );
-               }
-               $result->free();
-               $wgOut->addHTML( "</ul>\n" );
-
-               return true;
-       }
-
-       private function showRevision( $timestamp ) {
-               global $wgLang, $wgUser, $wgOut;
-               $self = SpecialPage::getTitleFor( 'Undelete' );
-               $skin = $wgUser->getSkin();
-
-               if(!preg_match("/[0-9]{14}/",$timestamp)) return 0;
-
-               $archive = new PageArchive( $this->mTargetObj );
-               $rev = $archive->getRevision( $timestamp );
-
-               if( !$rev ) {
-                       $wgOut->addWikiMsg( 'undeleterevision-missing' );
-                       return;
-               }
-
-               if( $rev->isDeleted(Revision::DELETED_TEXT) ) {
-                       if( !$rev->userCan(Revision::DELETED_TEXT) ) {
-                               $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
-                               return;
-                       } else {
-                               $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
-                               $wgOut->addHTML( '<br/>' );
-                               // and we are allowed to see...
-                       }
-               }
-
-               $wgOut->setPageTitle( wfMsg( 'undeletepage' ) );
-
-               $link = $skin->makeKnownLinkObj(
-                       SpecialPage::getTitleFor( 'Undelete', $this->mTargetObj->getPrefixedDBkey() ),
-                       htmlspecialchars( $this->mTargetObj->getPrefixedText() )
-               );
-               $time = htmlspecialchars( $wgLang->timeAndDate( $timestamp, true ) );
-               $user = $skin->revUserTools( $rev );
-
-               if( $this->mDiff ) {
-                       $previousRev = $archive->getPreviousRevision( $timestamp );
-                       if( $previousRev ) {
-                               $this->showDiff( $previousRev, $rev );
-                               if( $wgUser->getOption( 'diffonly' ) ) {
-                                       return;
-                               } else {
-                                       $wgOut->addHtml( '<hr />' );
-                               }
-                       } else {
-                               $wgOut->addHtml( wfMsgHtml( 'undelete-nodiff' ) );
-                       }
-               }
-
-               $wgOut->addHtml( '<p>' . wfMsgHtml( 'undelete-revision', $link, $time, $user ) . '</p>' );
-
-               wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) );
-
-               if( $this->mPreview ) {
-                       $wgOut->addHtml( "<hr />\n" );
-
-                       //Hide [edit]s
-                       $popts = $wgOut->parserOptions();
-                       $popts->setEditSection( false );
-                       $wgOut->parserOptions( $popts );
-                       $wgOut->addWikiTextTitleTidy( $rev->revText(), $this->mTargetObj, true );
-               }
-
-               $wgOut->addHtml(
-                       wfElement( 'textarea', array(
-                                       'readonly' => 'readonly',
-                                       'cols' => intval( $wgUser->getOption( 'cols' ) ),
-                                       'rows' => intval( $wgUser->getOption( 'rows' ) ) ),
-                               $rev->revText() . "\n" ) .
-                       wfOpenElement( 'div' ) .
-                       wfOpenElement( 'form', array(
-                               'method' => 'post',
-                               'action' => $self->getLocalURL( "action=submit" ) ) ) .
-                       wfElement( 'input', array(
-                               'type' => 'hidden',
-                               'name' => 'target',
-                               'value' => $this->mTargetObj->getPrefixedDbKey() ) ) .
-                       wfElement( 'input', array(
-                               'type' => 'hidden',
-                               'name' => 'timestamp',
-                               'value' => $timestamp ) ) .
-                       wfElement( 'input', array(
-                               'type' => 'hidden',
-                               'name' => 'wpEditToken',
-                               'value' => $wgUser->editToken() ) ) .
-                       wfElement( 'input', array(
-                               'type' => 'submit',
-                               'name' => 'preview',
-                               'value' => wfMsg( 'showpreview' ) ) ) .
-                       wfElement( 'input', array(
-                               'name' => 'diff',
-                               'type' => 'submit',
-                               'value' => wfMsg( 'showdiff' ) ) ) .
-                       wfCloseElement( 'form' ) .
-                       wfCloseElement( 'div' ) );
-       }
-
-       /**
-        * Build a diff display between this and the previous either deleted
-        * or non-deleted edit.
-        * @param Revision $previousRev
-        * @param Revision $currentRev
-        * @return string HTML
-        */
-       function showDiff( $previousRev, $currentRev ) {
-               global $wgOut, $wgUser;
-
-               $diffEngine = new DifferenceEngine();
-               $diffEngine->showDiffStyle();
-               $wgOut->addHtml(
-                       "<div>" .
-                       "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" .
-                       "<col class='diff-marker' />" .
-                       "<col class='diff-content' />" .
-                       "<col class='diff-marker' />" .
-                       "<col class='diff-content' />" .
-                       "<tr>" .
-                               "<td colspan='2' width='50%' align='center' class='diff-otitle'>" .
-                               $this->diffHeader( $previousRev ) .
-                               "</td>" .
-                               "<td colspan='2' width='50%' align='center' class='diff-ntitle'>" .
-                               $this->diffHeader( $currentRev ) .
-                               "</td>" .
-                       "</tr>" .
-                       $diffEngine->generateDiffBody(
-                               $previousRev->getText(), $currentRev->getText() ) .
-                       "</table>" .
-                       "</div>\n" );
-
-       }
-
-       private function diffHeader( $rev ) {
-               global $wgUser, $wgLang, $wgLang;
-               $sk = $wgUser->getSkin();
-               $isDeleted = !( $rev->getId() && $rev->getTitle() );
-               if( $isDeleted ) {
-                       /// @fixme $rev->getTitle() is null for deleted revs...?
-                       $targetPage = SpecialPage::getTitleFor( 'Undelete' );
-                       $targetQuery = 'target=' .
-                               $this->mTargetObj->getPrefixedUrl() .
-                               '&timestamp=' .
-                               wfTimestamp( TS_MW, $rev->getTimestamp() );
-               } else {
-                       /// @fixme getId() may return non-zero for deleted revs...
-                       $targetPage = $rev->getTitle();
-                       $targetQuery = 'oldid=' . $rev->getId();
-               }
-               return
-                       '<div id="mw-diff-otitle1"><strong>' .
-                               $sk->makeLinkObj( $targetPage,
-                                       wfMsgHtml( 'revisionasof',
-                                               $wgLang->timeanddate( $rev->getTimestamp(), true ) ),
-                                       $targetQuery ) .
-                               ( $isDeleted ? ' ' . wfMsgHtml( 'deletedrev' ) : '' ) .
-                       '</strong></div>' .
-                       '<div id="mw-diff-otitle2">' .
-                               $sk->revUserTools( $rev ) . '<br/>' .
-                       '</div>' .
-                       '<div id="mw-diff-otitle3">' .
-                               $sk->revComment( $rev ) . '<br/>' .
-                       '</div>';
-       }
-
-       /**
-        * Show a deleted file version requested by the visitor.
-        */
-       private function showFile( $key ) {
-               global $wgOut, $wgRequest;
-               $wgOut->disable();
-
-               # We mustn't allow the output to be Squid cached, otherwise
-               # if an admin previews a deleted image, and it's cached, then
-               # a user without appropriate permissions can toddle off and
-               # nab the image, and Squid will serve it
-               $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
-               $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
-               $wgRequest->response()->header( 'Pragma: no-cache' );
-
-               $store = FileStore::get( 'deleted' );
-               $store->stream( $key );
-       }
-
-       private function showHistory() {
-               global $wgLang, $wgUser, $wgOut;
-
-               $sk = $wgUser->getSkin();
-               if( $this->mAllowed ) {
-                       $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
-               } else {
-                       $wgOut->setPagetitle( wfMsg( 'viewdeletedpage' ) );
-               }
-
-               $wgOut->addWikiText( wfMsgHtml( 'undeletepagetitle', $this->mTargetObj->getPrefixedText()) );
-
-               $archive = new PageArchive( $this->mTargetObj );
-               /*
-               $text = $archive->getLastRevisionText();
-               if( is_null( $text ) ) {
-                       $wgOut->addWikiMsg( "nohistory" );
-                       return;
-               }
-               */
-               if ( $this->mAllowed ) {
-                       $wgOut->addWikiMsg( "undeletehistory" );
-                       $wgOut->addWikiMsg( "undeleterevdel" );
-               } else {
-                       $wgOut->addWikiMsg( "undeletehistorynoadmin" );
-               }
-
-               # List all stored revisions
-               $revisions = $archive->listRevisions();
-               $files = $archive->listFiles();
-
-               $haveRevisions = $revisions && $revisions->numRows() > 0;
-               $haveFiles = $files && $files->numRows() > 0;
-
-               # Batch existence check on user and talk pages
-               if( $haveRevisions ) {
-                       $batch = new LinkBatch();
-                       while( $row = $revisions->fetchObject() ) {
-                               $batch->addObj( Title::makeTitleSafe( NS_USER, $row->ar_user_text ) );
-                               $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ar_user_text ) );
-                       }
-                       $batch->execute();
-                       $revisions->seek( 0 );
-               }
-               if( $haveFiles ) {
-                       $batch = new LinkBatch();
-                       while( $row = $files->fetchObject() ) {
-                               $batch->addObj( Title::makeTitleSafe( NS_USER, $row->fa_user_text ) );
-                               $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->fa_user_text ) );
-                       }
-                       $batch->execute();
-                       $files->seek( 0 );
-               }
-
-               if ( $this->mAllowed ) {
-                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
-                       $action = $titleObj->getLocalURL( "action=submit" );
-                       # Start the form here
-                       $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) );
-                       $wgOut->addHtml( $top );
-               }
-
-               # Show relevant lines from the deletion log:
-               $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) . "\n" );
-               LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTargetObj->getPrefixedText() );
-
-               if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
-                       # Format the user-visible controls (comment field, submission button)
-                       # in a nice little table
-                       if( $wgUser->isAllowed( 'suppressrevision' ) ) {
-                               $unsuppressBox =
-                                       "<tr>
-                                               <td>&nbsp;</td>
-                                               <td class='mw-input'>" .
-                                                       Xml::checkLabel( wfMsg('revdelete-unsuppress'), 'wpUnsuppress',
-                                                               'mw-undelete-unsuppress', $this->mUnsuppress ).
-                                               "</td>
-                                       </tr>";
-                       } else {
-                               $unsuppressBox = "";
-                       }
-                       $table =
-                               Xml::openElement( 'fieldset' ) .
-                               Xml::element( 'legend', null, wfMsg( 'undelete') ).
-                               Xml::openElement( 'table', array( 'id' => 'mw-undelete-table' ) ) .
-                                       "<tr>
-                                               <td colspan='2'>" .
-                                                       wfMsgWikiHtml( 'undeleteextrahelp' ) .
-                                               "</td>
-                                       </tr>
-                                       <tr>
-                                               <td class='mw-label'>" .
-                                                       Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) .
-                                               "</td>
-                                               <td class='mw-input'>" .
-                                                       Xml::input( 'wpComment', 50, $this->mComment, array( 'id' =>  'wpComment' ) ) .
-                                               "</td>
-                                       </tr>
-                                       <tr>
-                                               <td>&nbsp;</td>
-                                               <td class='mw-submit'>" .
-                                                       Xml::submitButton( wfMsg( 'undeletebtn' ), array( 'name' => 'restore', 'id' => 'mw-undelete-submit' ) ) .
-                                                       Xml::element( 'input', array( 'type' => 'reset', 'value' => wfMsg( 'undeletereset' ), 'id' => 'mw-undelete-reset' ) ) .
-                                               "</td>
-                                       </tr>" .
-                                       $unsuppressBox .
-                               Xml::closeElement( 'table' ) .
-                               Xml::closeElement( 'fieldset' );
-
-                       $wgOut->addHtml( $table );
-               }
-
-               $wgOut->addHTML( Xml::element( 'h2', null, wfMsg( 'history' ) ) . "\n" );
-
-               if( $haveRevisions ) {
-                       # The page's stored (deleted) history:
-                       $wgOut->addHTML("<ul>");
-                       $target = urlencode( $this->mTarget );
-                       $remaining = $revisions->numRows();
-                       $earliestLiveTime = $this->getEarliestTime( $this->mTargetObj );
-
-                       while( $row = $revisions->fetchObject() ) {
-                               $remaining--;
-                               $wgOut->addHTML( $this->formatRevisionRow( $row, $earliestLiveTime, $remaining, $sk ) );
-                       }
-                       $revisions->free();
-                       $wgOut->addHTML("</ul>");
-               } else {
-                       $wgOut->addWikiMsg( "nohistory" );
-               }
-
-               if( $haveFiles ) {
-                       $wgOut->addHtml( Xml::element( 'h2', null, wfMsg( 'filehist' ) ) . "\n" );
-                       $wgOut->addHtml( "<ul>" );
-                       while( $row = $files->fetchObject() ) {
-                               $wgOut->addHTML( $this->formatFileRow( $row, $sk ) );
-                       }
-                       $files->free();
-                       $wgOut->addHTML( "</ul>" );
-               }
-
-               if ( $this->mAllowed ) {
-                       # Slip in the hidden controls here
-                       $misc  = Xml::hidden( 'target', $this->mTarget );
-                       $misc .= Xml::hidden( 'wpEditToken', $wgUser->editToken() );
-                       $misc .= Xml::closeElement( 'form' );
-                       $wgOut->addHtml( $misc );
-               }
-
-               return true;
-       }
-
-       private function formatRevisionRow( $row, $earliestLiveTime, $remaining, $sk ) {
-               global $wgUser, $wgLang;
-
-               $rev = new Revision( array(
-                               'page'       => $this->mTargetObj->getArticleId(),
-                               'comment'    => $row->ar_comment,
-                               'user'       => $row->ar_user,
-                               'user_text'  => $row->ar_user_text,
-                               'timestamp'  => $row->ar_timestamp,
-                               'minor_edit' => $row->ar_minor_edit,
-                               'deleted'    => $row->ar_deleted,
-                               'len'        => $row->ar_len ) );
-
-               $stxt = '';
-               $ts = wfTimestamp( TS_MW, $row->ar_timestamp );
-               if( $this->mAllowed ) {
-                       $checkBox = Xml::check( "ts$ts" );
-                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
-                       $pageLink = $this->getPageLink( $rev, $titleObj, $ts, $sk );
-                       # Last link
-                       if( !$rev->userCan( Revision::DELETED_TEXT ) ) {
-                               $last = wfMsgHtml('diff');
-                       } else if( $remaining > 0 || ($earliestLiveTime && $ts > $earliestLiveTime) ) {
-                               $last = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml('diff'),
-                                       "target=" . $this->mTargetObj->getPrefixedUrl() . "&timestamp=$ts&diff=prev" );
-                       } else {
-                               $last = wfMsgHtml('diff');
-                       }
-               } else {
-                       $checkBox = '';
-                       $pageLink = $wgLang->timeanddate( $ts, true );
-                       $last = wfMsgHtml('diff');
-               }
-               $userLink = $sk->revUserTools( $rev );
-
-               if(!is_null($size = $row->ar_len)) {
-                       if($size == 0)
-                               $stxt = wfMsgHtml('historyempty');
-                       else
-                               $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
-               }
-               $comment = $sk->revComment( $rev );
-               $revdlink = '';
-               if( $wgUser->isAllowed( 'deleterevision' ) ) {
-                       $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
-                       if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
-                       // If revision was hidden from sysops
-                               $del = wfMsgHtml('rev-delundel');
-                       } else {
-                               $ts = wfTimestamp( TS_MW, $row->ar_timestamp );
-                               $del = $sk->makeKnownLinkObj( $revdel,
-                                       wfMsgHtml('rev-delundel'),
-                                       'target=' . $this->mTargetObj->getPrefixedUrl() . "&artimestamp=$ts" );
-                               // Bolden oversighted content
-                               if( $rev->isDeleted( Revision::DELETED_RESTRICTED ) )
-                                       $del = "<strong>$del</strong>";
-                       }
-                       $revdlink = "<tt>(<small>$del</small>)</tt>";
-               }
-
-               return "<li>$checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment</li>";
-       }
-
-       private function formatFileRow( $row, $sk ) {
-               global $wgUser, $wgLang;
-
-               $file = ArchivedFile::newFromRow( $row );
-
-               $ts = wfTimestamp( TS_MW, $row->fa_timestamp );
-               if( $this->mAllowed && $row->fa_storage_key ) {
-                       $checkBox = Xml::check( "fileid" . $row->fa_id );
-                       $key = urlencode( $row->fa_storage_key );
-                       $target = urlencode( $this->mTarget );
-                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
-                       $pageLink = $this->getFileLink( $file, $titleObj, $ts, $key, $sk );
-               } else {
-                       $checkBox = '';
-                       $pageLink = $wgLang->timeanddate( $ts, true );
-               }
-               $userLink = $this->getFileUser( $file, $sk );
-               $data =
-                       wfMsgHtml( 'widthheight',
-                               $wgLang->formatNum( $row->fa_width ),
-                               $wgLang->formatNum( $row->fa_height ) ) .
-                       ' (' .
-                       wfMsgHtml( 'nbytes', $wgLang->formatNum( $row->fa_size ) ) .
-                       ')';
-               $comment = $this->getFileComment( $file, $sk );
-               $revdlink = '';
-               if( $wgUser->isAllowed( 'deleterevision' ) ) {
-                       $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
-                       if( !$file->userCan(File::DELETED_RESTRICTED ) ) {
-                       // If revision was hidden from sysops
-                               $del = wfMsgHtml('rev-delundel');
-                       } else {
-                               $del = $sk->makeKnownLinkObj( $revdel,
-                                       wfMsgHtml('rev-delundel'),
-                                       'target=' . $this->mTargetObj->getPrefixedUrl() .
-                                       '&fileid=' . $row->fa_id );
-                               // Bolden oversighted content
-                               if( $file->isDeleted( File::DELETED_RESTRICTED ) )
-                                       $del = "<strong>$del</strong>";
-                       }
-                       $revdlink = "<tt>(<small>$del</small>)</tt>";
-               }
-               return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
-       }
-
-       private function getEarliestTime( $title ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               if( $title->exists() ) {
-                       $min = $dbr->selectField( 'revision',
-                               'MIN(rev_timestamp)',
-                               array( 'rev_page' => $title->getArticleId() ),
-                               __METHOD__ );
-                       return wfTimestampOrNull( TS_MW, $min );
-               }
-               return null;
-       }
-
-       /**
-        * Fetch revision text link if it's available to all users
-        * @return string
-        */
-       function getPageLink( $rev, $titleObj, $ts, $sk ) {
-               global $wgLang;
-
-               if( !$rev->userCan(Revision::DELETED_TEXT) ) {
-                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
-               } else {
-                       $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ),
-                               "target=".$this->mTargetObj->getPrefixedUrl()."&timestamp=$ts" );
-                       if( $rev->isDeleted(Revision::DELETED_TEXT) )
-                               $link = '<span class="history-deleted">' . $link . '</span>';
-                       return $link;
-               }
-       }
-
-       /**
-        * Fetch image view link if it's available to all users
-        * @return string
-        */
-       function getFileLink( $file, $titleObj, $ts, $key, $sk ) {
-               global $wgLang;
-
-               if( !$file->userCan(File::DELETED_FILE) ) {
-                       return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
-               } else {
-                       $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ),
-                               "target=".$this->mTargetObj->getPrefixedUrl()."&file=$key" );
-                       if( $file->isDeleted(File::DELETED_FILE) )
-                               $link = '<span class="history-deleted">' . $link . '</span>';
-                       return $link;
-               }
-       }
-
-       /**
-        * Fetch file's user id if it's available to this user
-        * @return string
-        */
-       function getFileUser( $file, $sk ) {
-               if( !$file->userCan(File::DELETED_USER) ) {
-                       return '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
-               } else {
-                       $link = $sk->userLink( $file->getRawUser(), $file->getRawUserText() ) .
-                               $sk->userToolLinks( $file->getRawUser(), $file->getRawUserText() );
-                       if( $file->isDeleted(File::DELETED_USER) )
-                               $link = '<span class="history-deleted">' . $link . '</span>';
-                       return $link;
-               }
-       }
-
-       /**
-        * Fetch file upload comment if it's available to this user
-        * @return string
-        */
-       function getFileComment( $file, $sk ) {
-               if( !$file->userCan(File::DELETED_COMMENT) ) {
-                       return '<span class="history-deleted"><span class="comment">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></span>';
-               } else {
-                       $link = $sk->commentBlock( $file->getRawDescription() );
-                       if( $file->isDeleted(File::DELETED_COMMENT) )
-                               $link = '<span class="history-deleted">' . $link . '</span>';
-                       return $link;
-               }
-       }
-
-       function undelete() {
-               global $wgOut, $wgUser;
-               if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-               if( !is_null( $this->mTargetObj ) ) {
-                       $archive = new PageArchive( $this->mTargetObj );
-                       $ok = $archive->undelete(
-                               $this->mTargetTimestamp,
-                               $this->mComment,
-                               $this->mFileVersions,
-                               $this->mUnsuppress );
-
-                       if( is_array($ok) ) {
-                               if ( $ok[1] ) // Undeleted file count
-                                       wfRunHooks( 'FileUndeleteComplete', array(
-                                               $this->mTargetObj, $this->mFileVersions,
-                                               $wgUser, $this->mComment) );
-
-                               $skin = $wgUser->getSkin();
-                               $link = $skin->makeKnownLinkObj( $this->mTargetObj );
-                               $wgOut->addHtml( wfMsgWikiHtml( 'undeletedpage', $link ) );
-                       } else {
-                               $wgOut->showFatalError( wfMsg( "cannotundelete" ) );
-                               $wgOut->addHtml( '<p>' . wfMsgHtml( "undeleterevdel" ) . '</p>' );
-                       }
-
-                       // Show file deletion warnings and errors
-                       $status = $archive->getFileStatus();
-                       if( $status && !$status->isGood() ) {
-                               $wgOut->addWikiText( $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) );
-                       }
-               } else {
-                       $wgOut->showFatalError( wfMsg( "cannotundelete" ) );
-               }
-               return false;
-       }
-}
diff --git a/includes/specials/Unlockdb.php b/includes/specials/Unlockdb.php
deleted file mode 100644 (file)
index 0bf7e5a..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- *
- */
-function wfSpecialUnlockdb() {
-       global $wgUser, $wgOut, $wgRequest;
-
-       if( !$wgUser->isAllowed( 'siteadmin' ) ) {
-               $wgOut->permissionRequired( 'siteadmin' );
-               return;
-       }
-
-       $action = $wgRequest->getVal( 'action' );
-       $f = new DBUnlockForm();
-
-       if ( "success" == $action ) {
-               $f->showSuccess();
-       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
-               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               $f->doSubmit();
-       } else {
-               $f->showForm( "" );
-       }
-}
-
-/**
- * @ingroup SpecialPage
- */
-class DBUnlockForm {
-       function showForm( $err )
-       {
-               global $wgOut, $wgUser;
-
-               global $wgReadOnlyFile;
-               if( !file_exists( $wgReadOnlyFile ) ) {
-                       $wgOut->addWikiMsg( 'databasenotlocked' );
-                       return;
-               }
-
-               $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
-               $wgOut->addWikiMsg( "unlockdbtext" );
-
-               if ( "" != $err ) {
-                       $wgOut->setSubtitle( wfMsg( "formerror" ) );
-                       $wgOut->addHTML( '<p class="error">' . htmlspecialchars( $err ) . "</p>\n" );
-               }
-               $lc = htmlspecialchars( wfMsg( "unlockconfirm" ) );
-               $lb = htmlspecialchars( wfMsg( "unlockbtn" ) );
-               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
-               $action = $titleObj->escapeLocalURL( "action=submit" );
-               $token = htmlspecialchars( $wgUser->editToken() );
-
-               $wgOut->addHTML( <<<END
-
-<form id="unlockdb" method="post" action="{$action}">
-<table border="0">
-       <tr>
-               <td align="right">
-                       <input type="checkbox" name="wpLockConfirm" />
-               </td>
-               <td align="left">{$lc}</td>
-       </tr>
-       <tr>
-               <td>&nbsp;</td>
-               <td align="left">
-                       <input type="submit" name="wpLock" value="{$lb}" />
-               </td>
-       </tr>
-</table>
-<input type="hidden" name="wpEditToken" value="{$token}" />
-</form>
-END
-);
-
-       }
-
-       function doSubmit() {
-               global $wgOut, $wgRequest, $wgReadOnlyFile;
-
-               $wpLockConfirm = $wgRequest->getCheck( 'wpLockConfirm' );
-               if ( ! $wpLockConfirm ) {
-                       $this->showForm( wfMsg( "locknoconfirm" ) );
-                       return;
-               }
-               if ( @! unlink( $wgReadOnlyFile ) ) {
-                       $wgOut->showFileDeleteError( $wgReadOnlyFile );
-                       return;
-               }
-               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
-               $success = $titleObj->getFullURL( "action=success" );
-               $wgOut->redirect( $success );
-       }
-
-       function showSuccess() {
-               global $wgOut;
-               global $ip;
-
-               $wgOut->setPagetitle( wfMsg( "unlockdb" ) );
-               $wgOut->setSubtitle( wfMsg( "unlockdbsuccesssub" ) );
-               $wgOut->addWikiMsg( "unlockdbsuccesstext", $ip );
-       }
-}
diff --git a/includes/specials/Unusedcategories.php b/includes/specials/Unusedcategories.php
deleted file mode 100644 (file)
index 406f794..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * @ingroup SpecialPage
- */
-class UnusedCategoriesPage extends QueryPage {
-
-       function isExpensive() { return true; }
-
-       function getName() {
-               return 'Unusedcategories';
-       }
-
-       function getPageHeader() {
-               return wfMsgExt( 'unusedcategoriestext', array( 'parse' ) );
-       }
-
-       function getSQL() {
-               $NScat = NS_CATEGORY;
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               return "SELECT 'Unusedcategories' as type,
-                               {$NScat} as namespace, page_title as title, page_title as value
-                               FROM $page
-                               LEFT JOIN $categorylinks ON page_title=cl_to
-                               WHERE cl_from IS NULL
-                               AND page_namespace = {$NScat}
-                               AND page_is_redirect = 0";
-       }
-
-       function formatResult( $skin, $result ) {
-               $title = Title::makeTitle( NS_CATEGORY, $result->title );
-               return $skin->makeLinkObj( $title, $title->getText() );
-       }
-}
-
-/** constructor */
-function wfSpecialUnusedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $uc = new UnusedCategoriesPage();
-       return $uc->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Unusedimages.php b/includes/specials/Unusedimages.php
deleted file mode 100644 (file)
index d71b638..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * implements Special:Unusedimages
- * @ingroup SpecialPage
- */
-class UnusedimagesPage extends ImageQueryPage {
-
-       function isExpensive() { return true; }
-
-       function getName() {
-               return 'Unusedimages';
-       }
-
-       function sortDescending() {
-               return false;
-       }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               global $wgCountCategorizedImagesAsUsed;
-               $dbr = wfGetDB( DB_SLAVE );
-
-               if ( $wgCountCategorizedImagesAsUsed ) {
-                       list( $page, $image, $imagelinks, $categorylinks ) = $dbr->tableNamesN( 'page', 'image', 'imagelinks', 'categorylinks' );
-
-                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, img_timestamp as value,
-                                               img_user, img_user_text,  img_description
-                                       FROM ((($page AS I LEFT JOIN $categorylinks AS L ON I.page_id = L.cl_from)
-                                               LEFT JOIN $imagelinks AS P ON I.page_title = P.il_to)
-                                               INNER JOIN $image AS G ON I.page_title = G.img_name)
-                                       WHERE I.page_namespace = ".NS_IMAGE." AND L.cl_from IS NULL AND P.il_to IS NULL";
-               } else {
-                       list( $image, $imagelinks ) = $dbr->tableNamesN( 'image','imagelinks' );
-
-                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, img_timestamp as value,
-                               img_user, img_user_text,  img_description
-                               FROM $image LEFT JOIN $imagelinks ON img_name=il_to WHERE il_to IS NULL ";
-               }
-       }
-
-       function getPageHeader() {
-               return wfMsgExt( 'unusedimagestext', array( 'parse') );
-       }
-
-}
-
-/**
- * Entry point
- */
-function wfSpecialUnusedimages() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $uip = new UnusedimagesPage();
-
-       return $uip->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Unusedtemplates.php b/includes/specials/Unusedtemplates.php
deleted file mode 100644 (file)
index 89acd09..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * implements Special:Unusedtemplates
- * @author Rob Church <robchur@gmail.com>
- * @copyright Â© 2006 Rob Church
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- * @ingroup SpecialPage
- */
-class UnusedtemplatesPage extends QueryPage {
-
-       function getName() { return( 'Unusedtemplates' ); }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-       function sortDescending() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $templatelinks) = $dbr->tableNamesN( 'page', 'templatelinks' );
-               $sql = "SELECT 'Unusedtemplates' AS type, page_title AS title,
-                       page_namespace AS namespace, 0 AS value
-                       FROM $page
-                       LEFT JOIN $templatelinks
-                       ON page_namespace = tl_namespace AND page_title = tl_title
-                       WHERE page_namespace = 10 AND tl_from IS NULL
-                       AND page_is_redirect = 0";
-               return $sql;
-       }
-
-       function formatResult( $skin, $result ) {
-               $title = Title::makeTitle( NS_TEMPLATE, $result->title );
-               $pageLink = $skin->makeKnownLinkObj( $title, '', 'redirect=no' );
-               $wlhLink = $skin->makeKnownLinkObj(
-                       SpecialPage::getTitleFor( 'Whatlinkshere' ),
-                       wfMsgHtml( 'unusedtemplateswlh' ),
-                       'target=' . $title->getPrefixedUrl() );
-               return wfSpecialList( $pageLink, $wlhLink );
-       }
-
-       function getPageHeader() {
-               return wfMsgExt( 'unusedtemplatestext', array( 'parse' ) );
-       }
-
-}
-
-function wfSpecialUnusedtemplates() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $utp = new UnusedtemplatesPage();
-       $utp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Unwatchedpages.php b/includes/specials/Unwatchedpages.php
deleted file mode 100644 (file)
index 64ab372..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page that displays a list of pages that are not on anyones watchlist.
- * Implements Special:Unwatchedpages
- *
- * @ingroup SpecialPage
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-class UnwatchedpagesPage extends QueryPage {
-
-       function getName() { return 'Unwatchedpages'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $watchlist ) = $dbr->tableNamesN( 'page', 'watchlist' );
-               $mwns = NS_MEDIAWIKI;
-               return
-                       "
-                       SELECT
-                               'Unwatchedpages' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_namespace as value
-                       FROM $page
-                       LEFT JOIN $watchlist ON wl_namespace = page_namespace AND page_title = wl_title
-                       WHERE wl_title IS NULL AND page_is_redirect = 0 AND page_namespace<>$mwns
-                       ";
-       }
-
-       function sortDescending() { return false; }
-
-       function formatResult( $skin, $result ) {
-               global $wgContLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getPrefixedText() );
-
-               $plink = $skin->makeKnownLinkObj( $nt, htmlspecialchars( $text ) );
-               $wlink = $skin->makeKnownLinkObj( $nt, wfMsgHtml( 'watch' ), 'action=watch' );
-
-               return wfSpecialList( $plink, $wlink );
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialUnwatchedpages() {
-       global $wgUser, $wgOut;
-
-       if ( ! $wgUser->isAllowed( 'unwatchedpages' ) )
-               return $wgOut->permissionRequired( 'unwatchedpages' );
-
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new UnwatchedpagesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Upload.php b/includes/specials/Upload.php
deleted file mode 100644 (file)
index 0f37f50..0000000
+++ /dev/null
@@ -1,1755 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-
-/**
- * Entry point
- */
-function wfSpecialUpload() {
-       global $wgRequest;
-       $form = new UploadForm( $wgRequest );
-       $form->execute();
-}
-
-/**
- * implements Special:Upload
- * @ingroup SpecialPage
- */
-class UploadForm {
-       const SUCCESS = 0;
-       const BEFORE_PROCESSING = 1;
-       const LARGE_FILE_SERVER = 2;
-       const EMPTY_FILE = 3;
-       const MIN_LENGHT_PARTNAME = 4;
-       const ILLEGAL_FILENAME = 5;
-       const PROTECTED_PAGE = 6;
-       const OVERWRITE_EXISTING_FILE = 7;
-       const FILETYPE_MISSING = 8;
-       const FILETYPE_BADTYPE = 9;
-       const VERIFICATION_ERROR = 10;
-       const UPLOAD_VERIFICATION_ERROR = 11;
-       const UPLOAD_WARNING = 12;
-       const INTERNAL_ERROR = 13;
-
-       /**#@+
-        * @access private
-        */
-       var $mComment, $mLicense, $mIgnoreWarning, $mCurlError;
-       var $mDestName, $mTempPath, $mFileSize, $mFileProps;
-       var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked;
-       var $mSrcName, $mSessionKey, $mStashed, $mDesiredDestName, $mRemoveTempFile, $mSourceType;
-       var $mDestWarningAck, $mCurlDestHandle;
-       var $mLocalFile;
-
-       # Placeholders for text injection by hooks (must be HTML)
-       # extensions should take care to _append_ to the present value
-       var $uploadFormTextTop;
-       var $uploadFormTextAfterSummary;
-
-       const SESSION_VERSION = 1;
-       /**#@-*/
-
-       /**
-        * Constructor : initialise object
-        * Get data POSTed through the form and assign them to the object
-        * @param $request Data posted.
-        */
-       function UploadForm( &$request ) {
-               global $wgAllowCopyUploads;
-               $this->mDesiredDestName   = $request->getText( 'wpDestFile' );
-               $this->mIgnoreWarning     = $request->getCheck( 'wpIgnoreWarning' );
-               $this->mComment           = $request->getText( 'wpUploadDescription' );
-
-               if( !$request->wasPosted() ) {
-                       # GET requests just give the main form; no data except destination
-                       # filename and description
-                       return;
-               }
-
-               # Placeholders for text injection by hooks (empty per default)
-               $this->uploadFormTextTop = "";
-               $this->uploadFormTextAfterSummary = "";
-
-               $this->mReUpload          = $request->getCheck( 'wpReUpload' );
-               $this->mUploadClicked     = $request->getCheck( 'wpUpload' );
-
-               $this->mLicense           = $request->getText( 'wpLicense' );
-               $this->mCopyrightStatus   = $request->getText( 'wpUploadCopyStatus' );
-               $this->mCopyrightSource   = $request->getText( 'wpUploadSource' );
-               $this->mWatchthis         = $request->getBool( 'wpWatchthis' );
-               $this->mSourceType        = $request->getText( 'wpSourceType' );
-               $this->mDestWarningAck    = $request->getText( 'wpDestFileWarningAck' );
-
-               $this->mAction            = $request->getVal( 'action' );
-
-               $this->mSessionKey        = $request->getInt( 'wpSessionKey' );
-               if( !empty( $this->mSessionKey ) &&
-                       isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) &&
-                       $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) {
-                       /**
-                        * Confirming a temporarily stashed upload.
-                        * We don't want path names to be forged, so we keep
-                        * them in the session on the server and just give
-                        * an opaque key to the user agent.
-                        */
-                       $data = $_SESSION['wsUploadData'][$this->mSessionKey];
-                       $this->mTempPath         = $data['mTempPath'];
-                       $this->mFileSize         = $data['mFileSize'];
-                       $this->mSrcName          = $data['mSrcName'];
-                       $this->mFileProps        = $data['mFileProps'];
-                       $this->mCurlError        = 0/*UPLOAD_ERR_OK*/;
-                       $this->mStashed          = true;
-                       $this->mRemoveTempFile   = false;
-               } else {
-                       /**
-                        *Check for a newly uploaded file.
-                        */
-                       if( $wgAllowCopyUploads && $this->mSourceType == 'web' ) {
-                               $this->initializeFromUrl( $request );
-                       } else {
-                               $this->initializeFromUpload( $request );
-                       }
-               }
-       }
-
-       /**
-        * Initialize the uploaded file from PHP data
-        * @access private
-        */
-       function initializeFromUpload( $request ) {
-               $this->mTempPath       = $request->getFileTempName( 'wpUploadFile' );
-               $this->mFileSize       = $request->getFileSize( 'wpUploadFile' );
-               $this->mSrcName        = $request->getFileName( 'wpUploadFile' );
-               $this->mCurlError      = $request->getUploadError( 'wpUploadFile' );
-               $this->mSessionKey     = false;
-               $this->mStashed        = false;
-               $this->mRemoveTempFile = false; // PHP will handle this
-       }
-
-       /**
-        * Copy a web file to a temporary file
-        * @access private
-        */
-       function initializeFromUrl( $request ) {
-               global $wgTmpDirectory;
-               $url = $request->getText( 'wpUploadFileURL' );
-               $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
-
-               $this->mTempPath       = $local_file;
-               $this->mFileSize       = 0; # Will be set by curlCopy
-               $this->mCurlError      = $this->curlCopy( $url, $local_file );
-               $pathParts             = explode( '/', $url );
-               $this->mSrcName        = array_pop( $pathParts );
-               $this->mSessionKey     = false;
-               $this->mStashed        = false;
-
-               // PHP won't auto-cleanup the file
-               $this->mRemoveTempFile = file_exists( $local_file );
-       }
-
-       /**
-        * Safe copy from URL
-        * Returns true if there was an error, false otherwise
-        */
-       private function curlCopy( $url, $dest ) {
-               global $wgUser, $wgOut;
-
-               if( !$wgUser->isAllowed( 'upload_by_url' ) ) {
-                       $wgOut->permissionRequired( 'upload_by_url' );
-                       return true;
-               }
-
-               # Maybe remove some pasting blanks :-)
-               $url =  trim( $url );
-               if( stripos($url, 'http://') !== 0 && stripos($url, 'ftp://') !== 0 ) {
-                       # Only HTTP or FTP URLs
-                       $wgOut->showErrorPage( 'upload-proto-error', 'upload-proto-error-text' );
-                       return true;
-               }
-
-               # Open temporary file
-               $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
-               if( $this->mCurlDestHandle === false ) {
-                       # Could not open temporary file to write in
-                       $wgOut->showErrorPage( 'upload-file-error', 'upload-file-error-text');
-                       return true;
-               }
-
-               $ch = curl_init();
-               curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug
-               curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout
-               curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed
-               curl_setopt( $ch, CURLOPT_URL, $url);
-               curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
-               curl_exec( $ch );
-               $error = curl_errno( $ch ) ? true : false;
-               $errornum =  curl_errno( $ch );
-               // if ( $error ) print curl_error ( $ch ) ; # Debugging output
-               curl_close( $ch );
-
-               fclose( $this->mCurlDestHandle );
-               unset( $this->mCurlDestHandle );
-               if( $error ) {
-                       unlink( $dest );
-                       if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) )
-                               $wgOut->showErrorPage( 'upload-misc-error', 'upload-misc-error-text' );
-                       else
-                               $wgOut->showErrorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" );
-               }
-
-               return $error;
-       }
-
-       /**
-        * Callback function for CURL-based web transfer
-        * Write data to file unless we've passed the length limit;
-        * if so, abort immediately.
-        * @access private
-        */
-       function uploadCurlCallback( $ch, $data ) {
-               global $wgMaxUploadSize;
-               $length = strlen( $data );
-               $this->mFileSize += $length;
-               if( $this->mFileSize > $wgMaxUploadSize ) {
-                       return 0;
-               }
-               fwrite( $this->mCurlDestHandle, $data );
-               return $length;
-       }
-
-       /**
-        * Start doing stuff
-        * @access public
-        */
-       function execute() {
-               global $wgUser, $wgOut;
-               global $wgEnableUploads;
-
-               # Check uploading enabled
-               if( !$wgEnableUploads ) {
-                       $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) );
-                       return;
-               }
-
-               # Check permissions
-               if( !$wgUser->isAllowed( 'upload' ) ) {
-                       if( !$wgUser->isLoggedIn() ) {
-                               $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
-                       } else {
-                               $wgOut->permissionRequired( 'upload' );
-                       }
-                       return;
-               }
-
-               # Check blocks
-               if( $wgUser->isBlocked() ) {
-                       $wgOut->blockedPage();
-                       return;
-               }
-
-               if( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-
-               if( $this->mReUpload ) {
-                       if( !$this->unsaveUploadedFile() ) {
-                               return;
-                       }
-                       # Because it is probably checked and shouldn't be
-                       $this->mIgnoreWarning = false;
-                       
-                       $this->mainUploadForm();
-               } else if( 'submit' == $this->mAction || $this->mUploadClicked ) {
-                       $this->processUpload();
-               } else {
-                       $this->mainUploadForm();
-               }
-
-               $this->cleanupTempFile();
-       }
-
-       /**
-        * Do the upload
-        * Checks are made in SpecialUpload::execute()
-        *
-        * @access private
-        */
-       function processUpload(){
-               global $wgUser, $wgOut, $wgFileExtensions;
-               $details = null;
-               $value = null;
-               $value = $this->internalProcessUpload( $details );
-
-               switch($value) {
-                       case self::SUCCESS:
-                               $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() );
-                               break;
-
-                       case self::BEFORE_PROCESSING:
-                               break;
-
-                       case self::LARGE_FILE_SERVER:
-                               $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) );
-                               break;
-
-                       case self::EMPTY_FILE:
-                               $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) );
-                               break;
-
-                       case self::MIN_LENGHT_PARTNAME:
-                               $this->mainUploadForm( wfMsgHtml( 'minlength1' ) );
-                               break;
-
-                       case self::ILLEGAL_FILENAME:
-                               $filtered = $details['filtered'];
-                               $this->uploadError( wfMsgWikiHtml( 'illegalfilename', htmlspecialchars( $filtered ) ) );
-                               break;
-
-                       case self::PROTECTED_PAGE:
-                               $wgOut->showPermissionsErrorPage( $details['permissionserrors'] );
-                               break;
-
-                       case self::OVERWRITE_EXISTING_FILE:
-                               $errorText = $details['overwrite'];
-                               $overwrite = new WikiError( $wgOut->parse( $errorText ) );
-                               $this->uploadError( $overwrite->toString() );
-                               break;
-
-                       case self::FILETYPE_MISSING:
-                               $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) );
-                               break;
-
-                       case self::FILETYPE_BADTYPE:
-                               $finalExt = $details['finalExt'];
-                               $this->uploadError(
-                                       wfMsgExt( 'filetype-banned-type',
-                                               array( 'parseinline' ),
-                                               htmlspecialchars( $finalExt ),
-                                               implode(
-                                                       wfMsgExt( 'comma-separator', array( 'escapenoentities' ) ),
-                                                       $wgFileExtensions
-                                               )
-                                       )
-                               );
-                               break;
-
-                       case self::VERIFICATION_ERROR:
-                               $veri = $details['veri'];
-                               $this->uploadError( $veri->toString() );
-                               break;
-
-                       case self::UPLOAD_VERIFICATION_ERROR:
-                               $error = $details['error'];
-                               $this->uploadError( $error );
-                               break;
-
-                       case self::UPLOAD_WARNING:
-                               $warning = $details['warning'];
-                               $this->uploadWarning( $warning );
-                               break;
-
-                       case self::INTERNAL_ERROR:
-                               $internal = $details['internal'];
-                               $this->showError( $internal );
-                               break;
-
-                       default:
-                               throw new MWException( __METHOD__ . ": Unknown value `{$value}`" );
-               }
-       }
-
-       /**
-        * Really do the upload
-        * Checks are made in SpecialUpload::execute()
-        *
-        * @param array $resultDetails contains result-specific dict of additional values
-        *
-        * @access private
-        */
-       function internalProcessUpload( &$resultDetails ) {
-               global $wgUser;
-
-               if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) )
-               {
-                       wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." );
-                       return self::BEFORE_PROCESSING;
-               }
-
-               /**
-                * If there was no filename or a zero size given, give up quick.
-                */
-               if( trim( $this->mSrcName ) == '' || empty( $this->mFileSize ) ) {
-                       return self::EMPTY_FILE;
-               }
-
-               /* Check for curl error */
-               if( $this->mCurlError ) {
-                       return self::BEFORE_PROCESSING;
-               }
-
-               # Chop off any directories in the given filename
-               if( $this->mDesiredDestName ) {
-                       $basename = $this->mDesiredDestName;
-               } else {
-                       $basename = $this->mSrcName;
-               }
-               $filtered = wfBaseName( $basename );
-
-               /**
-                * We'll want to blacklist against *any* 'extension', and use
-                * only the final one for the whitelist.
-                */
-               list( $partname, $ext ) = $this->splitExtensions( $filtered );
-
-               if( count( $ext ) ) {
-                       $finalExt = $ext[count( $ext ) - 1];
-               } else {
-                       $finalExt = '';
-               }
-
-               # If there was more than one "extension", reassemble the base
-               # filename to prevent bogus complaints about length
-               if( count( $ext ) > 1 ) {
-                       for( $i = 0; $i < count( $ext ) - 1; $i++ )
-                               $partname .= '.' . $ext[$i];
-               }
-
-               if( strlen( $partname ) < 1 ) {
-                       return self::MIN_LENGHT_PARTNAME;
-               }
-
-               /**
-                * Filter out illegal characters, and try to make a legible name
-                * out of it. We'll strip some silently that Title would die on.
-                */
-               $filtered = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $filtered );
-               $nt = Title::makeTitleSafe( NS_IMAGE, $filtered );
-               if( is_null( $nt ) ) {
-                       $resultDetails = array( 'filtered' => $filtered );
-                       return self::ILLEGAL_FILENAME;
-               }
-               $this->mLocalFile = wfLocalFile( $nt );
-               $this->mDestName = $this->mLocalFile->getName();
-
-               /**
-                * If the image is protected, non-sysop users won't be able
-                * to modify it by uploading a new revision.
-                */
-               $permErrors = $nt->getUserPermissionsErrors( 'edit', $wgUser );
-               $permErrorsUpload = $nt->getUserPermissionsErrors( 'upload', $wgUser );
-               $permErrorsCreate = ( $nt->exists() ? array() : $nt->getUserPermissionsErrors( 'create', $wgUser ) );
-
-               if( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
-                       // merge all the problems into one list, avoiding duplicates
-                       $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
-                       $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
-                       $resultDetails = array( 'permissionserrors' => $permErrors );
-                       return self::PROTECTED_PAGE;
-               }
-
-               /**
-                * In some cases we may forbid overwriting of existing files.
-                */
-               $overwrite = $this->checkOverwrite( $this->mDestName );
-               if( $overwrite !== true ) {
-                       $resultDetails = array( 'overwrite' => $overwrite );
-                       return self::OVERWRITE_EXISTING_FILE;
-               }
-
-               /* Don't allow users to override the blacklist (check file extension) */
-               global $wgCheckFileExtensions, $wgStrictFileExtensions;
-               global $wgFileExtensions, $wgFileBlacklist;
-               if ($finalExt == '') {
-                       return self::FILETYPE_MISSING;
-               } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
-                               ($wgCheckFileExtensions && $wgStrictFileExtensions &&
-                                       !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
-                       $resultDetails = array( 'finalExt' => $finalExt );
-                       return self::FILETYPE_BADTYPE;
-               }
-
-               /**
-                * Look at the contents of the file; if we can recognize the
-                * type but it's corrupt or data of the wrong type, we should
-                * probably not accept it.
-                */
-               if( !$this->mStashed ) {
-                       $this->mFileProps = File::getPropsFromPath( $this->mTempPath, $finalExt );
-                       $this->checkMacBinary();
-                       $veri = $this->verify( $this->mTempPath, $finalExt );
-
-                       if( $veri !== true ) { //it's a wiki error...
-                               $resultDetails = array( 'veri' => $veri );
-                               return self::VERIFICATION_ERROR;
-                       }
-
-                       /**
-                        * Provide an opportunity for extensions to add further checks
-                        */
-                       $error = '';
-                       if( !wfRunHooks( 'UploadVerification',
-                                       array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
-                               $resultDetails = array( 'error' => $error );
-                               return self::UPLOAD_VERIFICATION_ERROR;
-                       }
-               }
-
-
-               /**
-                * Check for non-fatal conditions
-                */
-               if ( ! $this->mIgnoreWarning ) {
-                       $warning = '';
-
-                       global $wgCapitalLinks;
-                       if( $wgCapitalLinks ) {
-                               $filtered = ucfirst( $filtered );
-                       }
-                       if( $basename != $filtered ) {
-                               $warning .=  '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ).'</li>';
-                       }
-
-                       global $wgCheckFileExtensions;
-                       if ( $wgCheckFileExtensions ) {
-                               if ( !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) {
-                                       $warning .= '<li>' .
-                                       wfMsgExt( 'filetype-unwanted-type',
-                                               array( 'parseinline' ),
-                                               htmlspecialchars( $finalExt ),
-                                               implode(
-                                                       wfMsgExt( 'comma-separator', array( 'escapenoentities' ) ),
-                                                       $wgFileExtensions
-                                               )
-                                       ) . '</li>';
-                               }
-                       }
-
-                       global $wgUploadSizeWarning;
-                       if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) {
-                               $skin = $wgUser->getSkin();
-                               $wsize = $skin->formatSize( $wgUploadSizeWarning );
-                               $asize = $skin->formatSize( $this->mFileSize );
-                               $warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>';
-                       }
-                       if ( $this->mFileSize == 0 ) {
-                               $warning .= '<li>'.wfMsgHtml( 'emptyfile' ).'</li>';
-                       }
-
-                       if ( !$this->mDestWarningAck ) {
-                               $warning .= self::getExistsWarning( $this->mLocalFile );
-                       }
-                       
-                       $warning .= $this->getDupeWarning( $this->mTempPath );
-                       
-                       if( $warning != '' ) {
-                               /**
-                                * Stash the file in a temporary location; the user can choose
-                                * to let it through and we'll complete the upload then.
-                                */
-                               $resultDetails = array( 'warning' => $warning );
-                               return self::UPLOAD_WARNING;
-                       }
-               }
-
-               /**
-                * Try actually saving the thing...
-                * It will show an error form on failure.
-                */
-               $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
-                       $this->mCopyrightStatus, $this->mCopyrightSource );
-
-               $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
-                       File::DELETE_SOURCE, $this->mFileProps );
-               if ( !$status->isGood() ) {
-                       $resultDetails = array( 'internal' => $status->getWikiText() );
-                       return self::INTERNAL_ERROR;
-               } else {
-                       if ( $this->mWatchthis ) {
-                               global $wgUser;
-                               $wgUser->addWatch( $this->mLocalFile->getTitle() );
-                       }
-                       // Success, redirect to description page
-                       $img = null; // @todo: added to avoid passing a ref to null - should this be defined somewhere?
-                       wfRunHooks( 'UploadComplete', array( &$this ) );
-                       return self::SUCCESS;
-               }
-       }
-
-       /**
-        * Do existence checks on a file and produce a warning
-        * This check is static and can be done pre-upload via AJAX
-        * Returns an HTML fragment consisting of one or more LI elements if there is a warning
-        * Returns an empty string if there is no warning
-        */
-       static function getExistsWarning( $file ) {
-               global $wgUser, $wgContLang;
-               // Check for uppercase extension. We allow these filenames but check if an image
-               // with lowercase extension exists already
-               $warning = '';
-               $align = $wgContLang->isRtl() ? 'left' : 'right';
-
-               if( strpos( $file->getName(), '.' ) == false ) {
-                       $partname = $file->getName();
-                       $rawExtension = '';
-               } else {
-                       $n = strrpos( $file->getName(), '.' );
-                       $rawExtension = substr( $file->getName(), $n + 1 );
-                       $partname = substr( $file->getName(), 0, $n );
-               }
-
-               $sk = $wgUser->getSkin();
-
-               if ( $rawExtension != $file->getExtension() ) {
-                       // We're not using the normalized form of the extension.
-                       // Normal form is lowercase, using most common of alternate
-                       // extensions (eg 'jpg' rather than 'JPEG').
-                       //
-                       // Check for another file using the normalized form...
-                       $nt_lc = Title::makeTitle( NS_IMAGE, $partname . '.' . $file->getExtension() );
-                       $file_lc = wfLocalFile( $nt_lc );
-               } else {
-                       $file_lc = false;
-               }
-
-               if( $file->exists() ) {
-                       $dlink = $sk->makeKnownLinkObj( $file->getTitle() );
-                       if ( $file->allowInlineDisplay() ) {
-                               $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline' ),
-                                       $file->getName(), $align, array(), false, true );
-                       } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) {
-                               $icon = $file->iconThumb();
-                               $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
-                                       $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>';
-                       } else {
-                               $dlink2 = '';
-                       }
-
-                       $warning .= '<li>' . wfMsgExt( 'fileexists', array('parseinline','replaceafter'), $dlink ) . '</li>' . $dlink2;
-
-               } elseif( $file->getTitle()->getArticleID() ) {
-                       $lnk = $sk->makeKnownLinkObj( $file->getTitle(), '', 'redirect=no' );
-                       $warning .= '<li>' . wfMsgExt( 'filepageexists', array( 'parseinline', 'replaceafter' ), $lnk ) . '</li>';
-               } elseif ( $file_lc && $file_lc->exists() ) {
-                       # Check if image with lowercase extension exists.
-                       # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension
-                       $dlink = $sk->makeKnownLinkObj( $nt_lc );
-                       if ( $file_lc->allowInlineDisplay() ) {
-                               $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline' ),
-                                       $nt_lc->getText(), $align, array(), false, true );
-                       } elseif ( !$file_lc->allowInlineDisplay() && $file_lc->isSafeFile() ) {
-                               $icon = $file_lc->iconThumb();
-                               $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
-                                       $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>';
-                       } else {
-                               $dlink2 = '';
-                       }
-
-                       $warning .= '<li>' .
-                               wfMsgExt( 'fileexists-extension', 'parsemag',
-                                       $file->getTitle()->getPrefixedText(), $dlink ) .
-                               '</li>' . $dlink2;
-
-               } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' )
-                       && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) )
-               {
-                       # Check for filenames like 50px- or 180px-, these are mostly thumbnails
-                       $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $rawExtension );
-                       $file_thb = wfLocalFile( $nt_thb );
-                       if ($file_thb->exists() ) {
-                               # Check if an image without leading '180px-' (or similiar) exists
-                               $dlink = $sk->makeKnownLinkObj( $nt_thb);
-                               if ( $file_thb->allowInlineDisplay() ) {
-                                       $dlink2 = $sk->makeImageLinkObj( $nt_thb,
-                                               wfMsgExt( 'fileexists-thumb', 'parseinline' ),
-                                               $nt_thb->getText(), $align, array(), false, true );
-                               } elseif ( !$file_thb->allowInlineDisplay() && $file_thb->isSafeFile() ) {
-                                       $icon = $file_thb->iconThumb();
-                                       $dlink2 = '<div style="float:' . $align . '" id="mw-media-icon">' .
-                                               $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' .
-                                               $dlink . '</div>';
-                               } else {
-                                       $dlink2 = '';
-                               }
-
-                               $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) .
-                                       '</li>' . $dlink2;
-                       } else {
-                               # Image w/o '180px-' does not exists, but we do not like these filenames
-                               $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' ,
-                                       substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>';
-                       }
-               }
-
-               $filenamePrefixBlacklist = self::getFilenamePrefixBlacklist();
-               # Do the match
-               foreach( $filenamePrefixBlacklist as $prefix ) {
-                       if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
-                               $warning .= '<li>' . wfMsgExt( 'filename-bad-prefix', 'parseinline', $prefix ) . '</li>';
-                               break;
-                       }
-               }
-
-               if ( $file->wasDeleted() && !$file->exists() ) {
-                       # If the file existed before and was deleted, warn the user of this
-                       # Don't bother doing so if the file exists now, however
-                       $ltitle = SpecialPage::getTitleFor( 'Log' );
-                       $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ),
-                               'type=delete&page=' . $file->getTitle()->getPrefixedUrl() );
-                       $warning .= '<li>' . wfMsgWikiHtml( 'filewasdeleted', $llink ) . '</li>';
-               }
-               return $warning;
-       }
-
-       /**
-        * Get a list of warnings
-        *
-        * @param string local filename, e.g. 'file exists', 'non-descriptive filename'
-        * @return array list of warning messages
-        */
-       static function ajaxGetExistsWarning( $filename ) {
-               $file = wfFindFile( $filename );
-               if( !$file ) {
-                       // Force local file so we have an object to do further checks against
-                       // if there isn't an exact match...
-                       $file = wfLocalFile( $filename );
-               }
-               $s = '&nbsp;';
-               if ( $file ) {
-                       $warning = self::getExistsWarning( $file );
-                       if ( $warning !== '' ) {
-                               $s = "<ul>$warning</ul>";
-                       }
-               }
-               return $s;
-       }
-
-       /**
-        * Render a preview of a given license for the AJAX preview on upload
-        *
-        * @param string $license
-        * @return string
-        */
-       public static function ajaxGetLicensePreview( $license ) {
-               global $wgParser, $wgUser;
-               $text = '{{' . $license . '}}';
-               $title = Title::makeTitle( NS_IMAGE, 'Sample.jpg' );
-               $options = ParserOptions::newFromUser( $wgUser );
-
-               // Expand subst: first, then live templates...
-               $text = $wgParser->preSaveTransform( $text, $title, $wgUser, $options );
-               $output = $wgParser->parse( $text, $title, $options );
-
-               return $output->getText();
-       }
-       
-       /**
-        * Check for duplicate files and throw up a warning before the upload
-        * completes.
-        */
-       function getDupeWarning( $tempfile ) {
-               $hash = File::sha1Base36( $tempfile );
-               $dupes = RepoGroup::singleton()->findBySha1( $hash );
-               if( $dupes ) {
-                       global $wgOut;
-                       $msg = "<gallery>";
-                       foreach( $dupes as $file ) {
-                               $title = $file->getTitle();
-                               $msg .= $title->getPrefixedText() .
-                                       "|" . $title->getText() . "\n";
-                       }
-                       $msg .= "</gallery>";
-                       return "<li>" .
-                               wfMsgExt( "file-exists-duplicate", array( "parse" ), count( $dupes ) ) .
-                               $wgOut->parse( $msg ) .
-                               "</li>\n";
-               } else {
-                       return '';
-               }
-       }
-
-       /**
-        * Get a list of blacklisted filename prefixes from [[MediaWiki:filename-prefix-blacklist]]
-        *
-        * @return array list of prefixes
-        */
-       public static function getFilenamePrefixBlacklist() {
-               $blacklist = array();
-               $message = wfMsgForContent( 'filename-prefix-blacklist' );
-               if( $message && !( wfEmptyMsg( 'filename-prefix-blacklist', $message ) || $message == '-' ) ) {
-                       $lines = explode( "\n", $message );
-                       foreach( $lines as $line ) {
-                               // Remove comment lines
-                               $comment = substr( trim( $line ), 0, 1 );
-                               if ( $comment == '#' || $comment == '' ) {
-                                       continue;
-                               }
-                               // Remove additional comments after a prefix
-                               $comment = strpos( $line, '#' );
-                               if ( $comment > 0 ) {
-                                       $line = substr( $line, 0, $comment-1 );
-                               }
-                               $blacklist[] = trim( $line );
-                       }
-               }
-               return $blacklist;
-       }
-
-       /**
-        * Stash a file in a temporary directory for later processing
-        * after the user has confirmed it.
-        *
-        * If the user doesn't explicitly cancel or accept, these files
-        * can accumulate in the temp directory.
-        *
-        * @param string $saveName - the destination filename
-        * @param string $tempName - the source temporary file to save
-        * @return string - full path the stashed file, or false on failure
-        * @access private
-        */
-       function saveTempUploadedFile( $saveName, $tempName ) {
-               global $wgOut;
-               $repo = RepoGroup::singleton()->getLocalRepo();
-               $status = $repo->storeTemp( $saveName, $tempName );
-               if ( !$status->isGood() ) {
-                       $this->showError( $status->getWikiText() );
-                       return false;
-               } else {
-                       return $status->value;
-               }
-       }
-
-       /**
-        * Stash a file in a temporary directory for later processing,
-        * and save the necessary descriptive info into the session.
-        * Returns a key value which will be passed through a form
-        * to pick up the path info on a later invocation.
-        *
-        * @return int
-        * @access private
-        */
-       function stashSession() {
-               $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
-
-               if( !$stash ) {
-                       # Couldn't save the file.
-                       return false;
-               }
-
-               $key = mt_rand( 0, 0x7fffffff );
-               $_SESSION['wsUploadData'][$key] = array(
-                       'mTempPath'       => $stash,
-                       'mFileSize'       => $this->mFileSize,
-                       'mSrcName'        => $this->mSrcName,
-                       'mFileProps'      => $this->mFileProps,
-                       'version'         => self::SESSION_VERSION,
-               );
-               return $key;
-       }
-
-       /**
-        * Remove a temporarily kept file stashed by saveTempUploadedFile().
-        * @access private
-        * @return success
-        */
-       function unsaveUploadedFile() {
-               global $wgOut;
-               $repo = RepoGroup::singleton()->getLocalRepo();
-               $success = $repo->freeTemp( $this->mTempPath );
-               if ( ! $success ) {
-                       $wgOut->showFileDeleteError( $this->mTempPath );
-                       return false;
-               } else {
-                       return true;
-               }
-       }
-
-       /* -------------------------------------------------------------- */
-
-       /**
-        * @param string $error as HTML
-        * @access private
-        */
-       function uploadError( $error ) {
-               global $wgOut;
-               $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" );
-               $wgOut->addHTML( '<span class="error">' . $error . '</span>' );
-       }
-
-       /**
-        * There's something wrong with this file, not enough to reject it
-        * totally but we require manual intervention to save it for real.
-        * Stash it away, then present a form asking to confirm or cancel.
-        *
-        * @param string $warning as HTML
-        * @access private
-        */
-       function uploadWarning( $warning ) {
-               global $wgOut;
-               global $wgUseCopyrightUpload;
-
-               $this->mSessionKey = $this->stashSession();
-               if( !$this->mSessionKey ) {
-                       # Couldn't save file; an error has been displayed so let's go.
-                       return;
-               }
-
-               $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" );
-               $wgOut->addHTML( '<ul class="warning">' . $warning . "</ul>\n" );
-
-               $titleObj = SpecialPage::getTitleFor( 'Upload' );
-
-               if ( $wgUseCopyrightUpload ) {
-                       $copyright = Xml::hidden( 'wpUploadCopyStatus', $this->mCopyrightStatus ) . "\n" .
-                                       Xml::hidden( 'wpUploadSource', $this->mCopyrightSource ) . "\n";
-               } else {
-                       $copyright = '';
-               }
-
-               $wgOut->addHTML(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ),
-                                'enctype' => 'multipart/form-data', 'id' => 'uploadwarning' ) ) . "\n" .
-                       Xml::hidden( 'wpIgnoreWarning', '1' ) . "\n" .
-                       Xml::hidden( 'wpSessionKey', $this->mSessionKey ) . "\n" .
-                       Xml::hidden( 'wpUploadDescription', $this->mComment ) . "\n" .
-                       Xml::hidden( 'wpLicense', $this->mLicense ) . "\n" .
-                       Xml::hidden( 'wpDestFile', $this->mDesiredDestName ) . "\n" .
-                       Xml::hidden( 'wpWatchthis', $this->mWatchthis ) . "\n" .
-                       "{$copyright}<br />" .
-                       Xml::submitButton( wfMsg( 'ignorewarning' ), array ( 'name' => 'wpUpload', 'id' => 'wpUpload', 'checked' => 'checked' ) ) . ' ' .
-                       Xml::submitButton( wfMsg( 'reuploaddesc' ), array ( 'name' => 'wpReUpload', 'id' => 'wpReUpload' ) ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-       }
-
-       /**
-        * Displays the main upload form, optionally with a highlighted
-        * error message up at the top.
-        *
-        * @param string $msg as HTML
-        * @access private
-        */
-       function mainUploadForm( $msg='' ) {
-               global $wgOut, $wgUser, $wgLang, $wgMaxUploadSize;
-               global $wgUseCopyrightUpload, $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview;
-               global $wgRequest, $wgAllowCopyUploads;
-               global $wgStylePath, $wgStyleVersion;
-
-               $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck;
-               $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview;
-
-               $adc = wfBoolToStr( $useAjaxDestCheck );
-               $alp = wfBoolToStr( $useAjaxLicensePreview );
-               $autofill = wfBoolToStr( $this->mDesiredDestName == '' );
-
-               $wgOut->addScript( "<script type=\"text/javascript\">
-wgAjaxUploadDestCheck = {$adc};
-wgAjaxLicensePreview = {$alp};
-wgUploadAutoFill = {$autofill};
-</script>" );
-               $wgOut->addScriptFile( 'upload.js' );
-               $wgOut->addScriptFile( 'edit.js' ); // For <charinsert> support
-
-               if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) )
-               {
-                       wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" );
-                       return false;
-               }
-
-               if( $this->mDesiredDestName ) {
-                       $title = Title::makeTitleSafe( NS_IMAGE, $this->mDesiredDestName );
-                       // Show a subtitle link to deleted revisions (to sysops et al only)
-                       if( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) {
-                               $link = wfMsgExt(
-                                       $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted',
-                                       array( 'parse', 'replaceafter' ),
-                                       $wgUser->getSkin()->makeKnownLinkObj(
-                                               SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ),
-                                               wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count )
-                                       )
-                               );
-                               $wgOut->addHtml( "<div id=\"contentSub2\">{$link}</div>" );
-                       }
-
-                       // Show the relevant lines from deletion log (for still deleted files only)
-                       if( $title instanceof Title && $title->isDeleted() > 0 && !$title->exists() ) {
-                               $this->showDeletionLog( $wgOut, $title->getPrefixedText() );
-                       }
-               }
-
-               $cols = intval($wgUser->getOption( 'cols' ));
-
-               if( $wgUser->getOption( 'editwidth' ) ) {
-                       $width = " style=\"width:100%\"";
-               } else {
-                       $width = '';
-               }
-
-               if ( '' != $msg ) {
-                       $sub = wfMsgHtml( 'uploaderror' );
-                       $wgOut->addHTML( "<h2>{$sub}</h2>\n" .
-                         "<span class='error'>{$msg}</span>\n" );
-               }
-               $wgOut->addHTML( '<div id="uploadtext">' );
-               $wgOut->addWikiMsg( 'uploadtext', $this->mDesiredDestName );
-               $wgOut->addHTML( "</div>\n" );
-
-               # Print a list of allowed file extensions, if so configured.  We ignore
-               # MIME type here, it's incomprehensible to most people and too long.
-               global $wgCheckFileExtensions, $wgStrictFileExtensions,
-               $wgFileExtensions, $wgFileBlacklist;
-
-               $allowedExtensions = '';
-               if( $wgCheckFileExtensions ) {
-                       $delim = wfMsgExt( 'comma-separator', array( 'escapenoentities' ) );
-                       if( $wgStrictFileExtensions ) {
-                               # Everything not permitted is banned
-                               $extensionsList =
-                                       '<div id="mw-upload-permitted">' .
-                                       wfMsgWikiHtml( 'upload-permitted', implode( $wgFileExtensions, $delim ) ) .
-                                       "</div>\n";
-                       } else {
-                               # We have to list both preferred and prohibited
-                               $extensionsList =
-                                       '<div id="mw-upload-preferred">' .
-                                       wfMsgWikiHtml( 'upload-preferred', implode( $wgFileExtensions, $delim ) ) .
-                                       "</div>\n" .
-                                       '<div id="mw-upload-prohibited">' .
-                                       wfMsgWikiHtml( 'upload-prohibited', implode( $wgFileBlacklist, $delim ) ) .
-                                       "</div>\n";
-                       }
-               } else {
-                       # Everything is permitted.
-                       $extensionsList = '';
-               }
-
-               # Get the maximum file size from php.ini as $wgMaxUploadSize works for uploads from URL via CURL only
-               # See http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize for possible values of upload_max_filesize
-               $val = trim( ini_get( 'upload_max_filesize' ) );
-               $last = strtoupper( ( substr( $val, -1 ) ) );
-               switch( $last ) {
-                       case 'G':
-                               $val2 = substr( $val, 0, -1 ) * 1024 * 1024 * 1024;
-                               break;
-                       case 'M':
-                               $val2 = substr( $val, 0, -1 ) * 1024 * 1024;
-                               break;
-                       case 'K':
-                               $val2 = substr( $val, 0, -1 ) * 1024;
-                               break;
-                       default:
-                               $val2 = $val;
-               }
-               $val2 = $wgAllowCopyUploads ? min( $wgMaxUploadSize, $val2 ) : $val2;
-               $maxUploadSize = '<div id="mw-upload-maxfilesize">' . 
-                       wfMsgExt( 'upload-maxfilesize', array( 'parseinline', 'escapenoentities' ), 
-                               $wgLang->formatSize( $val2 ) ) .
-                               "</div>\n";
-
-               $sourcefilename = wfMsgExt( 'sourcefilename', array( 'parseinline', 'escapenoentities' ) );
-        $destfilename = wfMsgExt( 'destfilename', array( 'parseinline', 'escapenoentities' ) ); 
-               
-               $summary = wfMsgExt( 'fileuploadsummary', 'parseinline' );
-
-               $licenses = new Licenses();
-               $license = wfMsgExt( 'license', array( 'parseinline' ) );
-               $nolicense = wfMsgHtml( 'nolicense' );
-               $licenseshtml = $licenses->getHtml();
-
-               $ulb = wfMsgHtml( 'uploadbtn' );
-
-
-               $titleObj = SpecialPage::getTitleFor( 'Upload' );
-
-               $encDestName = htmlspecialchars( $this->mDesiredDestName );
-
-               $watchChecked = $this->watchCheck()
-                       ? 'checked="checked"'
-                       : '';
-               $warningChecked = $this->mIgnoreWarning ? 'checked' : '';
-
-               // Prepare form for upload or upload/copy
-               if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) {
-                       $filename_form =
-                               "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " .
-                                  "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked='checked' />" .
-                                "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
-                                  "onfocus='" .
-                                    "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" .
-                                    "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")' " .
-                                    "onchange='fillDestFilename(\"wpUploadFile\")' size='60' />" .
-                               wfMsgHTML( 'upload_source_file' ) . "<br/>" .
-                               "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' " .
-                                 "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" .
-                               "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " .
-                                 "onfocus='" .
-                                   "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" .
-                                   "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")' " .
-                                   "onchange='fillDestFilename(\"wpUploadFileURL\")' size='60' disabled='disabled' />" .
-                               wfMsgHtml( 'upload_source_url' ) ;
-               } else {
-                       $filename_form =
-                               "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
-                               ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
-                               "size='60' />" .
-                               "<input type='hidden' name='wpSourceType' value='file' />" ;
-               }
-               if ( $useAjaxDestCheck ) {
-                       $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'>&nbsp;</td></tr>";
-                       $destOnkeyup = 'onkeyup="wgUploadWarningObj.keypress();"';
-               } else {
-                       $warningRow = '';
-                       $destOnkeyup = '';
-               }
-
-               $encComment = htmlspecialchars( $this->mComment );
-
-               $wgOut->addHTML(
-                        Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL(),
-                                'enctype' => 'multipart/form-data', 'id' => 'mw-upload-form' ) ) .
-                        Xml::openElement( 'fieldset' ) .
-                        Xml::element( 'legend', null, wfMsg( 'upload' ) ) .
-                        Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-upload-table' ) ) .
-                        "<tr>
-                               {$this->uploadFormTextTop}
-                               <td class='mw-label'>
-                                       <label for='wpUploadFile'>{$sourcefilename}</label>
-                               </td>
-                               <td class='mw-input'>
-                                       {$filename_form}
-                               </td>
-                       </tr>
-                       <tr>
-                               <td></td>
-                               <td>
-                                       {$maxUploadSize}
-                                       {$extensionsList}
-                               </td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>
-                                       <label for='wpDestFile'>{$destfilename}</label>
-                               </td>
-                               <td class='mw-input'>
-                                       <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='60'
-                                               value=\"{$encDestName}\" onchange='toggleFilenameFiller()' $destOnkeyup />
-                               </td>
-                       </tr>
-                       <tr>
-                               <td class='mw-label'>
-                                       <label for='wpUploadDescription'>{$summary}</label>
-                               </td>
-                               <td class='mw-input'>
-                                       <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6'
-                                               cols='{$cols}'{$width}>$encComment</textarea>
-                                       {$this->uploadFormTextAfterSummary}
-                               </td>
-                       </tr>
-                       <tr>"
-               );
-
-               if ( $licenseshtml != '' ) {
-                       global $wgStylePath;
-                       $wgOut->addHTML( "
-                                       <td class='mw-label'>
-                                               <label for='wpLicense'>$license</label>
-                                       </td>
-                                       <td class='mw-input'>
-                                               <select name='wpLicense' id='wpLicense' tabindex='4'
-                                                       onchange='licenseSelectorCheck()'>
-                                                       <option value=''>$nolicense</option>
-                                                       $licenseshtml
-                                               </select>
-                                       </td>
-                               </tr>
-                               <tr>"
-                       );
-                       if( $useAjaxLicensePreview ) {
-                               $wgOut->addHtml( "
-                                               <td></td>
-                                               <td id=\"mw-license-preview\"></td>
-                                       </tr>
-                                       <tr>"
-                               );
-                       }
-               }
-
-               if ( $wgUseCopyrightUpload ) {
-                       $filestatus = wfMsgExt( 'filestatus', 'escapenoentities' );
-                       $copystatus =  htmlspecialchars( $this->mCopyrightStatus );
-                       $filesource = wfMsgExt( 'filesource', 'escapenoentities' );
-                       $uploadsource = htmlspecialchars( $this->mCopyrightSource );
-
-                       $wgOut->addHTML( "
-                                       <td class='mw-label' style='white-space: nowrap;'>
-                                               <label for='wpUploadCopyStatus'>$filestatus</label></td>
-                                       <td class='mw-input'>
-                                               <input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus'
-                                                       value=\"$copystatus\" size='60' />
-                                       </td>
-                               </tr>
-                               <tr>
-                                       <td class='mw-label'>
-                                               <label for='wpUploadCopyStatus'>$filesource</label>
-                                       </td>
-                                       <td class='mw-input'>
-                                               <input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus'
-                                                       value=\"$uploadsource\" size='60' />
-                                       </td>
-                               </tr>
-                               <tr>"
-                       );
-               }
-
-               $wgOut->addHtml( "
-                               <td></td>
-                               <td>
-                                       <input tabindex='7' type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' />
-                                       <label for='wpWatchthis'>" . wfMsgHtml( 'watchthisupload' ) . "</label>
-                                       <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' $warningChecked/>
-                                       <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label>
-                               </td>
-                       </tr>
-                       $warningRow
-                       <tr>
-                               <td></td>
-                                       <td class='mw-input'>
-                                               <input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\"" . $wgUser->getSkin()->tooltipAndAccesskey( 'upload' ) . " />
-                                       </td>
-                       </tr>
-                       <tr>
-                               <td></td>
-                               <td class='mw-input'>"
-               );
-               $wgOut->addWikiText( wfMsgForContent( 'edittools' ) );
-               $wgOut->addHTML( "
-                               </td>
-                       </tr>" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::hidden( 'wpDestFileWarningAck', '', array( 'id' => 'wpDestFileWarningAck' ) ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' )
-               );
-               $uploadfooter = wfMsgNoTrans( 'uploadfooter' );
-               if( $uploadfooter != '-' && !wfEmptyMsg( 'uploadfooter', $uploadfooter ) ){
-                       $wgOut->addWikiText( '<div id="mw-upload-footer-message">' . $uploadfooter . '</div>' );
-               }
-       }
-
-       /* -------------------------------------------------------------- */
-       
-       /**
-        * See if we should check the 'watch this page' checkbox on the form
-        * based on the user's preferences and whether we're being asked
-        * to create a new file or update an existing one.
-        *
-        * In the case where 'watch edits' is off but 'watch creations' is on,
-        * we'll leave the box unchecked.
-        *
-        * Note that the page target can be changed *on the form*, so our check
-        * state can get out of sync.
-        */
-       function watchCheck() {
-               global $wgUser;
-               if( $wgUser->getOption( 'watchdefault' ) ) {
-                       // Watch all edits!
-                       return true;
-               }
-               
-               $local = wfLocalFile( $this->mDesiredDestName );
-               if( $local && $local->exists() ) {
-                       // We're uploading a new version of an existing file.
-                       // No creation, so don't watch it if we're not already.
-                       return $local->getTitle()->userIsWatching();
-               } else {
-                       // New page should get watched if that's our option.
-                       return $wgUser->getOption( 'watchcreations' );
-               }
-       }
-
-       /**
-        * Split a file into a base name and all dot-delimited 'extensions'
-        * on the end. Some web server configurations will fall back to
-        * earlier pseudo-'extensions' to determine type and execute
-        * scripts, so the blacklist needs to check them all.
-        *
-        * @return array
-        */
-       function splitExtensions( $filename ) {
-               $bits = explode( '.', $filename );
-               $basename = array_shift( $bits );
-               return array( $basename, $bits );
-       }
-
-       /**
-        * Perform case-insensitive match against a list of file extensions.
-        * Returns true if the extension is in the list.
-        *
-        * @param string $ext
-        * @param array $list
-        * @return bool
-        */
-       function checkFileExtension( $ext, $list ) {
-               return in_array( strtolower( $ext ), $list );
-       }
-
-       /**
-        * Perform case-insensitive match against a list of file extensions.
-        * Returns true if any of the extensions are in the list.
-        *
-        * @param array $ext
-        * @param array $list
-        * @return bool
-        */
-       function checkFileExtensionList( $ext, $list ) {
-               foreach( $ext as $e ) {
-                       if( in_array( strtolower( $e ), $list ) ) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Verifies that it's ok to include the uploaded file
-        *
-        * @param string $tmpfile the full path of the temporary file to verify
-        * @param string $extension The filename extension that the file is to be served with
-        * @return mixed true of the file is verified, a WikiError object otherwise.
-        */
-       function verify( $tmpfile, $extension ) {
-               #magically determine mime type
-               $magic = MimeMagic::singleton();
-               $mime = $magic->guessMimeType($tmpfile,false);
-
-               #check mime type, if desired
-               global $wgVerifyMimeType;
-               if ($wgVerifyMimeType) {
-
-                 wfDebug ( "\n\nmime: <$mime> extension: <$extension>\n\n");
-                       #check mime type against file extension
-                       if( !$this->verifyExtension( $mime, $extension ) ) {
-                               return new WikiErrorMsg( 'uploadcorrupt' );
-                       }
-
-                       #check mime type blacklist
-                       global $wgMimeTypeBlacklist;
-                       if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
-                               && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
-                               return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) );
-                       }
-               }
-
-               #check for htmlish code and javascript
-               if( $this->detectScript ( $tmpfile, $mime, $extension ) ) {
-                       return new WikiErrorMsg( 'uploadscripted' );
-               }
-
-               /**
-               * Scan the uploaded file for viruses
-               */
-               $virus= $this->detectVirus($tmpfile);
-               if ( $virus ) {
-                       return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) );
-               }
-
-               wfDebug( __METHOD__.": all clear; passing.\n" );
-               return true;
-       }
-
-       /**
-        * Checks if the mime type of the uploaded file matches the file extension.
-        *
-        * @param string $mime the mime type of the uploaded file
-        * @param string $extension The filename extension that the file is to be served with
-        * @return bool
-        */
-       function verifyExtension( $mime, $extension ) {
-               $magic = MimeMagic::singleton();
-
-               if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' )
-                       if ( ! $magic->isRecognizableExtension( $extension ) ) {
-                               wfDebug( __METHOD__.": passing file with unknown detected mime type; " .
-                                       "unrecognized extension '$extension', can't verify\n" );
-                               return true;
-                       } else {
-                               wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ".
-                                       "recognized extension '$extension', so probably invalid file\n" );
-                               return false;
-                       }
-
-               $match= $magic->isMatchingExtension($extension,$mime);
-
-               if ($match===NULL) {
-                       wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" );
-                       return true;
-               } elseif ($match===true) {
-                       wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" );
-
-                       #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
-                       return true;
-
-               } else {
-                       wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" );
-                       return false;
-               }
-       }
-
-       /**
-        * Heuristic for detecting files that *could* contain JavaScript instructions or
-        * things that may look like HTML to a browser and are thus
-        * potentially harmful. The present implementation will produce false positives in some situations.
-        *
-        * @param string $file Pathname to the temporary upload file
-        * @param string $mime The mime type of the file
-        * @param string $extension The extension of the file
-        * @return bool true if the file contains something looking like embedded scripts
-        */
-       function detectScript($file, $mime, $extension) {
-               global $wgAllowTitlesInSVG;
-
-               #ugly hack: for text files, always look at the entire file.
-               #For binarie field, just check the first K.
-
-               if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file );
-               else {
-                       $fp = fopen( $file, 'rb' );
-                       $chunk = fread( $fp, 1024 );
-                       fclose( $fp );
-               }
-
-               $chunk= strtolower( $chunk );
-
-               if (!$chunk) return false;
-
-               #decode from UTF-16 if needed (could be used for obfuscation).
-               if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE";
-               elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE";
-               else $enc= NULL;
-
-               if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk);
-
-               $chunk= trim($chunk);
-
-               #FIXME: convert from UTF-16 if necessarry!
-
-               wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n");
-
-               #check for HTML doctype
-               if (eregi("<!DOCTYPE *X?HTML",$chunk)) return true;
-
-               /**
-               * Internet Explorer for Windows performs some really stupid file type
-               * autodetection which can cause it to interpret valid image files as HTML
-               * and potentially execute JavaScript, creating a cross-site scripting
-               * attack vectors.
-               *
-               * Apple's Safari browser also performs some unsafe file type autodetection
-               * which can cause legitimate files to be interpreted as HTML if the
-               * web server is not correctly configured to send the right content-type
-               * (or if you're really uploading plain text and octet streams!)
-               *
-               * Returns true if IE is likely to mistake the given file for HTML.
-               * Also returns true if Safari would mistake the given file for HTML
-               * when served with a generic content-type.
-               */
-
-               $tags = array(
-                       '<body',
-                       '<head',
-                       '<html',   #also in safari
-                       '<img',
-                       '<pre',
-                       '<script', #also in safari
-                       '<table'
-                       );
-               if( ! $wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
-                       $tags[] = '<title';
-               }
-
-               foreach( $tags as $tag ) {
-                       if( false !== strpos( $chunk, $tag ) ) {
-                               return true;
-                       }
-               }
-
-               /*
-               * look for javascript
-               */
-
-               #resolve entity-refs to look at attributes. may be harsh on big files... cache result?
-               $chunk = Sanitizer::decodeCharReferences( $chunk );
-
-               #look for script-types
-               if (preg_match('!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim',$chunk)) return true;
-
-               #look for html-style script-urls
-               if (preg_match('!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
-
-               #look for css-style script-urls
-               if (preg_match('!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true;
-
-               wfDebug("SpecialUpload::detectScript: no scripts found\n");
-               return false;
-       }
-
-       /**
-        * Generic wrapper function for a virus scanner program.
-        * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
-        * $wgAntivirusRequired may be used to deny upload if the scan fails.
-        *
-        * @param string $file Pathname to the temporary upload file
-        * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
-        *         or a string containing feedback from the virus scanner if a virus was found.
-        *         If textual feedback is missing but a virus was found, this function returns true.
-        */
-       function detectVirus($file) {
-               global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
-
-               if ( !$wgAntivirus ) {
-                       wfDebug( __METHOD__.": virus scanner disabled\n");
-                       return NULL;
-               }
-
-               if ( !$wgAntivirusSetup[$wgAntivirus] ) {
-                       wfDebug( __METHOD__.": unknown virus scanner: $wgAntivirus\n" );
-                       # @TODO: localise
-                       $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" );
-                       return "unknown antivirus: $wgAntivirus";
-               }
-
-               # look up scanner configuration
-               $command = $wgAntivirusSetup[$wgAntivirus]["command"];
-               $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"];
-               $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ?
-                       $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null;
-
-               if ( strpos( $command,"%f" ) === false ) {
-                       # simple pattern: append file to scan
-                       $command .= " " . wfEscapeShellArg( $file );
-               } else {
-                       # complex pattern: replace "%f" with file to scan
-                       $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
-               }
-
-               wfDebug( __METHOD__.": running virus scan: $command \n" );
-
-               # execute virus scanner
-               $exitCode = false;
-
-               #NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
-               #      that does not seem to be worth the pain.
-               #      Ask me (Duesentrieb) about it if it's ever needed.
-               $output = array();
-               if ( wfIsWindows() ) {
-                       exec( "$command", $output, $exitCode );
-               } else {
-                       exec( "$command 2>&1", $output, $exitCode );
-               }
-
-               # map exit code to AV_xxx constants.
-               $mappedCode = $exitCode;
-               if ( $exitCodeMap ) {
-                       if ( isset( $exitCodeMap[$exitCode] ) ) {
-                               $mappedCode = $exitCodeMap[$exitCode];
-                       } elseif ( isset( $exitCodeMap["*"] ) ) {
-                               $mappedCode = $exitCodeMap["*"];
-                       }
-               }
-
-               if ( $mappedCode === AV_SCAN_FAILED ) {
-                       # scan failed (code was mapped to false by $exitCodeMap)
-                       wfDebug( __METHOD__.": failed to scan $file (code $exitCode).\n" );
-
-                       if ( $wgAntivirusRequired ) {
-                               return "scan failed (code $exitCode)";
-                       } else {
-                               return NULL;
-                       }
-               } else if ( $mappedCode === AV_SCAN_ABORTED ) {
-                       # scan failed because filetype is unknown (probably imune)
-                       wfDebug( __METHOD__.": unsupported file type $file (code $exitCode).\n" );
-                       return NULL;
-               } else if ( $mappedCode === AV_NO_VIRUS ) {
-                       # no virus found
-                       wfDebug( __METHOD__.": file passed virus scan.\n" );
-                       return false;
-               } else {
-                       $output = join( "\n", $output );
-                       $output = trim( $output );
-
-                       if ( !$output ) {
-                               $output = true; #if there's no output, return true
-                       } elseif ( $msgPattern ) {
-                               $groups = array();
-                               if ( preg_match( $msgPattern, $output, $groups ) ) {
-                                       if ( $groups[1] ) {
-                                               $output = $groups[1];
-                                       }
-                               }
-                       }
-
-                       wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" );
-                       return $output;
-               }
-       }
-
-       /**
-        * Check if the temporary file is MacBinary-encoded, as some uploads
-        * from Internet Explorer on Mac OS Classic and Mac OS X will be.
-        * If so, the data fork will be extracted to a second temporary file,
-        * which will then be checked for validity and either kept or discarded.
-        *
-        * @access private
-        */
-       function checkMacBinary() {
-               $macbin = new MacBinary( $this->mTempPath );
-               if( $macbin->isValid() ) {
-                       $dataFile = tempnam( wfTempDir(), "WikiMacBinary" );
-                       $dataHandle = fopen( $dataFile, 'wb' );
-
-                       wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" );
-                       $macbin->extractData( $dataHandle );
-
-                       $this->mTempPath = $dataFile;
-                       $this->mFileSize = $macbin->dataForkLength();
-
-                       // We'll have to manually remove the new file if it's not kept.
-                       $this->mRemoveTempFile = true;
-               }
-               $macbin->close();
-       }
-
-       /**
-        * If we've modified the upload file we need to manually remove it
-        * on exit to clean up.
-        * @access private
-        */
-       function cleanupTempFile() {
-               if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) {
-                       wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" );
-                       unlink( $this->mTempPath );
-               }
-       }
-
-       /**
-        * Check if there's an overwrite conflict and, if so, if restrictions
-        * forbid this user from performing the upload.
-        *
-        * @return mixed true on success, WikiError on failure
-        * @access private
-        */
-       function checkOverwrite( $name ) {
-               $img = wfFindFile( $name );
-
-               $error = '';
-               if( $img ) {
-                       global $wgUser, $wgOut;
-                       if( $img->isLocal() ) {
-                               if( !self::userCanReUpload( $wgUser, $img->name ) ) {
-                                       $error = 'fileexists-forbidden';
-                               }
-                       } else {
-                               if( !$wgUser->isAllowed( 'reupload' ) ||
-                                   !$wgUser->isAllowed( 'reupload-shared' ) ) {
-                                       $error = "fileexists-shared-forbidden";
-                               }
-                       }
-               }
-
-               if( $error ) {
-                       $errorText = wfMsg( $error, wfEscapeWikiText( $img->getName() ) );
-                       return $errorText;
-               }
-
-               // Rockin', go ahead and upload
-               return true;
-       }
-
-        /**
-        * Check if a user is the last uploader
-        *
-        * @param User $user
-        * @param string $img, image name
-        * @return bool
-        */
-       public static function userCanReUpload( User $user, $img ) {
-               if( $user->isAllowed( 'reupload' ) )
-                       return true; // non-conditional
-               if( !$user->isAllowed( 'reupload-own' ) )
-                       return false;
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $row = $dbr->selectRow('image',
-               /* SELECT */ 'img_user',
-               /* WHERE */ array( 'img_name' => $img )
-               );
-               if ( !$row )
-                       return false;
-
-               return $user->getId() == $row->img_user;
-       }
-
-       /**
-        * Display an error with a wikitext description
-        */
-       function showError( $description ) {
-               global $wgOut;
-               $wgOut->setPageTitle( wfMsg( "internalerror" ) );
-               $wgOut->setRobotpolicy( "noindex,nofollow" );
-               $wgOut->setArticleRelated( false );
-               $wgOut->enableClientCache( false );
-               $wgOut->addWikiText( $description );
-       }
-
-       /**
-        * Get the initial image page text based on a comment and optional file status information
-        */
-       static function getInitialPageText( $comment, $license, $copyStatus, $source ) {
-               global $wgUseCopyrightUpload;
-               if ( $wgUseCopyrightUpload ) {
-                       if ( $license != '' ) {
-                               $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
-                       }
-                       $pageText = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n" .
-                         '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
-                         "$licensetxt" .
-                         '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
-               } else {
-                       if ( $license != '' ) {
-                               $filedesc = $comment == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n";
-                                $pageText = $filedesc .
-                                        '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
-                       } else {
-                               $pageText = $comment;
-                       }
-               }
-               return $pageText;
-       }
-
-       /**
-        * If there are rows in the deletion log for this file, show them,
-        * along with a nice little note for the user
-        *
-        * @param OutputPage $out
-        * @param string filename
-        */
-       private function showDeletionLog( $out, $filename ) {
-               global $wgUser;
-               $loglist = new LogEventsList( $wgUser->getSkin(), $out );
-               $pager = new LogPager( $loglist, 'delete', false, $filename );
-               if( $pager->getNumRows() > 0 ) {
-                       $out->addHtml( '<div id="mw-upload-deleted-warn">' );
-                       $out->addWikiMsg( 'upload-wasdeleted' );
-                       $out->addHTML(
-                               $loglist->beginLogEventsList() .
-                               $pager->getBody() .
-                               $loglist->endLogEventsList()
-                       );
-                       $out->addHtml( '</div>' );
-               }
-       }
-}
diff --git a/includes/specials/UploadMogile.php b/includes/specials/UploadMogile.php
deleted file mode 100644 (file)
index 7ff8fda..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * You will need the extension MogileClient to use this special page.
- */
-require_once( 'MogileFS.php' );
-
-/**
- * Entry point
- */
-function wfSpecialUploadMogile() {
-       global $wgRequest;
-       $form = new UploadFormMogile( $wgRequest );
-       $form->execute();
-}
-
-/**
- * Extends Special:Upload with MogileFS.
- * @ingroup SpecialPage
- */
-class UploadFormMogile extends UploadForm {
-       /**
-        * Move the uploaded file from its temporary location to the final
-        * destination. If a previous version of the file exists, move
-        * it into the archive subdirectory.
-        *
-        * @todo If the later save fails, we may have disappeared the original file.
-        *
-        * @param string $saveName
-        * @param string $tempName full path to the temporary file
-        * @param bool $useRename  Not used in this implementation
-        */
-       function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
-               global $wgOut;
-               $mfs = MogileFS::NewMogileFS();
-
-               $this->mSavedFile = "image!{$saveName}";
-
-               if( $mfs->getPaths( $this->mSavedFile )) {
-                       $this->mUploadOldVersion = gmdate( 'YmdHis' ) . "!{$saveName}";
-                       if( !$mfs->rename( $this->mSavedFile, "archive!{$this->mUploadOldVersion}" ) ) {
-                               $wgOut->showFileRenameError( $this->mSavedFile,
-                                 "archive!{$this->mUploadOldVersion}" );
-                               return false;
-                       }
-               } else {
-                       $this->mUploadOldVersion = '';
-               }
-
-               if ( $this->mStashed ) {
-                       if (!$mfs->rename($tempName,$this->mSavedFile)) {
-                               $wgOut->showFileRenameError($tempName, $this->mSavedFile );
-                               return false;
-                       }
-               } else {
-                       if ( !$mfs->saveFile($this->mSavedFile,'normal',$tempName )) {
-                               $wgOut->showFileCopyError( $tempName, $this->mSavedFile );
-                               return false;
-                       }
-                       unlink($tempName);
-               }
-               return true;
-       }
-
-       /**
-        * Stash a file in a temporary directory for later processing
-        * after the user has confirmed it.
-        *
-        * If the user doesn't explicitly cancel or accept, these files
-        * can accumulate in the temp directory.
-        *
-        * @param string $saveName - the destination filename
-        * @param string $tempName - the source temporary file to save
-        * @return string - full path the stashed file, or false on failure
-        * @access private
-        */
-       function saveTempUploadedFile( $saveName, $tempName ) {
-               global $wgOut;
-
-               $stash = 'stash!' . gmdate( "YmdHis" ) . '!' . $saveName;
-               $mfs = MogileFS::NewMogileFS();
-               if ( !$mfs->saveFile( $stash, 'normal', $tempName ) ) {
-                       $wgOut->showFileCopyError( $tempName, $stash );
-                       return false;
-               }
-               unlink($tempName);
-               return $stash;
-       }
-
-       /**
-        * Stash a file in a temporary directory for later processing,
-        * and save the necessary descriptive info into the session.
-        * Returns a key value which will be passed through a form
-        * to pick up the path info on a later invocation.
-        *
-        * @return int
-        * @access private
-        */
-       function stashSession() {
-               $stash = $this->saveTempUploadedFile(
-                       $this->mUploadSaveName, $this->mUploadTempName );
-
-               if( !$stash ) {
-                       # Couldn't save the file.
-                       return false;
-               }
-
-               $key = mt_rand( 0, 0x7fffffff );
-               $_SESSION['wsUploadData'][$key] = array(
-                       'mUploadTempName' => $stash,
-                       'mUploadSize'     => $this->mUploadSize,
-                       'mOname'          => $this->mOname );
-               return $key;
-       }
-
-       /**
-        * Remove a temporarily kept file stashed by saveTempUploadedFile().
-        * @access private
-        * @return success
-        */
-       function unsaveUploadedFile() {
-               global $wgOut;
-               $mfs = MogileFS::NewMogileFS();
-               if ( ! $mfs->delete( $this->mUploadTempName ) ) {
-                       $wgOut->showFileDeleteError( $this->mUploadTempName );
-                       return false;
-               } else {
-                       return true;
-               }
-       }
-}
diff --git a/includes/specials/Userlogin.php b/includes/specials/Userlogin.php
deleted file mode 100644 (file)
index 179ef3f..0000000
+++ /dev/null
@@ -1,928 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * constructor
- */
-function wfSpecialUserlogin( $par = '' ) {
-       global $wgRequest;
-       if( session_id() == '' ) {
-               wfSetupSession();
-       }
-
-       $form = new LoginForm( $wgRequest, $par );
-       $form->execute();
-}
-
-/**
- * implements Special:Login
- * @ingroup SpecialPage
- */
-class LoginForm {
-
-       const SUCCESS = 0;
-       const NO_NAME = 1;
-       const ILLEGAL = 2;
-       const WRONG_PLUGIN_PASS = 3;
-       const NOT_EXISTS = 4;
-       const WRONG_PASS = 5;
-       const EMPTY_PASS = 6;
-       const RESET_PASS = 7;
-       const ABORTED = 8;
-       const CREATE_BLOCKED = 9;
-
-       var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
-       var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
-       var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage, $mSkipCookieCheck;
-
-       /**
-        * Constructor
-        * @param WebRequest $request A WebRequest object passed by reference
-        */
-       function LoginForm( &$request, $par = '' ) {
-               global $wgLang, $wgAllowRealName, $wgEnableEmail;
-               global $wgAuth;
-
-               $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]]
-               $this->mName = $request->getText( 'wpName' );
-               $this->mPassword = $request->getText( 'wpPassword' );
-               $this->mRetype = $request->getText( 'wpRetype' );
-               $this->mDomain = $request->getText( 'wpDomain' );
-               $this->mReturnTo = $request->getVal( 'returnto' );
-               $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
-               $this->mPosted = $request->wasPosted();
-               $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' );
-               $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
-                                           && $wgEnableEmail;
-               $this->mMailmypassword = $request->getCheck( 'wpMailmypassword' )
-                                        && $wgEnableEmail;
-               $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
-               $this->mAction = $request->getVal( 'action' );
-               $this->mRemember = $request->getCheck( 'wpRemember' );
-               $this->mLanguage = $request->getText( 'uselang' );
-               $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
-
-               if( $wgEnableEmail ) {
-                       $this->mEmail = $request->getText( 'wpEmail' );
-               } else {
-                       $this->mEmail = '';
-               }
-               if( $wgAllowRealName ) {
-                   $this->mRealName = $request->getText( 'wpRealName' );
-               } else {
-                   $this->mRealName = '';
-               }
-
-               if( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       $this->mDomain = 'invaliddomain';
-               }
-               $wgAuth->setDomain( $this->mDomain );
-
-               # When switching accounts, it sucks to get automatically logged out
-               if( $this->mReturnTo == $wgLang->specialPage( 'Userlogout' ) ) {
-                       $this->mReturnTo = '';
-               }
-       }
-
-       function execute() {
-               if ( !is_null( $this->mCookieCheck ) ) {
-                       $this->onCookieRedirectCheck( $this->mCookieCheck );
-                       return;
-               } else if( $this->mPosted ) {
-                       if( $this->mCreateaccount ) {
-                               return $this->addNewAccount();
-                       } else if ( $this->mCreateaccountMail ) {
-                               return $this->addNewAccountMailPassword();
-                       } else if ( $this->mMailmypassword ) {
-                               return $this->mailPassword();
-                       } else if ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
-                               return $this->processLogin();
-                       }
-               }
-               $this->mainLoginForm( '' );
-       }
-
-       /**
-        * @private
-        */
-       function addNewAccountMailPassword() {
-               global $wgOut;
-
-               if ('' == $this->mEmail) {
-                       $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
-                       return;
-               }
-
-               $u = $this->addNewaccountInternal();
-
-               if ($u == NULL) {
-                       return;
-               }
-
-               // Wipe the initial password and mail a temporary one
-               $u->setPassword( null );
-               $u->saveSettings();
-               $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
-
-               wfRunHooks( 'AddNewAccount', array( $u, true ) );
-
-               $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               if( WikiError::isError( $result ) ) {
-                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
-               } else {
-                       $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
-                       $wgOut->returnToMain( false );
-               }
-               $u = 0;
-       }
-
-
-       /**
-        * @private
-        */
-       function addNewAccount() {
-               global $wgUser, $wgEmailAuthentication;
-
-               # Create the account and abort if there's a problem doing so
-               $u = $this->addNewAccountInternal();
-               if( $u == NULL )
-                       return;
-
-               # If we showed up language selection links, and one was in use, be
-               # smart (and sensible) and save that language as the user's preference
-               global $wgLoginLanguageSelector;
-               if( $wgLoginLanguageSelector && $this->mLanguage )
-                       $u->setOption( 'language', $this->mLanguage );
-
-               # Send out an email authentication message if needed
-               if( $wgEmailAuthentication && User::isValidEmailAddr( $u->getEmail() ) ) {
-                       global $wgOut;
-                       $error = $u->sendConfirmationMail();
-                       if( WikiError::isError( $error ) ) {
-                               $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
-                       } else {
-                               $wgOut->addWikiMsg( 'confirmemail_oncreate' );
-                       }
-               }
-
-               # Save settings (including confirmation token)
-               $u->saveSettings();
-
-               # If not logged in, assume the new account as the current one and set session cookies
-               # then show a "welcome" message or a "need cookies" message as needed
-               if( $wgUser->isAnon() ) {
-                       $wgUser = $u;
-                       $wgUser->setCookies();
-                       wfRunHooks( 'AddNewAccount', array( $wgUser ) );
-                       if( $this->hasSessionCookie() ) {
-                               return $this->successfulLogin( wfMsg( 'welcomecreation', $wgUser->getName() ), false );
-                       } else {
-                               return $this->cookieRedirectCheck( 'new' );
-                       }
-               } else {
-                       # Confirm that the account was created
-                       global $wgOut;
-                       $self = SpecialPage::getTitleFor( 'Userlogin' );
-                       $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
-                       $wgOut->setArticleRelated( false );
-                       $wgOut->setRobotPolicy( 'noindex,nofollow' );
-                       $wgOut->addHtml( wfMsgWikiHtml( 'accountcreatedtext', $u->getName() ) );
-                       $wgOut->returnToMain( false, $self );
-                       wfRunHooks( 'AddNewAccount', array( $u ) );
-                       return true;
-               }
-       }
-
-       /**
-        * @private
-        */
-       function addNewAccountInternal() {
-               global $wgUser, $wgOut;
-               global $wgEnableSorbs, $wgProxyWhitelist;
-               global $wgMemc, $wgAccountCreationThrottle;
-               global $wgAuth, $wgMinimalPasswordLength;
-               global $wgEmailConfirmToEdit;
-
-               // If the user passes an invalid domain, something is fishy
-               if( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                       return false;
-               }
-
-               // If we are not allowing users to login locally, we should
-               // be checking to see if the user is actually able to
-               // authenticate to the authentication server before they
-               // create an account (otherwise, they can create a local account
-               // and login as any domain user). We only need to check this for
-               // domains that aren't local.
-               if( 'local' != $this->mDomain && '' != $this->mDomain ) {
-                       if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               return false;
-                       }
-               }
-
-               if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return false;
-               }
-
-               # Check permissions
-               if ( !$wgUser->isAllowed( 'createaccount' ) ) {
-                       $this->userNotPrivilegedMessage();
-                       return false;
-               } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
-                       $this->userBlockedMessage();
-                       return false;
-               }
-
-               $ip = wfGetIP();
-               if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
-                 $wgUser->inSorbsBlacklist( $ip ) )
-               {
-                       $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
-                       return;
-               }
-
-               # Now create a dummy user ($u) and check if it is valid
-               $name = trim( $this->mName );
-               $u = User::newFromName( $name, 'creatable' );
-               if ( is_null( $u ) ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return false;
-               }
-
-               if ( 0 != $u->idForName() ) {
-                       $this->mainLoginForm( wfMsg( 'userexists' ) );
-                       return false;
-               }
-
-               if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
-                       $this->mainLoginForm( wfMsg( 'badretype' ) );
-                       return false;
-               }
-
-               # check for minimal password length
-               if ( !$u->isValidPassword( $this->mPassword ) ) {
-                       if ( !$this->mCreateaccountMail ) {
-                               $this->mainLoginForm( wfMsgExt( 'passwordtooshort', array( 'parsemag' ), $wgMinimalPasswordLength ) );
-                               return false;
-                       } else {
-                               # do not force a password for account creation by email
-                               # set invalid password, it will be replaced later by a random generated password
-                               $this->mPassword = null;
-                       }
-               }
-
-               # if you need a confirmed email address to edit, then obviously you need an email address.
-               if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
-                       $this->mainLoginForm( wfMsg( 'noemailtitle' ) );
-                       return false;
-               }
-
-               if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
-                       $this->mainLoginForm( wfMsg( 'invalidemailaddress' ) );
-                       return false;
-               }
-
-               # Set some additional data so the AbortNewAccount hook can be
-               # used for more than just username validation
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-
-               $abortError = '';
-               if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
-                       // Hook point to add extra creation throttles and blocks
-                       wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
-                       $this->mainLoginForm( $abortError );
-                       return false;
-               }
-
-               if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
-                       $key = wfMemcKey( 'acctcreate', 'ip', $ip );
-                       $value = $wgMemc->incr( $key );
-                       if ( !$value ) {
-                               $wgMemc->set( $key, 1, 86400 );
-                       }
-                       if ( $value > $wgAccountCreationThrottle ) {
-                               $this->throttleHit( $wgAccountCreationThrottle );
-                               return false;
-                       }
-               }
-
-               if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
-                       $this->mainLoginForm( wfMsg( 'externaldberror' ) );
-                       return false;
-               }
-
-               return $this->initUser( $u, false );
-       }
-
-       /**
-        * Actually add a user to the database.
-        * Give it a User object that has been initialised with a name.
-        *
-        * @param $u User object.
-        * @param $autocreate boolean -- true if this is an autocreation via auth plugin
-        * @return User object.
-        * @private
-        */
-       function initUser( $u, $autocreate ) {
-               global $wgAuth;
-
-               $u->addToDatabase();
-
-               if ( $wgAuth->allowPasswordChange() ) {
-                       $u->setPassword( $this->mPassword );
-               }
-
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-               $u->setToken();
-
-               $wgAuth->initUser( $u, $autocreate );
-
-               $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
-               $u->saveSettings();
-
-               # Update user count
-               $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
-               $ssUpdate->doUpdate();
-
-               return $u;
-       }
-
-       /**
-        * Internally authenticate the login request.
-        *
-        * This may create a local account as a side effect if the
-        * authentication plugin allows transparent local account
-        * creation.
-        *
-        * @public
-        */
-       function authenticateUserData() {
-               global $wgUser, $wgAuth;
-               if ( '' == $this->mName ) {
-                       return self::NO_NAME;
-               }
-
-               // Load $wgUser now, and check to see if we're logging in as the same name. 
-               // This is necessary because loading $wgUser (say by calling getName()) calls
-               // the UserLoadFromSession hook, which potentially creates the user in the 
-               // database. Until we load $wgUser, checking for user existence using 
-               // User::newFromName($name)->getId() below will effectively be using stale data.
-               if ( $wgUser->getName() === $this->mName ) {
-                       wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
-                       return self::SUCCESS;
-               }
-               $u = User::newFromName( $this->mName );
-               if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
-                       return self::ILLEGAL;
-               }
-
-               $isAutoCreated = false;
-               if ( 0 == $u->getID() ) {
-                       $status = $this->attemptAutoCreate( $u );
-                       if ( $status !== self::SUCCESS ) {
-                               return $status;
-                       } else {
-                               $isAutoCreated = true;
-                       }
-               } else {
-                       $u->load();
-               }
-
-               // Give general extensions, such as a captcha, a chance to abort logins
-               $abort = self::ABORTED;
-               if( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort ) ) ) {
-                       return $abort;
-               }
-
-               if (!$u->checkPassword( $this->mPassword )) {
-                       if( $u->checkTemporaryPassword( $this->mPassword ) ) {
-                               // The e-mailed temporary password should not be used
-                               // for actual logins; that's a very sloppy habit,
-                               // and insecure if an attacker has a few seconds to
-                               // click "search" on someone's open mail reader.
-                               //
-                               // Allow it to be used only to reset the password
-                               // a single time to a new value, which won't be in
-                               // the user's e-mail archives.
-                               //
-                               // For backwards compatibility, we'll still recognize
-                               // it at the login form to minimize surprises for
-                               // people who have been logging in with a temporary
-                               // password for some time.
-                               //
-                               // As a side-effect, we can authenticate the user's
-                               // e-mail address if it's not already done, since
-                               // the temporary password was sent via e-mail.
-                               //
-                               if( !$u->isEmailConfirmed() ) {
-                                       $u->confirmEmail();
-                                       $u->saveSettings();
-                               }
-
-                               // At this point we just return an appropriate code
-                               // indicating that the UI should show a password
-                               // reset form; bot interfaces etc will probably just
-                               // fail cleanly here.
-                               //
-                               $retval = self::RESET_PASS;
-                       } else {
-                               $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
-                       }
-               } else {
-                       $wgAuth->updateUser( $u );
-                       $wgUser = $u;
-
-                       if ( $isAutoCreated ) {
-                               // Must be run after $wgUser is set, for correct new user log
-                               wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
-                       }
-
-                       $retval = self::SUCCESS;
-               }
-               wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
-               return $retval;
-       }
-
-       /**
-        * Attempt to automatically create a user on login.
-        * Only succeeds if there is an external authentication method which allows it.
-        * @return integer Status code
-        */
-       function attemptAutoCreate( $user ) {
-               global $wgAuth, $wgUser;
-               /**
-                * If the external authentication plugin allows it,
-                * automatically create a new account for users that
-                * are externally defined but have not yet logged in.
-                */
-               if ( !$wgAuth->autoCreate() ) {
-                       return self::NOT_EXISTS;
-               }
-               if ( !$wgAuth->userExists( $user->getName() ) ) {
-                       wfDebug( __METHOD__.": user does not exist\n" );
-                       return self::NOT_EXISTS;
-               }
-               if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
-                       wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
-                       return self::WRONG_PLUGIN_PASS;
-               }
-               if ( $wgUser->isBlockedFromCreateAccount() ) {
-                       wfDebug( __METHOD__.": user is blocked from account creation\n" );
-                       return self::CREATE_BLOCKED;
-               }
-
-               wfDebug( __METHOD__.": creating account\n" );
-               $user = $this->initUser( $user, true );
-               return self::SUCCESS;
-       }
-
-       function processLogin() {
-               global $wgUser, $wgAuth;
-
-               switch ($this->authenticateUserData())
-               {
-                       case self::SUCCESS:
-                               # We've verified now, update the real record
-                               if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
-                                       $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
-                                       $wgUser->saveSettings();
-                               } else {
-                                       $wgUser->invalidateCache();
-                               }
-                               $wgUser->setCookies();
-
-                               if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
-                                       /* Replace the language object to provide user interface in correct
-                                        * language immediately on this first page load.
-                                        */
-                                       global $wgLang, $wgRequest;
-                                       $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
-                                       $wgLang = Language::factory( $code );
-                                       return $this->successfulLogin( wfMsg( 'loginsuccess', $wgUser->getName() ) );
-                               } else {
-                                       return $this->cookieRedirectCheck( 'login' );
-                               }
-                               break;
-
-                       case self::NO_NAME:
-                       case self::ILLEGAL:
-                               $this->mainLoginForm( wfMsg( 'noname' ) );
-                               break;
-                       case self::WRONG_PLUGIN_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               break;
-                       case self::NOT_EXISTS:
-                               if( $wgUser->isAllowed( 'createaccount' ) ){
-                                       $this->mainLoginForm( wfMsg( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
-                               } else {
-                                       $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
-                               }
-                               break;
-                       case self::WRONG_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               break;
-                       case self::EMPTY_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
-                               break;
-                       case self::RESET_PASS:
-                               $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
-                               break;
-                       case self::CREATE_BLOCKED:
-                               $this->userBlockedMessage();
-                               break;
-                       default:
-                               throw new MWException( "Unhandled case value" );
-               }
-       }
-
-       function resetLoginForm( $error ) {
-               global $wgOut;
-               $wgOut->addWikiText( "<div class=\"errorbox\">$error</div>" );
-               $reset = new PasswordResetForm( $this->mName, $this->mPassword );
-               $reset->execute( null );
-       }
-
-       /**
-        * @private
-        */
-       function mailPassword() {
-               global $wgUser, $wgOut, $wgAuth;
-
-               if( !$wgAuth->allowPasswordChange() ) {
-                       $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
-                       return;
-               }
-
-               # Check against blocked IPs
-               # fixme -- should we not?
-               if( $wgUser->isBlocked() ) {
-                       $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
-                       return;
-               }
-
-               # Check against the rate limiter
-               if( $wgUser->pingLimiter( 'mailpassword' ) ) {
-                       $wgOut->rateLimited();
-                       return;
-               }
-
-               if ( '' == $this->mName ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return;
-               }
-               $u = User::newFromName( $this->mName );
-               if( is_null( $u ) ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return;
-               }
-               if ( 0 == $u->getID() ) {
-                       $this->mainLoginForm( wfMsg( 'nosuchuser', $u->getName() ) );
-                       return;
-               }
-
-               # Check against password throttle
-               if ( $u->isPasswordReminderThrottled() ) {
-                       global $wgPasswordReminderResendTime;
-                       # Round the time in hours to 3 d.p., in case someone is specifying minutes or seconds.
-                       $this->mainLoginForm( wfMsgExt( 'throttled-mailpassword', array( 'parsemag' ),
-                               round( $wgPasswordReminderResendTime, 3 ) ) );
-                       return;
-               }
-
-               $result = $this->mailPasswordInternal( $u, true, 'passwordremindertitle', 'passwordremindertext' );
-               if( WikiError::isError( $result ) ) {
-                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
-               } else {
-                       $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' );
-               }
-       }
-
-
-       /**
-        * @param object user
-        * @param bool throttle
-        * @param string message name of email title
-        * @param string message name of email text
-        * @return mixed true on success, WikiError on failure
-        * @private
-        */
-       function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
-               global $wgCookiePath, $wgCookieDomain, $wgCookiePrefix, $wgCookieSecure;
-               global $wgServer, $wgScript;
-
-               if ( '' == $u->getEmail() ) {
-                       return new WikiError( wfMsg( 'noemail', $u->getName() ) );
-               }
-
-               $np = $u->randomPassword();
-               $u->setNewpassword( $np, $throttle );
-               $u->saveSettings();
-
-               $ip = wfGetIP();
-               if ( '' == $ip ) { $ip = '(Unknown)'; }
-
-               $m = wfMsg( $emailText, $ip, $u->getName(), $np, $wgServer . $wgScript );
-               $result = $u->sendMail( wfMsg( $emailTitle ), $m );
-
-               return $result;
-       }
-
-
-       /**
-        * @param string $msg Message that will be shown on success
-        * @param bool $auto Toggle auto-redirect to main page; default true
-        * @private
-        */
-       function successfulLogin( $msg, $auto = true ) {
-               global $wgUser;
-               global $wgOut;
-
-               # Run any hooks; ignore results
-
-               $injected_html = '';
-               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
-
-               $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-               $wgOut->addWikiText( $msg );
-               $wgOut->addHtml( $injected_html );
-               if ( !empty( $this->mReturnTo ) ) {
-                       $wgOut->returnToMain( $auto, $this->mReturnTo );
-               } else {
-                       $wgOut->returnToMain( $auto );
-               }
-       }
-
-       /** */
-       function userNotPrivilegedMessage($errors) {
-               global $wgOut;
-
-               $wgOut->setPageTitle( wfMsg( 'permissionserrors' ) );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               $wgOut->addWikitext( $wgOut->formatPermissionsErrorMessage( $errors, 'createaccount' ) );
-               // Stuff that might want to be added at the end. For example, instructions if blocked.
-               $wgOut->addWikiMsg( 'cantcreateaccount-nonblock-text' );
-
-               $wgOut->returnToMain( false );
-       }
-
-       /** */
-       function userBlockedMessage() {
-               global $wgOut, $wgUser;
-
-               # Let's be nice about this, it's likely that this feature will be used
-               # for blocking large numbers of innocent people, e.g. range blocks on
-               # schools. Don't blame it on the user. There's a small chance that it
-               # really is the user's fault, i.e. the username is blocked and they
-               # haven't bothered to log out before trying to create an account to
-               # evade it, but we'll leave that to their guilty conscience to figure
-               # out.
-
-               $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               $ip = wfGetIP();
-               $blocker = User::whoIs( $wgUser->mBlock->mBy );
-               $block_reason = $wgUser->mBlock->mReason;
-
-               if ( strval( $block_reason ) === '' ) {
-                       $block_reason = wfMsg( 'blockednoreason' );
-               }
-               $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
-               $wgOut->returnToMain( false );
-       }
-
-       /**
-        * @private
-        */
-       function mainLoginForm( $msg, $msgtype = 'error' ) {
-               global $wgUser, $wgOut, $wgAllowRealName, $wgEnableEmail;
-               global $wgCookiePrefix, $wgAuth, $wgLoginLanguageSelector;
-               global $wgAuth, $wgEmailConfirmToEdit;
-               
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-               
-               if ( $this->mType == 'signup' ) {
-                       // Block signup here if in readonly. Keeps user from 
-                       // going through the process (filling out data, etc) 
-                       // and being informed later.
-                       if ( wfReadOnly() ) {
-                               $wgOut->readOnlyPage();
-                               return;
-                       } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
-                               $this->userBlockedMessage();
-                               return;
-                       } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
-                               $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
-                               return;
-                       }
-               }
-
-               if ( '' == $this->mName ) {
-                       if ( $wgUser->isLoggedIn() ) {
-                               $this->mName = $wgUser->getName();
-                       } else {
-                               $this->mName = isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ? $_COOKIE[$wgCookiePrefix.'UserName'] : null;
-                       }
-               }
-
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-
-               if ( $this->mType == 'signup' ) {
-                       $template = new UsercreateTemplate();
-                       $q = 'action=submitlogin&type=signup';
-                       $linkq = 'type=login';
-                       $linkmsg = 'gotaccount';
-               } else {
-                       $template = new UserloginTemplate();
-                       $q = 'action=submitlogin&type=login';
-                       $linkq = 'type=signup';
-                       $linkmsg = 'nologin';
-               }
-
-               if ( !empty( $this->mReturnTo ) ) {
-                       $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
-                       $q .= $returnto;
-                       $linkq .= $returnto;
-               }
-
-               # Pass any language selection on to the mode switch link
-               if( $wgLoginLanguageSelector && $this->mLanguage )
-                       $linkq .= '&uselang=' . $this->mLanguage;
-
-               $link = '<a href="' . htmlspecialchars ( $titleObj->getLocalUrl( $linkq ) ) . '">';
-               $link .= wfMsgHtml( $linkmsg . 'link' ); # Calling either 'gotaccountlink' or 'nologinlink'
-               $link .= '</a>';
-
-               # Don't show a "create account" link if the user can't
-               if( $this->showCreateOrLoginLink( $wgUser ) )
-                       $template->set( 'link', wfMsgHtml( $linkmsg, $link ) );
-               else
-                       $template->set( 'link', '' );
-
-               $template->set( 'header', '' );
-               $template->set( 'name', $this->mName );
-               $template->set( 'password', $this->mPassword );
-               $template->set( 'retype', $this->mRetype );
-               $template->set( 'email', $this->mEmail );
-               $template->set( 'realname', $this->mRealName );
-               $template->set( 'domain', $this->mDomain );
-
-               $template->set( 'action', $titleObj->getLocalUrl( $q ) );
-               $template->set( 'message', $msg );
-               $template->set( 'messagetype', $msgtype );
-               $template->set( 'createemail', $wgEnableEmail && $wgUser->isLoggedIn() );
-               $template->set( 'userealname', $wgAllowRealName );
-               $template->set( 'useemail', $wgEnableEmail );
-               $template->set( 'emailrequired', $wgEmailConfirmToEdit );
-               $template->set( 'canreset', $wgAuth->allowPasswordChange() );
-               $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember  );
-
-               # Prepare language selection links as needed
-               if( $wgLoginLanguageSelector ) {
-                       $template->set( 'languages', $this->makeLanguageSelector() );
-                       if( $this->mLanguage )
-                               $template->set( 'uselang', $this->mLanguage );
-               }
-
-               // Give authentication and captcha plugins a chance to modify the form
-               $wgAuth->modifyUITemplate( $template );
-               if ( $this->mType == 'signup' ) {
-                       wfRunHooks( 'UserCreateForm', array( &$template ) );
-               } else {
-                       wfRunHooks( 'UserLoginForm', array( &$template ) );
-               }
-
-               $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
-               $wgOut->setRobotpolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-               $wgOut->disallowUserJs();  // just in case...
-               $wgOut->addTemplate( $template );
-       }
-
-       /**
-        * @private
-        */
-       function showCreateOrLoginLink( &$user ) {
-               if( $this->mType == 'signup' ) {
-                       return( true );
-               } elseif( $user->isAllowed( 'createaccount' ) ) {
-                       return( true );
-               } else {
-                       return( false );
-               }
-       }
-
-       /**
-        * Check if a session cookie is present.
-        *
-        * This will not pick up a cookie set during _this_ request, but is
-        * meant to ensure that the client is returning the cookie which was
-        * set on a previous pass through the system.
-        *
-        * @private
-        */
-       function hasSessionCookie() {
-               global $wgDisableCookieCheck, $wgRequest;
-               return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
-       }
-
-       /**
-        * @private
-        */
-       function cookieRedirectCheck( $type ) {
-               global $wgOut;
-
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-               $check = $titleObj->getFullURL( 'wpCookieCheck='.$type );
-
-               return $wgOut->redirect( $check );
-       }
-
-       /**
-        * @private
-        */
-       function onCookieRedirectCheck( $type ) {
-               global $wgUser;
-
-               if ( !$this->hasSessionCookie() ) {
-                       if ( $type == 'new' ) {
-                               return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
-                       } else if ( $type == 'login' ) {
-                               return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
-                       } else {
-                               # shouldn't happen
-                               return $this->mainLoginForm( wfMsg( 'error' ) );
-                       }
-               } else {
-                       return $this->successfulLogin( wfMsgExt( 'loginsuccess', array( 'parseinline' ), $wgUser->getName() ) );
-               }
-       }
-
-       /**
-        * @private
-        */
-       function throttleHit( $limit ) {
-               global $wgOut;
-
-               $wgOut->addWikiMsg( 'acct_creation_throttle_hit', $limit );
-       }
-
-       /**
-        * Produce a bar of links which allow the user to select another language
-        * during login/registration but retain "returnto"
-        *
-        * @return string
-        */
-       function makeLanguageSelector() {
-               $msg = wfMsgForContent( 'loginlanguagelinks' );
-               if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
-                       $langs = explode( "\n", $msg );
-                       $links = array();
-                       foreach( $langs as $lang ) {
-                               $lang = trim( $lang, '* ' );
-                               $parts = explode( '|', $lang );
-                               if (count($parts) >= 2) {
-                                       $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
-                               }
-                       }
-                       return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', implode( ' | ', $links ) ) : '';
-               } else {
-                       return '';
-               }
-       }
-
-       /**
-        * Create a language selector link for a particular language
-        * Links back to this page preserving type and returnto
-        *
-        * @param $text Link text
-        * @param $lang Language code
-        */
-       function makeLanguageSelectorLink( $text, $lang ) {
-               global $wgUser;
-               $self = SpecialPage::getTitleFor( 'Userlogin' );
-               $attr[] = 'uselang=' . $lang;
-               if( $this->mType == 'signup' )
-                       $attr[] = 'type=signup';
-               if( $this->mReturnTo )
-                       $attr[] = 'returnto=' . $this->mReturnTo;
-               $skin = $wgUser->getSkin();
-               return $skin->makeKnownLinkObj( $self, htmlspecialchars( $text ), implode( '&', $attr ) );
-       }
-}
diff --git a/includes/specials/Userlogout.php b/includes/specials/Userlogout.php
deleted file mode 100644 (file)
index 137eadb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * constructor
- */
-function wfSpecialUserlogout() {
-       global $wgUser, $wgOut;
-
-       $oldName = $wgUser->getName();
-       $wgUser->logout();
-       $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
-       // Hook.
-       $injected_html = '';
-       wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName) );
-
-       $wgOut->addHTML( wfMsgExt( 'logouttext', array( 'parse' ) ) . $injected_html );
-       $wgOut->returnToMain();
-}
diff --git a/includes/specials/Userrights.php b/includes/specials/Userrights.php
deleted file mode 100644 (file)
index bf4d440..0000000
+++ /dev/null
@@ -1,576 +0,0 @@
-<?php
-/**
- * Special page to allow managing user group membership
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A class to manage user levels rights.
- * @ingroup SpecialPage
- */
-class UserrightsPage extends SpecialPage {
-       # The target of the local right-adjuster's interest.  Can be gotten from
-       # either a GET parameter or a subpage-style parameter, so have a member
-       # variable for it.
-       protected $mTarget;
-       protected $isself = false;
-
-       public function __construct() {
-               parent::__construct( 'Userrights' );
-       }
-
-       public function isRestricted() {
-               return true;
-       }
-
-       public function userCanExecute( $user ) {
-               $available = $this->changeableGroups();
-               return !empty( $available['add'] )
-                       or !empty( $available['remove'] )
-                       or ($this->isself and
-                               (!empty( $available['add-self'] )
-                                or !empty( $available['remove-self'] )));
-       }
-
-       /**
-        * Manage forms to be shown according to posted data.
-        * Depending on the submit button used, call a form or a save function.
-        *
-        * @param $par Mixed: string if any subpage provided, else null
-        */
-       function execute( $par ) {
-               // If the visitor doesn't have permissions to assign or remove
-               // any groups, it's a bit silly to give them the user search prompt.
-               global $wgUser, $wgRequest;
-
-               if( $par ) {
-                       $this->mTarget = $par;
-               } else {
-                       $this->mTarget = $wgRequest->getVal( 'user' );
-               }
-
-               if (!$this->mTarget) {
-                       /*
-                        * If the user specified no target, and they can only
-                        * edit their own groups, automatically set them as the
-                        * target.
-                        */
-                       $available = $this->changeableGroups();
-                       if (empty($available['add']) && empty($available['remove']))
-                               $this->mTarget = $wgUser->getName();
-               }
-
-               if ($this->mTarget == $wgUser->getName())
-                       $this->isself = true;
-
-               if( !$this->userCanExecute( $wgUser ) ) {
-                       // fixme... there may be intermediate groups we can mention.
-                       global $wgOut;
-                       $wgOut->showPermissionsErrorPage( array(
-                               $wgUser->isAnon()
-                                       ? 'userrights-nologin'
-                                       : 'userrights-notallowed' ) );
-                       return;
-               }
-
-               if ( wfReadOnly() ) {
-                       global $wgOut;
-                       $wgOut->readOnlyPage();
-                       return;
-               }
-
-               $this->outputHeader();
-
-               $this->setHeaders();
-
-               // show the general form
-               $this->switchForm();
-
-               if( $wgRequest->wasPosted() ) {
-                       // save settings
-                       if( $wgRequest->getCheck( 'saveusergroups' ) ) {
-                               $reason = $wgRequest->getVal( 'user-reason' );
-                               if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $this->mTarget ) ) {
-                                       $this->saveUserGroups(
-                                               $this->mTarget,
-                                               $reason
-                                       );
-                               }
-                       }
-               }
-
-               // show some more forms
-               if( $this->mTarget ) {
-                       $this->editUserGroupsForm( $this->mTarget );
-               }
-       }
-
-       /**
-        * Save user groups changes in the database.
-        * Data comes from the editUserGroupsForm() form function
-        *
-        * @param $username String: username to apply changes to.
-        * @param $reason String: reason for group change
-        * @return null
-        */
-       function saveUserGroups( $username, $reason = '') {
-               global $wgRequest, $wgUser, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
-
-               $user = $this->fetchUser( $username );
-               if( !$user ) {
-                       return;
-               }
-
-               $allgroups = $this->getAllGroups();
-               $addgroup = array();
-               $removegroup = array();
-
-               // This could possibly create a highly unlikely race condition if permissions are changed between
-               //  when the form is loaded and when the form is saved. Ignoring it for the moment.
-               foreach ($allgroups as $group) {
-                       // We'll tell it to remove all unchecked groups, and add all checked groups.
-                       // Later on, this gets filtered for what can actually be removed
-                       if ($wgRequest->getCheck( "wpGroup-$group" )) {
-                               $addgroup[] = $group;
-                       } else {
-                               $removegroup[] = $group;
-                       }
-               }
-
-               // Validate input set...
-               $changeable = $this->changeableGroups();
-               if ($wgUser->getId() != 0 && $wgUser->getId() == $user->getId()) {
-                       $addable = array_merge($changeable['add'], $wgGroupsAddToSelf);
-                       $removable = array_merge($changeable['remove'], $wgGroupsRemoveFromSelf);
-               } else {
-                       $addable = $changeable['add'];
-                       $removable = $changeable['remove'];
-               }
-
-               $removegroup = array_unique(
-                       array_intersect( (array)$removegroup, $removable ) );
-               $addgroup = array_unique(
-                       array_intersect( (array)$addgroup, $addable ) );
-
-               $oldGroups = $user->getGroups();
-               $newGroups = $oldGroups;
-               // remove then add groups
-               if( $removegroup ) {
-                       $newGroups = array_diff($newGroups, $removegroup);
-                       foreach( $removegroup as $group ) {
-                               $user->removeGroup( $group );
-                       }
-               }
-               if( $addgroup ) {
-                       $newGroups = array_merge($newGroups, $addgroup);
-                       foreach( $addgroup as $group ) {
-                               $user->addGroup( $group );
-                       }
-               }
-               $newGroups = array_unique( $newGroups );
-
-               // Ensure that caches are cleared
-               $user->invalidateCache();
-
-               wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) );
-               wfDebug( 'newGroups: ' . print_r( $newGroups, true ) );
-               if( $user instanceof User ) {
-                       // hmmm
-                       wfRunHooks( 'UserRights', array( &$user, $addgroup, $removegroup ) );
-               }
-
-               if( $newGroups != $oldGroups ) {
-                       $this->addLogEntry( $user, $oldGroups, $newGroups );
-               }
-       }
-       
-       /**
-        * Add a rights log entry for an action.
-        */
-       function addLogEntry( $user, $oldGroups, $newGroups ) {
-               global $wgRequest;
-               $log = new LogPage( 'rights' );
-
-               $log->addEntry( 'rights',
-                       $user->getUserPage(),
-                       $wgRequest->getText( 'user-reason' ),
-                       array(
-                               $this->makeGroupNameList( $oldGroups ),
-                               $this->makeGroupNameList( $newGroups )
-                       )
-               );
-       }
-
-       /**
-        * Edit user groups membership
-        * @param $username String: name of the user.
-        */
-       function editUserGroupsForm( $username ) {
-               global $wgOut;
-
-               $user = $this->fetchUser( $username );
-               if( !$user ) {
-                       return;
-               }
-
-               $groups = $user->getGroups();
-
-               $this->showEditUserGroupsForm( $user, $groups );
-
-               // This isn't really ideal logging behavior, but let's not hide the
-               // interwiki logs if we're using them as is.
-               $this->showLogFragment( $user, $wgOut );
-       }
-
-       /**
-        * Normalize the input username, which may be local or remote, and
-        * return a user (or proxy) object for manipulating it.
-        *
-        * Side effects: error output for invalid access
-        * @return mixed User, UserRightsProxy, or null
-        */
-       function fetchUser( $username ) {
-               global $wgOut, $wgUser;
-
-               $parts = explode( '@', $username );
-               if( count( $parts ) < 2 ) {
-                       $name = trim( $username );
-                       $database = '';
-               } else {
-                       list( $name, $database ) = array_map( 'trim', $parts );
-
-                       if( !$wgUser->isAllowed( 'userrights-interwiki' ) ) {
-                               $wgOut->addWikiMsg( 'userrights-no-interwiki' );
-                               return null;
-                       }
-                       if( !UserRightsProxy::validDatabase( $database ) ) {
-                               $wgOut->addWikiMsg( 'userrights-nodatabase', $database );
-                               return null;
-                       }
-               }
-
-               if( $name == '' ) {
-                       $wgOut->addWikiMsg( 'nouserspecified' );
-                       return false;
-               }
-
-               if( $name{0} == '#' ) {
-                       // Numeric ID can be specified...
-                       // We'll do a lookup for the name internally.
-                       $id = intval( substr( $name, 1 ) );
-
-                       if( $database == '' ) {
-                               $name = User::whoIs( $id );
-                       } else {
-                               $name = UserRightsProxy::whoIs( $database, $id );
-                       }
-
-                       if( !$name ) {
-                               $wgOut->addWikiMsg( 'noname' );
-                               return null;
-                       }
-               }
-
-               if( $database == '' ) {
-                       $user = User::newFromName( $name );
-               } else {
-                       $user = UserRightsProxy::newFromName( $database, $name );
-               }
-
-               if( !$user || $user->isAnon() ) {
-                       $wgOut->addWikiMsg( 'nosuchusershort', $username );
-                       return null;
-               }
-
-               return $user;
-       }
-
-       function makeGroupNameList( $ids ) {
-               return implode( ', ', $ids );
-       }
-
-       /**
-        * Output a form to allow searching for a user
-        */
-       function switchForm() {
-               global $wgOut, $wgScript;
-               $wgOut->addHTML(
-                       Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'name' => 'uluser', 'id' => 'mw-userrights-form1' ) ) .
-                       Xml::hidden( 'title',  $this->getTitle()->getPrefixedText() ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', array(), wfMsg( 'userrights-lookup-user' ) ) .
-                       Xml::inputLabel( wfMsg( 'userrights-user-editname' ), 'user', 'username', 30, $this->mTarget ) . ' ' .
-                       Xml::submitButton( wfMsg( 'editusergroup' ) ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-       }
-
-       /**
-        * Go through used and available groups and return the ones that this
-        * form will be able to manipulate based on the current user's system
-        * permissions.
-        *
-        * @param $groups Array: list of groups the given user is in
-        * @return Array:  Tuple of addable, then removable groups
-        */
-       protected function splitGroups( $groups ) {
-               global $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
-               list($addable, $removable) = array_values( $this->changeableGroups() );
-
-               $removable = array_intersect(
-                               array_merge($this->isself ? $wgGroupsRemoveFromSelf : array(), $removable),
-                               $groups ); // Can't remove groups the user doesn't have
-               $addable   = array_diff(
-                               array_merge($this->isself ? $wgGroupsAddToSelf : array(), $addable),
-                               $groups ); // Can't add groups the user does have
-
-               return array( $addable, $removable );
-       }
-
-       /**
-        * Show the form to edit group memberships.
-        *
-        * @param $user      User or UserRightsProxy you're editing
-        * @param $groups    Array:  Array of groups the user is in
-        */
-       protected function showEditUserGroupsForm( $user, $groups ) {
-               global $wgOut, $wgUser;
-
-               list( $addable, $removable ) = $this->splitGroups( $groups );
-
-               $list = array();
-               foreach( $user->getGroups() as $group )
-                       $list[] = self::buildGroupLink( $group );
-
-               $grouplist = '';
-               if( count( $list ) > 0 ) {
-                       $grouplist = Xml::tags( 'p', null, wfMsgHtml( 'userrights-groupsmember' ) . ' ' . implode( ', ', $list ) );
-               }
-               $wgOut->addHTML(
-                       Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL(), 'name' => 'editGroup', 'id' => 'mw-userrights-form2' ) ) .
-                       Xml::hidden( 'user', $this->mTarget ) .
-                       Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->mTarget ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) .
-                       wfMsgExt( 'editinguser', array( 'parse' ), wfEscapeWikiText( $user->getName() ) ) .
-                       wfMsgExt( 'userrights-groups-help', array( 'parse' ) ) .
-                       $grouplist .
-                       Xml::tags( 'p', null, $this->groupCheckboxes( $groups ) ) .
-                       Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-userrights-table-outer' ) ) .
-                               "<tr>
-                                       <td class='mw-label'>" .
-                                               Xml::label( wfMsg( 'userrights-reason' ), 'wpReason' ) .
-                                       "</td>
-                                       <td class='mw-input'>" .
-                                               Xml::input( 'user-reason', 60, false, array( 'id' => 'wpReason', 'maxlength' => 255 ) ) .
-                                       "</td>
-                               </tr>
-                               <tr>
-                                       <td></td>
-                                       <td class='mw-submit'>" .
-                                               Xml::submitButton( wfMsg( 'saveusergroups' ), array( 'name' => 'saveusergroups' ) ) .
-                                       "</td>
-                               </tr>" .
-                       Xml::closeElement( 'table' ) . "\n" .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' ) . "\n"
-               );
-       }
-
-       /**
-        * Format a link to a group description page
-        *
-        * @param $group string
-        * @return string
-        */
-       private static function buildGroupLink( $group ) {
-               static $cache = array();
-               if( !isset( $cache[$group] ) )
-                       $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupName( $group ) );
-               return $cache[$group];
-       }
-       
-       /**
-        * Returns an array of all groups that may be edited
-        * @return array Array of groups that may be edited.
-        */
-        protected static function getAllGroups() {
-               return User::getAllGroups();
-        }
-
-       /**
-        * Adds a table with checkboxes where you can select what groups to add/remove
-        *
-        * @param $usergroups Array: groups the user belongs to
-        * @return string XHTML table element with checkboxes
-        */
-       private function groupCheckboxes( $usergroups ) {
-               $allgroups = $this->getAllGroups();
-               $ret = '';
-
-               $column = 1;
-               $settable_col = '';
-               $unsettable_col = '';
-
-               foreach ($allgroups as $group) {
-                       $set = in_array( $group, $usergroups );
-                       # Should the checkbox be disabled?
-                       $disabled = !(
-                               ( $set && $this->canRemove( $group ) ) ||
-                               ( !$set && $this->canAdd( $group ) ) );
-                       # Do we need to point out that this action is irreversible?
-                       $irreversible = !$disabled && (
-                               ($set && !$this->canAdd( $group )) ||
-                               (!$set && !$this->canRemove( $group ) ) );
-
-                       $attr = $disabled ? array( 'disabled' => 'disabled' ) : array();
-                       $text = $irreversible
-                               ? wfMsgHtml( 'userrights-irreversible-marker', User::getGroupMember( $group ) )
-                               : User::getGroupMember( $group );
-                       $checkbox = Xml::checkLabel( $text, "wpGroup-$group",
-                               "wpGroup-$group", $set, $attr );
-                       $checkbox = $disabled ? Xml::tags( 'span', array( 'class' => 'mw-userrights-disabled' ), $checkbox ) : $checkbox;
-
-                       if ($disabled) {
-                               $unsettable_col .= "$checkbox<br />\n";
-                       } else {
-                               $settable_col .= "$checkbox<br />\n";
-                       }
-               }
-
-               if ($column) {
-                       $ret .= Xml::openElement( 'table', array( 'border' => '0', 'class' => 'mw-userrights-groups' ) ) .
-                               "<tr>
-";
-                       if( $settable_col !== '' ) {
-                               $ret .= xml::element( 'th', null, wfMsg( 'userrights-changeable-col' ) );
-                       }
-                       if( $unsettable_col !== '' ) {
-                               $ret .= xml::element( 'th', null, wfMsg( 'userrights-unchangeable-col' ) );
-                       }
-                       $ret.= "</tr>
-                               <tr>
-";
-                       if( $settable_col !== '' ) {
-                               $ret .=
-"                                      <td style='vertical-align:top;'>
-                                               $settable_col
-                                       </td>
-";
-                       }
-                       if( $unsettable_col !== '' ) {
-                               $ret .=
-"                                      <td style='vertical-align:top;'>
-                                               $unsettable_col
-                                       </td>
-";
-                       }
-                       $ret .= Xml::closeElement( 'tr' ) . Xml::closeElement( 'table' );
-               }
-
-               return $ret;
-       }
-
-       /**
-        * @param  $group String: the name of the group to check
-        * @return bool Can we remove the group?
-        */
-       private function canRemove( $group ) {
-               // $this->changeableGroups()['remove'] doesn't work, of course. Thanks,
-               // PHP.
-               $groups = $this->changeableGroups();
-               return in_array( $group, $groups['remove'] ) || ($this->isself && in_array( $group, $groups['remove-self'] ));
-       }
-
-       /**
-        * @param $group string: the name of the group to check
-        * @return bool Can we add the group?
-        */
-       private function canAdd( $group ) {
-               $groups = $this->changeableGroups();
-               return in_array( $group, $groups['add'] ) || ($this->isself && in_array( $group, $groups['add-self'] ));
-       }
-
-       /**
-        * Returns an array of the groups that the user can add/remove.
-        *
-        * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) )
-        */
-       function changeableGroups() {
-               global $wgUser, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
-
-               if( $wgUser->isAllowed( 'userrights' ) ) {
-                       // This group gives the right to modify everything (reverse-
-                       // compatibility with old "userrights lets you change
-                       // everything")
-                       // Using array_merge to make the groups reindexed
-                       $all = array_merge( User::getAllGroups() );
-                       return array(
-                               'add' => $all,
-                               'remove' => $all,
-                               'add-self' => array(),
-                               'remove-self' => array()
-                       );
-               }
-
-               // Okay, it's not so simple, we will have to go through the arrays
-               $groups = array(
-                               'add' => array(),
-                               'remove' => array(),
-                               'add-self' => $wgGroupsAddToSelf,
-                               'remove-self' => $wgGroupsRemoveFromSelf);
-               $addergroups = $wgUser->getEffectiveGroups();
-
-               foreach ($addergroups as $addergroup) {
-                       $groups = array_merge_recursive(
-                               $groups, $this->changeableByGroup($addergroup)
-                       );
-                       $groups['add']    = array_unique( $groups['add'] );
-                       $groups['remove'] = array_unique( $groups['remove'] );
-               }
-               return $groups;
-       }
-
-       /**
-        * Returns an array of the groups that a particular group can add/remove.
-        *
-        * @param $group String: the group to check for whether it can add/remove
-        * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) )
-        */
-       private function changeableByGroup( $group ) {
-               global $wgAddGroups, $wgRemoveGroups;
-
-               $groups = array( 'add' => array(), 'remove' => array() );
-               if( empty($wgAddGroups[$group]) ) {
-                       // Don't add anything to $groups
-               } elseif( $wgAddGroups[$group] === true ) {
-                       // You get everything
-                       $groups['add'] = User::getAllGroups();
-               } elseif( is_array($wgAddGroups[$group]) ) {
-                       $groups['add'] = $wgAddGroups[$group];
-               }
-
-               // Same thing for remove
-               if( empty($wgRemoveGroups[$group]) ) {
-               } elseif($wgRemoveGroups[$group] === true ) {
-                       $groups['remove'] = User::getAllGroups();
-               } elseif( is_array($wgRemoveGroups[$group]) ) {
-                       $groups['remove'] = $wgRemoveGroups[$group];
-               }
-               return $groups;
-       }
-
-       /**
-        * Show a rights log fragment for the specified user
-        *
-        * @param $user User to show log for
-        * @param $output OutputPage to use
-        */
-       protected function showLogFragment( $user, $output ) {
-               $output->addHtml( Xml::element( 'h2', null, LogPage::logName( 'rights' ) . "\n" ) );
-               LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage()->getPrefixedText() );
-       }
-}
diff --git a/includes/specials/Version.php b/includes/specials/Version.php
deleted file mode 100644 (file)
index 8771fa1..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-<?php
-/**#@+
- * Give information about the version of MediaWiki, PHP, the DB and extensions
- *
- * @file
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-/**
- * constructor
- */
-function wfSpecialVersion() {
-       $version = new SpecialVersion;
-       $version->execute();
-}
-
-/**
- * @ingroup SpecialPage
- */
-class SpecialVersion {
-       private $firstExtOpened = true;
-
-       /**
-        * main()
-        */
-       function execute() {
-               global $wgOut, $wgMessageCache, $wgSpecialVersionShowHooks;
-               $wgMessageCache->loadAllMessages();
-
-               $wgOut->addHTML( '<div dir="ltr">' );
-               $text = 
-                       $this->MediaWikiCredits() .
-                       $this->softwareInformation() .
-                       $this->extensionCredits();
-               if  ( $wgSpecialVersionShowHooks ) {
-                       $text .= $this->wgHooks();
-               }
-               $wgOut->addWikiText( $text );
-               $wgOut->addHTML( $this->IPInfo() );
-               $wgOut->addHTML( '</div>' );
-       }
-
-       /**#@+
-        * @private
-        */
-
-       /**
-        * @return wiki text showing the license information
-        */
-       static function MediaWikiCredits() {
-               $ret = Xml::element( 'h2', array( 'id' => 'mw-version-license' ), wfMsg( 'version-license' ) ) .
-               "__NOTOC__
-               This wiki is powered by '''[http://www.mediawiki.org/ MediaWiki]''',
-               copyright (C) 2001-2008 Magnus Manske, Brion Vibber, Lee Daniel Crocker,
-               Tim Starling, Erik Möller, Gabriel Wicke, Ã†var Arnfjörð Bjarmason,
-               Niklas Laxström, Domas Mituzas, Rob Church, Yuri Astrakhan and others.
-
-               MediaWiki is free software; you can redistribute it and/or modify
-               it under the terms of the GNU General Public License as published by
-               the Free Software Foundation; either version 2 of the License, or
-               (at your option) any later version.
-
-               MediaWiki is distributed in the hope that it will be useful,
-               but WITHOUT ANY WARRANTY; without even the implied warranty of
-               MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-               GNU General Public License for more details.
-
-               You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public License]
-               along with this program; if not, write to the Free Software
-               Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-               or [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].
-               ";
-
-               return str_replace( "\t\t", '', $ret ) . "\n";
-       }
-
-       /**
-        * @return wiki text showing the third party software versions (apache, php, mysql).
-        */
-       static function softwareInformation() {
-               $dbr = wfGetDB( DB_SLAVE );
-
-               return Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMsg( 'version-software' ) ) .
-                       Xml::openElement( 'table', array( 'id' => 'sv-software' ) ) .
-                               "<tr>
-                                       <th>" . wfMsg( 'version-software-product' ) . "</th>
-                                       <th>" . wfMsg( 'version-software-version' ) . "</th>
-                               </tr>\n
-                               <tr>
-                                       <td>[http://www.mediawiki.org/ MediaWiki]</td>
-                                       <td>" . self::getVersionLinked() . "</td>
-                               </tr>\n
-                               <tr>
-                                       <td>[http://www.php.net/ PHP]</td>
-                                       <td>" . phpversion() . " (" . php_sapi_name() . ")</td>
-                               </tr>\n
-                               <tr>
-                                       <td>" . $dbr->getSoftwareLink() . "</td>
-                                       <td>" . $dbr->getServerVersion() . "</td>
-                               </tr>\n" .
-                       Xml::closeElement( 'table' );
-       }
-
-       /**
-        * Return a string of the MediaWiki version with SVN revision if available
-        *
-        * @return mixed
-        */
-       public static function getVersion() {
-               global $wgVersion, $IP;
-               wfProfileIn( __METHOD__ );
-               $svn = self::getSvnRevision( $IP );
-               $version = $svn ? "$wgVersion (r$svn)" : $wgVersion;
-               wfProfileOut( __METHOD__ );
-               return $version;
-       }
-       
-       /**
-        * Return a string of the MediaWiki version with a link to SVN revision if
-        * available
-        *
-        * @return mixed
-        */
-       public static function getVersionLinked() {
-               global $wgVersion, $IP;
-               wfProfileIn( __METHOD__ );
-               $svn = self::getSvnRevision( $IP );
-               $viewvc = 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/?pathrev=';
-               $version = $svn ? "$wgVersion ([{$viewvc}{$svn} r$svn])" : $wgVersion;
-               wfProfileOut( __METHOD__ );
-               return $version;
-       }
-
-       /** Generate wikitext showing extensions name, URL, author and description */
-       function extensionCredits() {
-               global $wgExtensionCredits, $wgExtensionFunctions, $wgParser, $wgSkinExtensionFunctions;
-
-               if ( ! count( $wgExtensionCredits ) && ! count( $wgExtensionFunctions ) && ! count( $wgSkinExtensionFunctions ) )
-                       return '';
-
-               $extensionTypes = array(
-                       'specialpage' => wfMsg( 'version-specialpages' ),
-                       'parserhook' => wfMsg( 'version-parserhooks' ),
-                       'variable' => wfMsg( 'version-variables' ),
-                       'media' => wfMsg( 'version-mediahandlers' ),
-                       'other' => wfMsg( 'version-other' ),
-               );
-               wfRunHooks( 'SpecialVersionExtensionTypes', array( &$this, &$extensionTypes ) );
-
-               $out = Xml::element( 'h2', array( 'id' => 'mw-version-ext' ), wfMsg( 'version-extensions' ) ) .
-                       Xml::openElement( 'table', array( 'id' => 'sv-ext' ) );
-
-               foreach ( $extensionTypes as $type => $text ) {
-                       if ( isset ( $wgExtensionCredits[$type] ) && count ( $wgExtensionCredits[$type] ) ) {
-                               $out .= $this->openExtType( $text );
-
-                               usort( $wgExtensionCredits[$type], array( $this, 'compare' ) );
-
-                               foreach ( $wgExtensionCredits[$type] as $extension ) {
-                                       if ( isset( $extension['version'] ) ) {
-                                               $version = $extension['version'];
-                                       } elseif ( isset( $extension['svn-revision'] ) && 
-                                               preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/', 
-                                                       $extension['svn-revision'], $m ) ) 
-                                       {
-                                               $version = 'r' . $m[1];
-                                       } else {
-                                               $version = null;
-                                       }
-
-                                       $out .= $this->formatCredits(
-                                               isset ( $extension['name'] )           ? $extension['name']        : '',
-                                               $version,
-                                               isset ( $extension['author'] )         ? $extension['author']      : '',
-                                               isset ( $extension['url'] )            ? $extension['url']         : null,
-                                               isset ( $extension['description'] )    ? $extension['description'] : '',
-                                               isset ( $extension['descriptionmsg'] ) ? $extension['descriptionmsg'] : ''
-                                       );
-                               }
-                       }
-               }
-
-               if ( count( $wgExtensionFunctions ) ) {
-                       $out .= $this->openExtType( wfMsg( 'version-extension-functions' ) );
-                       $out .= '<tr><td colspan="3">' . $this->listToText( $wgExtensionFunctions ) . "</td></tr>\n";
-               }
-
-               if ( $cnt = count( $tags = $wgParser->getTags() ) ) {
-                       for ( $i = 0; $i < $cnt; ++$i )
-                               $tags[$i] = "&lt;{$tags[$i]}&gt;";
-                       $out .= $this->openExtType( wfMsg( 'version-parser-extensiontags' ) );
-                       $out .= '<tr><td colspan="3">' . $this->listToText( $tags ). "</td></tr>\n";
-               }
-
-               if( $cnt = count( $fhooks = $wgParser->getFunctionHooks() ) ) {
-                       $out .= $this->openExtType( wfMsg( 'version-parser-function-hooks' ) );
-                       $out .= '<tr><td colspan="3">' . $this->listToText( $fhooks ) . "</td></tr>\n";
-               }
-
-               if ( count( $wgSkinExtensionFunctions ) ) {
-                       $out .= $this->openExtType( wfMsg( 'version-skin-extension-functions' ) );
-                       $out .= '<tr><td colspan="3">' . $this->listToText( $wgSkinExtensionFunctions ) . "</td></tr>\n";
-               }
-               $out .= Xml::closeElement( 'table' );
-               return $out;
-       }
-
-       /** Callback to sort extensions by type */
-       function compare( $a, $b ) {
-               global $wgLang;
-               if( $a['name'] === $b['name'] ) {
-                       return 0;
-               } else {
-                       return $wgLang->lc( $a['name'] ) > $wgLang->lc( $b['name'] )
-                               ? 1
-                               : -1;
-               }
-       }
-
-       function formatCredits( $name, $version = null, $author = null, $url = null, $description = null, $descriptionMsg = null ) {
-               $extension = isset( $url ) ? "[$url $name]" : $name;
-               $version = isset( $version ) ? "(" . wfMsg( 'version-version' ) . " $version)" : '';
-
-               # Look for a localized description
-               if( isset( $descriptionMsg ) ) {
-                       $msg = wfMsg( $descriptionMsg );
-                       if ( !wfEmptyMsg( $descriptionMsg, $msg ) && $msg != '' ) {
-                               $description = $msg;
-                       }
-               }
-
-               return "<tr>
-                               <td><em>$extension $version</em></td>
-                               <td>$description</td>
-                               <td>" . $this->listToText( (array)$author ) . "</td>
-                       </tr>\n";
-       }
-
-       /**
-        * @return string
-        */
-       function wgHooks() {
-               global $wgHooks;
-
-               if ( count( $wgHooks ) ) {
-                       $myWgHooks = $wgHooks;
-                       ksort( $myWgHooks );
-
-                       $ret = Xml::element( 'h2', array( 'id' => 'mw-version-hooks' ), wfMsg( 'version-hooks' ) ) .
-                               Xml::openElement( 'table', array( 'id' => 'sv-hooks' ) ) .
-                               "<tr>
-                                       <th>" . wfMsg( 'version-hook-name' ) . "</th>
-                                       <th>" . wfMsg( 'version-hook-subscribedby' ) . "</th>
-                               </tr>\n";
-
-                       foreach ( $myWgHooks as $hook => $hooks )
-                               $ret .= "<tr>
-                                               <td>$hook</td>
-                                               <td>" . $this->listToText( $hooks ) . "</td>
-                                       </tr>\n";
-
-                       $ret .= Xml::closeElement( 'table' );
-                       return $ret;
-               } else
-                       return '';
-       }
-
-       private function openExtType($text, $name = null) {
-               $opt = array( 'colspan' => 3 );
-               $out = '';
-
-               if(!$this->firstExtOpened) {
-                       // Insert a spacing line
-                       $out .= '<tr class="sv-space">' . Xml::element( 'td', $opt ) . "</tr>\n";
-               }
-               $this->firstExtOpened = false;
-
-               if($name) { $opt['id'] = "sv-$name"; }
-
-               $out .= "<tr>" . Xml::element( 'th', $opt, $text) . "</tr>\n";
-               return $out;
-       }
-
-       /**
-        * @static
-        *
-        * @return string
-        */
-       function IPInfo() {
-               $ip =  str_replace( '--', ' - ', htmlspecialchars( wfGetIP() ) );
-               return "<!-- visited from $ip -->\n" .
-                       "<span style='display:none'>visited from $ip</span>";
-       }
-
-       /**
-        * @param array $list
-        * @return string
-        */
-       function listToText( $list ) {
-               $cnt = count( $list );
-
-               if ( $cnt == 1 ) {
-                       // Enforce always returning a string
-                       return (string)$this->arrayToString( $list[0] );
-               } elseif ( $cnt == 0 ) {
-                       return '';
-               } else {
-                       sort( $list );
-                       $t = array_slice( $list, 0, $cnt - 1 );
-                       $one = array_map( array( &$this, 'arrayToString' ), $t );
-                       $two = $this->arrayToString( $list[$cnt - 1] );
-                       $and = wfMsg( 'and' );
-
-                       return implode( ', ', $one ) . " $and $two";
-               }
-       }
-
-       /**
-        * @static
-        *
-        * @param mixed $list Will convert an array to string if given and return
-        *                    the paramater unaltered otherwise
-        * @return mixed
-        */
-       function arrayToString( $list ) {
-               if( is_object( $list ) ) {
-                       $class = get_class( $list );
-                       return "($class)";
-               } elseif ( ! is_array( $list ) ) {
-                       return $list;
-               } else {
-                       $class = get_class( $list[0] );
-                       return "($class, {$list[1]})";
-               }
-       }
-
-       /**
-        * Retrieve the revision number of a Subversion working directory.
-        *
-        * @param string $dir
-        * @return mixed revision number as int, or false if not a SVN checkout
-        */
-       public static function getSvnRevision( $dir ) {
-               // http://svnbook.red-bean.com/nightly/en/svn.developer.insidewc.html
-               $entries = $dir . '/.svn/entries';
-
-               if( !file_exists( $entries ) ) {
-                       return false;
-               }
-
-               $content = file( $entries );
-
-               // check if file is xml (subversion release <= 1.3) or not (subversion release = 1.4)
-               if( preg_match( '/^<\?xml/', $content[0] ) ) {
-                       // subversion is release <= 1.3
-                       if( !function_exists( 'simplexml_load_file' ) ) {
-                               // We could fall back to expat... YUCK
-                               return false;
-                       }
-
-                       // SimpleXml whines about the xmlns...
-                       wfSuppressWarnings();
-                       $xml = simplexml_load_file( $entries );
-                       wfRestoreWarnings();
-
-                       if( $xml ) {
-                               foreach( $xml->entry as $entry ) {
-                                       if( $xml->entry[0]['name'] == '' ) {
-                                               // The directory entry should always have a revision marker.
-                                               if( $entry['revision'] ) {
-                                                       return intval( $entry['revision'] );
-                                               }
-                                       }
-                               }
-                       }
-                       return false;
-               } else {
-                       // subversion is release 1.4
-                       return intval( $content[3] );
-               }
-       }
-
-       /**#@-*/
-}
-
-/**#@-*/
diff --git a/includes/specials/Wantedcategories.php b/includes/specials/Wantedcategories.php
deleted file mode 100644 (file)
index 969a8d8..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A querypage to list the most wanted categories - implements Special:Wantedcategories
- *
- * @ingroup SpecialPage
- *
- * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
- * @copyright Copyright Â© 2005, Ã†var Arnfjörð Bjarmason
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-class WantedCategoriesPage extends QueryPage {
-
-       function getName() {
-               return 'Wantedcategories';
-       }
-
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() {
-               return false;
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               " . NS_CATEGORY . " as namespace,
-                               cl_to as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       LEFT JOIN $page ON cl_to = page_title AND page_namespace = ". NS_CATEGORY ."
-                       WHERE page_title IS NULL
-                       GROUP BY 1,2,3
-                       ";
-       }
-
-       function sortDescending() { return true; }
-
-       /**
-        * Fetch user page links and cache their existence
-        */
-       function preprocessResults( $db, $res ) {
-               $batch = new LinkBatch;
-               while ( $row = $db->fetchObject( $res ) )
-                       $batch->add( $row->namespace, $row->title );
-               $batch->execute();
-
-               // Back to start for display
-               if ( $db->numRows( $res ) > 0 )
-                       // If there are no rows we get an error seeking.
-                       $db->dataSeek( $res, 0 );
-       }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getText() );
-
-               $plink = $this->isCached() ?
-                       $skin->makeLinkObj( $nt, htmlspecialchars( $text ) ) :
-                       $skin->makeBrokenLinkObj( $nt, htmlspecialchars( $text ) );
-
-               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               return wfSpecialList($plink, $nlinks);
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialWantedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new WantedCategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
diff --git a/includes/specials/Wantedpages.php b/includes/specials/Wantedpages.php
deleted file mode 100644 (file)
index 650e04f..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * implements Special:Wantedpages
- * @ingroup SpecialPage
- */
-class WantedPagesPage extends QueryPage {
-       var $nlinks;
-
-       function WantedPagesPage( $inc = false, $nlinks = true ) {
-               $this->setListoutput( $inc );
-               $this->nlinks = $nlinks;
-       }
-
-       function getName() {
-               return 'Wantedpages';
-       }
-
-       function isExpensive() {
-               return true;
-       }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               global $wgWantedPagesThreshold;
-               $count = $wgWantedPagesThreshold - 1;
-               $dbr = wfGetDB( DB_SLAVE );
-               $pagelinks = $dbr->tableName( 'pagelinks' );
-               $page      = $dbr->tableName( 'page' );
-               return
-                       "SELECT 'Wantedpages' AS type,
-                               pl_namespace AS namespace,
-                               pl_title AS title,
-                               COUNT(*) AS value
-                        FROM $pagelinks
-                        LEFT JOIN $page AS pg1
-                        ON pl_namespace = pg1.page_namespace AND pl_title = pg1.page_title
-                        LEFT JOIN $page AS pg2
-                        ON pl_from = pg2.page_id
-                        WHERE pg1.page_namespace IS NULL
-                        AND pl_namespace NOT IN ( 2, 3 )
-                        AND pg2.page_namespace != 8
-                        GROUP BY 1,2,3
-                        HAVING COUNT(*) > $count";
-       }
-
-       /**
-        * Cache page existence for performance
-        */
-       function preprocessResults( $db, $res ) {
-               $batch = new LinkBatch;
-               while ( $row = $db->fetchObject( $res ) )
-                       $batch->add( $row->namespace, $row->title );
-               $batch->execute();
-
-               // Back to start for display
-               if ( $db->numRows( $res ) > 0 )
-                       // If there are no rows we get an error seeking.
-                       $db->dataSeek( $res, 0 );
-       }
-
-       /**
-        * Format an individual result
-        *
-        * @param $skin Skin to use for UI elements
-        * @param $result Result row
-        * @return string
-        */
-       public function formatResult( $skin, $result ) {
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               if( $title instanceof Title ) {
-                       if( $this->isCached() ) {
-                               $pageLink = $title->exists()
-                                       ? '<s>' . $skin->makeLinkObj( $title ) . '</s>'
-                                       : $skin->makeBrokenLinkObj( $title );
-                       } else {
-                               $pageLink = $skin->makeBrokenLinkObj( $title );
-                       }
-                       return wfSpecialList( $pageLink, $this->makeWlhLink( $title, $skin, $result ) );
-               } else {
-                       $tsafe = htmlspecialchars( $result->title );
-                       return "Invalid title in result set; {$tsafe}";
-               }
-       }
-
-       /**
-        * Make a "what links here" link for a specified result if required
-        *
-        * @param $title Title to make the link for
-        * @param $skin Skin to use
-        * @param $result Result row
-        * @return string
-        */
-       private function makeWlhLink( $title, $skin, $result ) {
-               global $wgLang;
-               if( $this->nlinks ) {
-                       $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
-                       $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
-                               $wgLang->formatNum( $result->value ) );
-                       return $skin->makeKnownLinkObj( $wlh, $label, 'target=' . $title->getPrefixedUrl() );
-               } else {
-                       return null;
-               }
-       }
-
-}
-
-/**
- * constructor
- */
-function wfSpecialWantedpages( $par = null, $specialPage ) {
-       $inc = $specialPage->including();
-
-       if ( $inc ) {
-               @list( $limit, $nlinks ) = explode( '/', $par, 2 );
-               $limit = (int)$limit;
-               $nlinks = $nlinks === 'nlinks';
-               $offset = 0;
-       } else {
-               list( $limit, $offset ) = wfCheckLimits();
-               $nlinks = true;
-       }
-
-       $wpp = new WantedPagesPage( $inc, $nlinks );
-
-       $wpp->doQuery( $offset, $limit, !$inc );
-}
diff --git a/includes/specials/Watchlist.php b/includes/specials/Watchlist.php
deleted file mode 100644 (file)
index 4af22c7..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage Watchlist
- */
-
-/**
- * Constructor
- *
- * @param $par Parameter passed to the page
- */
-function wfSpecialWatchlist( $par ) {
-       global $wgUser, $wgOut, $wgLang, $wgRequest;
-       global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
-       global $wgEnotifWatchlist;
-       $fname = 'wfSpecialWatchlist';
-
-       $skin = $wgUser->getSkin();
-       $specialTitle = SpecialPage::getTitleFor( 'Watchlist' );
-       $wgOut->setRobotPolicy( 'noindex,nofollow' );
-
-       # Anons don't get a watchlist
-       if( $wgUser->isAnon() ) {
-               $wgOut->setPageTitle( wfMsg( 'watchnologin' ) );
-               $llink = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Userlogin' ), wfMsgHtml( 'loginreqlink' ), 'returnto=' . $specialTitle->getPrefixedUrl() );
-               $wgOut->addHtml( wfMsgWikiHtml( 'watchlistanontext', $llink ) );
-               return;
-       }
-
-       $wgOut->setPageTitle( wfMsg( 'watchlist' ) );
-
-       $sub  = wfMsgExt( 'watchlistfor', 'parseinline', $wgUser->getName() );
-       $sub .= '<br />' . WatchlistEditor::buildTools( $wgUser->getSkin() );
-       $wgOut->setSubtitle( $sub );
-
-       if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) {
-               $editor = new WatchlistEditor();
-               $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
-               return;
-       }
-
-       $uid = $wgUser->getId();
-       if( ($wgEnotifWatchlist || $wgShowUpdatedMarker) && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
-               $wgUser->clearAllNotifications( $uid );
-               $wgOut->redirect( $specialTitle->getFullUrl() );
-               return;
-       }
-
-       $defaults = array(
-       /* float */ 'days' => floatval( $wgUser->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
-       /* bool  */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ),
-       /* bool  */ 'hideBots' => (int)$wgUser->getBoolOption( 'watchlisthidebots' ),
-       /* bool */ 'hideMinor' => (int)$wgUser->getBoolOption( 'watchlisthideminor' ),
-       /* ?     */ 'namespace' => 'all',
-       );
-
-       extract($defaults);
-
-       # Extract variables from the request, falling back to user preferences or
-       # other default values if these don't exist
-       $prefs['days'    ] = floatval( $wgUser->getOption( 'watchlistdays' ) );
-       $prefs['hideown' ] = $wgUser->getBoolOption( 'watchlisthideown' );
-       $prefs['hidebots'] = $wgUser->getBoolOption( 'watchlisthidebots' );
-       $prefs['hideminor'] = $wgUser->getBoolOption( 'watchlisthideminor' );
-
-       # Get query variables
-       $days     = $wgRequest->getVal(  'days', $prefs['days'] );
-       $hideOwn  = $wgRequest->getBool( 'hideOwn', $prefs['hideown'] );
-       $hideBots = $wgRequest->getBool( 'hideBots', $prefs['hidebots'] );
-       $hideMinor = $wgRequest->getBool( 'hideMinor', $prefs['hideminor'] );
-
-       # Get namespace value, if supplied, and prepare a WHERE fragment
-       $nameSpace = $wgRequest->getIntOrNull( 'namespace' );
-       if( !is_null( $nameSpace ) ) {
-               $nameSpace = intval( $nameSpace );
-               $nameSpaceClause = " AND rc_namespace = $nameSpace";
-       } else {
-               $nameSpace = '';
-               $nameSpaceClause = '';
-       }
-
-       $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
-       list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' );
-
-       $watchlistCount = $dbr->selectField( 'watchlist', 'COUNT(*)',
-               array( 'wl_user' => $uid ), __METHOD__ );
-       // Adjust for page X, talk:page X, which are both stored separately,
-       // but treated together
-       $nitems = floor($watchlistCount / 2);
-
-       if( is_null($days) || !is_numeric($days) ) {
-               $big = 1000; /* The magical big */
-               if($nitems > $big) {
-                       # Set default cutoff shorter
-                       $days = $defaults['days'] = (12.0 / 24.0); # 12 hours...
-               } else {
-                       $days = $defaults['days']; # default cutoff for shortlisters
-               }
-       } else {
-               $days = floatval($days);
-       }
-
-       // Dump everything here
-       $nondefaults = array();
-
-       wfAppendToArrayIfNotDefault('days'     , $days         , $defaults, $nondefaults);
-       wfAppendToArrayIfNotDefault('hideOwn'  , (int)$hideOwn , $defaults, $nondefaults);
-       wfAppendToArrayIfNotDefault('hideBots' , (int)$hideBots, $defaults, $nondefaults);
-       wfAppendToArrayIfNotDefault( 'hideMinor', (int)$hideMinor, $defaults, $nondefaults );
-       wfAppendToArrayIfNotDefault('namespace', $nameSpace    , $defaults, $nondefaults);
-
-       $hookSql = "";
-       if( ! wfRunHooks('BeforeWatchlist', array($nondefaults, $wgUser, &$hookSql)) ) {
-               return;
-       }
-
-       if($nitems == 0) {
-               $wgOut->addWikiMsg( 'nowatchlist' );
-               return;
-       }
-
-       if ( $days <= 0 ) {
-               $andcutoff = '';
-       } else {
-               $andcutoff = "AND rc_timestamp > '".$dbr->timestamp( time() - intval( $days * 86400 ) )."'";
-               /*
-               $sql = "SELECT COUNT(*) AS n FROM $page, $revision  WHERE rev_timestamp>'$cutoff' AND page_id=rev_page";
-               $res = $dbr->query( $sql, $fname );
-               $s = $dbr->fetchObject( $res );
-               $npages = $s->n;
-               */
-       }
-
-       # If the watchlist is relatively short, it's simplest to zip
-       # down its entirety and then sort the results.
-
-       # If it's relatively long, it may be worth our while to zip
-       # through the time-sorted page list checking for watched items.
-
-       # Up estimate of watched items by 15% to compensate for talk pages...
-
-       # Toggles
-       $andHideOwn = $hideOwn ? "AND (rc_user <> $uid)" : '';
-       $andHideBots = $hideBots ? "AND (rc_bot = 0)" : '';
-       $andHideMinor = $hideMinor ? 'AND rc_minor = 0' : '';
-
-       # Show watchlist header
-       $header = '';
-       if( $wgUser->getOption( 'enotifwatchlistpages' ) && $wgEnotifWatchlist) {
-               $header .= wfMsg( 'wlheader-enotif' ) . "\n";
-       }
-       if ( $wgShowUpdatedMarker ) {
-               $header .= wfMsg( 'wlheader-showupdated' ) . "\n";
-       }
-
-  # Toggle watchlist content (all recent edits or just the latest)
-       if( $wgUser->getOption( 'extendwatchlist' )) {
-               $andLatest='';
-               $limitWatchlist = 'LIMIT ' . intval( $wgUser->getOption( 'wllimit' ) );
-       } else {
-       # Top log Ids for a page are not stored
-               $andLatest = 'AND (rc_this_oldid=page_latest OR rc_type=' . RC_LOG . ') ';
-               $limitWatchlist = '';
-       }
-
-       $header .= wfMsgExt( 'watchlist-details', array( 'parsemag' ), $wgLang->formatNum( $nitems ) );
-       $wgOut->addWikiText( $header );
-
-       # Show a message about slave lag, if applicable
-       if( ( $lag = $dbr->getLag() ) > 0 )
-               $wgOut->showLagWarning( $lag );
-
-       if ( $wgShowUpdatedMarker ) {
-               $wgOut->addHTML( '<form action="' .
-                       $specialTitle->escapeLocalUrl() .
-                       '" method="post"><input type="submit" name="dummy" value="' .
-                       htmlspecialchars( wfMsg( 'enotif_reset' ) ) .
-                       '" /><input type="hidden" name="reset" value="all" /></form>' .
-                       "\n\n" );
-       }
-       if ( $wgShowUpdatedMarker ) {
-               $wltsfield = ", ${watchlist}.wl_notificationtimestamp ";
-       } else {
-               $wltsfield = '';
-       }
-       $sql = "SELECT ${recentchanges}.* ${wltsfield}
-         FROM $watchlist,$recentchanges
-         LEFT JOIN $page ON rc_cur_id=page_id
-         WHERE wl_user=$uid
-         AND wl_namespace=rc_namespace
-         AND wl_title=rc_title
-         $andcutoff
-         $andLatest
-         $andHideOwn
-         $andHideBots
-         $andHideMinor
-         $nameSpaceClause
-         $hookSql
-         ORDER BY rc_timestamp DESC
-         $limitWatchlist";
-
-       $res = $dbr->query( $sql, $fname );
-       $numRows = $dbr->numRows( $res );
-
-       /* Start bottom header */
-       $wgOut->addHTML( "<hr />\n" );
-
-       if($days >= 1) {
-               $wgOut->addWikiText( wfMsgExt( 'rcnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
-                       $wgLang->formatNum( $days ), $wgLang->timeAndDate( wfTimestampNow(), true ) ) . '<br />' , false );
-       } elseif($days > 0) {
-               $wgOut->addWikiText( wfMsgExt( 'wlnote', array( 'parseinline' ), $wgLang->formatNum( $numRows ),
-                       $wgLang->formatNum( round($days*24) ) ) . '<br />' , false );
-       }
-
-       $wgOut->addHTML( "\n" . wlCutoffLinks( $days, 'Watchlist', $nondefaults ) . "<br />\n" );
-
-       # Spit out some control panel links
-       $thisTitle = SpecialPage::getTitleFor( 'Watchlist' );
-       $skin = $wgUser->getSkin();
-
-       # Hide/show bot edits
-       $label = $hideBots ? wfMsgHtml( 'watchlist-show-bots' ) : wfMsgHtml( 'watchlist-hide-bots' );
-       $linkBits = wfArrayToCGI( array( 'hideBots' => 1 - (int)$hideBots ), $nondefaults );
-       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
-
-       # Hide/show own edits
-       $label = $hideOwn ? wfMsgHtml( 'watchlist-show-own' ) : wfMsgHtml( 'watchlist-hide-own' );
-       $linkBits = wfArrayToCGI( array( 'hideOwn' => 1 - (int)$hideOwn ), $nondefaults );
-       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
-
-       # Hide/show minor edits
-       $label = $hideMinor ? wfMsgHtml( 'watchlist-show-minor' ) : wfMsgHtml( 'watchlist-hide-minor' );
-       $linkBits = wfArrayToCGI( array( 'hideMinor' => 1 - (int)$hideMinor ), $nondefaults );
-       $links[] = $skin->makeKnownLinkObj( $thisTitle, $label, $linkBits );
-
-       $wgOut->addHTML( implode( ' | ', $links ) );
-
-       # Form for namespace filtering
-       $form  = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
-       $form .= '<p>';
-       $form .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;';
-       $form .= Xml::namespaceSelector( $nameSpace, '' ) . '&nbsp;';
-       $form .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . '</p>';
-       $form .= Xml::hidden( 'days', $days );
-       if( $hideOwn )
-               $form .= Xml::hidden( 'hideOwn', 1 );
-       if( $hideBots )
-               $form .= Xml::hidden( 'hideBots', 1 );
-       if( $hideMinor )
-               $form .= Xml::hidden( 'hideMinor', 1 );
-       $form .= Xml::closeElement( 'form' );
-       $wgOut->addHtml( $form );
-
-       # If there's nothing to show, stop here
-       if( $numRows == 0 ) {
-               $wgOut->addWikiMsg( 'watchnochange' );
-               return;
-       }
-
-       /* End bottom header */
-
-       /* Do link batch query */
-       $linkBatch = new LinkBatch;
-       while ( $row = $dbr->fetchObject( $res ) ) {
-               $userNameUnderscored = str_replace( ' ', '_', $row->rc_user_text );
-               if ( $row->rc_user != 0 ) {
-                       $linkBatch->add( NS_USER, $userNameUnderscored );
-               }
-               $linkBatch->add( NS_USER_TALK, $userNameUnderscored );
-       }
-       $linkBatch->execute();
-       $dbr->dataSeek( $res, 0 );
-
-       $list = ChangesList::newFromUser( $wgUser );
-
-       $s = $list->beginRecentChangesList();
-       $counter = 1;
-       while ( $obj = $dbr->fetchObject( $res ) ) {
-               # Make RC entry
-               $rc = RecentChange::newFromRow( $obj );
-               $rc->counter = $counter++;
-
-               if ( $wgShowUpdatedMarker ) {
-                       $updated = $obj->wl_notificationtimestamp;
-               } else {
-                       $updated = false;
-               }
-
-               if ($wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
-                       $rc->numberofWatchingusers = $dbr->selectField( 'watchlist',
-                               'COUNT(*)',
-                               array(
-                                       'wl_namespace' => $obj->rc_namespace,
-                                       'wl_title' => $obj->rc_title,
-                               ),
-                               __METHOD__ );
-               } else {
-                       $rc->numberofWatchingusers = 0;
-               }
-
-               $s .= $list->recentChangesLine( $rc, $updated );
-       }
-       $s .= $list->endRecentChangesList();
-
-       $dbr->freeResult( $res );
-       $wgOut->addHTML( $s );
-
-}
-
-function wlHoursLink( $h, $page, $options = array() ) {
-       global $wgUser, $wgLang, $wgContLang;
-       $sk = $wgUser->getSkin();
-       $s = $sk->makeKnownLink(
-         $wgContLang->specialPage( $page ),
-         $wgLang->formatNum( $h ),
-         wfArrayToCGI( array('days' => ($h / 24.0)), $options ) );
-       return $s;
-}
-
-function wlDaysLink( $d, $page, $options = array() ) {
-       global $wgUser, $wgLang, $wgContLang;
-       $sk = $wgUser->getSkin();
-       $s = $sk->makeKnownLink(
-         $wgContLang->specialPage( $page ),
-         ($d ? $wgLang->formatNum( $d ) : wfMsgHtml( 'watchlistall2' ) ),
-         wfArrayToCGI( array('days' => $d), $options ) );
-       return $s;
-}
-
-/**
- * Returns html
- */
-function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) {
-       $hours = array( 1, 2, 6, 12 );
-       $days = array( 1, 3, 7 );
-       $i = 0;
-       foreach( $hours as $h ) {
-               $hours[$i++] = wlHoursLink( $h, $page, $options );
-       }
-       $i = 0;
-       foreach( $days as $d ) {
-               $days[$i++] = wlDaysLink( $d, $page, $options );
-       }
-       return wfMsgExt('wlshowlast',
-               array('parseinline', 'replaceafter'),
-               implode(' | ', $hours),
-               implode(' | ', $days),
-               wlDaysLink( 0, $page, $options ) );
-}
-
-/**
- * Count the number of items on a user's watchlist
- *
- * @param $talk Include talk pages
- * @return integer
- */
-function wlCountItems( &$user, $talk = true ) {
-       $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
-
-       # Fetch the raw count
-       $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->mId ), 'wlCountItems' );
-       $row = $dbr->fetchObject( $res );
-       $count = $row->count;
-       $dbr->freeResult( $res );
-
-       # Halve to remove talk pages if needed
-       if( !$talk )
-               $count = floor( $count / 2 );
-
-       return( $count );
-}
diff --git a/includes/specials/Whatlinkshere.php b/includes/specials/Whatlinkshere.php
deleted file mode 100644 (file)
index a57df5e..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-<?php
-/**
- * @todo Use some variant of Pager or something; the pagination here is lousy.
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Entry point
- * @param $par String: An article name ??
- */
-function wfSpecialWhatlinkshere($par = NULL) {
-       global $wgRequest;
-       $page = new WhatLinksHerePage( $wgRequest, $par );
-       $page->execute();
-}
-
-/**
- * implements Special:Whatlinkshere
- * @ingroup SpecialPage
- */
-class WhatLinksHerePage {
-       // Stored data
-       protected $par;
-
-       // Stored objects
-       protected $opts, $target, $selfTitle;
-
-       // Stored globals
-       protected $skin, $request;
-
-       protected $limits = array( 20, 50, 100, 250, 500 );
-
-       function WhatLinksHerePage( $request, $par = null ) {
-               global $wgUser;
-               $this->request = $request;
-               $this->skin = $wgUser->getSkin();
-               $this->par = $par;
-       }
-
-       function execute() {
-               global $wgOut;
-
-               $opts = new FormOptions();
-
-               $opts->add( 'target', '' );
-               $opts->add( 'namespace', '', FormOptions::INTNULL );
-               $opts->add( 'limit', 50 );
-               $opts->add( 'from', 0 );
-               $opts->add( 'back', 0 );
-               $opts->add( 'hideredirs', false );
-               $opts->add( 'hidetrans', false );
-               $opts->add( 'hidelinks', false );
-               $opts->add( 'hideimages', false );
-
-               $opts->fetchValuesFromRequest( $this->request );
-               $opts->validateIntBounds( 'limit', 0, 5000 );
-
-               // Give precedence to subpage syntax
-               if ( isset($this->par) ) {
-                       $opts->setValue( 'target', $this->par );
-               }
-
-               // Bind to member variable
-               $this->opts = $opts;
-
-               $this->target = Title::newFromURL( $opts->getValue( 'target' ) );
-               if( !$this->target ) {
-                       $wgOut->addHTML( $this->whatlinkshereForm() );
-                       return;
-               }
-
-               $this->selfTitle = SpecialPage::getTitleFor( 'Whatlinkshere', $this->target->getPrefixedDBkey() );
-
-               $wgOut->setPageTitle( wfMsgExt( 'whatlinkshere-title', 'escape', $this->target->getPrefixedText() ) );
-               $wgOut->setSubtitle( wfMsgHtml( 'linklistsub' ) );
-
-               $wgOut->addHTML( wfMsgExt( 'whatlinkshere-barrow', array( 'escapenoentities') ) . ' '  .$this->skin->makeLinkObj($this->target, '', 'redirect=no' )."<br />\n");
-
-               $this->showIndirectLinks( 0, $this->target, $opts->getValue( 'limit' ),
-                       $opts->getValue( 'from' ), $opts->getValue( 'back' ) );
-       }
-
-       /**
-        * @param $level  int     Recursion level
-        * @param $target Title   Target title
-        * @param $limit  int     Number of entries to display
-        * @param $from   Title   Display from this article ID
-        * @param $back   Title   Display from this article ID at backwards scrolling
-        * @private
-        */
-       function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
-               global $wgOut, $wgMaxRedirectLinksRetrieved;
-               $dbr = wfGetDB( DB_SLAVE );
-               $options = array();
-
-               $hidelinks = $this->opts->getValue( 'hidelinks' );
-               $hideredirs = $this->opts->getValue( 'hideredirs' );
-               $hidetrans = $this->opts->getValue( 'hidetrans' );
-               $hideimages = $target->getNamespace() != NS_IMAGE || $this->opts->getValue( 'hideimages' );
-
-               $fetchlinks = (!$hidelinks || !$hideredirs);
-
-               // Make the query
-               $plConds = array(
-                       'page_id=pl_from',
-                       'pl_namespace' => $target->getNamespace(),
-                       'pl_title' => $target->getDBkey(),
-               );
-               if( $hideredirs ) {
-                       $plConds['page_is_redirect'] = 0;
-               } elseif( $hidelinks ) {
-                       $plConds['page_is_redirect'] = 1;
-               }
-
-               $tlConds = array(
-                       'page_id=tl_from',
-                       'tl_namespace' => $target->getNamespace(),
-                       'tl_title' => $target->getDBkey(),
-               );
-
-               $ilConds = array(
-                       'page_id=il_from',
-                       'il_to' => $target->getDBkey(),
-               );
-
-               $namespace = $this->opts->getValue( 'namespace' );
-               if ( is_int($namespace) ) {
-                       $plConds['page_namespace'] = $namespace;
-                       $tlConds['page_namespace'] = $namespace;
-                       $ilConds['page_namespace'] = $namespace;
-               }
-
-               if ( $from ) {
-                       $tlConds[] = "tl_from >= $from";
-                       $plConds[] = "pl_from >= $from";
-                       $ilConds[] = "il_from >= $from";
-               }
-
-               // Read an extra row as an at-end check
-               $queryLimit = $limit + 1;
-
-               // Enforce join order, sometimes namespace selector may
-               // trigger filesorts which are far less efficient than scanning many entries
-               $options[] = 'STRAIGHT_JOIN';
-
-               $options['LIMIT'] = $queryLimit;
-               $fields = array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' );
-
-               if( $fetchlinks ) {
-                       $options['ORDER BY'] = 'pl_from';
-                       $plRes = $dbr->select( array( 'pagelinks', 'page' ), $fields,
-                               $plConds, __METHOD__, $options );
-               }
-
-               if( !$hidetrans ) {
-                       $options['ORDER BY'] = 'tl_from';
-                       $tlRes = $dbr->select( array( 'templatelinks', 'page' ), $fields,
-                               $tlConds, __METHOD__, $options );
-               }
-
-               if( !$hideimages ) {
-                       $options['ORDER BY'] = 'il_from';
-                       $ilRes = $dbr->select( array( 'imagelinks', 'page' ), $fields,
-                               $ilConds, __METHOD__, $options );
-               }
-
-               if( ( !$fetchlinks || !$dbr->numRows($plRes) ) && ( $hidetrans || !$dbr->numRows($tlRes) ) && ( $hideimages || !$dbr->numRows($ilRes) ) ) {
-                       if ( 0 == $level ) {
-                               $wgOut->addHTML( $this->whatlinkshereForm() );
-                               $errMsg = is_int($namespace) ? 'nolinkshere-ns' : 'nolinkshere';
-                               $wgOut->addWikiMsg( $errMsg, $this->target->getPrefixedText() );
-                               // Show filters only if there are links
-                               if( $hidelinks || $hidetrans || $hideredirs || $hideimages )
-                                       $wgOut->addHTML( $this->getFilterPanel() );
-                       }
-                       return;
-               }
-
-               // Read the rows into an array and remove duplicates
-               // templatelinks comes second so that the templatelinks row overwrites the
-               // pagelinks row, so we get (inclusion) rather than nothing
-               if( $fetchlinks ) {
-                       while ( $row = $dbr->fetchObject( $plRes ) ) {
-                               $row->is_template = 0;
-                               $row->is_image = 0;
-                               $rows[$row->page_id] = $row;
-                       }
-                       $dbr->freeResult( $plRes );
-
-               }
-               if( !$hidetrans ) {
-                       while ( $row = $dbr->fetchObject( $tlRes ) ) {
-                               $row->is_template = 1;
-                               $row->is_image = 0;
-                               $rows[$row->page_id] = $row;
-                       }
-                       $dbr->freeResult( $tlRes );
-               }
-               if( !$hideimages ) {
-                       while ( $row = $dbr->fetchObject( $ilRes ) ) {
-                               $row->is_template = 0;
-                               $row->is_image = 1;
-                               $rows[$row->page_id] = $row;
-                       }
-                       $dbr->freeResult( $ilRes );
-               }
-
-               // Sort by key and then change the keys to 0-based indices
-               ksort( $rows );
-               $rows = array_values( $rows );
-
-               $numRows = count( $rows );
-
-               // Work out the start and end IDs, for prev/next links
-               if ( $numRows > $limit ) {
-                       // More rows available after these ones
-                       // Get the ID from the last row in the result set
-                       $nextId = $rows[$limit]->page_id;
-                       // Remove undisplayed rows
-                       $rows = array_slice( $rows, 0, $limit );
-               } else {
-                       // No more rows after
-                       $nextId = false;
-               }
-               $prevId = $from;
-
-               if ( $level == 0 ) {
-                       $wgOut->addHTML( $this->whatlinkshereForm() );
-                       $wgOut->addHTML( $this->getFilterPanel() );
-                       $wgOut->addWikiMsg( 'linkshere', $this->target->getPrefixedText() );
-
-                       $prevnext = $this->getPrevNext( $prevId, $nextId );
-                       $wgOut->addHTML( $prevnext );
-               }
-
-               $wgOut->addHTML( $this->listStart() );
-               foreach ( $rows as $row ) {
-                       $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
-
-                       if ( $row->page_is_redirect && $level < 2 ) {
-                               $wgOut->addHTML( $this->listItem( $row, $nt, true ) );
-                               $this->showIndirectLinks( $level + 1, $nt, $wgMaxRedirectLinksRetrieved );
-                               $wgOut->addHTML( Xml::closeElement( 'li' ) );
-                       } else {
-                               $wgOut->addHTML( $this->listItem( $row, $nt ) );
-                       }
-               }
-
-               $wgOut->addHTML( $this->listEnd() );
-
-               if( $level == 0 ) {
-                       $wgOut->addHTML( $prevnext );
-               }
-       }
-
-       protected function listStart() {
-               return Xml::openElement( 'ul' );
-       }
-
-       protected function listItem( $row, $nt, $notClose = false ) {
-               # local message cache
-               static $msgcache = null;
-               if ( $msgcache === null ) {
-                       static $msgs = array( 'isredirect', 'istemplate', 'semicolon-separator',
-                               'whatlinkshere-links', 'isimage' );
-                       $msgcache = array();
-                       foreach ( $msgs as $msg ) {
-                               $msgcache[$msg] = wfMsgHtml( $msg );
-                       }
-               }
-
-               $suppressRedirect = $row->page_is_redirect ? 'redirect=no' : '';
-               $link = $this->skin->makeKnownLinkObj( $nt, '', $suppressRedirect );
-
-               // Display properties (redirect or template)
-               $propsText = '';
-               $props = array();
-               if ( $row->page_is_redirect )
-                       $props[] = $msgcache['isredirect'];
-               if ( $row->is_template )
-                       $props[] = $msgcache['istemplate'];
-               if( $row->is_image )
-                       $props[] = $msgcache['isimage'];
-
-               if ( count( $props ) ) {
-                       $propsText = '(' . implode( $msgcache['semicolon-separator'], $props ) . ')';
-               }
-
-               # Space for utilities links, with a what-links-here link provided
-               $wlhLink = $this->wlhLink( $nt, $msgcache['whatlinkshere-links'] );
-               $wlh = Xml::wrapClass( "($wlhLink)", 'mw-whatlinkshere-tools' );
-
-               return $notClose ?
-                       Xml::openElement( 'li' ) . "$link $propsText $wlh\n" :
-                       Xml::tags( 'li', null, "$link $propsText $wlh" ) . "\n";
-       }
-
-       protected function listEnd() {
-               return Xml::closeElement( 'ul' );
-       }
-
-       protected function wlhLink( Title $target, $text ) {
-               static $title = null;
-               if ( $title === null )
-                       $title = SpecialPage::getTitleFor( 'Whatlinkshere' );
-
-               $targetText = $target->getPrefixedUrl();
-               return $this->skin->makeKnownLinkObj( $title, $text, 'target=' . $targetText );
-       }
-
-       function makeSelfLink( $text, $query ) {
-               return $this->skin->makeKnownLinkObj( $this->selfTitle, $text, $query );
-       }
-
-       function getPrevNext( $prevId, $nextId ) {
-               global $wgLang;
-               $currentLimit = $this->opts->getValue( 'limit' );
-               $fmtLimit = $wgLang->formatNum( $currentLimit );
-               $prev = wfMsgExt( 'whatlinkshere-prev', array( 'parsemag', 'escape' ), $fmtLimit );
-               $next = wfMsgExt( 'whatlinkshere-next', array( 'parsemag', 'escape' ), $fmtLimit );
-
-               $changed = $this->opts->getChangedValues();
-               unset($changed['target']); // Already in the request title
-
-               if ( 0 != $prevId ) {
-                       $overrides = array( 'from' => $this->opts->getValue( 'back' ) );
-                       $prev = $this->makeSelfLink( $prev, wfArrayToCGI( $overrides, $changed ) );
-               }
-               if ( 0 != $nextId ) {
-                       $overrides = array( 'from' => $nextId, 'back' => $prevId );
-                       $next = $this->makeSelfLink( $next, wfArrayToCGI( $overrides, $changed ) );
-               }
-
-               $limitLinks = array();
-               foreach ( $this->limits as $limit ) {
-                       $prettyLimit = $wgLang->formatNum( $limit );
-                       $overrides = array( 'limit' => $limit );
-                       $limitLinks[] = $this->makeSelfLink( $prettyLimit, wfArrayToCGI( $overrides, $changed ) );
-               }
-
-               $nums = implode ( ' | ', $limitLinks );
-
-               return wfMsgHtml( 'viewprevnext', $prev, $next, $nums );
-       }
-
-       function whatlinkshereForm() {
-               global $wgScript, $wgTitle;
-
-               // We get nicer value from the title object
-               $this->opts->consumeValue( 'target' );
-               // Reset these for new requests
-               $this->opts->consumeValues( array( 'back', 'from' ) );
-
-               $target = $this->target ? $this->target->getPrefixedText() : '';
-               $namespace = $this->opts->consumeValue( 'namespace' );
-
-               # Build up the form
-               $f = Xml::openElement( 'form', array( 'action' => $wgScript ) );
-               
-               # Values that should not be forgotten
-               $f .= Xml::hidden( 'title', $wgTitle->getPrefixedText() );
-               foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
-                       $f .= Xml::hidden( $name, $value );
-               }
-
-               $f .= Xml::fieldset( wfMsg( 'whatlinkshere' ) );
-
-               # Target input
-               $f .= Xml::inputLabel( wfMsg( 'whatlinkshere-page' ), 'target',
-                               'mw-whatlinkshere-target', 40, $target );
-
-               $f .= ' ';
-
-               # Namespace selector
-               $f .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '&nbsp;' .
-                       Xml::namespaceSelector( $namespace, '' );
-
-               # Submit
-               $f .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
-
-               # Close
-               $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
-
-               return $f;
-       }
-
-       function getFilterPanel() {
-               $show = wfMsgHtml( 'show' );
-               $hide = wfMsgHtml( 'hide' );
-
-               $changed = $this->opts->getChangedValues();
-               unset($changed['target']); // Already in the request title
-
-               $links = array();
-               $types = array( 'hidetrans', 'hidelinks', 'hideredirs' );
-               if( $this->target->getNamespace() == NS_IMAGE )
-                       $types[] = 'hideimages';
-               foreach( $types as $type ) {
-                       $chosen = $this->opts->getValue( $type );
-                       $msg = wfMsgHtml( "whatlinkshere-{$type}", $chosen ? $show : $hide );
-                       $overrides = array( $type => !$chosen );
-                       $links[] = $this->makeSelfLink( $msg, wfArrayToCGI( $overrides, $changed ) );
-               }
-               return Xml::fieldset( wfMsg( 'whatlinkshere-filters' ), implode( '&nbsp;|&nbsp;', $links ) );
-       }
-}
diff --git a/includes/specials/Withoutinterwiki.php b/includes/specials/Withoutinterwiki.php
deleted file mode 100644 (file)
index 2092e43..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * Special page lists pages without language links
- *
- * @ingroup SpecialPage
- * @author Rob Church <robchur@gmail.com>
- */
-class WithoutInterwikiPage extends PageQueryPage {
-       private $prefix = '';
-
-       function getName() {
-               return 'Withoutinterwiki';
-       }
-
-       function getPageHeader() {
-               global $wgScript, $wgMiserMode;
-
-               # Do not show useless input form if wiki is running in misermode
-               if( $wgMiserMode ) {
-                       return '';
-               }
-
-               $prefix = $this->prefix;
-               $t = SpecialPage::getTitleFor( $this->getName() );
-
-               return  Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Xml::element( 'legend', null, wfMsg( 'withoutinterwiki-legend' ) ) .
-                       Xml::hidden( 'title', $t->getPrefixedText() ) .
-                       Xml::inputLabel( wfMsg( 'allpagesprefix' ), 'prefix', 'wiprefix', 20, $prefix ) . ' ' .
-                       Xml::submitButton( wfMsg( 'withoutinterwiki-submit' ) ) .
-                       Xml::closeElement( 'fieldset' ) .
-                       Xml::closeElement( 'form' );
-       }
-
-       function sortDescending() {
-               return false;
-       }
-
-       function isExpensive() {
-               return true;
-       }
-
-       function isSyndicated() {
-               return false;
-       }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $langlinks ) = $dbr->tableNamesN( 'page', 'langlinks' );
-               $prefix = $this->prefix ? "AND page_title LIKE '" . $dbr->escapeLike( $this->prefix ) . "%'" : '';
-               return
-                 "SELECT 'Withoutinterwiki'  AS type,
-                         page_namespace AS namespace,
-                         page_title     AS title,
-                         page_title     AS value
-                    FROM $page
-               LEFT JOIN $langlinks
-                      ON ll_from = page_id
-                   WHERE ll_title IS NULL
-                     AND page_namespace=" . NS_MAIN . "
-                     AND page_is_redirect = 0
-                         {$prefix}";
-       }
-
-       function setPrefix( $prefix = '' ) {
-               $this->prefix = $prefix;
-       }
-
-}
-
-function wfSpecialWithoutinterwiki() {
-       global $wgRequest, $wgContLang, $wgCapitalLinks;
-       list( $limit, $offset ) = wfCheckLimits();
-       if( $wgCapitalLinks ) {
-               $prefix = $wgContLang->ucfirst( $wgRequest->getVal( 'prefix' ) );
-       } else {
-               $prefix = $wgRequest->getVal( 'prefix' );
-       }
-       $wip = new WithoutInterwikiPage();
-       $wip->setPrefix( $prefix );
-       $wip->doQuery( $offset, $limit );
-}