'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',
$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;
}
+++ /dev/null
-<?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;
-}
+++ /dev/null
-<?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;
- }
-}
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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> </td>
- <td class='mw-input'>" .
- Xml::checkLabel( wfMsg( 'ipbanononly' ),
- 'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
- array( 'tabindex' => '6' ) ) . "
- </td>
- </tr>
- <tr id='wpCreateAccountRow'>
- <td> </td>
- <td class='mw-input'>" .
- Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
- 'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
- array( 'tabindex' => '7' ) ) . "
- </td>
- </tr>
- <tr id='wpEnableAutoblockRow'>
- <td> </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> </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> </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> </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'> </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' ) );
- }
- }
-}
+++ /dev/null
-<?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" );
-}
+++ /dev/null
-<?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 .= ' ' . 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>';
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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' ) ) ) );
- }
-}
+++ /dev/null
-<?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' );
- }
- }
-}
+++ /dev/null
-<?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;
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-
-}
+++ /dev/null
-<?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 );
- }
-}
+++ /dev/null
-<?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 ) . ' ';
- $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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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>'
- );
- }
- }
-}
+++ /dev/null
-<?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' )
- );
- }
-}
+++ /dev/null
-<?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();
- }
-}
+++ /dev/null
-<?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" );
- }
- }
-}
+++ /dev/null
-<?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> </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 ) .
- ' ' .
- 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';
- }
-}
+++ /dev/null
-<?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>';
- }
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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> </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' );
- }
-}
+++ /dev/null
-<?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' );
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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> </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×tamp=$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';
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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> </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() );
- }
-
-}
+++ /dev/null
-<?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' );
- }
-}
+++ /dev/null
-<?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>";
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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(
- ' ',
- 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(
- ' ',
- 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( ' ', $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> ' );
- $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' ) )
- );
- }
-}
+++ /dev/null
-<?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 );
- }
-}
+++ /dev/null
-<?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() ) . " \n" .
- $this->getNamespaceMenu( $namespace ) . " \n" .
- $this->getTypeMenu( $type ) . " \n" .
- $this->getLevelMenu( $level ) . " \n" .
- "<br /><span style='white-space: nowrap'> " .
- $this->getExpiryCheck( $indefOnly ) . " \n" .
- $this->getSizeLimit( $sizetype, $size ) . " \n" .
- "</span>" .
- " " . 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' ) . ' '
- . 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 ) .
- ' ' .
- Xml::radioLabel( wfMsg('maximum-size'), 'sizetype', 'max', 'wpmax', $max ) .
- ' ' .
- Xml::input( 'size', 9, $size, array( 'id' => 'wpsize' ) ) .
- ' ' .
- 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 ) . ' ' .
- 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 ) . ' ' .
- 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();
-}
+++ /dev/null
-<?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 ) . " \n" .
- $this->getNamespaceMenu( $namespace ) . " \n" .
- // $this->getLevelMenu( $level ) . "<br/>\n" .
- " " . 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' )
- . ' '
- . 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 ) . ' ' .
- 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();
-}
+++ /dev/null
-<?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 );
- }
-}
+++ /dev/null
-<?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;
- }
-}
+++ /dev/null
-<?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
+++ /dev/null
-<?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 ) .
- " <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("< ".$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 );
-}
+++ /dev/null
-<?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();
- }
-}
+++ /dev/null
-<?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×tamp=" . $rev->getTimestamp() );
- $difflink = '(' . $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml('diff'),
- "target=$target&diff=prev×tamp=" . $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 );
- }
- }
-}
+++ /dev/null
-<?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' ) ) .
- " " .
- $searchField .
- " " .
- $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;
- }
-}
+++ /dev/null
-<?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 );
-}
--- /dev/null
+<?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;
+}
--- /dev/null
+<?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;
+ }
+}
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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> </td>
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'ipbanononly' ),
+ 'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
+ array( 'tabindex' => '6' ) ) . "
+ </td>
+ </tr>
+ <tr id='wpCreateAccountRow'>
+ <td> </td>
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
+ 'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
+ array( 'tabindex' => '7' ) ) . "
+ </td>
+ </tr>
+ <tr id='wpEnableAutoblockRow'>
+ <td> </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> </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> </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> </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'> </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' ) );
+ }
+ }
+}
--- /dev/null
+<?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" );
+}
--- /dev/null
+<?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 .= ' ' . 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>';
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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' ) ) ) );
+ }
+}
--- /dev/null
+<?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' );
+ }
+ }
+}
--- /dev/null
+<?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;
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+
+}
--- /dev/null
+<?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 );
+ }
+}
--- /dev/null
+<?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 ) . ' ';
+ $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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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>'
+ );
+ }
+ }
+}
--- /dev/null
+<?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' )
+ );
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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" );
+ }
+ }
+}
--- /dev/null
+<?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> </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 ) .
+ ' ' .
+ 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';
+ }
+}
--- /dev/null
+<?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>';
+ }
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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> </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' );
+ }
+}
--- /dev/null
+<?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' );
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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> </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×tamp=$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';
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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> </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() );
+ }
+
+}
--- /dev/null
+<?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' );
+ }
+}
--- /dev/null
+<?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>";
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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(
+ ' ',
+ 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(
+ ' ',
+ 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( ' ', $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> ' );
+ $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' ) )
+ );
+ }
+}
--- /dev/null
+<?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 );
+ }
+}
--- /dev/null
+<?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() ) . " \n" .
+ $this->getNamespaceMenu( $namespace ) . " \n" .
+ $this->getTypeMenu( $type ) . " \n" .
+ $this->getLevelMenu( $level ) . " \n" .
+ "<br /><span style='white-space: nowrap'> " .
+ $this->getExpiryCheck( $indefOnly ) . " \n" .
+ $this->getSizeLimit( $sizetype, $size ) . " \n" .
+ "</span>" .
+ " " . 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' ) . ' '
+ . 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 ) .
+ ' ' .
+ Xml::radioLabel( wfMsg('maximum-size'), 'sizetype', 'max', 'wpmax', $max ) .
+ ' ' .
+ Xml::input( 'size', 9, $size, array( 'id' => 'wpsize' ) ) .
+ ' ' .
+ 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 ) . ' ' .
+ 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 ) . ' ' .
+ 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();
+}
--- /dev/null
+<?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 ) . " \n" .
+ $this->getNamespaceMenu( $namespace ) . " \n" .
+ // $this->getLevelMenu( $level ) . "<br/>\n" .
+ " " . 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' )
+ . ' '
+ . 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 ) . ' ' .
+ 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();
+}
--- /dev/null
+<?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 );
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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
--- /dev/null
+<?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 ) .
+ " <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("< ".$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 );
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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×tamp=" . $rev->getTimestamp() );
+ $difflink = '(' . $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml('diff'),
+ "target=$target&diff=prev×tamp=" . $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 );
+ }
+ }
+}
--- /dev/null
+<?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' ) ) .
+ " " .
+ $searchField .
+ " " .
+ $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;
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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')
+ );
+}
--- /dev/null
+<?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 );
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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() .
+ '×tamp=' .
+ 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> </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> </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() . "×tamp=$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()."×tamp=$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;
+ }
+}
--- /dev/null
+<?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> </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 );
+ }
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 = ' ';
+ 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'> </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>' );
+ }
+ }
+}
--- /dev/null
+<?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;
+ }
+ }
+}
--- /dev/null
+<?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 ) );
+ }
+}
--- /dev/null
+<?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();
+}
--- /dev/null
+<?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() );
+ }
+}
--- /dev/null
+<?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] = "<{$tags[$i]}>";
+ $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] );
+ }
+ }
+
+ /**#@-*/
+}
+
+/**#@-*/
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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' ) . ' ';
+ $form .= Xml::namespaceSelector( $nameSpace, '' ) . ' ';
+ $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 );
+}
--- /dev/null
+<?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' ) . ' ' .
+ 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( ' | ', $links ) );
+ }
+}
--- /dev/null
+<?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 );
+}
+++ /dev/null
-<?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')
- );
-}
+++ /dev/null
-<?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 );
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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() .
- '×tamp=' .
- 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> </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> </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() . "×tamp=$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()."×tamp=$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;
- }
-}
+++ /dev/null
-<?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> </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 );
- }
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 = ' ';
- 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'> </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>' );
- }
- }
-}
+++ /dev/null
-<?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;
- }
- }
-}
+++ /dev/null
-<?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 ) );
- }
-}
+++ /dev/null
-<?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();
-}
+++ /dev/null
-<?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() );
- }
-}
+++ /dev/null
-<?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] = "<{$tags[$i]}>";
- $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] );
- }
- }
-
- /**#@-*/
-}
-
-/**#@-*/
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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 );
-}
+++ /dev/null
-<?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' ) . ' ';
- $form .= Xml::namespaceSelector( $nameSpace, '' ) . ' ';
- $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 );
-}
+++ /dev/null
-<?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' ) . ' ' .
- 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( ' | ', $links ) );
- }
-}
+++ /dev/null
-<?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 );
-}