diff options
Diffstat (limited to 'includes/specials')
46 files changed, 1600 insertions, 849 deletions
diff --git a/includes/specials/SpecialAllmessages.php b/includes/specials/SpecialAllmessages.php index 0ff94b49..38181c08 100644 --- a/includes/specials/SpecialAllmessages.php +++ b/includes/specials/SpecialAllmessages.php @@ -10,7 +10,7 @@ */ function wfSpecialAllmessages() { global $wgOut, $wgRequest, $wgMessageCache, $wgTitle; - global $wgUseDatabaseMessages; + global $wgUseDatabaseMessages, $wgLang; # The page isn't much use if the MediaWiki namespace is not being used if( !$wgUseDatabaseMessages ) { @@ -49,16 +49,22 @@ function wfSpecialAllmessages() { $wgOut->addScriptFile( 'allmessages.js' ); if ( $ot == 'php' ) { $navText .= wfAllMessagesMakePhp( $messages ); - $wgOut->addHTML( 'PHP | <a href="' . $wgTitle->escapeLocalUrl( 'ot=html' ) . '">HTML</a> | ' . + $wgOut->addHTML( $wgLang->pipeList( array( + 'PHP', + '<a href="' . $wgTitle->escapeLocalUrl( 'ot=html' ) . '">HTML</a>', '<a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' . - '<pre>' . htmlspecialchars( $navText ) . '</pre>' ); + '<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->addHTML( $wgLang->pipeList( array( + '<a href="' . $wgTitle->escapeLocalUrl( 'ot=php' ) . '">PHP</a>', + 'HTML', + '<a href="' . $wgTitle->escapeLocalUrl( 'ot=xml' ) . '">XML</a>' + ) ) ); $wgOut->addWikiText( $navText ); $wgOut->addHTML( wfAllMessagesMakeHTMLText( $messages ) ); } diff --git a/includes/specials/SpecialAllpages.php b/includes/specials/SpecialAllpages.php index bf68dfa6..bded8835 100644 --- a/includes/specials/SpecialAllpages.php +++ b/includes/specials/SpecialAllpages.php @@ -27,7 +27,7 @@ class SpecialAllpages extends IncludableSpecialPage { protected $nsfromMsg = 'allpagesfrom'; function __construct( $name = 'Allpages' ){ - parent::__construct( $name ); + parent::__construct( $name ); } /** @@ -217,9 +217,9 @@ class SpecialAllpages extends IncludableSpecialPage { $out2 .= '<tr valign="top"><td>' . $nsForm; $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' . $wgUser->getSkin()->makeKnownLinkObj( $this->getTitle(), wfMsgHtml ( 'allpages' ) ); - $out2 .= "</td></tr></table><hr />"; + $out2 .= "</td></tr></table>"; } else { - $out2 = $nsForm . '<hr />'; + $out2 = $nsForm; } } $wgOut->addHTML( $out2 . $out ); @@ -237,8 +237,8 @@ class SpecialAllpages extends IncludableSpecialPage { $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) ); $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) ); // Don't let the length runaway - $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength, '...' ); - $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength, '...' ); + $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength ); + $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength ); $queryparams = $namespace ? "namespace=$namespace&" : ''; $special = $this->getTitle(); @@ -257,7 +257,7 @@ class SpecialAllpages extends IncludableSpecialPage { * @param string $to list all pages to this name (default FALSE) */ function showChunk( $namespace = NS_MAIN, $from = false, $to = false ) { - global $wgOut, $wgUser, $wgContLang; + global $wgOut, $wgUser, $wgContLang, $wgLang; $sk = $wgUser->getSkin(); @@ -382,7 +382,7 @@ class SpecialAllpages extends IncludableSpecialPage { . ( $namespace ? '&namespace=' . $namespace : '' ); $prevLink = $sk->makeKnownLinkObj( $self, wfMsgHTML( 'prevpage', htmlspecialchars( $pt ) ), $q ); - $out2 .= ' | ' . $prevLink; + $out2 = $wgLang->pipeList( array( $out2, $prevLink ) ); } if( $n == $this->maxPerPage && $s = $res->fetchObject() ) { @@ -392,9 +392,9 @@ class SpecialAllpages extends IncludableSpecialPage { . ( $namespace ? '&namespace=' . $namespace : '' ); $nextLink = $sk->makeKnownLinkObj( $self, wfMsgHtml( 'nextpage', htmlspecialchars( $t->getText() ) ), $q ); - $out2 .= ' | ' . $nextLink; + $out2 = $wgLang->pipeList( array( $out2, $nextLink ) ); } - $out2 .= "</td></tr></table><hr />"; + $out2 .= "</td></tr></table>"; } $wgOut->addHTML( $out2 . $out ); @@ -404,7 +404,7 @@ class SpecialAllpages extends IncludableSpecialPage { $wgOut->addHTML( $prevLink ); } if( isset( $prevLink ) && isset( $nextLink ) ) { - $wgOut->addHTML( ' | ' ); + $wgOut->addHTML( wfMsgExt( 'pipe-separator' , 'escapenoentities' ) ); } if( isset( $nextLink ) ) { $wgOut->addHTML( $nextLink ); diff --git a/includes/specials/SpecialBlankpage.php b/includes/specials/SpecialBlankpage.php index fdabe49d..29d6b96c 100644 --- a/includes/specials/SpecialBlankpage.php +++ b/includes/specials/SpecialBlankpage.php @@ -2,5 +2,5 @@ function wfSpecialBlankpage() { global $wgOut; - $wgOut->addHTML(wfMsg('intentionallyblankpage')); + $wgOut->addWikiMsg('intentionallyblankpage'); } diff --git a/includes/specials/SpecialBlockip.php b/includes/specials/SpecialBlockip.php index 4d82997f..0efaedf1 100644 --- a/includes/specials/SpecialBlockip.php +++ b/includes/specials/SpecialBlockip.php @@ -45,6 +45,8 @@ function wfSpecialBlockip( $par ) { class IPBlockForm { var $BlockAddress, $BlockExpiry, $BlockReason; # var $BlockEmail; + // The maximum number of edits a user can have and still be hidden + const HIDEUSER_CONTRIBLIMIT = 1000; function IPBlockForm( $par ) { global $wgRequest, $wgUser, $wgBlockAllowsUTEdit; @@ -106,6 +108,19 @@ class IPBlockForm { ( $currentBlock->mAddress == $this->BlockAddress ) ) ) { $wgOut->addWikiMsg( 'ipb-needreblock', $this->BlockAddress ); $alreadyBlocked = true; + # Set the block form settings to the existing block + $this->BlockAnonOnly = $currentBlock->mAnonOnly; + $this->BlockCreateAccount = $currentBlock->mCreateAccount; + $this->BlockEnableAutoblock = $currentBlock->mEnableAutoblock; + $this->BlockEmail = $currentBlock->mBlockEmail; + $this->BlockHideName = $currentBlock->mHideName; + $this->BlockAllowUsertalk = $currentBlock->mAllowUsertalk; + if( $currentBlock->mExpiry == 'infinity' ) { + $this->BlockOther = 'indefinite'; + } else { + $this->BlockOther = wfTimestamp( TS_ISO_8601, $currentBlock->mExpiry ); + } + $this->BlockReason = $currentBlock->mReason; } } @@ -238,11 +253,11 @@ class IPBlockForm { $wgOut->addHTML(" <tr id='wpEnableHideUser'> <td> </td> - <td class='mw-input'>" . + <td class='mw-input'><strong>" . Xml::checkLabel( wfMsg( 'ipbhidename' ), 'wpHideName', 'wpHideName', $this->BlockHideName, array( 'tabindex' => '10' ) ) . " - </td> + </strong></td> </tr>" ); } @@ -363,7 +378,7 @@ class IPBlockForm { $reasonstr = $this->BlockReasonList; if ( $reasonstr != 'other' && $this->BlockReason != '' ) { // Entry from drop down menu + additional comment - $reasonstr .= ': ' . $this->BlockReason; + $reasonstr .= wfMsgForContent( 'colon-separator' ) . $this->BlockReason; } elseif ( $reasonstr == 'other' ) { $reasonstr = $this->BlockReason; } @@ -381,9 +396,18 @@ class IPBlockForm { return array('ipb_expiry_invalid'); } - if( $this->BlockHideName && $expiry != 'infinity' ) { - // Bad expiry. - return array('ipb_expiry_temp'); + if( $this->BlockHideName ) { + if( !$userId ) { + // IP users should not be hidden + $this->BlockHideName = false; + } else if( $expiry !== 'infinity' ) { + // Bad expiry. + return array('ipb_expiry_temp'); + } else if( User::edits($userId) > self::HIDEUSER_CONTRIBLIMIT ) { + // Typically, the user should have a handful of edits. + // Disallow hiding users with many edits for performance. + return array('ipb_hide_invalid'); + } } # Create block @@ -391,13 +415,18 @@ class IPBlockForm { $block = new Block( $this->BlockAddress, $userId, $wgUser->getId(), $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly, $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName, - $this->BlockEmail, isset( $this->BlockAllowUsertalk ) ? $this->BlockAllowUsertalk : $wgBlockAllowsUTEdit ); + $this->BlockEmail, isset( $this->BlockAllowUsertalk ) ? $this->BlockAllowUsertalk : $wgBlockAllowsUTEdit + ); + # Should this be privately logged? + $suppressLog = (bool)$this->BlockHideName; if ( wfRunHooks('BlockIp', array(&$block, &$wgUser)) ) { - + # Try to insert block. Is there a conflicting block? if ( !$block->insert() ) { + # Show form unless the user is already aware of this... if ( !$this->BlockReblock ) { return array( 'ipb_already_blocked' ); + # Otherwise, try to update the block... } else { # This returns direct blocks before autoblocks/rangeblocks, since we should # be sure the user is blocked by now it should work for our purposes @@ -405,18 +434,40 @@ class IPBlockForm { if( $block->equals( $currentBlock ) ) { return array( 'ipb_already_blocked' ); } + # If the name was hidden and the blocking user cannot hide + # names, then don't allow any block changes... + if( $currentBlock->mHideName && !$wgUser->isAllowed('hideuser') ) { + return array( 'hookaborted' ); + } $currentBlock->delete(); $block->insert(); + # If hiding/unhiding a name, this should go in the private logs + $suppressLog = $suppressLog || (bool)$currentBlock->mHideName; $log_action = 'reblock'; + # Unset _deleted fields if requested + if( $currentBlock->mHideName && !$this->BlockHideName ) { + self::unsuppressUserName( $this->BlockAddress, $userId ); + } } } else { $log_action = 'block'; } wfRunHooks('BlockIpComplete', array($block, $wgUser)); - if ( $this->BlockWatchUser ) { + # Set *_deleted fields if requested + if( $this->BlockHideName ) { + self::suppressUserName( $this->BlockAddress, $userId ); + } + + if ( $this->BlockWatchUser && + # Only show watch link when this is no range block + $block->mRangeStart == $block->mRangeEnd) { $wgUser->addWatch ( Title::makeTitle( NS_USER, $this->BlockAddress ) ); } + + # Block constructor sanitizes certain block options on insert + $this->BlockEmail = $block->mBlockEmail; + $this->BlockEnableAutoblock = $block->mEnableAutoblock; # Prepare log parameters $logParams = array(); @@ -424,16 +475,70 @@ class IPBlockForm { $logParams[] = $this->blockLogFlags(); # Make log entry, if the name is hidden, put it in the oversight log - $log_type = ($this->BlockHideName) ? 'suppress' : 'block'; + $log_type = $suppressLog ? 'suppress' : 'block'; $log = new LogPage( $log_type ); $log->addEntry( $log_action, Title::makeTitle( NS_USER, $this->BlockAddress ), $reasonstr, $logParams ); # Report to the user return array(); - } - else + } else { return array('hookaborted'); + } + } + + public static function suppressUserName( $name, $userId ) { + $op = '|'; // bitwise OR + return self::setUsernameBitfields( $name, $userId, $op ); + } + + public static function unsuppressUserName( $name, $userId ) { + $op = '&'; // bitwise AND + return self::setUsernameBitfields( $name, $userId, $op ); + } + + private static function setUsernameBitfields( $name, $userId, $op ) { + if( $op !== '|' && $op !== '&' ) + return false; // sanity check + $dbw = wfGetDB( DB_MASTER ); + $delUser = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; + $delAction = LogPage::DELETED_ACTION | Revision::DELETED_RESTRICTED; + # Normalize user name + $userTitle = Title::makeTitleSafe( NS_USER, $name ); + $userDbKey = $userTitle->getDBKey(); + # To suppress, we OR the current bitfields with Revision::DELETED_USER + # to put a 1 in the username *_deleted bit. To unsuppress we AND the + # current bitfields with the inverse of Revision::DELETED_USER. The + # username bit is made to 0 (x & 0 = 0), while others are unchanged (x & 1 = x). + # The same goes for the sysop-restricted *_deleted bit. + if( $op == '&' ) { + $delUser = "~{$delUser}"; + $delAction = "~{$delAction}"; + } + # Hide name from live edits + $dbw->update( 'revision', array("rev_deleted = rev_deleted $op $delUser"), + array('rev_user' => $userId), __METHOD__ ); + # Hide name from deleted edits + $dbw->update( 'archive', array("ar_deleted = ar_deleted $op $delUser"), + array('ar_user_text' => $name), __METHOD__ ); + # Hide name from logs + $dbw->update( 'logging', array("log_deleted = log_deleted $op $delUser"), + array('log_user' => $userId, "log_type != 'suppress'"), __METHOD__ ); + $dbw->update( 'logging', array("log_deleted = log_deleted $op $delAction"), + array('log_namespace' => NS_USER, 'log_title' => $userDbKey, + "log_type != 'suppress'"), __METHOD__ ); + # Hide name from RC + $dbw->update( 'recentchanges', array("rc_deleted = rc_deleted $op $delUser"), + array('rc_user_text' => $name), __METHOD__ ); + # Hide name from live images + $dbw->update( 'oldimage', array("oi_deleted = oi_deleted $op $delUser"), + array('oi_user_text' => $name), __METHOD__ ); + # Hide name from deleted images + # WMF - schema change pending + # $dbw->update( 'filearchive', array("fa_deleted = fa_deleted $op $delUser"), + # array('fa_user_text' => $name), __METHOD__ ); + # Done! + return true; } /** @@ -487,7 +592,7 @@ class IPBlockForm { global $wgBlockAllowsUTEdit; $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 + // 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'; @@ -497,6 +602,8 @@ class IPBlockForm { $flags[] = 'noemail'; if ( !$this->BlockAllowUsertalk && $wgBlockAllowsUTEdit ) $flags[] = 'nousertalk'; + if ( $this->BlockHideName ) + $flags[] = 'hiddenname'; return implode( ',', $flags ); } @@ -506,14 +613,14 @@ class IPBlockForm { * @return string */ private function getConvenienceLinks() { - global $wgUser; + global $wgUser, $wgLang; $skin = $wgUser->getSkin(); if( $this->BlockAddress ) $links[] = $this->getContribsLink( $skin ); $links[] = $this->getUnblockLink( $skin ); $links[] = $this->getBlockListLink( $skin ); $links[] = $skin->makeLink ( 'MediaWiki:Ipbreason-dropdown', wfMsgHtml( 'ipb-edit-dropdown' ) ); - return '<p class="mw-ipb-conveniencelinks">' . implode( ' | ', $links ) . '</p>'; + return '<p class="mw-ipb-conveniencelinks">' . $wgLang->pipeList( $links ) . '</p>'; } /** diff --git a/includes/specials/SpecialBooksources.php b/includes/specials/SpecialBooksources.php index 12b119d8..db466c14 100644 --- a/includes/specials/SpecialBooksources.php +++ b/includes/specials/SpecialBooksources.php @@ -34,7 +34,7 @@ class SpecialBookSources extends SpecialPage { $wgOut->addWikiMsg( 'booksources-summary' ); $wgOut->addHTML( $this->makeForm() ); if( strlen( $this->isbn ) > 0 ) { - if( !$this->isValidIsbn( $this->isbn ) ) { + if( !self::isValidISBN( $this->isbn ) ) { $wgOut->wrapWikiMsg( '<div class="error">$1</div>', 'booksources-invalid-isbn' ); } $this->showList(); diff --git a/includes/specials/SpecialCategories.php b/includes/specials/SpecialCategories.php index 8c2ae2b6..c6e73f2b 100644 --- a/includes/specials/SpecialCategories.php +++ b/includes/specials/SpecialCategories.php @@ -44,7 +44,6 @@ class CategoryPager extends AlphabeticPager { } function getQueryInfo() { - global $wgRequest; return array( 'tables' => array( 'category' ), 'fields' => array( 'cat_title','cat_pages' ), @@ -61,6 +60,7 @@ class CategoryPager extends AlphabeticPager { function getDefaultQuery() { parent::getDefaultQuery(); unset( $this->mDefaultQuery['from'] ); + return $this->mDefaultQuery; } # protected function getOrderTypeMessages() { # return array( 'abc' => 'special-categories-sort-abc', diff --git a/includes/specials/SpecialConfirmemail.php b/includes/specials/SpecialConfirmemail.php index e19aa99b..9c6f857d 100644 --- a/includes/specials/SpecialConfirmemail.php +++ b/includes/specials/SpecialConfirmemail.php @@ -68,13 +68,13 @@ class EmailConfirmation extends UnlistedSpecialPage { $wgOut->addWikiMsg( 'emailauthenticated', $time, $d, $t ); } if( $wgUser->isEmailConfirmationPending() ) { - $wgOut->addWikiMsg( 'confirmemail_pending' ); + $wgOut->wrapWikiMsg( "<div class=\"error mw-confirmemail-pending\">$1</div>", 'confirmemail_pending' ); } $wgOut->addWikiMsg( 'confirmemail_text' ); $self = SpecialPage::getTitleFor( 'Confirmemail' ); $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) ); $form .= Xml::hidden( 'token', $wgUser->editToken() ); - $form .= Xml::submitButton( wfMsgHtml( 'confirmemail_send' ) ); + $form .= Xml::submitButton( wfMsg( 'confirmemail_send' ) ); $form .= Xml::closeElement( 'form' ); $wgOut->addHTML( $form ); } diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php index 3d8c18dd..9263336e 100644 --- a/includes/specials/SpecialContributions.php +++ b/includes/specials/SpecialContributions.php @@ -35,7 +35,7 @@ class SpecialContributions extends SpecialPage { } if( !strlen( $target ) ) { - $wgOut->addHTML( $this->getForm( '' ) ); + $wgOut->addHTML( $this->getForm() ); return; } @@ -44,7 +44,7 @@ class SpecialContributions extends SpecialPage { $nt = Title::makeTitleSafe( NS_USER, $target ); if( !$nt ) { - $wgOut->addHTML( $this->getForm( '' ) ); + $wgOut->addHTML( $this->getForm() ); return; } $id = User::idFromName( $nt->getText() ); @@ -52,7 +52,7 @@ class SpecialContributions extends SpecialPage { if( $target != 'newbies' ) { $target = $nt->getText(); $wgOut->setSubtitle( $this->contributionsSub( $nt, $id ) ); - $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsg( 'contributions-title', $target ) ) ); + $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsgExt( 'contributions-title', array( 'parsemag' ),$target ) ) ); } else { $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') ); $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsg( 'sp-contributions-newbies-title' ) ) ); @@ -63,6 +63,8 @@ class SpecialContributions extends SpecialPage { } else { $this->opts['namespace'] = ''; } + + $this->opts['tagfilter'] = (string) $wgRequest->getVal( 'tagfilter' ); // Allows reverts to have the bot flag in recent changes. It is just here to // be passed in the form at the top of the page @@ -72,27 +74,12 @@ class SpecialContributions extends SpecialPage { $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev'; # Offset overrides year/month selection - if( ( $month = $wgRequest->getIntOrNull( 'month' ) ) !== null && $month !== -1 ) { - $this->opts['month'] = intval( $month ); - } else { - $this->opts['month'] = ''; - } - if( ( $year = $wgRequest->getIntOrNull( 'year' ) ) !== null ) { - $this->opts['year'] = intval( $year ); - } else if( $this->opts['month'] ) { - $thisMonth = intval( gmdate( 'n' ) ); - $thisYear = intval( gmdate( 'Y' ) ); - if( intval( $this->opts['month'] ) > $thisMonth ) { - $thisYear--; - } - $this->opts['year'] = $thisYear; - } else { - $this->opts['year'] = ''; - } - if( $skip ) { $this->opts['year'] = ''; $this->opts['month'] = ''; + } else { + $this->opts['year'] = $wgRequest->getIntOrNull( 'year' ); + $this->opts['month'] = $wgRequest->getIntOrNull( 'month' ); } // Add RSS/atom links @@ -104,11 +91,11 @@ class SpecialContributions extends SpecialPage { wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id ); - $wgOut->addHTML( $this->getForm( $this->opts ) ); + $wgOut->addHTML( $this->getForm() ); $pager = new ContribsPager( $target, $this->opts['namespace'], $this->opts['year'], $this->opts['month'] ); if( !$pager->getNumRows() ) { - $wgOut->addWikiMsg( 'nocontribs' ); + $wgOut->addWikiMsg( 'nocontribs', $target ); return; } @@ -177,18 +164,27 @@ class SpecialContributions extends SpecialPage { wfMsgHtml( 'sp-contributions-blocklog' ), 'type=block&page=' . $nt->getPrefixedUrl() ); } # Other logs link - $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), - 'user=' . $nt->getPartialUrl() ); + $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsg( 'sp-contributions-logs' ), + 'user=' . $nt->getPartialUrl() ); # Add link to deleted user contributions for priviledged users if( $wgUser->isAllowed( 'deletedhistory' ) ) { - $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'DeletedContributions', + $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'DeletedContributions', $nt->getDBkey() ), wfMsgHtml( 'deletedcontributions' ) ); } - + + # Add a link to change user rights for privileged users + $userrightsPage = new UserrightsPage(); + if( 0 !== $id && $userrightsPage->userCanChangeRights( User::newFromId( $id ) ) ) { + $tools[] = $sk->makeKnownLinkObj( + SpecialPage::getTitleFor( 'Userrights', $nt->getDBkey() ), + wfMsgHtml( 'userrights' ) + ); + } + wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) ); - $links = implode( ' | ', $tools ); + $links = $wgLang->pipeList( $tools ); } // Old message 'contribsub' had one parameter, but that doesn't work for @@ -235,15 +231,21 @@ class SpecialContributions extends SpecialPage { if( $this->opts['contribs'] == 'newbie' ) { $this->opts['target'] = ''; } + + if( !isset( $this->opts['tagfilter'] ) ) { + $this->opts['tagfilter'] = ''; + } $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); - + # Add hidden params for tracking foreach ( $this->opts as $name => $value ) { if( in_array( $name, array( 'namespace', 'target', 'contribs', 'year', 'month' ) ) ) { continue; } $f .= "\t" . Xml::hidden( $name, $value ) . "\n"; } + + $tagFilter = ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] ); $f .= '<fieldset>' . Xml::element( 'legend', array(), wfMsg( 'sp-contributions-search' ) ) . @@ -256,16 +258,11 @@ class SpecialContributions extends SpecialPage { Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' ' . Xml::namespaceSelector( $this->opts['namespace'], '' ) . '</span>' . + ( $tagFilter ? Xml::tags( 'p', null, implode( ' ', $tagFilter ) ) : '' ) . Xml::openElement( 'p' ) . '<span style="white-space: nowrap">' . - Xml::label( wfMsg( 'year' ), 'year' ) . ' '. - Xml::input( 'year', 4, $this->opts['year'], array('id' => 'year', 'maxlength' => 4) ) . - '</span>' . - ' '. - '<span style="white-space: nowrap">' . - Xml::label( wfMsg( 'month' ), 'month' ) . ' '. - Xml::monthSelector( $this->opts['month'], -1 ) . ' '. - '</span>' . + Xml::dateMenu( $this->opts['year'], $this->opts['month'] ) . + '</span>' . ' ' . Xml::submitButton( wfMsg( 'sp-contributions-submit' ) ) . Xml::closeElement( 'p' ); @@ -278,7 +275,7 @@ class SpecialContributions extends SpecialPage { return $f; } - /** + /** * Output a subscription feed listing recent edits to this page. * @param string $type */ @@ -300,14 +297,15 @@ class SpecialContributions extends SpecialPage { $feed = new $wgFeedClasses[$type]( $this->feedTitle(), wfMsgExt( 'tagline', 'parsemag' ), - $this->getTitle()->getFullUrl() ); + $this->getTitle()->getFullUrl() . "/" . urlencode($this->opts['target']) + ); // Already valid title $nt = Title::makeTitleSafe( NS_USER, $this->opts['target'] ); $target = $this->opts['target'] == 'newbies' ? 'newbies' : $nt->getText(); $pager = new ContribsPager( $target, $this->opts['namespace'], - $this->opts['year'], $this->opts['month'] ); + $this->opts['year'], $this->opts['month'], $this->opts['tagfilter'] ); $pager->mLimit = min( $this->opts['limit'], $wgFeedLimit ); @@ -353,7 +351,7 @@ class SpecialContributions extends SpecialPage { protected function feedItemDesc( $revision ) { if( $revision ) { - return '<p>' . htmlspecialchars( $revision->getUserText() ) . ': ' . + return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) . htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . "</p>\n<hr />\n<div>" . nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>"; @@ -371,13 +369,14 @@ class ContribsPager extends ReverseChronologicalPager { var $messages, $target; var $namespace = '', $mDb; - function __construct( $target, $namespace = false, $year = false, $month = false ) { + function __construct( $target, $namespace = false, $year = false, $month = false, $tagFilter = 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; + $this->tagFilter = $tagFilter; $this->getDateCond( $year, $month ); @@ -391,19 +390,35 @@ class ContribsPager extends ReverseChronologicalPager { } function getQueryInfo() { + global $wgUser; list( $tables, $index, $userCond, $join_cond ) = $this->getUserCond(); - $conds = array_merge( array('page_id=rev_page'), $userCond, $this->getNamespaceCond() ); + + $conds = array_merge( $userCond, $this->getNamespaceCond() ); + // Paranoia: avoid brute force searches (bug 17342) + if( !$wgUser->isAllowed( 'suppressrevision' ) ) { + $conds[] = 'rev_deleted & ' . Revision::DELETED_USER . ' = 0'; + } + $join_cond['page'] = array( 'INNER JOIN', 'page_id=rev_page' ); + $queryInfo = array( 'tables' => $tables, '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' + 'page_namespace', 'page_title', 'page_is_new', 'page_latest', 'page_is_redirect', + 'page_len','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' => array('revision' => $index) ), 'join_conds' => $join_cond ); + + ChangeTags::modifyDisplayQuery( $queryInfo['tables'], + $queryInfo['fields'], + $queryInfo['conds'], + $queryInfo['join_conds'], + $queryInfo['options'], + $this->tagFilter ); + wfRunHooks( 'ContribsPager::getQueryInfo', array( &$this, &$queryInfo ) ); return $queryInfo; } @@ -458,36 +473,38 @@ class ContribsPager extends ReverseChronologicalPager { * @todo This would probably look a lot nicer in a table. */ function formatRow( $row ) { - wfProfileIn( __METHOD__ ); - global $wgLang, $wgUser, $wgContLang; + wfProfileIn( __METHOD__ ); $sk = $this->getSkin(); $rev = new Revision( $row ); + $classes = array(); - $page = Title::makeTitle( $row->page_namespace, $row->page_title ); - $link = $sk->makeKnownLinkObj( $page ); + $page = Title::newFromRow( $row ); + $page->resetArticleId( $row->rev_page ); // use process cache + $link = $sk->makeLinkObj( $page, $page->getPrefixedText(), $page->isRedirect() ? 'redirect=no' : '' ); + # Mark current revisions $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' ) . ')'; + # Add rollback link + if( $page->quickUserCan( 'rollback') && $page->quickUserCan( 'edit' ) ) { + $topmarktext .= ' '.$sk->generateRollback( $rev ); + } } else { $difftext .= $this->messages['newarticle']; } - - if( $page->userCan( 'rollback') && $page->userCan( 'edit' ) ) { - $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 ) . ')'; + $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' ) . ')'; + $histlink = '('.$sk->makeKnownLinkObj( $page, $this->messages['hist'], 'action=history' ) . ')'; $comment = $wgContLang->getDirMark() . $sk->revComment( $rev, false, true ); $date = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true ); @@ -510,7 +527,7 @@ class ContribsPager extends ReverseChronologicalPager { $nflag = ''; } - if( $row->rev_minor_edit ) { + if( $rev->isMinor() ) { $mflag = '<span class="minor">' . $this->messages['minoreditletter'] . '</span> '; } else { $mflag = ''; @@ -520,10 +537,17 @@ class ContribsPager extends ReverseChronologicalPager { if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { $ret .= ' ' . wfMsgHtml( 'deletedrev' ); } + + # Tags, if any. + list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'contributions' ); + $classes = array_merge( $classes, $newClasses ); + $ret .= " $tagSummary"; + // Let extensions add data wfRunHooks( 'ContributionsLineEnding', array( &$this, &$ret, $row ) ); - - $ret = "<li>$ret</li>\n"; + + $classes = implode( ' ', $classes ); + $ret = "<li class=\"$classes\">$ret</li>\n"; wfProfileOut( __METHOD__ ); return $ret; } diff --git a/includes/specials/SpecialDeletedContributions.php b/includes/specials/SpecialDeletedContributions.php index 513d25e2..67b05ca1 100644 --- a/includes/specials/SpecialDeletedContributions.php +++ b/includes/specials/SpecialDeletedContributions.php @@ -26,9 +26,13 @@ class DeletedContribsPager extends IndexPager { } function getQueryInfo() { + global $wgUser; list( $index, $userCond ) = $this->getUserCond(); $conds = array_merge( $userCond, $this->getNamespaceCond() ); - + // Paranoia: avoid brute force searches (bug 17792) + if( !$wgUser->isAllowed( 'suppressrevision' ) ) { + $conds[] = 'ar_deleted & ' . Revision::DELETED_USER . ' = 0'; + } return array( 'tables' => array( 'archive' ), 'fields' => array( @@ -36,7 +40,7 @@ class DeletedContribsPager extends IndexPager { 'ar_user', 'ar_user_text', 'ar_deleted' ), 'conds' => $conds, - 'options' => array( 'FORCE INDEX' => $index ) + 'options' => array( 'USE INDEX' => $index ) ); } @@ -62,6 +66,8 @@ class DeletedContribsPager extends IndexPager { } function getNavigationBar() { + global $wgLang; + if ( isset( $this->mNavigationBar ) ) { return $this->mNavigationBar; } @@ -74,9 +80,9 @@ class DeletedContribsPager extends IndexPager { $pagingLinks = $this->getPagingLinks( $linkTexts ); $limitLinks = $this->getLimitLinks(); - $limits = implode( ' | ', $limitLinks ); + $limits = $wgLang->pipeList( $limitLinks ); - $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " . + $this->mNavigationBar = "(" . $wgLang->pipeList( array( $pagingLinks['first'], $pagingLinks['last'] ) ) . ") " . wfMsgExt( 'viewprevnext', array( 'parsemag' ), $pagingLinks['prev'], $pagingLinks['next'], $limits ); return $this->mNavigationBar; } @@ -113,7 +119,7 @@ class DeletedContribsPager extends IndexPager { 'user_text' => $row->ar_user_text, 'timestamp' => $row->ar_timestamp, 'minor_edit' => $row->ar_minor_edit, - 'rev_deleted' => $row->ar_deleted, + 'deleted' => $row->ar_deleted, ) ); $page = Title::makeTitle( $row->ar_namespace, $row->ar_title ); @@ -204,6 +210,8 @@ class DeletedContributionsPage extends SpecialPage { global $wgUser, $wgOut, $wgLang, $wgRequest; + $wgOut->setPageTitle( wfMsgExt( 'deletedcontributions-title', array( 'parsemag' ) ) ); + $options = array(); if ( isset( $par ) ) { @@ -306,7 +314,7 @@ class DeletedContributionsPage extends SpecialPage { wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) ); - $links = implode( ' | ', $tools ); + $links = $wgLang->pipeList( $tools ); } // Old message 'contribsub' had one parameter, but that doesn't work for diff --git a/includes/specials/SpecialEmailuser.php b/includes/specials/SpecialEmailuser.php index cf90f94d..58e2514e 100644 --- a/includes/specials/SpecialEmailuser.php +++ b/includes/specials/SpecialEmailuser.php @@ -241,7 +241,7 @@ class EmailUserForm { $user = $this->target; $wgOut->setPagetitle( wfMsg( "emailsent" ) ); - $wgOut->addHTML( wfMsg( "emailsenttext" ) ); + $wgOut->addWikiMsg( 'emailsenttext' ); $wgOut->returnToMain( false, $user->getUserPage() ); } diff --git a/includes/specials/SpecialExport.php b/includes/specials/SpecialExport.php index 898b5a78..8bf16a71 100644 --- a/includes/specials/SpecialExport.php +++ b/includes/specials/SpecialExport.php @@ -21,208 +21,197 @@ * @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; +class SpecialExport extends SpecialPage { + + private $curonly, $doExport, $pageLinkDepth, $templates; + private $images; + + public function __construct() { + parent::__construct( 'Export' ); } - $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_FILE . ' 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; + + public function execute( $par ) { + global $wgOut, $wgRequest, $wgSitename, $wgExportAllowListContributors; + global $wgExportAllowHistory, $wgExportMaxHistory, $wgExportMaxLinkDepth; + global $wgExportFromNamespaces; + + $this->setHeaders(); + $this->outputHeader(); + + // Set some variables + $this->curonly = true; + $this->doExport = false; + $this->templates = $wgRequest->getCheck( 'templates' ); + $this->images = $wgRequest->getCheck( 'images' ); // Doesn't do anything yet + $this->pageLinkDepth = $this->validateLinkDepth( + $wgRequest->getIntOrNull( 'pagelink-depth' ) ); + + if ( $wgRequest->getCheck( 'addcat' ) ) { + $page = $wgRequest->getText( 'pages' ); + $catname = $wgRequest->getText( 'catname' ); + + if ( $catname !== '' && $catname !== NULL && $catname !== false ) { + $t = Title::makeTitleSafe( NS_MAIN, $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 = $this->getPagesFromCategory( $t ); + if ( $catpages ) $page .= "\n" . implode( "\n", $catpages ); + } } } - } - 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_MAIN, $catname ); - if ( $t ) { + else if( $wgRequest->getCheck( 'addns' ) && $wgExportFromNamespaces ) { + $page = $wgRequest->getText( 'pages' ); + $nsindex = $wgRequest->getText( 'nsindex' ); + + if ( $nsindex !== '' && $nsindex !== NULL && $nsindex !== false ) { /** - * @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. + * Same implementation as above, so same @fixme */ - $catpages = wfExportGetPagesFromCategory( $t ); - if ( $catpages ) $page .= "\n" . implode( "\n", $catpages ); - } + $nspages = $this->getPagesFromNamespace( $nsindex ); + if ( $nspages ) $page .= "\n" . implode( "\n", $nspages ); + } } - } - 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 if( $wgRequest->wasPosted() && $par == '' ) { + $page = $wgRequest->getText( 'pages' ); + $this->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 ( $this->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 != '' ) $this->doExport = true; } else { - $offset = null; + // Default to current-only for GET requests + $page = $wgRequest->getText( 'pages', $par ); + $historyCheck = $wgRequest->getCheck( 'history' ); + if( $historyCheck ) { + $history = WikiExporter::FULL; + } else { + $history = WikiExporter::CURRENT; + } + + if( $page != '' ) $this->doExport = true; } - $limit = $wgRequest->getInt( 'limit' ); - $dir = $wgRequest->getVal( 'dir' ); - $history = array( - 'dir' => 'asc', - 'offset' => false, - 'limit' => $wgExportMaxHistory, - ); - $historyCheck = $wgRequest->getCheck( 'history' ); - if ( $curonly ) { + + if( !$wgExportAllowHistory ) { + // Override $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'; + } + + $list_authors = $wgRequest->getCheck( 'listauthors' ); + if ( !$this->curonly || !$wgExportAllowListContributors ) $list_authors = false ; + + if ( $this->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}" ); } + $this->doExport( $page, $history, $list_authors ); + return; } - - 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; + + $wgOut->addWikiMsg( 'exporttext' ); + + $form = Xml::openElement( 'form', array( 'method' => 'post', + 'action' => $this->getTitle()->getLocalUrl( 'action=submit' ) ) ); + $form .= Xml::inputLabel( wfMsg( 'export-addcattext' ) , 'catname', 'catname', 40 ) . ' '; + $form .= Xml::submitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />'; + + if ( $wgExportFromNamespaces ) { + $form .= Xml::namespaceSelector( '', null, 'nsindex', wfMsg( 'export-addnstext' ) ) . ' '; + $form .= Xml::submitButton( wfMsg( 'export-addns' ), array( 'name' => 'addns' ) ) . '<br />'; + } + + $form .= Xml::element( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ), $page, false ); + $form .= '<br />'; + + if( $wgExportAllowHistory ) { + $form .= Xml::checkLabel( wfMsg( 'exportcuronly' ), 'curonly', 'curonly', true ) . '<br />'; } else { - $history = WikiExporter::CURRENT; + $wgOut->addHTML( wfMsgExt( 'exportnohistory', 'parse' ) ); } - - if( $page != '' ) $doexport = true; + $form .= Xml::checkLabel( wfMsg( 'export-templates' ), 'templates', 'wpExportTemplates', false ) . '<br />'; + if( $wgExportMaxLinkDepth || $this->userCanOverrideExportDepth() ) { + $form .= Xml::inputLabel( wfMsg( 'export-pagelinks' ), 'pagelink-depth', 'pagelink-depth', 20, 0 ) . '<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' ), array( 'accesskey' => 's' ) ); + $form .= Xml::closeElement( 'form' ); + $wgOut->addHTML( $form ); } + + private function userCanOverrideExportDepth() { + global $wgUser; - if( !$wgExportAllowHistory ) { - // Override - $history = WikiExporter::CURRENT; + return $wgUser->isAllowed( 'override-export-depth' ); } - - $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}" ); - } - + + /** + * Do the actual page exporting + * @param string $page User input on what page(s) to export + * @param mixed $history one of the WikiExporter history export constants + */ + private function doExport( $page, $history, $list_authors ) { + global $wgExportMaxHistory; + /* Split up the input and look up linked pages */ - $inputPages = array_filter( explode( "\n", $page ), 'wfFilterPage' ); + $inputPages = array_filter( explode( "\n", $page ), array( $this, 'filterPage' ) ); $pageSet = array_flip( $inputPages ); - - if( $wgRequest->getCheck( 'templates' ) ) { - $pageSet = wfExportGetTemplates( $inputPages, $pageSet ); + + if( $this->templates ) { + $pageSet = $this->getTemplates( $inputPages, $pageSet ); } - - /* - // Enable this when we can do something useful exporting/importing image information. :) - if( $wgRequest->getCheck( 'images' ) ) { - $pageSet = wfExportGetImages( $inputPages, $pageSet ); + + if( $linkDepth = $this->pageLinkDepth ) { + $pageSet = $this->getPageLinks( $inputPages, $pageSet, $linkDepth ); } - */ - + + /* + // Enable this when we can do something useful exporting/importing image information. :) + if( $this->images ) ) { + $pageSet = $this->getImages( $inputPages, $pageSet ); + } + */ + $pages = array_keys( $pageSet ); - + /* Ok, let's get to it... */ - if( $history == WikiExporter::CURRENT ) { $lb = false; $db = wfGetDB( DB_SLAVE ); @@ -238,65 +227,177 @@ function wfSpecialExport( $page = '' ) { set_time_limit(0); wfRestoreWarnings(); } - $exporter = new WikiExporter( $db, $history, $buffer ); - $exporter->list_authors = $list_authors ; + $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; - } - } - }*/ - + if( $wgExportMaxHistory && !$this->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(); if( $lb ) { $lb->closeAll(); } - 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' ) ); + + + private function getPagesFromCategory( $title ) { + global $wgContLang; + + $name = $title->getDBkey(); + + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( array('page', 'categorylinks' ), + array( 'page_namespace', 'page_title' ), + array('cl_from=page_id', 'cl_to' => $name ), + __METHOD__, array('LIMIT' => '5000')); + + $pages = array(); + 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; + } + + private function getPagesFromNamespace( $nsindex ) { + global $wgContLang; + + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( 'page', array('page_namespace', 'page_title'), + array('page_namespace' => $nsindex), + __METHOD__, array('LIMIT' => '5000') ); + + $pages = array(); + 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 + */ + private function getTemplates( $inputPages, $pageSet ) { + return $this->getLinks( $inputPages, $pageSet, + 'templatelinks', + array( 'tl_namespace AS namespace', 'tl_title AS title' ), + array( 'page_id=tl_from' ) ); + } + + /** + * Validate link depth setting, if available. + */ + private function validateLinkDepth( $depth ) { + global $wgExportMaxLinkDepth, $wgExportMaxLinkDepthLimit; + if( $depth < 0 ) { + return 0; + } + if ( !$this->userCanOverrideExportDepth() ) { + if( $depth > $wgExportMaxLinkDepth ) { + return $wgExportMaxLinkDepth; + } + } + /* + * There's a HARD CODED limit of 5 levels of recursion here to prevent a + * crazy-big export from being done by someone setting the depth + * number too high. In other words, last resort safety net. + */ + return intval( min( $depth, 5 ) ); + } + + /** Expand a list of pages to include pages linked to from that page. */ + private function getPageLinks( $inputPages, $pageSet, $depth ) { + for( $depth=$depth; $depth>0; --$depth ) { + $pageSet = $this->getLinks( $inputPages, $pageSet, 'pagelinks', + array( 'pl_namespace AS namespace', 'pl_title AS title' ), + array( 'page_id=pl_from' ) ); + $inputPages = array_keys( $pageSet ); + } + return $pageSet; + } + + /** + * 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 + */ + private function getImages( $inputPages, $pageSet ) { + return $this->getLinks( $inputPages, $pageSet, + 'imagelinks', + array( NS_FILE . ' AS namespace', 'il_to AS title' ), + array( 'page_id=il_from' ) ); + } + + /** + * Expand a list of pages to include items used in those pages. + * @private + */ + private function getLinks( $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. + */ + private function filterPage( $page ) { + return $page !== '' && $page !== null; } - $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' ), array( 'accesskey' => 's' ) ); - $form .= Xml::closeElement( 'form' ); - $wgOut->addHTML( $form ); } + diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php index 49a218c8..4fde0a60 100644 --- a/includes/specials/SpecialFileDuplicateSearch.php +++ b/includes/specials/SpecialFileDuplicateSearch.php @@ -64,7 +64,7 @@ class FileDuplicateSearchPage extends QueryPage { * Output the HTML search form, and constructs the FileDuplicateSearch object. */ function wfSpecialFileDuplicateSearch( $par = null ) { - global $wgRequest, $wgTitle, $wgOut, $wgLang, $wgContLang; + global $wgRequest, $wgOut, $wgLang, $wgContLang, $wgScript; $hash = ''; $filename = isset( $par ) ? $par : $wgRequest->getText( 'filename' ); @@ -85,7 +85,8 @@ function wfSpecialFileDuplicateSearch( $par = null ) { # Create the input form $wgOut->addHTML( - Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgTitle->getLocalUrl() ) ) . + Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgScript ) ) . + Xml::hidden( 'title', SpecialPage::getTitleFor( 'FileDuplicateSearch' )->getPrefixedDbKey() ) . Xml::openElement( 'fieldset' ) . Xml::element( 'legend', null, wfMsg( 'fileduplicatesearch-legend' ) ) . Xml::inputLabel( wfMsg( 'fileduplicatesearch-filename' ), 'filename', 'filename', 50, $filename ) . ' ' . diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 5e1a6533..457e03b4 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -30,6 +30,7 @@ class SpecialImport extends SpecialPage { private $frompage = ''; private $logcomment= false; private $history = true; + private $includeTemplates = false; /** * Constructor @@ -65,12 +66,13 @@ class SpecialImport extends SpecialPage { * Do the actual import */ private function doImport() { - global $wgOut, $wgRequest, $wgUser, $wgImportSources; + global $wgOut, $wgRequest, $wgUser, $wgImportSources, $wgExportMaxLinkDepth; $isUpload = false; $this->namespace = $wgRequest->getIntOrNull( 'namespace' ); $sourceName = $wgRequest->getVal( "source" ); $this->logcomment = $wgRequest->getText( 'log-comment' ); + $this->pageLinkDepth = $wgExportMaxLinkDepth == 0 ? 0 : $wgRequest->getIntOrNull( 'pagelink-depth' ); if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'editToken' ) ) ) { $source = new WikiErrorMsg( 'import-token-mismatch' ); @@ -88,10 +90,13 @@ class SpecialImport extends SpecialPage { } else { $this->history = $wgRequest->getCheck( 'interwikiHistory' ); $this->frompage = $wgRequest->getText( "frompage" ); + $this->includeTemplates = $wgRequest->getCheck( 'interwikiTemplates' ); $source = ImportStreamSource::newFromInterwiki( $this->interwiki, $this->frompage, - $this->history ); + $this->history, + $this->includeTemplates, + $this->pageLinkDepth ); } } else { $source = new WikiErrorMsg( "importunknownsource" ); @@ -127,9 +132,7 @@ class SpecialImport extends SpecialPage { } private function showForm() { - global $wgUser, $wgOut, $wgRequest, $wgTitle, $wgImportSources; - # FIXME: Quick hack to disable import for non privileged users /Raymond - # Regression from 43963 + global $wgUser, $wgOut, $wgRequest, $wgTitle, $wgImportSources, $wgExportMaxLinkDepth; if( !$wgUser->isAllowed( 'import' ) && !$wgUser->isAllowed( 'importupload' ) ) return $wgOut->permissionRequired( 'import' ); @@ -138,9 +141,9 @@ class SpecialImport extends SpecialPage { 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::fieldset( wfMsg( 'import-upload' ) ). + Xml::openElement( 'form', array( 'enctype' => 'multipart/form-data', 'method' => 'post', + 'action' => $action, 'id' => 'mw-import-upload-form' ) ) . Xml::hidden( 'action', 'submit' ) . Xml::hidden( 'source', 'upload' ) . Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) . @@ -164,7 +167,7 @@ class SpecialImport extends SpecialPage { </tr> <tr> <td></td> - <td class='mw-input'>" . + <td class='mw-submit'>" . Xml::submitButton( wfMsg( 'uploadbtn' ) ) . "</td> </tr>" . @@ -180,10 +183,22 @@ class SpecialImport extends SpecialPage { } if( $wgUser->isAllowed( 'import' ) && !empty( $wgImportSources ) ) { + # Show input field for import depth only if $wgExportMaxLinkDepth > 0 + $importDepth = ''; + if( $wgExportMaxLinkDepth > 0 ) { + $importDepth = "<tr> + <td class='mw-label'>" . + wfMsgExt( 'export-pagelinks', 'parseinline' ) . + "</td> + <td class='mw-input'>" . + Xml::input( 'pagelink-depth', 3, 0 ) . + "</td> + </tr>"; + } + $wgOut->addHTML( - Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', null, wfMsg( 'importinterwiki' ) ) . - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action ) ) . + Xml::fieldset( wfMsg( 'importinterwiki' ) ) . + Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'mw-import-interwiki-form' ) ) . wfMsgExt( 'import-interwiki-text', array( 'parse' ) ) . Xml::hidden( 'action', 'submit' ) . Xml::hidden( 'source', 'interwiki' ) . @@ -200,6 +215,7 @@ class SpecialImport extends SpecialPage { $selected = ( $this->interwiki === $prefix ) ? ' selected="selected"' : ''; $wgOut->addHTML( Xml::option( $prefix, $prefix, $selected ) ); } + $wgOut->addHTML( Xml::closeElement( 'select' ) . Xml::input( 'frompage', 50, $this->frompage ) . @@ -213,7 +229,15 @@ class SpecialImport extends SpecialPage { "</td> </tr> <tr> - <td>" . + <td> + </td> + <td class='mw-input'>" . + Xml::checkLabel( wfMsg( 'import-interwiki-templates' ), 'interwikiTemplates', 'interwikiTemplates', $this->includeTemplates ) . + "</td> + </tr> + $importDepth + <tr> + <td class='mw-label'>" . Xml::label( wfMsg( 'import-interwiki-namespace' ), 'namespace' ) . "</td> <td class='mw-input'>" . @@ -232,7 +256,7 @@ class SpecialImport extends SpecialPage { <tr> <td> </td> - <td class='mw-input'>" . + <td class='mw-submit'>" . Xml::submitButton( wfMsg( 'import-interwiki-submit' ), array( 'accesskey' => 's' ) ) . "</td> </tr>" . diff --git a/includes/specials/SpecialIpblocklist.php b/includes/specials/SpecialIpblocklist.php index 8d573547..4ba1c811 100644 --- a/includes/specials/SpecialIpblocklist.php +++ b/includes/specials/SpecialIpblocklist.php @@ -162,7 +162,7 @@ class IPUnblockForm { * @return array array(message key, parameters) on failure, empty array on success */ - static function doUnblock(&$id, &$ip, &$reason, &$range = null) { + static function doUnblock(&$id, &$ip, &$reason, &$range = null, $blocker=null) { if ( $id ) { $block = Block::newFromID( $id ); if ( !$block ) { @@ -195,11 +195,22 @@ class IPUnblockForm { } // Yes, this is really necessary $id = $block->mId; + + # If the name was hidden and the blocking user cannot hide + # names, then don't allow any block removals... + if( $blocker && $block->mHideName && !$blocker->isAllowed('hideuser') ) { + return array('ipb_cant_unblock', htmlspecialchars($id)); + } # Delete block if ( !$block->delete() ) { return array('ipb_cant_unblock', htmlspecialchars($id)); } + + # Unset _deleted fields as needed + if( $block->mHideName ) { + IPBlockForm::unsuppressUserName( $block->mAddress, $block->mUser ); + } # Make log entry $log = new LogPage( 'block' ); @@ -208,8 +219,8 @@ class IPUnblockForm { } function doSubmit() { - global $wgOut; - $retval = self::doUnblock($this->id, $this->ip, $this->reason, $range); + global $wgOut, $wgUser; + $retval = self::doUnblock($this->id, $this->ip, $this->reason, $range, $wgUser); if(!empty($retval)) { $key = array_shift($retval); @@ -238,7 +249,7 @@ class IPUnblockForm { $conds = array(); $matches = array(); // Is user allowed to see all the blocks? - if ( !$wgUser->isAllowed( 'suppress' ) ) + if ( !$wgUser->isAllowed( 'hideuser' ) ) $conds['ipb_deleted'] = 0; if ( $this->ip == '' ) { // No extra conditions @@ -306,7 +317,7 @@ class IPUnblockForm { } function searchForm() { - global $wgTitle, $wgScript, $wgRequest; + global $wgTitle, $wgScript, $wgRequest, $wgLang; $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' ) ); $nondefaults = array(); @@ -330,7 +341,7 @@ class IPUnblockForm { $links[] = wfMsgHtml( 'ipblocklist-sh-userblocks', $ubLink ); $links[] = wfMsgHtml( 'ipblocklist-sh-tempblocks', $tbLink ); $links[] = wfMsgHtml( 'ipblocklist-sh-addressblocks', $sipbLink ); - $hl = implode( ' ' . wfMsg( 'pipe-separator' ) . ' ', $links ); + $hl = $wgLang->pipeList( $links ); return Xml::tags( 'form', array( 'action' => $wgScript ), @@ -418,7 +429,7 @@ class IPUnblockForm { $properties[] = $msg['blocklist-nousertalk']; } - $properties = implode( ', ', $properties ); + $properties = $wgLang->commaList( $properties ); $line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) ); @@ -434,7 +445,7 @@ class IPUnblockForm { # Create changeblocklink for all blocks with exception of autoblocks if( !$block->mAuto ) { - $changeblocklink = ' ' . wfMsg( 'pipe-separator' ) . ' ' . + $changeblocklink = wfMsg( 'pipe-separator' ) . $sk->link( SpecialPage::getTitleFor( 'Blockip', $block->mAddress ), $msg['change-blocklink'], array(), array(), 'known' ); diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index 6b9df58f..267ef690 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -15,7 +15,7 @@ function wfSpecialLinkSearch( $par ) { list( $limit, $offset ) = wfCheckLimits(); - global $wgOut, $wgRequest, $wgUrlProtocols, $wgMiserMode; + global $wgOut, $wgRequest, $wgUrlProtocols, $wgMiserMode, $wgLang; $target = $GLOBALS['wgRequest']->getVal( 'target', $par ); $namespace = $GLOBALS['wgRequest']->getIntorNull( 'namespace', null ); @@ -48,7 +48,7 @@ function wfSpecialLinkSearch( $par ) { $self = Title::makeTitle( NS_SPECIAL, 'Linksearch' ); - $wgOut->addWikiText( wfMsg( 'linksearch-text', '<nowiki>' . implode( ', ', $wgUrlProtocols) . '</nowiki>' ) ); + $wgOut->addWikiText( wfMsg( 'linksearch-text', '<nowiki>' . $wgLang->commaList( $wgUrlProtocols) . '</nowiki>' ) ); $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) . Xml::hidden( 'title', $self->getPrefixedDbKey() ) . '<fieldset>' . diff --git a/includes/specials/SpecialListUserRestrictions.php b/includes/specials/SpecialListUserRestrictions.php index 27b24298..98e7111f 100644 --- a/includes/specials/SpecialListUserRestrictions.php +++ b/includes/specials/SpecialListUserRestrictions.php @@ -24,9 +24,10 @@ function wfSpecialListUserRestrictions() { class SpecialListUserRestrictionsForm { public function getHTML() { global $wgRequest, $wgScript, $wgTitle; + $action = htmlspecialchars( $wgScript ); $s = ''; $s .= Xml::fieldset( wfMsg( 'listuserrestrictions-legend' ) ); - $s .= "<form action=\"{$wgScript}\">"; + $s .= "<form action=\"{$action}\">"; $s .= Xml::hidden( 'title', $wgTitle->getPrefixedDbKey() ); $s .= Xml::label( wfMsgHtml( 'listuserrestrictions-type' ), 'type' ) . ' ' . self::typeSelector( 'type', $wgRequest->getVal( 'type' ), 'type' ); diff --git a/includes/specials/SpecialListfiles.php b/includes/specials/SpecialListfiles.php index d2178ee0..e15b6959 100644 --- a/includes/specials/SpecialListfiles.php +++ b/includes/specials/SpecialListfiles.php @@ -58,7 +58,7 @@ class ImageListPager extends TablePager { 'img_description' => wfMsg( 'listfiles_description' ), ); if( !$wgMiserMode ) { - $this->mFieldNames['COUNT(oi_archive_name)'] = wfMsg( 'listfiles_count' ); + $this->mFieldNames['count'] = wfMsg( 'listfiles_count' ); } } return $this->mFieldNames; @@ -74,11 +74,25 @@ class ImageListPager extends TablePager { $fields = array_keys( $this->getFieldNames() ); $fields[] = 'img_user'; $options = $join_conds = array(); + # Depends on $wgMiserMode - if( isset($this->mFieldNames['COUNT(oi_archive_name)']) ) { + if( isset( $this->mFieldNames['count'] ) ) { $tables[] = 'oldimage'; - $options = array('GROUP BY' => 'img_name'); - $join_conds = array('oldimage' => array('LEFT JOIN','oi_name = img_name') ); + + # Need to rewrite this one + foreach ( $fields as &$field ) + if ( $field == 'count' ) + $field = 'COUNT(oi_archive_name) as count'; + unset( $field ); + + $dbr = wfGetDB( DB_SLAVE ); + if( $dbr->implicitGroupby() ) { + $options = array( 'GROUP BY' => 'img_name' ); + } else { + $columnlist = implode( ',', preg_grep( '/^img/', array_keys( $this->getFieldNames() ) ) ); + $options = array( 'GROUP BY' => "img_user, $columnlist" ); + } + $join_conds = array( 'oldimage' => array( 'LEFT JOIN', 'oi_name = img_name' ) ); } return array( 'tables' => $tables, @@ -136,7 +150,7 @@ class ImageListPager extends TablePager { return $this->getSkin()->formatSize( $value ); case 'img_description': return $this->getSkin()->commentBlock( $value ); - case 'COUNT(oi_archive_name)': + case 'count': return intval($value)+1; } } diff --git a/includes/specials/SpecialListgrouprights.php b/includes/specials/SpecialListgrouprights.php index 5c76df8c..d1fc0818 100644 --- a/includes/specials/SpecialListgrouprights.php +++ b/includes/specials/SpecialListgrouprights.php @@ -63,10 +63,13 @@ class SpecialListGroupRights extends SpecialPage { $grouppage = $this->skin->makeLink( $grouppageLocalized, $groupnameLocalized ); } - if ( !in_array( $group, $wgImplicitGroups ) ) { + if ( $group === 'user' ) { + // Link to Special:listusers for implicit group 'user' + $grouplink = '<br />' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Listusers' ), wfMsgHtml( 'listgrouprights-members' ), '' ); + } elseif ( !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 + // No link to Special:listusers for other implicit groups as they are unlistable $grouplink = ''; } diff --git a/includes/specials/SpecialListusers.php b/includes/specials/SpecialListusers.php index 17bec70e..aa057801 100644 --- a/includes/specials/SpecialListusers.php +++ b/includes/specials/SpecialListusers.php @@ -53,6 +53,7 @@ class UsersPager extends AlphabeticPager { $this->requestedGroup = ''; } $this->editsOnly = $wgRequest->getBool( 'editsOnly' ); + $this->creationSort = $wgRequest->getBool( 'creationSort' ); $this->requestedUser = ''; if ( $un != '' ) { @@ -66,7 +67,7 @@ class UsersPager extends AlphabeticPager { function getIndexField() { - return 'user_name'; + return $this->creationSort ? 'user_id' : 'user_name'; } function getQueryInfo() { @@ -74,14 +75,19 @@ class UsersPager extends AlphabeticPager { $conds = array(); // Don't show hidden names $conds[] = 'ipb_deleted IS NULL OR ipb_deleted = 0'; - if ($this->requestedGroup != "") { + if( $this->requestedGroup != '' ) { $conds['ug_group'] = $this->requestedGroup; $useIndex = ''; } else { - $useIndex = $dbr->useIndexClause('user_name'); + $useIndex = $dbr->useIndexClause( $this->creationSort ? 'PRIMARY' : 'user_name'); } - if ($this->requestedUser != "") { - $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser ); + if( $this->requestedUser != '' ) { + # Sorted either by account creation or name + if( $this->creationSort ) { + $conds[] = 'user_id >= ' . User::idFromName( $this->requestedUser ); + } else { + $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser ); + } } if( $this->editsOnly ) { $conds[] = 'user_editcount > 0'; @@ -92,12 +98,14 @@ class UsersPager extends AlphabeticPager { $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', + 'fields' => array( + $this->creationSort ? 'MAX(user_name) AS user_name' : 'user_name', + $this->creationSort ? 'user_id' : 'MAX(user_id) AS user_id', 'MAX(user_editcount) AS edits', 'COUNT(ug_group) AS numgroups', - 'MAX(ug_group) AS singlegroup'), - 'options' => array('GROUP BY' => 'user_name'), + 'MAX(ug_group) AS singlegroup', + 'MIN(user_registration) AS creation'), + 'options' => array('GROUP BY' => $this->creationSort ? 'user_id' : 'user_name'), 'conds' => $conds ); @@ -115,7 +123,7 @@ class UsersPager extends AlphabeticPager { $list = array(); foreach( self::getGroups( $row->user_id ) as $group ) $list[] = self::buildGroupLink( $group ); - $groups = implode( ', ', $list ); + $groups = $wgLang->commaList( $list ); } elseif( $row->numgroups == 1 ) { $groups = self::buildGroupLink( $row->singlegroup ); } else { @@ -131,8 +139,17 @@ class UsersPager extends AlphabeticPager { } else { $edits = ''; } + + $created = ''; + # Some rows may be NULL + if( $row->creation ) { + $d = $wgLang->date( wfTimestamp( TS_MW, $row->creation ), true ); + $t = $wgLang->time( wfTimestamp( TS_MW, $row->creation ), true ); + $created = ' (' . wfMsgHtml( 'usercreated', $d, $t ) . ')'; + } + wfRunHooks( 'SpecialListusersFormatRow', array( &$item, $row ) ); - return "<li>{$item}{$edits}</li>"; + return "<li>{$item}{$edits}{$created}</li>"; } function getBody() { @@ -154,10 +171,9 @@ class UsersPager extends AlphabeticPager { $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() ); + $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listusers-form' ) ) . + Xml::fieldset( wfMsg( 'listusers' ) ) . + Xml::hidden( 'title', $self->getPrefixedDbKey() ); # Username field $out .= Xml::label( wfMsg( 'listusersfrom' ), 'offset' ) . ' ' . @@ -172,25 +188,31 @@ class UsersPager extends AlphabeticPager { $out .= Xml::closeElement( 'select' ) . '<br/>'; $out .= Xml::checkLabel( wfMsg('listusers-editsonly'), 'editsOnly', 'editsOnly', $this->editsOnly ); $out .= ' '; + $out .= Xml::checkLabel( wfMsg('listusers-creationsort'), 'creationSort', 'creationSort', $this->creationSort ); + $out .= '<br/>'; wfRunHooks( 'SpecialListusersHeaderForm', array( $this, &$out ) ); # Submit button and form bottom - if( $this->mLimit ) - $out .= Xml::hidden( 'limit', $this->mLimit ); + $out .= Xml::hidden( 'limit', $this->mLimit ); $out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ); wfRunHooks( 'SpecialListusersHeader', array( $this, &$out ) ); - $out .= '</fieldset>' . + $out .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ); return $out; } + /** + * Get a list of all explicit groups + * @return array + */ function getAllGroups() { $result = array(); foreach( User::getAllGroups() as $group ) { $result[$group] = User::getGroupName( $group ); } + asort( $result ); return $result; } diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php index 492c2608..2382344b 100644 --- a/includes/specials/SpecialLog.php +++ b/includes/specials/SpecialLog.php @@ -45,6 +45,7 @@ function wfSpecialLog( $par = '' ) { $pattern = $wgRequest->getBool( 'pattern' ); $y = $wgRequest->getIntOrNull( 'year' ); $m = $wgRequest->getIntOrNull( 'month' ); + $tagFilter = $wgRequest->getVal( 'tagfilter' ); # Don't let the user get stuck with a certain date $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev'; if( $skip ) { @@ -53,12 +54,12 @@ function wfSpecialLog( $par = '' ) { } # 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 ); + $pager = new LogPager( $loglist, $type, $user, $title, $pattern, array(), $y, $m, $tagFilter ); # 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(), $pager->getFilterParams() ); + $pager->getYear(), $pager->getMonth(), $pager->getFilterParams(), $tagFilter ); # Insert list $logBody = $pager->getBody(); if( $logBody ) { diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php index f870406c..c51ce7c3 100644 --- a/includes/specials/SpecialMergeHistory.php +++ b/includes/specials/SpecialMergeHistory.php @@ -97,7 +97,7 @@ class MergehistoryForm { ); } - if ( $this->mTargetObj->equals( $this->mDestObj ) ) { + if ( $this->mTargetObj && $this->mDestObj && $this->mTargetObj->equals( $this->mDestObj ) ) { $errors[] = wfMsgExt( 'mergehistory-same-destination', array( 'parse' ) ); } @@ -142,7 +142,7 @@ class MergehistoryForm { } private function showHistory() { - global $wgLang, $wgContLang, $wgUser, $wgOut; + global $wgLang, $wgUser, $wgOut; $this->sk = $wgUser->getSkin(); @@ -163,27 +163,22 @@ class MergehistoryForm { 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' ) . + wfMsgExt( 'mergehistory-merge', array('parseinline'), + $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText() ) . + Xml::openElement( 'table', array( 'id' => 'mw-mergehistory-table' ) ) . "<tr> - <td colspan='2'>" . - wfMsgExt( 'mergehistory-merge', array('parseinline'), - $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText() ) . + <td class='mw-label'>" . + Xml::label( wfMsg( 'mergehistory-reason' ), 'wpComment' ) . "</td> - </tr> - <tr> - <td align='$align'>" . - Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) . - "</td> - <td>" . - Xml::input( 'wpComment', 50, $this->mComment ) . + <td class='mw-input'>" . + Xml::input( 'wpComment', 50, $this->mComment, array('id' => 'wpComment') ) . "</td> </tr> <tr> <td> </td> - <td>" . + <td class='mw-submit'>" . Xml::submitButton( wfMsg( 'mergehistory-submit' ), array( 'name' => 'merge', 'id' => 'mw-merge-submit' ) ) . "</td> </tr>" . @@ -439,7 +434,7 @@ class MergeHistoryPager extends ReverseChronologicalPager { return array( 'tables' => array('revision','page'), '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' ), + 'rev_id', 'rev_page', 'rev_parent_id', 'rev_text_id', 'rev_len', 'rev_deleted' ), 'conds' => $conds ); } diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php index acc27625..8fcf33a9 100644 --- a/includes/specials/SpecialMovepage.php +++ b/includes/specials/SpecialMovepage.php @@ -234,15 +234,22 @@ class MovePageForm { } if( ($this->oldTitle->hasSubpages() || $this->oldTitle->getTalkPage()->hasSubpages()) - && $this->oldTitle->userCan( 'move-subpages' ) ) { + && $this->oldTitle->userCan( 'move-subpages' ) ) + { + global $wgMaximumMovedPages, $wgLang; + $wgOut->addHTML( " <tr> <td></td> <td class=\"mw-input\">" . - Xml::checkLabel( wfMsg( - $this->oldTitle->hasSubpages() - ? 'move-subpages' - : 'move-talk-subpages' + Xml::checkLabel( wfMsgExt( + ( $this->oldTitle->hasSubpages() + ? 'move-subpages' + : 'move-talk-subpages' ), + array( 'parsemag' ), + $wgLang->formatNum( $wgMaximumMovedPages ), + # $2 to allow use of PLURAL in message. + $wgMaximumMovedPages ), 'wpMovesubpages', 'wpMovesubpages', # Don't check the box if we only have talk subpages to @@ -278,6 +285,7 @@ class MovePageForm { ); $this->showLogFragment( $this->oldTitle, $wgOut ); + $this->showSubpages( $this->oldTitle, $wgOut ); } @@ -375,6 +383,8 @@ class MovePageForm { # 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. + + // FIXME: Use Title::moveSubpages() here $dbr = wfGetDB( DB_MASTER ); if( $this->moveSubpages && ( MWNamespace::hasSubpages( $nt->getNamespace() ) || ( @@ -489,4 +499,32 @@ class MovePageForm { LogEventsList::showLogExtract( $out, 'move', $title->getPrefixedText() ); } + function showSubpages( $title, $out ) { + global $wgUser, $wgLang; + + if( !MWNamespace::hasSubpages( $title->getNamespace() ) ) + return; + + $subpages = $title->getSubpages(); + $count = $subpages instanceof TitleArray ? $subpages->count() : 0; + + $out->wrapWikiMsg( '== $1 ==', array( 'movesubpage', $count ) ); + + # No subpages. + if ( $count == 0 ) { + $out->addWikiMsg( 'movenosubpage' ); + return; + } + + $out->addWikiMsg( 'movesubpagetext', $wgLang->formatNum( $count ) ); + $skin = $wgUser->getSkin(); + $out->addHTML( "<ul>\n" ); + + foreach( $subpages as $subpage ) { + $link = $skin->link( $subpage ); + $out->addHTML( "<li>$link</li>\n" ); + } + $out->addHTML( "</ul>\n" ); + } } + diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php index 08e776d8..886c41a2 100644 --- a/includes/specials/SpecialNewpages.php +++ b/includes/specials/SpecialNewpages.php @@ -24,7 +24,7 @@ class SpecialNewpages extends SpecialPage { $opts = new FormOptions(); $this->opts = $opts; // bind $opts->add( 'hideliu', false ); - $opts->add( 'hidepatrolled', false ); + $opts->add( 'hidepatrolled', $wgUser->getBoolOption( 'newpageshidepatrolled' ) ); $opts->add( 'hidebots', false ); $opts->add( 'hideredirs', true ); $opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) ); @@ -32,6 +32,7 @@ class SpecialNewpages extends SpecialPage { $opts->add( 'namespace', '0' ); $opts->add( 'username', '' ); $opts->add( 'feed', '' ); + $opts->add( 'tagfilter', '' ); // Set values $opts->fetchValuesFromRequest( $wgRequest ); @@ -121,7 +122,7 @@ class SpecialNewpages extends SpecialPage { } protected function filterLinks() { - global $wgGroupPermissions, $wgUser; + global $wgGroupPermissions, $wgUser, $wgLang; // show/hide links $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) ); @@ -154,7 +155,7 @@ class SpecialNewpages extends SpecialPage { $links[$key] = wfMsgHtml( $msg, $link ); } - return implode( ' | ', $links ); + return $wgLang->pipeList( $links ); } protected function form() { @@ -176,6 +177,10 @@ class SpecialNewpages extends SpecialPage { } $hidden = implode( "\n", $hidden ); + $tagFilter = ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] ); + if ($tagFilter) + list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter; + $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) . Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . Xml::fieldset( wfMsg( 'newpages' ) ) . @@ -187,7 +192,15 @@ class SpecialNewpages extends SpecialPage { <td class='mw-input'>" . Xml::namespaceSelector( $namespace, 'all' ) . "</td> - </tr>" . + </tr>" . ( $tagFilter ? ( + "<tr> + <td class='mw-label'>" . + $tagFilterLabel . + "</td> + <td class='mw-input'>" . + $tagFilterSelector . + "</td> + </tr>" ) : '' ) . ($wgEnableNewpagesUserFilter ? "<tr> <td class='mw-label'>" . @@ -235,20 +248,32 @@ class SpecialNewpages extends SpecialPage { */ public function formatRow( $result ) { global $wgLang, $wgContLang, $wgUser; + + $classes = array(); + $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 : '' ); + $query = $this->patrollable( $result ) ? "rcid={$result->rc_id}&redirect=no" : 'redirect=no'; + $plink = $this->skin->makeKnownLinkObj( $title, '', $query ); $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'" : ''; + + if ( $this->patrollable( $result ) ) + $classes[] = 'not-patrolled'; + + # Tags, if any. + list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' ); + $classes = array_merge( $classes, $newClasses ); + + $css = count($classes) ? ' class="'.implode( " ", $classes).'"' : ''; - return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment}</li>\n"; + return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment} {$tagDisplay}</li>\n"; } /** @@ -331,7 +356,7 @@ class SpecialNewpages extends SpecialPage { protected function feedItemDesc( $row ) { $revision = Revision::newFromId( $row->rev_id ); if( $revision ) { - return '<p>' . htmlspecialchars( $revision->getUserText() ) . ': ' . + return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) . htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . "</p>\n<hr />\n<div>" . nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>"; @@ -377,7 +402,6 @@ class NewPagesPager extends ReverseChronologicalPager { } else { $rcIndexes = array( 'rc_timestamp' ); } - $conds[] = 'page_id = rc_cur_id'; # $wgEnableNewpagesUserFilter - temp WMF hack if( $wgEnableNewpagesUserFilter && $user ) { @@ -399,13 +423,29 @@ class NewPagesPager extends ReverseChronologicalPager { $conds['page_is_redirect'] = 0; } - return array( + $info = 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', + rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags', 'conds' => $conds, - 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ) + 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ), + 'join_conds' => array( + 'page' => array('INNER JOIN', 'page_id=rc_cur_id'), + ), ); + + ## Empty array for fields, it'll be set by us anyway. + $fields = array(); + + ## Modify query for tags + ChangeTags::modifyDisplayQuery( $info['tables'], + $fields, + $info['conds'], + $info['join_conds'], + $info['options'], + $this->opts['tagfilter'] ); + + return $info; } function getIndexField() { diff --git a/includes/specials/SpecialPreferences.php b/includes/specials/SpecialPreferences.php index ca2236ee..f4a42ef4 100644 --- a/includes/specials/SpecialPreferences.php +++ b/includes/specials/SpecialPreferences.php @@ -26,13 +26,13 @@ class PreferencesForm { var $mUserLanguage, $mUserVariant; var $mSearch, $mRecent, $mRecentDays, $mTimeZone, $mHourDiff, $mSearchLines, $mSearchChars, $mAction; var $mReset, $mPosted, $mToggles, $mSearchNs, $mRealName, $mImageSize; - var $mUnderline, $mWatchlistEdits; + var $mUnderline, $mWatchlistEdits, $mGender; /** * Constructor * Load some values */ - function PreferencesForm( &$request ) { + function __construct( &$request ) { global $wgContLang, $wgUser, $wgAllowRealName; $this->mQuickbar = $request->getVal( 'wpQuickbar' ); @@ -60,11 +60,13 @@ class PreferencesForm { $this->mUnderline = $request->getInt( 'wpOpunderline' ); $this->mAction = $request->getVal( 'action' ); $this->mReset = $request->getCheck( 'wpReset' ); + $this->mRestoreprefs = $request->getCheck( 'wpRestore' ); $this->mPosted = $request->wasPosted(); $this->mSuccess = $request->getCheck( 'success' ); $this->mWatchlistDays = $request->getVal( 'wpWatchlistDays' ); $this->mWatchlistEdits = $request->getVal( 'wpWatchlistEdits' ); $this->mDisableMWSuggest = $request->getCheck( 'wpDisableMWSuggest' ); + $this->mGender = $request->getVal( 'wpGender' ); $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && $this->mPosted && @@ -117,6 +119,8 @@ class PreferencesForm { $this->mainPrefsForm( 'reset', wfMsg( 'prefsreset' ) ); } else if ( $this->mSaveprefs ) { $this->savePreferences(); + } else if ( $this->mRestoreprefs ) { + $this->restorePreferences(); } else { $this->resetPrefs(); $this->mainPrefsForm( '' ); @@ -204,6 +208,15 @@ class PreferencesForm { } } + function validateGender( $val ) { + $valid = array( 'male', 'female', 'unknown' ); + if ( in_array($val, $valid) ) { + return $val; + } else { + return User::getDefaultOption( 'gender' ); + } + } + /** * @access private */ @@ -269,6 +282,7 @@ class PreferencesForm { $wgUser->setOption( 'underline', $this->validateInt($this->mUnderline, 0, 2) ); $wgUser->setOption( 'watchlistdays', $this->validateFloat( $this->mWatchlistDays, 0, 7 ) ); $wgUser->setOption( 'disablesuggest', $this->mDisableMWSuggest ); + $wgUser->setOption( 'gender', $this->validateGender( $this->mGender ) ); # Set search namespace options foreach( $this->mSearchNs as $i => $value ) { @@ -420,6 +434,7 @@ class PreferencesForm { $this->mUnderline = $wgUser->getOption( 'underline' ); $this->mWatchlistDays = $wgUser->getOption( 'watchlistdays' ); $this->mDisableMWSuggest = $wgUser->getBoolOption( 'disablesuggest' ); + $this->mGender = $wgUser->getOption( 'gender' ); $togs = User::getToggles(); foreach ( $togs as $tname ) { @@ -435,6 +450,18 @@ class PreferencesForm { wfRunHooks( 'ResetPreferences', array( $this, $wgUser ) ); } + + /** + * @access private + */ + function restorePreferences() { + global $wgUser, $wgOut; + $wgUser->restoreOptions(); + $wgUser->setCookies(); + $wgUser->saveSettings(); + $title = SpecialPage::getTitleFor( 'Preferences' ); + $wgOut->redirect( $title->getFullURL( 'success' ) ); + } /** * @access private @@ -647,12 +674,12 @@ class PreferencesForm { $userInformationHtml = $this->tableRow( wfMsgHtml( 'username' ), htmlspecialchars( $wgUser->getName() ) ) . - $this->tableRow( wfMsgHtml( 'uid' ), $wgLang->formatNum( htmlspecialchars( $wgUser->getId() ) ) ). + $this->tableRow( wfMsgHtml( 'uid' ), htmlspecialchars( $wgUser->getId() ) ) . $this->tableRow( wfMsgExt( 'prefs-memberingroups', array( 'parseinline' ), count( $userEffectiveGroupsArray ) ), $wgLang->commaList( $userEffectiveGroupsArray ) . - '<br />(' . implode( ' | ', $toolLinks ) . ')' + '<br />(' . $wgLang->pipeList( $toolLinks ) . ')' ) . $this->tableRow( @@ -720,7 +747,22 @@ class PreferencesForm { $this->tableRow( ' ', $this->getToggle( 'fancysig' ) ) ); - list( $lsLabel, $lsSelect) = Xml::languageSelector( $this->mUserLanguage ); + $gender = new XMLSelect( 'wpGender', 'wpGender', $this->mGender ); + $gender->addOption( wfMsg( 'gender-unknown' ), 'unknown' ); + $gender->addOption( wfMsg( 'gender-male' ), 'male' ); + $gender->addOption( wfMsg( 'gender-female' ), 'female' ); + + $wgOut->addHTML( + $this->tableRow( + Xml::label( wfMsg( 'yourgender' ), 'wpGender' ), + $gender->getHTML(), + Xml::tags( 'div', array( 'class' => 'prefsectiontip' ), + wfMsgExt( 'prefs-help-gender', 'parseinline' ) + ) + ) + ); + + list( $lsLabel, $lsSelect) = Xml::languageSelector( $this->mUserLanguage, false ); $wgOut->addHTML( $this->tableRow( $lsLabel, $lsSelect ) ); @@ -849,13 +891,26 @@ class PreferencesForm { } } asort($validSkinNames); - foreach ($validSkinNames as $skinkey => $sn ) { + foreach( $validSkinNames as $skinkey => $sn ) { $checked = $skinkey == $this->mSkin ? ' checked="checked"' : ''; $mplink = htmlspecialchars( $mptitle->getLocalURL( "useskin=$skinkey" ) ); $previewlink = "(<a target='_blank' href=\"$mplink\">$previewtext</a>)"; + $extraLinks = ''; + global $wgAllowUserCss, $wgAllowUserJs; + if( $wgAllowUserCss ) { + $cssPage = Title::makeTitleSafe( NS_USER, $wgUser->getName().'/'.$skinkey.'.css' ); + $customCSS = $sk->makeLinkObj( $cssPage, wfMsgExt('prefs-custom-css', array() ) ); + $extraLinks .= " ($customCSS)"; + } + if( $wgAllowUserJs ) { + $jsPage = Title::makeTitleSafe( NS_USER, $wgUser->getName().'/'.$skinkey.'.js' ); + $customJS = $sk->makeLinkObj( $jsPage, wfMsgHtml('prefs-custom-js') ); + $extraLinks .= " ($customJS)"; + } 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( "<input type='radio' name='wpSkin' id=\"wpSkin$skinkey\" value=\"$skinkey\"$checked /> + <label for=\"wpSkin$skinkey\">{$sn}</label> $previewlink{$extraLinks}<br />\n" ); } $wgOut->addHTML( "</fieldset>\n\n" ); } @@ -972,26 +1027,54 @@ class PreferencesForm { 'onchange' => 'javascript:updateTimezoneSelection(false)' ) ); $opt .= Xml::option( wfMsg( 'timezoneuseserverdefault' ), "System|$wgLocalTZoffset", $this->mTimeZone === "System|$wgLocalTZoffset" ); $opt .= Xml::option( wfMsg( 'timezoneuseoffset' ), 'Offset', $this->mTimeZone === 'Offset' ); + if ( function_exists( 'timezone_identifiers_list' ) ) { - $optgroup = ''; + # Read timezone list $tzs = timezone_identifiers_list(); sort( $tzs ); - $selZone = explode( '|', $this->mTimeZone, 3); + + # Precache localized region names + $tzRegions = array(); + $tzRegions['Africa'] = wfMsg( 'timezoneregion-africa' ); + $tzRegions['America'] = wfMsg( 'timezoneregion-america' ); + $tzRegions['Antarctica'] = wfMsg( 'timezoneregion-antarctica' ); + $tzRegions['Arctic'] = wfMsg( 'timezoneregion-arctic' ); + $tzRegions['Asia'] = wfMsg( 'timezoneregion-asia' ); + $tzRegions['Atlantic'] = wfMsg( 'timezoneregion-atlantic' ); + $tzRegions['Australia'] = wfMsg( 'timezoneregion-australia' ); + $tzRegions['Europe'] = wfMsg( 'timezoneregion-europe' ); + $tzRegions['Indian'] = wfMsg( 'timezoneregion-indian' ); + $tzRegions['Pacific'] = wfMsg( 'timezoneregion-pacific' ); + asort( $tzRegions ); + + $selZone = explode( '|', $this->mTimeZone, 3 ); $selZone = ( $selZone[0] == 'ZoneInfo' ) ? $selZone[2] : null; $now = date_create( 'now' ); + $optgroup = ''; + foreach ( $tzs as $tz ) { $z = explode( '/', $tz, 2 ); + # timezone_identifiers_list() returns a number of # backwards-compatibility entries. This filters them out of the # list presented to the user. - if ( count( $z ) != 2 || !in_array( $z[0], array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific' ) ) ) continue; + if ( count( $z ) != 2 || !array_key_exists( $z[0], $tzRegions ) ) + continue; + + # Localize region + $z[0] = $tzRegions[$z[0]]; + + # Create region groups if ( $optgroup != $z[0] ) { - if ( $optgroup !== '' ) $opt .= Xml::closeElement( 'optgroup' ); + if ( $optgroup !== '' ) { + $opt .= Xml::closeElement( 'optgroup' ); + } $optgroup = $z[0]; - $opt .= Xml::openElement( 'optgroup', array( 'label' => $z[0] ) ); + $opt .= Xml::openElement( 'optgroup', array( 'label' => $z[0] ) ) . "\n"; } + $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 ); - $opt .= Xml::option( str_replace( '_', ' ', $tz ), "ZoneInfo|$minDiff|$tz", $selZone === $tz, array( 'label' => $z[1] ) ); + $opt .= Xml::option( str_replace( '_', ' ', $z[0] . '/' . $z[1] ), "ZoneInfo|$minDiff|$tz", $selZone === $tz, array( 'label' => $z[1] ) ) . "\n"; } if ( $optgroup !== '' ) $opt .= Xml::closeElement( 'optgroup' ); } @@ -1052,7 +1135,7 @@ class PreferencesForm { $wgOut->addHTML( Xml::closeElement( 'fieldset' ) ); # Recent changes - global $wgRCMaxAge; + global $wgRCMaxAge, $wgUseRCPatrol; $wgOut->addHTML( Xml::fieldset( wfMsg( 'prefs-rc' ) ) . Xml::openElement( 'table' ) . @@ -1078,8 +1161,11 @@ class PreferencesForm { ); $toggles[] = 'hideminor'; - if( $wgRCShowWatchingUsers ) - $toggles[] = 'shownumberswatching'; + if( $wgUseRCPatrol ) { + $toggles[] = 'hidepatrolled'; + $toggles[] = 'newpageshidepatrolled'; + } + if( $wgRCShowWatchingUsers ) $toggles[] = 'shownumberswatching'; $toggles[] = 'usenewrc'; $wgOut->addHTML( @@ -1088,6 +1174,10 @@ class PreferencesForm { ); # Watchlist + $watchlistToggles = array( 'watchlisthideminor', 'watchlisthidebots', 'watchlisthideown', + 'watchlisthideanons', 'watchlisthideliu' ); + if( $wgUseRCPatrol ) $watchlistToggles[] = 'watchlisthidepatrolled'; + $wgOut->addHTML( Xml::fieldset( wfMsg( 'prefs-watchlist' ) ) . Xml::inputLabel( wfMsg( 'prefs-watchlist-days' ), 'wpWatchlistDays', 'wpWatchlistDays', 3, $this->mWatchlistDays ) . ' ' . @@ -1097,7 +1187,7 @@ class PreferencesForm { Xml::inputLabel( wfMsg( 'prefs-watchlist-edits' ), 'wpWatchlistEdits', 'wpWatchlistEdits', 3, $this->mWatchlistEdits ) . ' ' . wfMsgHTML( 'prefs-watchlist-edits-max' ) . '<br /><br />' . - $this->getToggles( array( 'watchlisthideminor', 'watchlisthidebots', 'watchlisthideown', 'watchlisthideanons', 'watchlisthideliu' ) ) + $this->getToggles( $watchlistToggles ) ); if( $wgUser->isAllowed( 'createpage' ) || $wgUser->isAllowed( 'createtalk' ) ) { @@ -1156,25 +1246,34 @@ class PreferencesForm { # 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>"); + $uopt = $wgUser->getOption( 'underline' ); + $wgOut->addHTML( + Xml::fieldset( wfMsg( 'prefs-misc' ) ) . + Xml::openElement( 'table' ) . + '<tr> + <td class="mw-label">' . + // Xml::label() cannot be used because 'stub-threshold' contains plain HTML + Xml::tags( 'label', array( 'for' => 'wpStubs' ), wfMsg( 'stub-threshold' ) ) . + '</td> + <td class="mw-input">' . + Xml::input( 'wpStubs', 6, $this->mStubs, array( 'id' => 'wpStubs' ) ) . + '</td> + </tr><tr> + <td class="mw-label">' . + Xml::label( wfMsg( 'tog-underline' ), 'wpOpunderline' ) . + '</td> + <td class="mw-input">' . + Xml::openElement( 'select', array( 'id' => 'wpOpunderline', 'name' => 'wpOpunderline' ) ) . + Xml::option( wfMsg ( 'underline-never' ), '0', $uopt == 0 ) . + Xml::option( wfMsg ( 'underline-always' ), '1', $uopt == 1 ) . + Xml::option( wfMsg ( 'underline-default' ), '2', $uopt == 2 ) . + Xml::closeElement( 'select' ) . + '</td> + </tr>' . + Xml::closeElement( 'table' ) + ); + # And now the rest = Misc. foreach ( $togs as $tname ) { if( !array_key_exists( $tname, $this->mUsedToggles ) ) { if( $tname == 'norollbackdiff' && $wgUser->isAllowed( 'rollback' ) ) @@ -1190,14 +1289,14 @@ class PreferencesForm { $token = htmlspecialchars( $wgUser->editToken() ); $skin = $wgUser->getSkin(); + $rtl = $wgContLang->isRTL() ? 'left' : 'right'; $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> + <table id='prefsubmit' cellpadding='0' width='100%' style='background:none;'><tr> + <td><input type='submit' name='wpSaveprefs' class='btnSavePrefs' value=\"" . wfMsgHtml( 'saveprefs' ) . + '"'.$skin->tooltipAndAccesskey('save')." /> + <input type='submit' name='wpReset' value=\"" . wfMsgHtml( 'resetprefs' ) . "\" /></td> + <td align='$rtl'><input type='submit' name='wpRestore' value=\"" . wfMsgHtml( 'restoreprefs' ) . "\" /></td> + </tr></table> <input type='hidden' name='wpEditToken' value=\"{$token}\" /> </div></form>\n" ); diff --git a/includes/specials/SpecialPrefixindex.php b/includes/specials/SpecialPrefixindex.php index ea0c1135..680fe343 100644 --- a/includes/specials/SpecialPrefixindex.php +++ b/includes/specials/SpecialPrefixindex.php @@ -6,9 +6,6 @@ */ class SpecialPrefixindex extends SpecialAllpages { // Inherit $maxPerPage - - // Define other properties - protected $nsfromMsg = 'allpagesprefix'; function __construct(){ parent::__construct( 'Prefixindex' ); @@ -26,7 +23,7 @@ class SpecialPrefixindex extends SpecialAllpages { # GET values $from = $wgRequest->getVal( 'from' ); - $prefix = $wgRequest->getVal( 'prefix' ); + $prefix = $wgRequest->getVal( 'prefix', '' ); $namespace = $wgRequest->getInt( 'namespace' ); $namespaces = $wgContLang->getNamespaces(); @@ -63,7 +60,7 @@ class SpecialPrefixindex extends SpecialAllpages { $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) ); $out .= "<tr> <td class='mw-label'>" . - Xml::label( wfMsg( 'allpagesfrom' ), 'nsfrom' ) . + Xml::label( wfMsg( 'allpagesprefix' ), 'nsfrom' ) . "</td> <td class='mw-input'>" . Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) . @@ -90,7 +87,7 @@ class SpecialPrefixindex extends SpecialAllpages { * @param string $from list all pages from this name (default FALSE) */ function showPrefixChunk( $namespace = NS_MAIN, $prefix, $from = null ) { - global $wgOut, $wgUser, $wgContLang; + global $wgOut, $wgUser, $wgContLang, $wgLang; $sk = $wgUser->getSkin(); @@ -99,7 +96,6 @@ class SpecialPrefixindex extends SpecialAllpages { $fromList = $this->getNamespaceKeyAndText($namespace, $from); $prefixList = $this->getNamespaceKeyAndText($namespace, $prefix); $namespaces = $wgContLang->getNamespaces(); - $align = $wgContLang->isRtl() ? 'left' : 'right'; if ( !$prefixList || !$fromList ) { $out = wfMsgWikiHtml( 'allpagesbadtitle' ); @@ -134,7 +130,7 @@ class SpecialPrefixindex extends SpecialAllpages { $n = 0; if( $res->numRows() > 0 ) { - $out = '<table style="background: inherit;" border="0" width="100%">'; + $out = Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-prefixindex-list-table' ) ); while( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) { $t = Title::makeTitle( $s->page_namespace, $s->page_title ); @@ -157,7 +153,7 @@ class SpecialPrefixindex extends SpecialAllpages { if( ($n % 3) != 0 ) { $out .= '</tr>'; } - $out .= '</table>'; + $out .= Xml::closeElement( 'table' ); } else { $out = ''; } @@ -168,20 +164,27 @@ class SpecialPrefixindex extends SpecialAllpages { } else { $nsForm = $this->namespacePrefixForm( $namespace, $prefix ); $self = $this->getTitle(); - $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->makeKnownLinkObj( $self, - wfMsg ( 'allpages' ) ); + $out2 = Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-prefixindex-nav-table' ) ) . + '<tr> + <td>' . + $nsForm . + '</td> + <td id="mw-prefixindex-nav-form">' . + $sk->makeKnownLinkObj( $self, wfMsg ( 'allpages' ) ); + if( isset( $res ) && $res && ( $n == $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) { $namespaceparam = $namespace ? "&namespace=$namespace" : ""; - $out2 .= " | " . $sk->makeKnownLinkObj( - $self, - wfMsgHtml( 'nextpage', htmlspecialchars( $s->page_title ) ), - "from=" . wfUrlEncode( $s->page_title ) . - "&prefix=" . wfUrlEncode( $prefix ) . $namespaceparam ); + $out2 = $wgLang->pipeList( array( + $out2, + $sk->makeKnownLinkObj( + $self, + wfMsgHtml( 'nextpage', str_replace( '_',' ', htmlspecialchars( $s->page_title ) ) ), + "from=" . wfUrlEncode( $s->page_title ) . + "&prefix=" . wfUrlEncode( $prefix ) . $namespaceparam ) + ) ); } - $out2 .= "</td></tr></table><hr />"; + $out2 .= "</td></tr>" . + Xml::closeElement( 'table' ); } $wgOut->addHTML( $out2 . $out ); diff --git a/includes/specials/SpecialProtectedpages.php b/includes/specials/SpecialProtectedpages.php index 4e56ca42..a38a8cd1 100644 --- a/includes/specials/SpecialProtectedpages.php +++ b/includes/specials/SpecialProtectedpages.php @@ -16,12 +16,12 @@ class ProtectedPagesForm { public function showList( $msg = '' ) { global $wgOut, $wgRequest; - if ( "" != $msg ) { + if( "" != $msg ) { $wgOut->setSubtitle( $msg ); } // Purge expired entries on one in every 10 queries - if ( !mt_rand( 0, 10 ) ) { + if( !mt_rand( 0, 10 ) ) { Title::purgeExpiredRestrictions(); } @@ -37,7 +37,7 @@ class ProtectedPagesForm { $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size, $indefOnly, $cascadeOnly ) ); - if ( $pager->getNumRows() ) { + if( $pager->getNumRows() ) { $s = $pager->getNavigationBar(); $s .= "<ul>" . $pager->getBody() . @@ -73,31 +73,34 @@ class ProtectedPagesForm { $description_items[] = $protType; - if ( $row->pr_cascade ) { + if( $row->pr_cascade ) { $description_items[] = wfMsg( 'protect-summary-cascade' ); } $expiry_description = ''; $stxt = ''; - if ( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) { + if( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) { $expiry = Block::decodeExpiry( $row->pr_expiry ); - $expiry_description = wfMsg( 'protect-expiring' , $wgLang->timeanddate( $expiry ) , $wgLang->date( $expiry ) , $wgLang->time( $expiry ) ); + $expiry_description = wfMsg( 'protect-expiring' , $wgLang->timeanddate( $expiry ) , + $wgLang->date( $expiry ) , $wgLang->time( $expiry ) ); $description_items[] = $expiry_description; } - if (!is_null($size = $row->page_len)) { + if(!is_null($size = $row->page_len)) { $stxt = $wgContLang->getDirMark() . ' ' . $skin->formatRevisionSize( $size ); } # 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' ) . ')'; + $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() ) . ')'; + $changeProtection = ' (' . $skin->makeKnownLinkObj( $ltitle, wfMsgHtml( 'protectlogpage' ), + 'type=protect&page=' . $title->getPrefixedUrl() ) . ')'; } wfProfileOut( __METHOD__ ); @@ -220,7 +223,7 @@ class ProtectedPagesForm { // First pass to load the log names foreach( $wgRestrictionLevels as $type ) { - if ( $type !='' && $type !='*') { + if( $type !='' && $type !='*') { $text = wfMsg("restriction-level-$type"); $m[$text] = $type; } @@ -248,8 +251,9 @@ 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, $cascadeonly = false ) { + function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0, + $indefonly = false, $cascadeonly = false ) + { $this->mForm = $form; $this->mConds = $conds; $this->type = ( $type ) ? $type : 'edit'; @@ -263,15 +267,12 @@ class ProtectedPagesPager extends AlphabeticPager { } 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 ''; } @@ -281,10 +282,11 @@ class ProtectedPagesPager extends AlphabeticPager { function getQueryInfo() { $conds = $this->mConds; - $conds[] = 'pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() ); + $conds[] = '(pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() ) . + 'OR pr_expiry IS NULL)'; $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' ) { @@ -294,7 +296,7 @@ class ProtectedPagesPager extends AlphabeticPager { if( $this->indefonly ) { $conds[] = "pr_expiry = 'infinity' OR pr_expiry IS NULL"; } - if ( $this->cascadeonly ) { + if( $this->cascadeonly ) { $conds[] = "pr_cascade = '1'"; } @@ -318,8 +320,6 @@ class ProtectedPagesPager extends AlphabeticPager { * Constructor */ function wfSpecialProtectedpages() { - $ppForm = new ProtectedPagesForm(); - $ppForm->showList(); } diff --git a/includes/specials/SpecialRandompage.php b/includes/specials/SpecialRandompage.php index f4bff30b..31199b23 100644 --- a/includes/specials/SpecialRandompage.php +++ b/includes/specials/SpecialRandompage.php @@ -23,7 +23,7 @@ class RandomPage extends SpecialPage { } public function setNamespace ( $ns ) { - if( $ns < NS_MAIN ) $ns = NS_MAIN; + if( !$ns || $ns < NS_MAIN ) $ns = NS_MAIN; $this->namespaces = array( $ns ); } diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php index 8c14e1fc..91c0ecbe 100644 --- a/includes/specials/SpecialRecentchanges.php +++ b/includes/specials/SpecialRecentchanges.php @@ -23,11 +23,11 @@ class SpecialRecentChanges extends SpecialPage { $opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) ); $opts->add( 'from', '' ); - $opts->add( 'hideminor', (bool)$wgUser->getOption( 'hideminor' ) ); + $opts->add( 'hideminor', $wgUser->getBoolOption( 'hideminor' ) ); $opts->add( 'hidebots', true ); $opts->add( 'hideanons', false ); $opts->add( 'hideliu', false ); - $opts->add( 'hidepatrolled', false ); + $opts->add( 'hidepatrolled', $wgUser->getBoolOption( 'hidepatrolled' ) ); $opts->add( 'hidemyself', false ); $opts->add( 'namespace', '', FormOptions::INTNULL ); @@ -35,6 +35,7 @@ class SpecialRecentChanges extends SpecialPage { $opts->add( 'categories', '' ); $opts->add( 'categories_any', false ); + $opts->add( 'tagfilter', '' ); return $opts; } @@ -54,7 +55,7 @@ class SpecialRecentChanges extends SpecialPage { $this->parseParameters( $parameters, $opts ); } - $opts->validateIntBounds( 'limit', 0, 5000 ); + $opts->validateIntBounds( 'limit', 0, 500 ); return $opts; } @@ -66,7 +67,8 @@ class SpecialRecentChanges extends SpecialPage { public function feedSetup() { global $wgFeedLimit, $wgRequest; $opts = $this->getDefaultOptions(); - $opts->fetchValuesFromRequest( $wgRequest, array( 'days', 'limit', 'hideminor' ) ); + # Feed is cached on limit,hideminor; other params would randomly not work + $opts->fetchValuesFromRequest( $wgRequest, array( 'limit', 'hideminor' ) ); $opts->validateIntBounds( 'limit', 0, $wgFeedLimit ); return $opts; } @@ -108,13 +110,14 @@ class SpecialRecentChanges extends SpecialPage { foreach( $rows as $row ) { $batch->add( NS_USER, $row->rc_user_text ); $batch->add( NS_USER_TALK, $row->rc_user_text ); + $batch->add( $row->rc_namespace, $row->rc_title ); } $batch->execute(); } - + $target = isset($opts['target']) ? $opts['target'] : ''; // RCL has targets if( $feedFormat ) { list( $feed, $feedObj ) = $this->getFeedObject( $feedFormat ); - $feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod ); + $feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod, $target ); } else { $this->webOutput( $rows, $opts ); } @@ -267,6 +270,7 @@ class SpecialRecentChanges extends SpecialPage { $tables = array( 'recentchanges' ); $join_conds = array(); + $query_options = array( 'USE INDEX' => array('recentchanges' => 'rc_timestamp') ); $uid = $wgUser->getId(); $dbr = wfGetDB( DB_SLAVE ); @@ -274,21 +278,38 @@ class SpecialRecentChanges extends SpecialPage { $namespace = $opts['namespace']; $invert = $opts['invert']; + $join_conds = array(); + // JOIN on watchlist for users if( $uid ) { $tables[] = 'watchlist'; - $join_conds = array( 'watchlist' => array('LEFT JOIN', - "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") ); + $join_conds['watchlist'] = array('LEFT JOIN', + "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace"); } + if ($wgUser->isAllowed("rollback")) { + $tables[] = 'page'; + $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id'); + } + // Tag stuff. + $fields = array(); + // Fields are * in this case, so let the function modify an empty array to keep it happy. + ChangeTags::modifyDisplayQuery( $tables, + $fields, + $conds, + $join_conds, + $query_options, + $opts['tagfilter'] + ); wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) ); // Is there either one namespace selected or excluded? + // Tag filtering also has a better index. // Also, if this is "all" or main namespace, just use timestamp index. - if( is_null($namespace) || $invert || $namespace == NS_MAIN ) { + if( is_null($namespace) || $invert || $namespace == NS_MAIN || $opts['tagfilter'] ) { $res = $dbr->select( $tables, '*', $conds, __METHOD__, - array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, - 'USE INDEX' => array('recentchanges' => 'rc_timestamp') ), + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit ) + + $query_options, $join_conds ); // We have a new_namespace_time index! UNION over new=(0,1) and sort result set! } else { @@ -372,7 +393,7 @@ class SpecialRecentChanges extends SpecialPage { } $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title]; } - $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ) ); + $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter ); --$limit; } $s .= $list->endRecentChangesList(); @@ -453,6 +474,10 @@ class SpecialRecentChanges extends SpecialPage { $extraOpts['category'] = $this->categoryFilterForm( $opts ); } + $tagFilter = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] ); + if ( count($tagFilter) ) + $extraOpts['tagfilter'] = $tagFilter; + wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) ); return $extraOpts; } @@ -587,14 +612,14 @@ class SpecialRecentChanges extends SpecialPage { $options = $nondefaults + $defaults; $note = ''; + if( !wfEmptyMsg( 'rclegend', wfMsg('rclegend') ) ) { + $note .= '<div class="mw-rclegend">' . wfMsgExt( 'rclegend', array('parseinline') ) . "</div>\n"; + } if( $options['from'] ) { $note .= wfMsgExt( 'rcnotefrom', array( 'parseinline' ), $wgLang->formatNum( $options['limit'] ), $wgLang->timeanddate( $options['from'], true ) ) . '<br />'; } - if( !wfEmptyMsg( 'rclegend', wfMsg('rclegend') ) ) { - $note .= wfMsgExt( 'rclegend', array('parseinline') ) . '<br />'; - } # Sort data for display and make sure it's unique after we've added user data. $wgRCLinkLimits[] = $options['limit']; @@ -609,14 +634,14 @@ class SpecialRecentChanges extends SpecialPage { $cl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ), array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ; } - $cl = implode( ' | ', $cl ); + $cl = $wgLang->pipeList( $cl ); // day links, reset 'from' to none foreach( $wgRCLinkDays as $value ) { $dl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ), array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ; } - $dl = implode( ' | ', $dl ); + $dl = $wgLang->pipeList( $dl ); // show/hide links @@ -641,7 +666,7 @@ class SpecialRecentChanges extends SpecialPage { if( $wgUser->useRCPatrol() ) $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink ); $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink ); - $hl = implode( ' | ', $links ); + $hl = $wgLang->pipeList( $links ); // show from this onward link $now = $wgLang->timeanddate( wfTimestampNow(), true ); diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php index c0734354..c58ffff0 100644 --- a/includes/specials/SpecialRecentchangeslinked.php +++ b/includes/specials/SpecialRecentchangeslinked.php @@ -15,6 +15,7 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { $opts = parent::getDefaultOptions(); $opts->add( 'target', '' ); $opts->add( 'showlinkedto', false ); + $opts->add( 'tagfilter', '' ); return $opts; } @@ -22,9 +23,10 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { $opts['target'] = $par; } - public function feedSetup(){ + public function feedSetup() { global $wgRequest; $opts = parent::feedSetup(); + # Feed is cached on limit,hideminor,target; other params would randomly not work $opts['target'] = $wgRequest->getVal( 'target' ); return $opts; } @@ -74,6 +76,7 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { $tables = array( 'recentchanges' ); $select = array( $dbr->tableName( 'recentchanges' ) . '.*' ); $join_conds = array(); + $query_options = array(); // left join with watchlist table to highlight watched rows if( $uid = $wgUser->getId() ) { @@ -82,6 +85,9 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { $join_conds['watchlist'] = array( 'LEFT JOIN', "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace" ); } + ChangeTags::modifyDisplayQuery( $tables, $select, $conds, $join_conds, + $query_options, $opts['tagfilter'] ); + // XXX: parent class does this, should we too? // wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) ); @@ -133,9 +139,14 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { } } - $subsql[] = $dbr->selectSQLText( array_merge( $tables, array( $link_table ) ), $select, $conds + $subconds, - __METHOD__, array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit ), - $join_conds + array( $link_table => array( 'INNER JOIN', $subjoin ) ) ); + $subsql[] = $dbr->selectSQLText( + array_merge( $tables, array( $link_table ) ), + $select, + $conds + $subconds, + __METHOD__, + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit ) + $query_options, + $join_conds + array( $link_table => array( 'INNER JOIN', $subjoin ) ) + ); } if( count($subsql) == 0 ) @@ -163,6 +174,9 @@ class SpecialRecentchangeslinked extends SpecialRecentchanges { Xml::input( 'target', 40, str_replace('_',' ',$opts['target']) ) . Xml::check( 'showlinkedto', $opts['showlinkedto'], array('id' => 'showlinkedto') ) . ' ' . Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) ); + $tagFilter = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] ); + if ($tagFilter) + $extraOpts['tagfilter'] = $tagFilter; return $extraOpts; } diff --git a/includes/specials/SpecialRestrictUser.php b/includes/specials/SpecialRestrictUser.php index 761e0cd6..b946cde8 100644 --- a/includes/specials/SpecialRestrictUser.php +++ b/includes/specials/SpecialRestrictUser.php @@ -37,7 +37,8 @@ function wfSpecialRestrictUser( $par = null ) { class RestrictUserForm { public static function selectUserForm( $val = null, $error = null ) { global $wgScript, $wgTitle; - $s = Xml::fieldset( wfMsg( 'restrictuser-userselect' ) ) . "<form action=\"{$wgScript}\">"; + $action = htmlspecialchars( $wgScript ); + $s = Xml::fieldset( wfMsg( 'restrictuser-userselect' ) ) . "<form action=\"{$action}\">"; if( $error ) $s .= '<p>' . $error . '</p>'; $s .= Xml::hidden( 'title', $wgTitle->getPrefixedDbKey() ); diff --git a/includes/specials/SpecialRevisiondelete.php b/includes/specials/SpecialRevisiondelete.php index 74b118e2..7fdb3cc4 100644 --- a/includes/specials/SpecialRevisiondelete.php +++ b/includes/specials/SpecialRevisiondelete.php @@ -7,87 +7,81 @@ * @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->addWikiMsg( '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() ); - } -} +class SpecialRevisionDelete extends UnlistedSpecialPage { -/** - * 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; + public function __construct() { + parent::__construct( 'Revisiondelete', 'deleterevision' ); + $this->includable( false ); + } - $this->page = $page; + public function execute( $par ) { + global $wgOut, $wgUser, $wgRequest; + if( wfReadOnly() ) { + $wgOut->readOnlyPage(); + return; + } + if( !$wgUser->isAllowed( 'deleterevision' ) ) { + $wgOut->permissionRequired( 'deleterevision' ); + return; + } + $this->skin =& $wgUser->getSkin(); + # Set title and such + $this->setHeaders(); + $this->outputHeader(); + $this->wasPosted = $wgRequest->wasPosted(); + # Handle our many different possible input types + $this->target = $wgRequest->getText( 'target' ); + $this->oldids = $wgRequest->getArray( 'oldid' ); + $this->artimestamps = $wgRequest->getArray( 'artimestamp' ); + $this->logids = $wgRequest->getArray( 'logid' ); + $this->oldimgs = $wgRequest->getArray( 'oldimage' ); + $this->fileids = $wgRequest->getArray( 'fileid' ); # For reviewing deleted files... - if( $file ) { - $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $page, $file ); + $this->file = $wgRequest->getVal( 'file' ); + # Only one target set at a time please! + $i = (bool)$this->file + (bool)$this->oldids + (bool)$this->logids + + (bool)$this->artimestamps + (bool)$this->fileids + (bool)$this->oldimgs; + # No targets? + if( $i == 0 ) { + $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); + return; + } + # Too many targets? + if( $i !== 1 ) { + $wgOut->showErrorPage( 'revdelete-toomanytargets-title', 'revdelete-toomanytargets-text' ); + return; + } + $this->page = Title::newFromUrl( $this->target ); + # If we have revisions, get the title from the first one + # since they should all be from the same page. This allows + # for more flexibility with page moves... + if( count($this->oldids) > 0 ) { + $rev = Revision::newFromId( $this->oldids[0] ); + $this->page = $rev ? $rev->getTitle() : $this->page; + } + # We need a target page! + if( is_null($this->page) ) { + $wgOut->addWikiMsg( 'undelete-header' ); + return; + } + # Logs must have a type given + if( $this->logids && !strpos($this->page->getDBKey(),'/') ) { + $wgOut->showErrorPage( 'revdelete-nologtype-title', 'revdelete-nologtype-text' ); + return; + } + # For reviewing deleted files...show it now if allowed + if( $this->file ) { + $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->page, $this->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 ); + $this->showFile( $this->file ); } return; } - $this->skin = $wgUser->getSkin(); - # Give a link to the log for this page + # Give a link to the logs/hist for this page if( !is_null($this->page) && $this->page->getNamespace() > -1 ) { $links = array(); @@ -101,39 +95,65 @@ class RevisionDeleteForm { if( $wgUser->isAllowed('undelete') ) { $undelete = SpecialPage::getTitleFor( 'Undelete' ); $links[] = $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml( 'deletedhist' ), - wfArrayToCGI( array( 'target' => $this->page->getPrefixedUrl() ) ) ); + wfArrayToCGI( array( 'target' => $this->page->getPrefixedDBkey() ) ) ); } # Logs themselves don't have histories or archived revisions $wgOut->setSubtitle( '<p>'.implode($links,' / ').'</p>' ); } + # Lock the operation and the form context + $this->secureOperation(); + # Either submit or create our form + if( $this->wasPosted ) { + $this->submit( $wgRequest ); + } else if( $this->deleteKey == 'oldid' || $this->deleteKey == 'artimestamp' ) { + $this->showRevs(); + } else if( $this->deleteKey == 'fileid' || $this->deleteKey == 'oldimage' ) { + $this->showImages(); + } else if( $this->deleteKey == 'logid' ) { + $this->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', $this->page->getPrefixedText() ); + if( $wgUser->isAllowed( 'suppressionlog' ) ){ + $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "</h2>\n" ); + LogEventsList::showLogExtract( $wgOut, 'suppress', $this->page->getPrefixedText() ); + } + } + + private function secureOperation() { + global $wgUser; + $this->deleteKey = ''; // At this point, we should only have one of these - if( $oldids ) { - $this->revisions = $oldids; + if( $this->oldids ) { + $this->revisions = $this->oldids; $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT ); - $this->deleteKey='oldid'; - } else if( $artimestamps ) { - $this->archrevs = $artimestamps; + $this->deleteKey = 'oldid'; + } else if( $this->artimestamps ) { + $this->archrevs = $this->artimestamps; $hide_content_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT ); - $this->deleteKey='artimestamp'; - } else if( $img ) { - $this->ofiles = $img; + $this->deleteKey = 'artimestamp'; + } else if( $this->oldimgs ) { + $this->ofiles = $this->oldimgs; $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE ); - $this->deleteKey='oldimage'; - } else if( $fileids ) { - $this->afiles = $fileids; + $this->deleteKey = 'oldimage'; + } else if( $this->fileids ) { + $this->afiles = $this->fileids; $hide_content_name = array( 'revdelete-hide-image', 'wpHideImage', File::DELETED_FILE ); - $this->deleteKey='fileid'; - } else if( $logids ) { - $this->events = $logids; + $this->deleteKey = 'fileid'; + } else if( $this->logids ) { + $this->events = $this->logids; $hide_content_name = array( 'revdelete-hide-name', 'wpHideName', LogPage::DELETED_ACTION ); - $this->deleteKey='logid'; + $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 ) ); + array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ) + ); if( $wgUser->isAllowed('suppressrevision') ) { $this->checks[] = array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED ); } @@ -161,9 +181,8 @@ class RevisionDeleteForm { /** * This lets a user set restrictions for live and archived revisions */ - function showRevs() { - global $wgOut, $wgUser, $action; - + private function showRevs() { + global $wgOut, $wgUser; $UserAllowed = true; $count = ($this->deleteKey=='oldid') ? @@ -174,7 +193,7 @@ class RevisionDeleteForm { $wgOut->addHTML( "<ul>" ); $where = $revObjs = array(); - $dbr = wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_MASTER ); $revisions = 0; // Live revisions... @@ -183,10 +202,11 @@ class RevisionDeleteForm { 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' ), + array( + 'rev_page' => $this->page->getArticleID(), + 'rev_id' => $where, + 'rev_page = page_id' ), __METHOD__ ); while( $row = $dbr->fetchObject( $result ) ) { $revObjs[$row->rev_id] = new Revision( $row ); @@ -197,7 +217,7 @@ class RevisionDeleteForm { continue; } else if( !$revObjs[$revid]->userCan(Revision::DELETED_RESTRICTED) ) { // If a rev is hidden from sysops - if( $action != 'submit') { + if( !$this->wasPosted ) { $wgOut->permissionRequired( 'suppressrevision' ); return; } @@ -211,34 +231,35 @@ class RevisionDeleteForm { } else { // Run through and pull all our data in one query foreach( $this->archrevs as $timestamp ) { - $where[] = $dbr->addQuotes( $timestamp ); + $where[] = $dbr->timestamp( $timestamp ); } - $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')'; $result = $dbr->select( 'archive', '*', - array( 'ar_namespace' => $this->page->getNamespace(), + array( + 'ar_namespace' => $this->page->getNamespace(), 'ar_title' => $this->page->getDBKey(), - $whereClause ), + 'ar_timestamp' => $where ), __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) ); + $timestamp = wfTimestamp( TS_MW, $row->ar_timestamp ); + $revObjs[$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' => $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') { + if( !$this->wasPosted ) { $wgOut->permissionRequired( 'suppressrevision' ); return; } @@ -255,8 +276,8 @@ class RevisionDeleteForm { } $wgOut->addHTML( "</ul>" ); - - $wgOut->addWikiMsg( 'revdelete-text' ); + // Explanation text + $this->addUsageText(); // Normal sysops can always see what they did, but can't always change it if( !$UserAllowed ) return; @@ -284,11 +305,8 @@ class RevisionDeleteForm { 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 ) ) ); - } + + $wgOut->addHTML( $this->buildCheckBoxes( $bitfields ) ); foreach( $items as $item ) { $wgOut->addHTML( Xml::tags( 'p', null, $item ) ); } @@ -299,39 +317,35 @@ class RevisionDeleteForm { Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n" ); - } /** * This lets a user set restrictions for archived images */ - function showImages() { - // What is $action doing here??? - global $wgOut, $wgUser, $action, $wgLang; - + private function showImages() { + global $wgOut, $wgUser, $wgLang; $UserAllowed = true; $count = ($this->deleteKey=='oldimage') ? count($this->ofiles) : count($this->afiles); - $wgOut->addWikiMsg( 'revdelete-selected', - $this->page->getPrefixedText(), + $wgOut->addWikiMsg( 'revdelete-selected', $this->page->getPrefixedText(), $wgLang->formatNum($count) ); $bitfields = 0; $wgOut->addHTML( "<ul>" ); $where = $filesObjs = array(); - $dbr = wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_MASTER ); // 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() ); + $where[] = $timestamp.'!'.$this->page->getDBKey(); } - $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')'; $result = $dbr->select( 'oldimage', '*', - array( 'oi_name' => $this->page->getDBKey(), - $whereClause ), + array( + 'oi_name' => $this->page->getDBKey(), + 'oi_archive_name' => $where ), __METHOD__ ); while( $row = $dbr->fetchObject( $result ) ) { $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row ); @@ -345,7 +359,7 @@ class RevisionDeleteForm { continue; } else if( !$filesObjs[$archivename]->userCan(File::DELETED_RESTRICTED) ) { // If a rev is hidden from sysops - if( $action != 'submit' ) { + if( !$this->wasPosted ) { $wgOut->permissionRequired( 'suppressrevision' ); return; } @@ -362,10 +376,10 @@ class RevisionDeleteForm { 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 ), + array( + 'fa_name' => $this->page->getDBKey(), + 'fa_id' => $where ), __METHOD__ ); while( $row = $dbr->fetchObject( $result ) ) { $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row ); @@ -376,7 +390,7 @@ class RevisionDeleteForm { continue; } else if( !$filesObjs[$fileid]->userCan(File::DELETED_RESTRICTED) ) { // If a rev is hidden from sysops - if( $action != 'submit' ) { + if( !$this->wasPosted ) { $wgOut->permissionRequired( 'suppressrevision' ); return; } @@ -394,9 +408,9 @@ class RevisionDeleteForm { } $wgOut->addHTML( "</ul>" ); - - $wgOut->addWikiMsg('revdelete-text' ); - //Normal sysops can always see what they did, but can't always change it + // Explanation text + $this->addUsageText(); + // Normal sysops can always see what they did, but can't always change it if( !$UserAllowed ) return; $items = array( @@ -421,11 +435,8 @@ class RevisionDeleteForm { 'id' => 'mw-revdel-form-filerevisions' ) ) . Xml::fieldset( 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 ) ) ); - } + + $wgOut->addHTML( $this->buildCheckBoxes( $bitfields ) ); foreach( $items as $item ) { $wgOut->addHTML( "<p>$item</p>" ); } @@ -442,26 +453,27 @@ class RevisionDeleteForm { /** * This lets a user set restrictions for log items */ - function showLogItems() { - global $wgOut, $wgUser, $action, $wgMessageCache, $wgLang; - + private function showLogItems() { + global $wgOut, $wgUser, $wgMessageCache, $wgLang; $UserAllowed = true; + $wgOut->addWikiMsg( 'logdelete-selected', $wgLang->formatNum( count($this->events) ) ); $bitfields = 0; $wgOut->addHTML( "<ul>" ); $where = $logRows = array(); - $dbr = wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_MASTER ); // 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 ), + array( + 'log_type' => $logtype, + 'log_id' => $where ), __METHOD__ ); while( $row = $dbr->fetchObject( $result ) ) { $logRows[$row->log_id] = $row; @@ -473,7 +485,7 @@ class RevisionDeleteForm { continue; } else if( !LogEventsList::userCan( $logRows[$logid],Revision::DELETED_RESTRICTED) ) { // If an event is hidden from sysops - if( $action != 'submit') { + if( !$this->wasPosted ) { $wgOut->permissionRequired( 'suppressrevision' ); return; } @@ -484,13 +496,13 @@ class RevisionDeleteForm { $bitfields |= $logRows[$logid]->log_deleted; } if( !$logItems ) { - $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' ); + $wgOut->showErrorPage( 'revdelete-nologid-title', 'revdelete-nologid-text' ); return; } $wgOut->addHTML( "</ul>" ); - - $wgOut->addWikiMsg( 'revdelete-text' ); + // Explanation text + $this->addUsageText(); // Normal sysops can always see what they did, but can't always change it if( !$UserAllowed ) return; @@ -511,11 +523,8 @@ class RevisionDeleteForm { 'id' => 'mw-revdel-form-logs' ) ) . Xml::fieldset( 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 ) ) ); - } + + $wgOut->addHTML( $this->buildCheckBoxes( $bitfields ) ); foreach( $items as $item ) { $wgOut->addHTML( "<p>$item</p>" ); } @@ -528,21 +537,47 @@ class RevisionDeleteForm { Xml::closeElement( 'form' ) . "\n" ); } + + private function addUsageText() { + global $wgOut, $wgUser; + $wgOut->addWikiMsg( 'revdelete-text' ); + if( $wgUser->isAllowed( 'suppressrevision' ) ) { + $wgOut->addWikiMsg( 'revdelete-suppress-text' ); + } + } + + /** + * @param int $bitfields, aggregate bitfield of all the bitfields + * @returns string HTML + */ + private function buildCheckBoxes( $bitfields ) { + $html = ''; + // 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; + $line = Xml::tags( 'div', null, Xml::checkLabel( wfMsg($message), $name, $name, + $bitfields & $field ) ); + if( $field == Revision::DELETED_RESTRICTED ) $line = "<b>$line</b>"; + $html .= $line; + } + return $html; + } /** * @param Revision $rev * @returns string */ private function historyLine( $rev ) { - global $wgLang; + global $wgLang, $wgUser; $date = $wgLang->timeanddate( $rev->getTimestamp() ); $difflink = $del = ''; // Live revisions if( $this->deleteKey=='oldid' ) { - $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ); + $tokenParams = '&unhide=1&token='.urlencode( $wgUser->editToken( $rev->getId() ) ); + $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid='.$rev->getId() . $tokenParams ); $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'), - 'diff=' . $rev->getId() . '&oldid=prev' ) . ')'; + 'diff=' . $rev->getId() . '&oldid=prev' . $tokenParams ) . ')'; // Archived revisions } else { $undelete = SpecialPage::getTitleFor( 'Undelete' ); @@ -552,7 +587,7 @@ class RevisionDeleteForm { $difflink = '(' . $this->skin->makeKnownLinkObj( $undelete, wfMsgHtml('diff'), "target=$target&diff=prev×tamp=" . $rev->getTimestamp() ) . ')'; } - + // Check permissions; items may be "suppressed" if( $rev->isDeleted(Revision::DELETED_TEXT) ) { $revlink = '<span class="history-deleted">'.$revlink.'</span>'; $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>'; @@ -561,8 +596,10 @@ class RevisionDeleteForm { $difflink = '(' . wfMsgHtml('diff') . ')'; } } + $userlink = $this->skin->revUserLink( $rev ); + $comment = $this->skin->revComment( $rev ); - return "<li> $difflink $revlink ".$this->skin->revUserLink( $rev )." ".$this->skin->revComment( $rev )."$del</li>"; + return "<li>$difflink $revlink $userlink $comment{$del}</li>"; } /** @@ -704,9 +741,13 @@ class RevisionDeleteForm { /** * @param WebRequest $request */ - function submit( $request ) { + private function submit( $request ) { global $wgUser, $wgOut; - + # Check edit token on submission + if( $this->wasPosted && !$wgUser->matchEditToken( $request->getVal('wpEditToken') ) ) { + $wgOut->addWikiMsg( 'sessionfailure' ); + return false; + } $bitfield = $this->extractBitfield( $request ); $comment = $request->getText( 'wpReason' ); # Can the user set this field? @@ -812,10 +853,10 @@ class RevisionDeleter { foreach( $items as $revid ) { $where[] = intval($revid); } - $whereClause = 'rev_id IN(' . implode(',',$where) . ')'; $result = $this->dbw->select( 'revision', '*', - array( 'rev_page' => $title->getArticleID(), - $whereClause ), + array( + 'rev_page' => $title->getArticleID(), + 'rev_id' => $where ), __METHOD__ ); while( $row = $this->dbw->fetchObject( $result ) ) { $revObjs[$row->rev_id] = new Revision( $row ); @@ -869,27 +910,28 @@ class RevisionDeleter { $Id_set = array(); // Run through and pull all our data in one query foreach( $items as $timestamp ) { - $where[] = $this->dbw->addQuotes( $timestamp ); + $where[] = $this->dbw->timestamp( $timestamp ); } - $whereClause = 'ar_timestamp IN(' . implode(',',$where) . ')'; $result = $this->dbw->select( 'archive', '*', - array( 'ar_namespace' => $title->getNamespace(), + array( + 'ar_namespace' => $title->getNamespace(), 'ar_title' => $title->getDBKey(), - $whereClause ), + 'ar_timestamp' => $where ), __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) ); + $timestamp = wfTimestamp( TS_MW, $row->ar_timestamp ); + $revObjs[$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' => $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 ) { @@ -939,12 +981,12 @@ class RevisionDeleter { $set = array(); // Run through and pull all our data in one query foreach( $items as $timestamp ) { - $where[] = $this->dbw->addQuotes( $timestamp.'!'.$title->getDBKey() ); + $where[] = $timestamp.'!'.$title->getDBKey(); } - $whereClause = 'oi_archive_name IN(' . implode(',',$where) . ')'; $result = $this->dbw->select( 'oldimage', '*', - array( 'oi_name' => $title->getDBKey(), - $whereClause ), + array( + 'oi_name' => $title->getDBKey(), + 'oi_archive_name' => $where ), __METHOD__ ); while( $row = $this->dbw->fetchObject( $result ) ) { $filesObjs[$row->oi_archive_name] = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row ); @@ -1034,10 +1076,9 @@ class RevisionDeleter { foreach( $items as $id ) { $where[] = intval($id); } - $whereClause = 'fa_id IN(' . implode(',',$where) . ')'; $result = $this->dbw->select( 'filearchive', '*', array( 'fa_name' => $title->getDBKey(), - $whereClause ), + 'fa_id' => $where ), __METHOD__ ); while( $row = $this->dbw->fetchObject( $result ) ) { $filesObjs[$row->fa_id] = ArchivedFile::newFromRow( $row ); @@ -1091,9 +1132,10 @@ class RevisionDeleter { $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 ), + array( + 'log_type' => $logtype, + 'log_id' => $where ), __METHOD__ ); while( $row = $this->dbw->fetchObject( $result ) ) { $logRows[$row->log_id] = $row; diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index f3117242..cb783819 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -41,7 +41,7 @@ function wfSpecialSearch( $par = '' ) { || !is_null( $wgRequest->getVal( 'offset' )) || !is_null( $wgRequest->getVal( 'searchx' )) ) { - $searchPage->showResults( $search, 'search' ); + $searchPage->showResults( $search ); } else { $searchPage->goResult( $search ); } @@ -74,6 +74,7 @@ class SpecialSearch { $this->active = 'advanced'; $this->sk = $user->getSkin(); $this->didYouMeanHtml = ''; # html of did you mean... link + $this->fulltext = $request->getVal('fulltext'); } /** @@ -163,9 +164,13 @@ class SpecialSearch { // did you mean... suggestions if( $textMatches && $textMatches->hasSuggestion() ) { - $st = SpecialPage::getTitleFor( 'Search' ); + $st = SpecialPage::getTitleFor( 'Search' ); + # mirror Go/Search behaviour of original request .. + $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); + if($this->fulltext != NULL) + $didYouMeanParams['fulltext'] = $this->fulltext; $stParams = wfArrayToCGI( - array( 'search' => $textMatches->getSuggestionQuery(), 'fulltext' => wfMsg('search') ), + $didYouMeanParams, $this->powerSearchOptions() ); $suggestLink = $sk->makeKnownLinkObj( $st, @@ -610,7 +615,8 @@ class SpecialSearch { $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' ) ); + $searchField = Xml::inputLabel( wfMsg('powersearch-field'), 'search', 'powerSearchText', 50, $term, + array( 'type' => 'text') ); $searchButton = Xml::submitButton( wfMsg( 'powersearch' ), array( 'name' => 'fulltext' )) . "\n"; $searchTitle = SpecialPage::getTitleFor( 'Search' ); @@ -630,10 +636,9 @@ class SpecialSearch { "<hr style=\"clear: both;\" />\n". $redirectText ."\n". "<div style=\"padding-top:2px;padding-bottom:2px;\">". - wfMsgExt( 'powersearch-field', array( 'parseinline' ) ) . - " " . $searchField . " " . + Xml::hidden( 'fulltext', 'Advanced search' ) . "\n" . $searchButton . "</div>". "</form>"; @@ -666,7 +671,7 @@ class SpecialSearch { } protected function formHeader( $term ) { - global $wgContLang, $wgCanonicalNamespaceNames; + global $wgContLang, $wgCanonicalNamespaceNames, $wgLang; $sep = ' '; $out = Xml::openElement('div', array( 'style' => 'padding-bottom:0.5em;' ) ); @@ -680,7 +685,7 @@ class SpecialSearch { // search profiles headers $m = wfMsg( 'searchprofile-articles' ); $tt = wfMsg( 'searchprofile-articles-tooltip', - implode( ', ', SearchEngine::namespacesAsText( SearchEngine::defaultNamespaces() ) ) ); + $wgLang->commaList( SearchEngine::namespacesAsText( SearchEngine::defaultNamespaces() ) ) ); if( $this->active == 'default' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { @@ -697,22 +702,10 @@ class SpecialSearch { $out .= $this->makeSearchLink( $imageTextForm, array( NS_FILE ) , $m, $tt ); } $out .= $sep; - - /* - $m = wfMsg( 'searchprofile-articles-and-proj' ); - $tt = wfMsg( 'searchprofile-project-tooltip', - implode( ', ', SearchEngine::namespacesAsText( SearchEngine::defaultAndProjectNamespaces() ) ) ); - if( $this->active == 'withproject' ) { - $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); - } else { - $out .= $this->makeSearchLink( $bareterm, SearchEngine::defaultAndProjectNamespaces(), $m, $tt ); - } - $out .= $sep; - */ - + $m = wfMsg( 'searchprofile-project' ); $tt = wfMsg( 'searchprofile-project-tooltip', - implode( ', ', SearchEngine::namespacesAsText( SearchEngine::projectNamespaces() ) ) ); + $wgLang->commaList( SearchEngine::namespacesAsText( SearchEngine::projectNamespaces() ) ) ); if( $this->active == 'project' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { @@ -765,6 +758,7 @@ class SpecialSearch { $out .= Xml::hidden( "redirs", (int)$this->searchRedirects ); // Term box $out .= Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'searchText' ) ) . "\n"; + $out .= Xml::hidden( 'fulltext', 'Search' ); $out .= Xml::submitButton( wfMsg( 'searchbutton' ), array( 'name' => 'fulltext' ) ); $out .= ' (' . wfMsgExt('searchmenu-help',array('parseinline') ) . ')'; $out .= Xml::closeElement( 'form' ); @@ -867,6 +861,7 @@ class SpecialSearchOld { } $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false; + $this->fulltext = $request->getVal('fulltext'); } /** @@ -906,21 +901,21 @@ class SpecialSearchOld { } } - $wgOut->wrapWikiMsg( "==$1==\n", 'notitlematches' ); + $extra = $wgOut->parse( '=='.wfMsgNoTrans( 'notitlematches' )."==\n" ); if( $t->quickUserCan( 'create' ) && $t->quickUserCan( 'edit' ) ) { - $wgOut->addWikiMsg( 'noexactmatch', wfEscapeWikiText( $term ) ); + $extra .= wfMsgExt( 'noexactmatch', 'parse', wfEscapeWikiText( $term ) ); } else { - $wgOut->addWikiMsg( 'noexactmatch-nocreate', wfEscapeWikiText( $term ) ); + $extra .= wfMsgExt( 'noexactmatch-nocreate', 'parse', wfEscapeWikiText( $term ) ); } - return $this->showResults( $term ); + $this->showResults( $term, $extra ); } /** * @param string $term - * @public + * @param string $extra Extra HTML to add after "did you mean" */ - function showResults( $term ) { + public function showResults( $term, $extra = '' ) { wfProfileIn( __METHOD__ ); global $wgOut, $wgUser; $sk = $wgUser->getSkin(); @@ -931,7 +926,7 @@ class SpecialSearchOld { $search->showRedirects = $this->searchRedirects; $search->prefix = $this->mPrefix; $term = $search->transformSearchTerm($term); - + $this->setupPage( $term ); $rewritten = $search->replacePrefixes($term); @@ -940,20 +935,27 @@ class SpecialSearchOld { // did you mean... suggestions if($textMatches && $textMatches->hasSuggestion()){ - $st = SpecialPage::getTitleFor( 'Search' ); - $stParams = wfArrayToCGI( array( - 'search' => $textMatches->getSuggestionQuery(), - 'fulltext' => wfMsg('search')), - $this->powerSearchOptions()); - + $st = SpecialPage::getTitleFor( 'Search' ); + + # mirror Go/Search behaviour of original request + $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); + if($this->fulltext != NULL) + $didYouMeanParams['fulltext'] = $this->fulltext; + $stParams = wfArrayToCGI( + $didYouMeanParams, + $this->powerSearchOptions() + ); + $suggestLink = $sk->makeKnownLinkObj( $st, $textMatches->getSuggestionSnippet(), $stParams ); - + $wgOut->addHTML('<div class="searchdidyoumean">'.wfMsg('search-suggest',$suggestLink).'</div>'); } - $wgOut->addWikiMsg( 'searchresulttext' ); + $wgOut->addHTML( $extra ); + + $wgOut->wrapWikiMsg( "<div class='mw-searchresult'>\n$1</div>", 'searchresulttext' ); if( '' === trim( $term ) ) { // Empty query -- straight view of search form @@ -1428,10 +1430,11 @@ class SpecialSearchOld { $searchField = Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'powerSearchText' ) ); $searchButton = Xml::submitButton( wfMsg( 'powersearch' ), array( 'name' => 'fulltext' ) ) . "\n"; $searchTitle = SpecialPage::getTitleFor( 'Search' ); + $searchHiddens = Xml::hidden( 'title', $searchTitle->getPrefixedText() ) . "\n"; + $searchHiddens .= Xml::hidden( 'fulltext', 'Advanced search' ) . "\n"; $out = Xml::openElement( 'form', array( 'id' => 'powersearch', 'method' => 'get', 'action' => $wgScript ) ) . - Xml::fieldset( wfMsg( 'powersearch-legend' ), - Xml::hidden( 'title', $searchTitle->getPrefixedText() ) . "\n" . + Xml::fieldset( wfMsg( 'powersearch-legend' ), "<p>" . wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) . "</p>\n" . @@ -1444,6 +1447,7 @@ class SpecialSearchOld { " " . $searchField . " " . + $searchHiddens . $searchButton ) . "</form>"; @@ -1468,13 +1472,14 @@ class SpecialSearchOld { 'action' => $wgScript )); $searchTitle = SpecialPage::getTitleFor( 'Search' ); - $out .= Xml::hidden( 'title', $searchTitle->getPrefixedText() ); $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::hidden( 'title', $searchTitle->getPrefixedText() ); + $out .= Xml::hidden( 'fulltext', 'Search' ); $out .= Xml::submitButton( wfMsg( 'searchbutton' ), array( 'name' => 'fulltext' ) ); $out .= Xml::closeElement( 'form' ); diff --git a/includes/specials/SpecialSpecialpages.php b/includes/specials/SpecialSpecialpages.php index 560ba445..4959f107 100644 --- a/includes/specials/SpecialSpecialpages.php +++ b/includes/specials/SpecialSpecialpages.php @@ -48,19 +48,21 @@ function wfSpecialSpecialpages() { $groups['other'] = $other; } + $includesRestrictedPages = false; /** 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->wrapWikiMsg( "<h4 class='mw-specialpagesgroup'>$1</h4>\n", "specialpages-group-$group" ); $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 ) { + $includesRestrictedPages = true; $wgOut->addHTML( "<li class='mw-specialpages-page mw-specialpagerestricted'>{$link}</li>\n" ); } else { $wgOut->addHTML( "<li>{$link}</li>\n" ); @@ -74,9 +76,8 @@ function wfSpecialSpecialpages() { } $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') - ); + + if ( $includesRestrictedPages ) { + $wgOut->wrapWikiMsg( "<div class=\"mw-specialpages-notes\">\n$1\n</div>", 'specialpages-note' ); + } } diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php new file mode 100644 index 00000000..981eb2ff --- /dev/null +++ b/includes/specials/SpecialTags.php @@ -0,0 +1,73 @@ +<?php + +if (!defined('MEDIAWIKI')) + die; + +class SpecialTags extends SpecialPage { + + function __construct() { + parent::__construct( 'Tags' ); + } + + function execute( $par ) { + global $wgOut, $wgUser, $wgMessageCache; + + $wgMessageCache->loadAllMessages(); + + $sk = $wgUser->getSkin(); + $wgOut->setPageTitle( wfMsg( 'tags-title' ) ); + $wgOut->wrapWikiMsg( "<div class='mw-tags-intro'>\n$1</div>", 'tags-intro' ); + + // Write the headers + $html = ''; + $html = Xml::tags( 'tr', null, Xml::tags( 'th', null, wfMsgExt( 'tags-tag', 'parseinline' ) ) . + Xml::tags( 'th', null, wfMsgExt( 'tags-display-header', 'parseinline' ) ) . + Xml::tags( 'th', null, wfMsgExt( 'tags-description-header', 'parseinline' ) ) . + Xml::tags( 'th', null, wfMsgExt( 'tags-hitcount-header', 'parseinline' ) ) + ); + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( 'change_tag', array( 'ct_tag', 'count(*) as hitcount' ), array(), __METHOD__, array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' ) ); + + while ( $row = $res->fetchObject() ) { + $html .= $this->doTagRow( $row->ct_tag, $row->hitcount ); + } + + foreach( ChangeTags::listDefinedTags() as $tag ) { + $html .= $this->doTagRow( $tag, 0 ); + } + + $wgOut->addHTML( Xml::tags( 'table', array( 'class' => 'mw-tags-table' ), $html ) ); + } + + function doTagRow( $tag, $hitcount ) { + static $sk=null, $doneTags=array(); + if (!$sk) { + global $wgUser; + $sk = $wgUser->getSkin(); + } + + if ( in_array( $tag, $doneTags ) ) { + return ''; + } + + $newRow = ''; + $newRow .= Xml::tags( 'td', null, Xml::element( 'tt', null, $tag ) ); + + $disp = ChangeTags::tagDescription( $tag ); + $disp .= ' (' . $sk->link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ), wfMsg( 'tags-edit' ) ) . ')'; + $newRow .= Xml::tags( 'td', null, $disp ); + + $desc = wfMsgExt( "tag-$tag-description", 'parseinline' ); + $desc = wfEmptyMsg( "tag-$tag-description", $desc ) ? '' : $desc; + $desc .= ' (' . $sk->link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ), wfMsg( 'tags-edit' ) ) . ')'; + $newRow .= Xml::tags( 'td', null, $desc ); + + $hitcount = wfMsg( 'tags-hitcount', $hitcount ); + $hitcount = $sk->link( SpecialPage::getTitleFor( 'RecentChanges' ), $hitcount, array(), array( 'tagfilter' => $tag ) ); + $newRow .= Xml::tags( 'td', null, $hitcount ); + + $doneTags[] = $tag; + + return Xml::tags( 'tr', null, $newRow ) . "\n"; + } +}
\ No newline at end of file diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index a9fb4ef1..d97efb59 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -737,10 +737,10 @@ class UndeleteForm { if( $rev->isDeleted(Revision::DELETED_TEXT) ) { if( !$rev->userCan(Revision::DELETED_TEXT) ) { - $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) ); + $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' ); return; } else { - $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) ); + $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' ); $wgOut->addHTML( '<br/>' ); // and we are allowed to see... } @@ -996,6 +996,11 @@ class UndeleteForm { # Show relevant lines from the deletion log: $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) . "\n" ); LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTargetObj->getPrefixedText() ); + # Show relevant lines from the suppression log: + if( $wgUser->isAllowed( 'suppressionlog' ) ) { + $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'suppress' ) ) . "\n" ); + LogEventsList::showLogExtract( $wgOut, 'suppress', $this->mTargetObj->getPrefixedText() ); + } if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) { # Format the user-visible controls (comment field, submission button) @@ -1132,19 +1137,15 @@ class UndeleteForm { $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'); + $revdlink = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.wfMsgHtml('rev-delundel').')' ); } else { - $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>"; + $query = array( 'target' => $this->mTargetObj->getPrefixedDBkey(), + 'artimestamp[]' => $ts + ); + $revdlink = $sk->revDeleteLink( $query, $rev->isDeleted( Revision::DELETED_RESTRICTED ) ); } - $revdlink = "<tt>(<small>$del</small>)</tt>"; } return "<li>$checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment</li>"; @@ -1178,20 +1179,15 @@ class UndeleteForm { $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'); + $revdlink = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.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>"; + $query = array( 'target' => $this->mTargetObj->getPrefixedDBkey(), + 'fileid' => $row->fa_id + ); + $revdlink = $sk->revDeleteLink( $query, $file->isDeleted( File::DELETED_RESTRICTED ) ); } - $revdlink = "<tt>(<small>$del</small>)</tt>"; } return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n"; } diff --git a/includes/specials/SpecialUnlockdb.php b/includes/specials/SpecialUnlockdb.php index 0bf7e5aa..a3e8a0c4 100644 --- a/includes/specials/SpecialUnlockdb.php +++ b/includes/specials/SpecialUnlockdb.php @@ -98,10 +98,9 @@ END function showSuccess() { global $wgOut; - global $ip; $wgOut->setPagetitle( wfMsg( "unlockdb" ) ); $wgOut->setSubtitle( wfMsg( "unlockdbsuccesssub" ) ); - $wgOut->addWikiMsg( "unlockdbsuccesstext", $ip ); + $wgOut->addWikiMsg( "unlockdbsuccesstext" ); } } diff --git a/includes/specials/SpecialUnusedimages.php b/includes/specials/SpecialUnusedimages.php index 4adf405d..fa66555d 100644 --- a/includes/specials/SpecialUnusedimages.php +++ b/includes/specials/SpecialUnusedimages.php @@ -22,13 +22,17 @@ class UnusedimagesPage extends ImageQueryPage { function isSyndicated() { return false; } function getSQL() { - global $wgCountCategorizedImagesAsUsed; + global $wgCountCategorizedImagesAsUsed, $wgDBtype; $dbr = wfGetDB( DB_SLAVE ); + $epoch = $wgDBtype == 'mysql' ? + 'UNIX_TIMESTAMP(img_timestamp)' : + 'EXTRACT(epoch FROM img_timestamp)'; + 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, + return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, $epoch 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) @@ -37,14 +41,14 @@ class UnusedimagesPage extends ImageQueryPage { } else { list( $image, $imagelinks ) = $dbr->tableNamesN( 'image','imagelinks' ); - return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, img_timestamp as value, + return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, $epoch 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') ); + return wfMsgExt( 'unusedimagestext', array( 'parse' ) ); } } diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index 450c8728..4c5bb160 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -62,6 +62,8 @@ class UploadForm { $this->mDesiredDestName = $request->getText( 'wpDestFile' ); $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); $this->mComment = $request->getText( 'wpUploadDescription' ); + $this->mForReUpload = $request->getBool( 'wpForReUpload' ); + $this->mReUpload = $request->getCheck( 'wpReUpload' ); if( !$request->wasPosted() ) { # GET requests just give the main form; no data except destination @@ -72,8 +74,6 @@ class UploadForm { # 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' ); @@ -155,7 +155,7 @@ class UploadForm { * Returns true if there was an error, false otherwise */ private function curlCopy( $url, $dest ) { - global $wgUser, $wgOut; + global $wgUser, $wgOut, $wgHTTPProxy; if( !$wgUser->isAllowed( 'upload_by_url' ) ) { $wgOut->permissionRequired( 'upload_by_url' ); @@ -183,6 +183,9 @@ class UploadForm { 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); + if( $wgHTTPProxy ) { + curl_setopt( $ch, CURLOPT_PROXY, $wgHTTPProxy ); + } curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) ); curl_exec( $ch ); $error = curl_errno( $ch ) ? true : false; @@ -228,6 +231,12 @@ class UploadForm { global $wgUser, $wgOut; global $wgEnableUploads; + # Check php's file_uploads setting + if( !wfIniGetBool( 'file_uploads' ) ) { + $wgOut->showErrorPage( 'uploaddisabled', 'php-uploaddisabledtext', array( $this->mDesiredDestName ) ); + return; + } + # Check uploading enabled if( !$wgEnableUploads ) { $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) ); @@ -372,7 +381,7 @@ class UploadForm { if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { - wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." ); + wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); return self::BEFORE_PROCESSING; } @@ -504,11 +513,13 @@ class UploadForm { if ( ! $this->mIgnoreWarning ) { $warning = ''; - global $wgCapitalLinks; - if( $wgCapitalLinks ) { - $filtered = ucfirst( $filtered ); + $comparableName = str_replace( ' ', '_', $basename ); + global $wgCapitalLinks, $wgContLang; + if ( $wgCapitalLinks ) { + $comparableName = $wgContLang->ucfirst( $comparableName ); } - if( $basename != $filtered ) { + + if( $comparableName !== $filtered ) { $warning .= '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ).'</li>'; } @@ -541,7 +552,7 @@ class UploadForm { $warning .= self::getExistsWarning( $this->mLocalFile ); } - $warning .= $this->getDupeWarning( $this->mTempPath, $finalExt ); + $warning .= $this->getDupeWarning( $this->mTempPath, $finalExt, $nt ); if( $warning != '' ) { /** @@ -557,8 +568,10 @@ class UploadForm { * Try actually saving the thing... * It will show an error form on failure. */ - $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, - $this->mCopyrightStatus, $this->mCopyrightSource ); + if( !$this->mForReUpload ) { + $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 ); @@ -748,7 +761,7 @@ class UploadForm { * Check for duplicate files and throw up a warning before the upload * completes. */ - function getDupeWarning( $tempfile, $extension ) { + function getDupeWarning( $tempfile, $extension, $destinationTitle ) { $hash = File::sha1Base36( $tempfile ); $dupes = RepoGroup::singleton()->findBySha1( $hash ); $archivedImage = new ArchivedFile( null, 0, $hash.".$extension" ); @@ -757,8 +770,12 @@ class UploadForm { $msg = "<gallery>"; foreach( $dupes as $file ) { $title = $file->getTitle(); - $msg .= $title->getPrefixedText() . - "|" . $title->getText() . "\n"; + # Don't throw the warning when the titles are the same, it's a reupload + # and highly redundant. + if ( !$title->equals( $destinationTitle ) || !$this->mForReUpload ) { + $msg .= $title->getPrefixedText() . + "|" . $title->getText() . "\n"; + } } $msg .= "</gallery>"; return "<li>" . @@ -860,6 +877,7 @@ class UploadForm { */ function unsaveUploadedFile() { global $wgOut; + if( !$this->mTempPath ) return true; // nothing to delete $repo = RepoGroup::singleton()->getLocalRepo(); $success = $repo->freeTemp( $this->mTempPath ); if ( ! $success ) { @@ -958,7 +976,7 @@ wgUploadAutoFill = {$autofill}; if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) { - wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" ); + wfDebug( "Hook 'UploadForm:initial' broke output of the upload form\n" ); return false; } @@ -978,7 +996,7 @@ wgUploadAutoFill = {$autofill}; } // Show the relevant lines from deletion log (for still deleted files only) - if( $title instanceof Title && $title->isDeleted() > 0 && !$title->exists() ) { + if( $title instanceof Title && $title->isDeletedQuick() && !$title->exists() ) { $this->showDeletionLog( $wgOut, $title->getPrefixedText() ); } } @@ -1054,7 +1072,8 @@ wgUploadAutoFill = {$autofill}; $sourcefilename = wfMsgExt( 'sourcefilename', array( 'parseinline', 'escapenoentities' ) ); $destfilename = wfMsgExt( 'destfilename', array( 'parseinline', 'escapenoentities' ) ); - $summary = wfMsgExt( 'fileuploadsummary', 'parseinline' ); + $msg = $this->mForReUpload ? 'filereuploadsummary' : 'fileuploadsummary'; + $summary = wfMsgExt( $msg, 'parseinline' ); $licenses = new Licenses(); $license = wfMsgExt( 'license', array( 'parseinline' ) ); @@ -1068,10 +1087,9 @@ wgUploadAutoFill = {$autofill}; $encDestName = htmlspecialchars( $this->mDesiredDestName ); - $watchChecked = $this->watchCheck() - ? 'checked="checked"' - : ''; - $warningChecked = $this->mIgnoreWarning ? 'checked' : ''; + $watchChecked = $this->watchCheck() ? 'checked="checked"' : ''; + # Re-uploads should not need "file exist already" warnings + $warningChecked = ($this->mIgnoreWarning || $this->mForReUpload) ? 'checked="checked"' : ''; // Prepare form for upload or upload/copy if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) { @@ -1106,8 +1124,8 @@ wgUploadAutoFill = {$autofill}; $warningRow = ''; $destOnkeyup = ''; } - $encComment = htmlspecialchars( $this->mComment ); + $wgOut->addHTML( Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL(), @@ -1135,10 +1153,26 @@ wgUploadAutoFill = {$autofill}; <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> + <td class='mw-input'>" + ); + if( $this->mForReUpload ) { + $wgOut->addHTML( + Xml::hidden( 'wpDestFile', $this->mDesiredDestName, array('id'=>'wpDestFile','tabindex'=>2) ) . + "<tt>" . + $encDestName . + "</tt>" + ); + } + else { + $wgOut->addHTML( + "<input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='60' + value=\"{$encDestName}\" onchange='toggleFilenameFiller()' $destOnkeyup />" + ); + } + + + $wgOut->addHTML( + "</td> </tr> <tr> <td class='mw-label'> @@ -1152,8 +1186,8 @@ wgUploadAutoFill = {$autofill}; </tr> <tr>" ); - - if ( $licenseshtml != '' ) { + # Re-uploads should not need license info + if ( !$this->mForReUpload && $licenseshtml != '' ) { global $wgStylePath; $wgOut->addHTML( " <td class='mw-label'> @@ -1179,7 +1213,7 @@ wgUploadAutoFill = {$autofill}; } } - if ( $wgUseCopyrightUpload ) { + if ( !$this->mForReUpload && $wgUseCopyrightUpload ) { $filestatus = wfMsgExt( 'filestatus', 'escapenoentities' ); $copystatus = htmlspecialchars( $this->mCopyrightStatus ); $filesource = wfMsgExt( 'filesource', 'escapenoentities' ); @@ -1211,7 +1245,7 @@ wgUploadAutoFill = {$autofill}; <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/> + <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' $warningChecked /> <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label> </td> </tr> @@ -1219,19 +1253,23 @@ wgUploadAutoFill = {$autofill}; <tr> <td></td> <td class='mw-input'> - <input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\"" . $wgUser->getSkin()->tooltipAndAccesskey( 'upload' ) . " /> + <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( '<div class="mw-editTools">' ); + $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) ); + $wgOut->addHTML( '</div>' ); $wgOut->addHTML( " </td> </tr>" . Xml::closeElement( 'table' ) . Xml::hidden( 'wpDestFileWarningAck', '', array( 'id' => 'wpDestFileWarningAck' ) ) . + Xml::hidden( 'wpForReUpload', $this->mForReUpload, array( 'id' => 'wpForReUpload' ) ) . Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) ); @@ -1648,7 +1686,7 @@ wgUploadAutoFill = {$autofill}; } } - wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" ); + wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output \n" ); return $output; } } @@ -1685,7 +1723,7 @@ wgUploadAutoFill = {$autofill}; * @access private */ function cleanupTempFile() { - if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) { + if ( $this->mRemoveTempFile && $this->mTempPath && file_exists( $this->mTempPath ) ) { wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" ); unlink( $this->mTempPath ); } diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php index 6a4da7a4..b065bdd6 100644 --- a/includes/specials/SpecialUserlogin.php +++ b/includes/specials/SpecialUserlogin.php @@ -45,7 +45,7 @@ class LoginForm { */ function LoginForm( &$request, $par = '' ) { global $wgLang, $wgAllowRealName, $wgEnableEmail; - global $wgAuth; + global $wgAuth, $wgRedirectOnLogin; $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]] $this->mName = $request->getText( 'wpName' ); @@ -66,6 +66,10 @@ class LoginForm { $this->mLanguage = $request->getText( 'uselang' ); $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' ); + if ( $wgRedirectOnLogin ) { + $this->mReturnTo = $wgRedirectOnLogin; + } + if( $wgEnableEmail ) { $this->mEmail = $request->getText( 'wpEmail' ); } else { @@ -593,7 +597,12 @@ class LoginForm { */ function mailPassword() { global $wgUser, $wgOut, $wgAuth; - + + if ( wfReadOnly() ) { + $wgOut->readOnlyPage(); + return false; + } + if( !$wgAuth->allowPasswordChange() ) { $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) ); return; @@ -654,7 +663,7 @@ class LoginForm { * @private */ function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) { - global $wgServer, $wgScript, $wgUser; + global $wgServer, $wgScript, $wgUser, $wgNewPasswordExpiry; if ( '' == $u->getEmail() ) { return new WikiError( wfMsg( 'noemail', $u->getName() ) ); @@ -670,7 +679,8 @@ class LoginForm { $u->setNewpassword( $np, $throttle ); $u->saveSettings(); - $m = wfMsg( $emailText, $ip, $u->getName(), $np, $wgServer . $wgScript ); + $m = wfMsgExt( $emailText, array( 'parsemag' ), $ip, $u->getName(), $np, + $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) ); $result = $u->sendMail( wfMsg( $emailTitle ), $m ); return $result; @@ -968,6 +978,8 @@ class LoginForm { * @return string */ function makeLanguageSelector() { + global $wgLang; + $msg = wfMsgForContent( 'loginlanguagelinks' ); if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) { $langs = explode( "\n", $msg ); @@ -979,7 +991,7 @@ class LoginForm { $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] ); } } - return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', implode( ' | ', $links ) ) : ''; + return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : ''; } else { return ''; } diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php index ce0097b2..90619109 100644 --- a/includes/specials/SpecialUserrights.php +++ b/includes/specials/SpecialUserrights.php @@ -96,11 +96,18 @@ class UserrightsPage extends SpecialPage { // save settings if( $wgRequest->getCheck( 'saveusergroups' ) ) { $reason = $wgRequest->getVal( 'user-reason' ); - if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $this->mTarget ) ) { + $tok = $wgRequest->getVal( 'wpEditToken' ); + if( $wgUser->matchEditToken( $tok, $this->mTarget ) ) { $this->saveUserGroups( $this->mTarget, $reason ); + + global $wgOut; + + $url = $this->getSuccessURL(); + $wgOut->redirect( $url ); + return; } } } @@ -110,6 +117,10 @@ class UserrightsPage extends SpecialPage { $this->editUserGroupsForm( $this->mTarget ); } } + + function getSuccessURL() { + return $this->getTitle( $this->mTarget )->getFullURL(); + } /** * Save user groups changes in the database. @@ -231,9 +242,9 @@ class UserrightsPage extends SpecialPage { * @return mixed User, UserRightsProxy, or null */ function fetchUser( $username ) { - global $wgOut, $wgUser; + global $wgOut, $wgUser, $wgUserrightsInterwikiDelimiter; - $parts = explode( '@', $username ); + $parts = explode( $wgUserrightsInterwikiDelimiter, $username ); if( count( $parts ) < 2 ) { $name = trim( $username ); $database = ''; diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php index 29f527f2..95e06f4b 100644 --- a/includes/specials/SpecialVersion.php +++ b/includes/specials/SpecialVersion.php @@ -50,7 +50,7 @@ class SpecialVersion extends SpecialPage { $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, + copyright (C) 2001-2009 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, Aryeh Gregor, Aaron Schulz and others. diff --git a/includes/specials/SpecialWantedfiles.php b/includes/specials/SpecialWantedfiles.php index c2731fa9..4957531e 100644 --- a/includes/specials/SpecialWantedfiles.php +++ b/includes/specials/SpecialWantedfiles.php @@ -72,9 +72,26 @@ class WantedFilesPage extends QueryPage { $skin->makeLinkObj( $nt, htmlspecialchars( $text ) ) : $skin->makeBrokenLinkObj( $nt, htmlspecialchars( $text ) ); - $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'), - $wgLang->formatNum( $result->value ) ); - return wfSpecialList($plink, $nlinks); + return wfSpecialList( + $plink, + $this->makeWlhLink( $nt, $skin, $result ) + ); + } + + /** + * 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->link( $wlh, $label, array(), array( 'target' => $title->getPrefixedText() ) ); } } diff --git a/includes/specials/SpecialWantedpages.php b/includes/specials/SpecialWantedpages.php index 10133409..7307b335 100644 --- a/includes/specials/SpecialWantedpages.php +++ b/includes/specials/SpecialWantedpages.php @@ -31,8 +31,7 @@ class WantedPagesPage extends QueryPage { $dbr = wfGetDB( DB_SLAVE ); $pagelinks = $dbr->tableName( 'pagelinks' ); $page = $dbr->tableName( 'page' ); - return - "SELECT 'Wantedpages' AS type, + $sql = "SELECT 'Wantedpages' AS type, pl_namespace AS namespace, pl_title AS title, COUNT(*) AS value @@ -46,6 +45,9 @@ class WantedPagesPage extends QueryPage { AND pg2.page_namespace != 8 GROUP BY pl_namespace, pl_title HAVING COUNT(*) > $count"; + + wfRunHooks( 'WantedPages::getSQL', array( &$this, &$sql ) ); + return $sql; } /** @@ -83,7 +85,7 @@ class WantedPagesPage extends QueryPage { return wfSpecialList( $pageLink, $this->makeWlhLink( $title, $skin, $result ) ); } else { $tsafe = htmlspecialchars( $result->title ); - return "Invalid title in result set; {$tsafe}"; + return wfMsg( 'wantedpages-badtitle', $tsafe ); } } diff --git a/includes/specials/SpecialWantedtemplates.php b/includes/specials/SpecialWantedtemplates.php index 43b5cf8f..7dd9a262 100644 --- a/includes/specials/SpecialWantedtemplates.php +++ b/includes/specials/SpecialWantedtemplates.php @@ -42,7 +42,7 @@ class WantedTemplatesPage extends QueryPage { FROM $templatelinks LEFT JOIN $page ON tl_title = page_title AND tl_namespace = page_namespace WHERE page_title IS NULL AND tl_namespace = ". NS_TEMPLATE ." - GROUP BY tl_title + GROUP BY tl_namespace, tl_title "; } @@ -73,8 +73,6 @@ class WantedTemplatesPage extends QueryPage { $skin->makeLinkObj( $nt, htmlspecialchars( $text ) ) : $skin->makeBrokenLinkObj( $nt, htmlspecialchars( $text ) ); - $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'), - $wgLang->formatNum( $result->value ) ); return wfSpecialList( $plink, $this->makeWlhLink( $nt, $skin, $result ) diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index 61dd6b3e..b14577b5 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -54,7 +54,7 @@ function wfSpecialWatchlist( $par ) { /* bool */ 'hideBots' => (int)$wgUser->getBoolOption( 'watchlisthidebots' ), /* bool */ 'hideAnons' => (int)$wgUser->getBoolOption( 'watchlisthideanons' ), /* bool */ 'hideLiu' => (int)$wgUser->getBoolOption( 'watchlisthideliu' ), - /* bool */ 'hidePatrolled' => (int)$wgUser->getBoolOption( 'watchlisthidepatrolled' ), // TODO + /* bool */ 'hidePatrolled' => (int)$wgUser->getBoolOption( 'watchlisthidepatrolled' ), /* bool */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ), /* ? */ 'namespace' => 'all', /* ? */ 'invert' => false, @@ -96,7 +96,7 @@ function wfSpecialWatchlist( $par ) { } $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); - list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' ); + $recentchanges = $dbr->tableName( 'recentchanges' ); $watchlistCount = $dbr->selectField( 'watchlist', 'COUNT(*)', array( 'wl_user' => $uid ), __METHOD__ ); @@ -159,10 +159,12 @@ function wfSpecialWatchlist( $par ) { if( $wgUser->getOption( 'extendwatchlist' )) { $andLatest=''; $limitWatchlist = intval( $wgUser->getOption( 'wllimit' ) ); + $usePage = false; } else { # Top log Ids for a page are not stored $andLatest = 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG; $limitWatchlist = 0; + $usePage = true; } # Show a message about slave lag, if applicable @@ -189,12 +191,11 @@ function wfSpecialWatchlist( $par ) { } $form .= '<hr />'; - $tables = array( 'recentchanges', 'watchlist', 'page' ); + $tables = array( 'recentchanges', 'watchlist' ); $fields = array( "{$recentchanges}.*" ); $conds = array(); $join_conds = array( 'watchlist' => array('INNER JOIN',"wl_user='{$uid}' AND wl_namespace=rc_namespace AND wl_title=rc_title"), - 'page' => array('LEFT JOIN','rc_cur_id=page_id') ); $options = array( 'ORDER BY' => 'rc_timestamp DESC' ); if( $wgShowUpdatedMarker ) { @@ -212,7 +213,16 @@ function wfSpecialWatchlist( $par ) { if( $andHideAnons ) $conds[] = $andHideAnons; if( $andHidePatrolled ) $conds[] = $andHidePatrolled; if( $nameSpaceClause ) $conds[] = $nameSpaceClause; - + + $rollbacker = $wgUser->isAllowed('rollback'); + if ( $usePage || $rollbacker ) { + $tables[] = 'page'; + $join_conds['page'] = array('LEFT JOIN','rc_cur_id=page_id'); + if ($rollbacker) + $fields[] = 'page_latest'; + } + + ChangeTags::modifyDisplayQuery( $tables, $fields, $conds, $join_conds, $options, '' ); wfRunHooks('SpecialWatchlistQuery', array(&$conds,&$tables,&$join_conds,&$fields) ); $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options, $join_conds ); @@ -279,7 +289,7 @@ function wfSpecialWatchlist( $par ) { # Namespace filter and put the whole form together. $form .= $wlInfo; $form .= $cutofflinks; - $form .= implode( ' | ', $links ); + $form .= $wgLang->pipeList( $links ); $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) ); $form .= '<hr /><p>'; $form .= Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' '; @@ -317,6 +327,8 @@ function wfSpecialWatchlist( $par ) { $linkBatch->add( NS_USER, $userNameUnderscored ); } $linkBatch->add( NS_USER_TALK, $userNameUnderscored ); + + $linkBatch->add( $row->rc_namespace, $row->rc_title ); } $linkBatch->execute(); $dbr->dataSeek( $res, 0 ); @@ -348,7 +360,7 @@ function wfSpecialWatchlist( $par ) { $rc->numberofWatchingusers = 0; } - $s .= $list->recentChangesLine( $rc, $updated ); + $s .= $list->recentChangesLine( $rc, $updated, $counter ); } $s .= $list->endRecentChangesList(); @@ -380,6 +392,8 @@ function wlDaysLink( $d, $page, $options = array() ) { * Returns html */ function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) { + global $wgLang; + $hours = array( 1, 2, 6, 12 ); $days = array( 1, 3, 7 ); $i = 0; @@ -392,8 +406,8 @@ function wlCutoffLinks( $days, $page = 'Watchlist', $options = array() ) { } return wfMsgExt('wlshowlast', array('parseinline', 'replaceafter'), - implode(' | ', $hours), - implode(' | ', $days), + $wgLang->pipeList( $hours ), + $wgLang->pipeList( $days ), wlDaysLink( 0, $page, $options ) ); } diff --git a/includes/specials/SpecialWhatlinkshere.php b/includes/specials/SpecialWhatlinkshere.php index d91b4960..3f485bd8 100644 --- a/includes/specials/SpecialWhatlinkshere.php +++ b/includes/specials/SpecialWhatlinkshere.php @@ -340,7 +340,7 @@ class WhatLinksHerePage { $limitLinks[] = $this->makeSelfLink( $prettyLimit, wfArrayToCGI( $overrides, $changed ) ); } - $nums = implode ( ' | ', $limitLinks ); + $nums = $wgLang->pipeList( $limitLinks ); return wfMsgHtml( 'viewprevnext', $prev, $next, $nums ); } @@ -389,6 +389,7 @@ class WhatLinksHerePage { } function getFilterPanel() { + global $wgLang; $show = wfMsgHtml( 'show' ); $hide = wfMsgHtml( 'hide' ); @@ -405,6 +406,6 @@ class WhatLinksHerePage { $overrides = array( $type => !$chosen ); $links[] = $this->makeSelfLink( $msg, wfArrayToCGI( $overrides, $changed ) ); } - return Xml::fieldset( wfMsg( 'whatlinkshere-filters' ), implode( ' | ', $links ) ); + return Xml::fieldset( wfMsg( 'whatlinkshere-filters' ), $wgLang->pipeList( $links ) ); } } |