MediaWiki  REL1_24
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 
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 = '&#160;' ) {
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 }