MediaWiki
REL1_23
|
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 00044 public function __construct( $obj ) { 00045 if ( $obj instanceof IContextSource ) { 00046 $this->setContext( $obj ); 00047 $this->skin = $obj->getSkin(); 00048 } else { 00049 $this->setContext( $obj->getContext() ); 00050 $this->skin = $obj; 00051 } 00052 $this->preCacheMessages(); 00053 } 00054 00062 public static function newFromContext( IContextSource $context ) { 00063 $user = $context->getUser(); 00064 $sk = $context->getSkin(); 00065 $list = null; 00066 if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) { 00067 $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) ); 00068 00069 return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context ); 00070 } else { 00071 return $list; 00072 } 00073 } 00074 00079 public function setWatchlistDivs( $value = true ) { 00080 $this->watchlist = $value; 00081 } 00082 00087 public function isWatchlist() { 00088 return (bool)$this->watchlist; 00089 } 00090 00095 private function preCacheMessages() { 00096 if ( !isset( $this->message ) ) { 00097 foreach ( array( 00098 'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history', 00099 'semicolon-separator', 'pipe-separator' ) as $msg 00100 ) { 00101 $this->message[$msg] = $this->msg( $msg )->escaped(); 00102 } 00103 } 00104 } 00105 00112 public function recentChangesFlags( $flags, $nothing = ' ' ) { 00113 global $wgRecentChangesFlags; 00114 $f = ''; 00115 foreach ( array_keys( $wgRecentChangesFlags ) as $flag ) { 00116 $f .= isset( $flags[$flag] ) && $flags[$flag] 00117 ? self::flag( $flag ) 00118 : $nothing; 00119 } 00120 00121 return $f; 00122 } 00123 00133 public static function flag( $flag ) { 00134 static $flagInfos = null; 00135 if ( is_null( $flagInfos ) ) { 00136 global $wgRecentChangesFlags; 00137 $flagInfos = array(); 00138 foreach ( $wgRecentChangesFlags as $key => $value ) { 00139 $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped(); 00140 $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped(); 00141 // Allow customized class name, fall back to flag name 00142 $flagInfos[$key]['class'] = Sanitizer::escapeClass( 00143 isset( $value['class'] ) ? $value['class'] : $key ); 00144 } 00145 } 00146 00147 // Inconsistent naming, bleh, kepted for b/c 00148 $map = array( 00149 'minoredit' => 'minor', 00150 'botedit' => 'bot', 00151 ); 00152 if ( isset( $map[$flag] ) ) { 00153 $flag = $map[$flag]; 00154 } 00155 00156 return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . 00157 $flagInfos[$flag]['title'] . "'>" . $flagInfos[$flag]['letter'] . 00158 '</abbr>'; 00159 } 00160 00165 public function beginRecentChangesList() { 00166 $this->rc_cache = array(); 00167 $this->rcMoveIndex = 0; 00168 $this->rcCacheIndex = 0; 00169 $this->lastdate = ''; 00170 $this->rclistOpen = false; 00171 $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' ); 00172 00173 return '<div class="mw-changeslist">'; 00174 } 00175 00179 public function initChangesListRows( $rows ) { 00180 wfRunHooks( 'ChangesListInitRows', array( $this, $rows ) ); 00181 } 00182 00190 public static function showCharacterDifference( $old, $new, IContextSource $context = null ) { 00191 global $wgRCChangedSizeThreshold, $wgMiserMode; 00192 00193 if ( !$context ) { 00194 $context = RequestContext::getMain(); 00195 } 00196 00197 $new = (int)$new; 00198 $old = (int)$old; 00199 $szdiff = $new - $old; 00200 00201 $lang = $context->getLanguage(); 00202 $code = $lang->getCode(); 00203 static $fastCharDiff = array(); 00204 if ( !isset( $fastCharDiff[$code] ) ) { 00205 $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1'; 00206 } 00207 00208 $formattedSize = $lang->formatNum( $szdiff ); 00209 00210 if ( !$fastCharDiff[$code] ) { 00211 $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text(); 00212 } 00213 00214 if ( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) { 00215 $tag = 'strong'; 00216 } else { 00217 $tag = 'span'; 00218 } 00219 00220 if ( $szdiff === 0 ) { 00221 $formattedSizeClass = 'mw-plusminus-null'; 00222 } elseif ( $szdiff > 0 ) { 00223 $formattedSize = '+' . $formattedSize; 00224 $formattedSizeClass = 'mw-plusminus-pos'; 00225 } else { 00226 $formattedSizeClass = 'mw-plusminus-neg'; 00227 } 00228 00229 $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text(); 00230 00231 return Html::element( $tag, 00232 array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ), 00233 $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark(); 00234 } 00235 00243 public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) { 00244 $oldlen = $old->mAttribs['rc_old_len']; 00245 00246 if ( $new ) { 00247 $newlen = $new->mAttribs['rc_new_len']; 00248 } else { 00249 $newlen = $old->mAttribs['rc_new_len']; 00250 } 00251 00252 if ( $oldlen === null || $newlen === null ) { 00253 return ''; 00254 } 00255 00256 return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() ); 00257 } 00258 00263 public function endRecentChangesList() { 00264 $out = $this->rclistOpen ? "</ul>\n" : ''; 00265 $out .= '</div>'; 00266 00267 return $out; 00268 } 00269 00274 public function insertDateHeader( &$s, $rc_timestamp ) { 00275 # Make date header if necessary 00276 $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() ); 00277 if ( $date != $this->lastdate ) { 00278 if ( $this->lastdate != '' ) { 00279 $s .= "</ul>\n"; 00280 } 00281 $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">"; 00282 $this->lastdate = $date; 00283 $this->rclistOpen = true; 00284 } 00285 } 00286 00292 public function insertLog( &$s, $title, $logtype ) { 00293 $page = new LogPage( $logtype ); 00294 $logname = $page->getName()->escaped(); 00295 $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped(); 00296 } 00297 00303 public function insertDiffHist( &$s, &$rc, $unpatrolled ) { 00304 # Diff link 00305 if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) { 00306 $diffLink = $this->message['diff']; 00307 } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) { 00308 $diffLink = $this->message['diff']; 00309 } else { 00310 $query = array( 00311 'curid' => $rc->mAttribs['rc_cur_id'], 00312 'diff' => $rc->mAttribs['rc_this_oldid'], 00313 'oldid' => $rc->mAttribs['rc_last_oldid'] 00314 ); 00315 00316 $diffLink = Linker::linkKnown( 00317 $rc->getTitle(), 00318 $this->message['diff'], 00319 array( 'tabindex' => $rc->counter ), 00320 $query 00321 ); 00322 } 00323 $diffhist = $diffLink . $this->message['pipe-separator']; 00324 # History link 00325 $diffhist .= Linker::linkKnown( 00326 $rc->getTitle(), 00327 $this->message['hist'], 00328 array(), 00329 array( 00330 'curid' => $rc->mAttribs['rc_cur_id'], 00331 'action' => 'history' 00332 ) 00333 ); 00334 // @todo FIXME: Hard coded ". .". Is there a message for this? Should there be? 00335 $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . 00336 ' <span class="mw-changeslist-separator">. .</span> '; 00337 } 00338 00345 public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) { 00346 $params = array(); 00347 00348 $articlelink = Linker::linkKnown( 00349 $rc->getTitle(), 00350 null, 00351 array( 'class' => 'mw-changeslist-title' ), 00352 $params 00353 ); 00354 if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) { 00355 $articlelink = '<span class="history-deleted">' . $articlelink . '</span>'; 00356 } 00357 # To allow for boldening pages watched by this user 00358 $articlelink = "<span class=\"mw-title\">{$articlelink}</span>"; 00359 # RTL/LTR marker 00360 $articlelink .= $this->getLanguage()->getDirMark(); 00361 00362 wfRunHooks( 'ChangesListInsertArticleLink', 00363 array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) ); 00364 00365 $s .= " $articlelink"; 00366 } 00367 00375 public function getTimestamp( $rc ) { 00376 // @todo FIXME: Hard coded ". .". Is there a message for this? Should there be? 00377 return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' . 00378 $this->getLanguage()->userTime( 00379 $rc->mAttribs['rc_timestamp'], 00380 $this->getUser() 00381 ) . '</span> <span class="mw-changeslist-separator">. .</span> '; 00382 } 00383 00390 public function insertTimestamp( &$s, $rc ) { 00391 $s .= $this->getTimestamp( $rc ); 00392 } 00393 00400 public function insertUserRelatedLinks( &$s, &$rc ) { 00401 if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) { 00402 $s .= ' <span class="history-deleted">' . 00403 $this->msg( 'rev-deleted-user' )->escaped() . '</span>'; 00404 } else { 00405 $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'], 00406 $rc->mAttribs['rc_user_text'] ); 00407 $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); 00408 } 00409 } 00410 00417 public function insertLogEntry( $rc ) { 00418 $formatter = LogFormatter::newFromRow( $rc->mAttribs ); 00419 $formatter->setContext( $this->getContext() ); 00420 $formatter->setShowUserToolLinks( true ); 00421 $mark = $this->getLanguage()->getDirMark(); 00422 00423 return $formatter->getActionText() . " $mark" . $formatter->getComment(); 00424 } 00425 00431 public function insertComment( $rc ) { 00432 if ( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) { 00433 if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) { 00434 return ' <span class="history-deleted">' . 00435 $this->msg( 'rev-deleted-comment' )->escaped() . '</span>'; 00436 } else { 00437 return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() ); 00438 } 00439 } 00440 00441 return ''; 00442 } 00443 00450 public static function usePatrol() { 00451 global $wgUser; 00452 00453 wfDeprecated( __METHOD__, '1.22' ); 00454 00455 return $wgUser->useRCPatrol(); 00456 } 00457 00463 protected function numberofWatchingusers( $count ) { 00464 static $cache = array(); 00465 if ( $count > 0 ) { 00466 if ( !isset( $cache[$count] ) ) { 00467 $cache[$count] = $this->msg( 'number_of_watching_users_RCview' ) 00468 ->numParams( $count )->escaped(); 00469 } 00470 00471 return $cache[$count]; 00472 } else { 00473 return ''; 00474 } 00475 } 00476 00483 public static function isDeleted( $rc, $field ) { 00484 return ( $rc->mAttribs['rc_deleted'] & $field ) == $field; 00485 } 00486 00495 public static function userCan( $rc, $field, User $user = null ) { 00496 if ( $rc->mAttribs['rc_type'] == RC_LOG ) { 00497 return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user ); 00498 } else { 00499 return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user ); 00500 } 00501 } 00502 00508 protected function maybeWatchedLink( $link, $watched = false ) { 00509 if ( $watched ) { 00510 return '<strong class="mw-watched">' . $link . '</strong>'; 00511 } else { 00512 return '<span class="mw-rc-unwatched">' . $link . '</span>'; 00513 } 00514 } 00515 00521 public function insertRollback( &$s, &$rc ) { 00522 if ( $rc->mAttribs['rc_type'] == RC_EDIT 00523 && $rc->mAttribs['rc_this_oldid'] 00524 && $rc->mAttribs['rc_cur_id'] 00525 ) { 00526 $page = $rc->getTitle(); 00529 if ( $this->getUser()->isAllowed( 'rollback' ) 00530 && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] 00531 ) { 00532 $rev = new Revision( array( 00533 'title' => $page, 00534 'id' => $rc->mAttribs['rc_this_oldid'], 00535 'user' => $rc->mAttribs['rc_user'], 00536 'user_text' => $rc->mAttribs['rc_user_text'], 00537 'deleted' => $rc->mAttribs['rc_deleted'] 00538 ) ); 00539 $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() ); 00540 } 00541 } 00542 } 00543 00549 public function insertTags( &$s, &$rc, &$classes ) { 00550 if ( empty( $rc->mAttribs['ts_tags'] ) ) { 00551 return; 00552 } 00553 00554 list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( 00555 $rc->mAttribs['ts_tags'], 00556 'changeslist' 00557 ); 00558 $classes = array_merge( $classes, $newClasses ); 00559 $s .= ' ' . $tagSummary; 00560 } 00561 00562 public function insertExtra( &$s, &$rc, &$classes ) { 00563 // Empty, used for subclasses to add anything special. 00564 } 00565 00566 protected function showAsUnpatrolled( RecentChange $rc ) { 00567 return self::isUnpatrolled( $rc, $this->getUser() ); 00568 } 00569 00575 public static function isUnpatrolled( $rc, User $user ) { 00576 if ( $rc instanceof RecentChange ) { 00577 $isPatrolled = $rc->mAttribs['rc_patrolled']; 00578 $rcType = $rc->mAttribs['rc_type']; 00579 } else { 00580 $isPatrolled = $rc->rc_patrolled; 00581 $rcType = $rc->rc_type; 00582 } 00583 00584 if ( !$isPatrolled ) { 00585 if ( $user->useRCPatrol() ) { 00586 return true; 00587 } 00588 if ( $user->useNPPatrol() && $rcType == RC_NEW ) { 00589 return true; 00590 } 00591 } 00592 00593 return false; 00594 } 00595 }