MediaWiki  REL1_23
ChangesList.php
Go to the documentation of this file.
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 = '&#160;' ) {
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 }