MediaWiki
REL1_24
|
00001 <?php 00025 class ChangesList extends ContextSource { 00029 public $skin; 00030 00031 protected $watchlist = false; 00032 protected $lastdate; 00033 protected $message; 00034 protected $rc_cache; 00035 protected $rcCacheIndex; 00036 protected $rclistOpen; 00037 protected $rcMoveIndex; 00038 00040 protected $watchingCache; 00041 00047 public function __construct( $obj ) { 00048 if ( $obj instanceof IContextSource ) { 00049 $this->setContext( $obj ); 00050 $this->skin = $obj->getSkin(); 00051 } else { 00052 $this->setContext( $obj->getContext() ); 00053 $this->skin = $obj; 00054 } 00055 $this->preCacheMessages(); 00056 $this->watchingCache = new MapCacheLRU( 50 ); 00057 } 00058 00066 public static function newFromContext( IContextSource $context ) { 00067 $user = $context->getUser(); 00068 $sk = $context->getSkin(); 00069 $list = null; 00070 if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) { 00071 $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) ); 00072 00073 return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context ); 00074 } else { 00075 return $list; 00076 } 00077 } 00078 00083 public function setWatchlistDivs( $value = true ) { 00084 $this->watchlist = $value; 00085 } 00086 00091 public function isWatchlist() { 00092 return (bool)$this->watchlist; 00093 } 00094 00099 private function preCacheMessages() { 00100 if ( !isset( $this->message ) ) { 00101 foreach ( array( 00102 'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history', 00103 'semicolon-separator', 'pipe-separator' ) as $msg 00104 ) { 00105 $this->message[$msg] = $this->msg( $msg )->escaped(); 00106 } 00107 } 00108 } 00109 00116 public function recentChangesFlags( $flags, $nothing = ' ' ) { 00117 $f = ''; 00118 foreach ( array_keys( $this->getConfig()->get( 'RecentChangesFlags' ) ) as $flag ) { 00119 $f .= isset( $flags[$flag] ) && $flags[$flag] 00120 ? self::flag( $flag ) 00121 : $nothing; 00122 } 00123 00124 return $f; 00125 } 00126 00136 public static function flag( $flag ) { 00137 static $flagInfos = null; 00138 if ( is_null( $flagInfos ) ) { 00139 global $wgRecentChangesFlags; 00140 $flagInfos = array(); 00141 foreach ( $wgRecentChangesFlags as $key => $value ) { 00142 $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped(); 00143 $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped(); 00144 // Allow customized class name, fall back to flag name 00145 $flagInfos[$key]['class'] = Sanitizer::escapeClass( 00146 isset( $value['class'] ) ? $value['class'] : $key ); 00147 } 00148 } 00149 00150 // Inconsistent naming, bleh, kepted for b/c 00151 $map = array( 00152 'minoredit' => 'minor', 00153 'botedit' => 'bot', 00154 ); 00155 if ( isset( $map[$flag] ) ) { 00156 $flag = $map[$flag]; 00157 } 00158 00159 return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . 00160 $flagInfos[$flag]['title'] . "'>" . $flagInfos[$flag]['letter'] . 00161 '</abbr>'; 00162 } 00163 00168 public function beginRecentChangesList() { 00169 $this->rc_cache = array(); 00170 $this->rcMoveIndex = 0; 00171 $this->rcCacheIndex = 0; 00172 $this->lastdate = ''; 00173 $this->rclistOpen = false; 00174 $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' ); 00175 00176 return '<div class="mw-changeslist">'; 00177 } 00178 00182 public function initChangesListRows( $rows ) { 00183 wfRunHooks( 'ChangesListInitRows', array( $this, $rows ) ); 00184 } 00185 00193 public static function showCharacterDifference( $old, $new, IContextSource $context = null ) { 00194 if ( !$context ) { 00195 $context = RequestContext::getMain(); 00196 } 00197 00198 $new = (int)$new; 00199 $old = (int)$old; 00200 $szdiff = $new - $old; 00201 00202 $lang = $context->getLanguage(); 00203 $config = $context->getConfig(); 00204 $code = $lang->getCode(); 00205 static $fastCharDiff = array(); 00206 if ( !isset( $fastCharDiff[$code] ) ) { 00207 $fastCharDiff[$code] = $config->get( 'MiserMode' ) || $context->msg( 'rc-change-size' )->plain() === '$1'; 00208 } 00209 00210 $formattedSize = $lang->formatNum( $szdiff ); 00211 00212 if ( !$fastCharDiff[$code] ) { 00213 $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text(); 00214 } 00215 00216 if ( abs( $szdiff ) > abs( $config->get( 'RCChangedSizeThreshold' ) ) ) { 00217 $tag = 'strong'; 00218 } else { 00219 $tag = 'span'; 00220 } 00221 00222 if ( $szdiff === 0 ) { 00223 $formattedSizeClass = 'mw-plusminus-null'; 00224 } elseif ( $szdiff > 0 ) { 00225 $formattedSize = '+' . $formattedSize; 00226 $formattedSizeClass = 'mw-plusminus-pos'; 00227 } else { 00228 $formattedSizeClass = 'mw-plusminus-neg'; 00229 } 00230 00231 $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text(); 00232 00233 return Html::element( $tag, 00234 array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ), 00235 $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark(); 00236 } 00237 00245 public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) { 00246 $oldlen = $old->mAttribs['rc_old_len']; 00247 00248 if ( $new ) { 00249 $newlen = $new->mAttribs['rc_new_len']; 00250 } else { 00251 $newlen = $old->mAttribs['rc_new_len']; 00252 } 00253 00254 if ( $oldlen === null || $newlen === null ) { 00255 return ''; 00256 } 00257 00258 return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() ); 00259 } 00260 00265 public function endRecentChangesList() { 00266 $out = $this->rclistOpen ? "</ul>\n" : ''; 00267 $out .= '</div>'; 00268 00269 return $out; 00270 } 00271 00276 public function insertDateHeader( &$s, $rc_timestamp ) { 00277 # Make date header if necessary 00278 $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() ); 00279 if ( $date != $this->lastdate ) { 00280 if ( $this->lastdate != '' ) { 00281 $s .= "</ul>\n"; 00282 } 00283 $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">"; 00284 $this->lastdate = $date; 00285 $this->rclistOpen = true; 00286 } 00287 } 00288 00294 public function insertLog( &$s, $title, $logtype ) { 00295 $page = new LogPage( $logtype ); 00296 $logname = $page->getName()->escaped(); 00297 $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped(); 00298 } 00299 00305 public function insertDiffHist( &$s, &$rc, $unpatrolled ) { 00306 # Diff link 00307 if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) { 00308 $diffLink = $this->message['diff']; 00309 } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) { 00310 $diffLink = $this->message['diff']; 00311 } else { 00312 $query = array( 00313 'curid' => $rc->mAttribs['rc_cur_id'], 00314 'diff' => $rc->mAttribs['rc_this_oldid'], 00315 'oldid' => $rc->mAttribs['rc_last_oldid'] 00316 ); 00317 00318 $diffLink = Linker::linkKnown( 00319 $rc->getTitle(), 00320 $this->message['diff'], 00321 array( 'tabindex' => $rc->counter ), 00322 $query 00323 ); 00324 } 00325 $diffhist = $diffLink . $this->message['pipe-separator']; 00326 # History link 00327 $diffhist .= Linker::linkKnown( 00328 $rc->getTitle(), 00329 $this->message['hist'], 00330 array(), 00331 array( 00332 'curid' => $rc->mAttribs['rc_cur_id'], 00333 'action' => 'history' 00334 ) 00335 ); 00336 // @todo FIXME: Hard coded ". .". Is there a message for this? Should there be? 00337 $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . 00338 ' <span class="mw-changeslist-separator">. .</span> '; 00339 } 00340 00347 public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) { 00348 $params = array(); 00349 if ( $rc->getTitle()->isRedirect() ) { 00350 $params = array( 'redirect' => 'no' ); 00351 } 00352 00353 $articlelink = Linker::linkKnown( 00354 $rc->getTitle(), 00355 null, 00356 array( 'class' => 'mw-changeslist-title' ), 00357 $params 00358 ); 00359 if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) { 00360 $articlelink = '<span class="history-deleted">' . $articlelink . '</span>'; 00361 } 00362 # To allow for boldening pages watched by this user 00363 $articlelink = "<span class=\"mw-title\">{$articlelink}</span>"; 00364 # RTL/LTR marker 00365 $articlelink .= $this->getLanguage()->getDirMark(); 00366 00367 wfRunHooks( 'ChangesListInsertArticleLink', 00368 array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) ); 00369 00370 $s .= " $articlelink"; 00371 } 00372 00380 public function getTimestamp( $rc ) { 00381 // @todo FIXME: Hard coded ". .". Is there a message for this? Should there be? 00382 return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' . 00383 $this->getLanguage()->userTime( 00384 $rc->mAttribs['rc_timestamp'], 00385 $this->getUser() 00386 ) . '</span> <span class="mw-changeslist-separator">. .</span> '; 00387 } 00388 00395 public function insertTimestamp( &$s, $rc ) { 00396 $s .= $this->getTimestamp( $rc ); 00397 } 00398 00405 public function insertUserRelatedLinks( &$s, &$rc ) { 00406 if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) { 00407 $s .= ' <span class="history-deleted">' . 00408 $this->msg( 'rev-deleted-user' )->escaped() . '</span>'; 00409 } else { 00410 $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'], 00411 $rc->mAttribs['rc_user_text'] ); 00412 $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); 00413 } 00414 } 00415 00422 public function insertLogEntry( $rc ) { 00423 $formatter = LogFormatter::newFromRow( $rc->mAttribs ); 00424 $formatter->setContext( $this->getContext() ); 00425 $formatter->setShowUserToolLinks( true ); 00426 $mark = $this->getLanguage()->getDirMark(); 00427 00428 return $formatter->getActionText() . " $mark" . $formatter->getComment(); 00429 } 00430 00436 public function insertComment( $rc ) { 00437 if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) { 00438 return ' <span class="history-deleted">' . 00439 $this->msg( 'rev-deleted-comment' )->escaped() . '</span>'; 00440 } else { 00441 return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() ); 00442 } 00443 } 00444 00451 public static function usePatrol() { 00452 global $wgUser; 00453 00454 wfDeprecated( __METHOD__, '1.22' ); 00455 00456 return $wgUser->useRCPatrol(); 00457 } 00458 00464 protected function numberofWatchingusers( $count ) { 00465 $cache = $this->watchingCache; 00466 if ( $count > 0 ) { 00467 if ( !$cache->has( $count ) ) { 00468 $cache->set( $count, $this->msg( 'number_of_watching_users_RCview' ) 00469 ->numParams( $count )->escaped() ); 00470 } 00471 00472 return $cache->get( $count ); 00473 } else { 00474 return ''; 00475 } 00476 } 00477 00484 public static function isDeleted( $rc, $field ) { 00485 return ( $rc->mAttribs['rc_deleted'] & $field ) == $field; 00486 } 00487 00496 public static function userCan( $rc, $field, User $user = null ) { 00497 if ( $rc->mAttribs['rc_type'] == RC_LOG ) { 00498 return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user ); 00499 } else { 00500 return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user ); 00501 } 00502 } 00503 00509 protected function maybeWatchedLink( $link, $watched = false ) { 00510 if ( $watched ) { 00511 return '<strong class="mw-watched">' . $link . '</strong>'; 00512 } else { 00513 return '<span class="mw-rc-unwatched">' . $link . '</span>'; 00514 } 00515 } 00516 00522 public function insertRollback( &$s, &$rc ) { 00523 if ( $rc->mAttribs['rc_type'] == RC_EDIT 00524 && $rc->mAttribs['rc_this_oldid'] 00525 && $rc->mAttribs['rc_cur_id'] 00526 ) { 00527 $page = $rc->getTitle(); 00530 if ( $this->getUser()->isAllowed( 'rollback' ) 00531 && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] 00532 ) { 00533 $rev = new Revision( array( 00534 'title' => $page, 00535 'id' => $rc->mAttribs['rc_this_oldid'], 00536 'user' => $rc->mAttribs['rc_user'], 00537 'user_text' => $rc->mAttribs['rc_user_text'], 00538 'deleted' => $rc->mAttribs['rc_deleted'] 00539 ) ); 00540 $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() ); 00541 } 00542 } 00543 } 00544 00550 public function insertTags( &$s, &$rc, &$classes ) { 00551 if ( empty( $rc->mAttribs['ts_tags'] ) ) { 00552 return; 00553 } 00554 00555 list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( 00556 $rc->mAttribs['ts_tags'], 00557 'changeslist' 00558 ); 00559 $classes = array_merge( $classes, $newClasses ); 00560 $s .= ' ' . $tagSummary; 00561 } 00562 00563 public function insertExtra( &$s, &$rc, &$classes ) { 00564 // Empty, used for subclasses to add anything special. 00565 } 00566 00567 protected function showAsUnpatrolled( RecentChange $rc ) { 00568 return self::isUnpatrolled( $rc, $this->getUser() ); 00569 } 00570 00576 public static function isUnpatrolled( $rc, User $user ) { 00577 if ( $rc instanceof RecentChange ) { 00578 $isPatrolled = $rc->mAttribs['rc_patrolled']; 00579 $rcType = $rc->mAttribs['rc_type']; 00580 } else { 00581 $isPatrolled = $rc->rc_patrolled; 00582 $rcType = $rc->rc_type; 00583 } 00584 00585 if ( !$isPatrolled ) { 00586 if ( $user->useRCPatrol() ) { 00587 return true; 00588 } 00589 if ( $user->useNPPatrol() && $rcType == RC_NEW ) { 00590 return true; 00591 } 00592 } 00593 00594 return false; 00595 } 00596 }