00001 <?php
00002
00007 class SpecialRecentChanges extends SpecialPage {
00008 var $rcOptions, $rcSubpage;
00009
00010 public function __construct() {
00011 parent::__construct( 'Recentchanges' );
00012 $this->includable( true );
00013 }
00014
00020 public function getDefaultOptions() {
00021 global $wgUser;
00022 $opts = new FormOptions();
00023
00024 $opts->add( 'days', (int)$wgUser->getOption( 'rcdays' ) );
00025 $opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) );
00026 $opts->add( 'from', '' );
00027
00028 $opts->add( 'hideminor', $wgUser->getBoolOption( 'hideminor' ) );
00029 $opts->add( 'hidebots', true );
00030 $opts->add( 'hideanons', false );
00031 $opts->add( 'hideliu', false );
00032 $opts->add( 'hidepatrolled', $wgUser->getBoolOption( 'hidepatrolled' ) );
00033 $opts->add( 'hidemyself', false );
00034
00035 $opts->add( 'namespace', '', FormOptions::INTNULL );
00036 $opts->add( 'invert', false );
00037
00038 $opts->add( 'categories', '' );
00039 $opts->add( 'categories_any', false );
00040 $opts->add( 'tagfilter', '' );
00041 return $opts;
00042 }
00043
00049 public function setup( $parameters ) {
00050 global $wgRequest;
00051
00052 $opts = $this->getDefaultOptions();
00053 $opts->fetchValuesFromRequest( $wgRequest );
00054
00055
00056 if( $parameters !== null ) {
00057 $this->parseParameters( $parameters, $opts );
00058 }
00059
00060 $opts->validateIntBounds( 'limit', 0, 5000 );
00061 return $opts;
00062 }
00063
00069 public function feedSetup() {
00070 global $wgFeedLimit, $wgRequest;
00071 $opts = $this->getDefaultOptions();
00072 # Feed is cached on limit,hideminor,namespace; other params would randomly not work
00073 $opts->fetchValuesFromRequest( $wgRequest, array( 'limit', 'hideminor', 'namespace' ) );
00074 $opts->validateIntBounds( 'limit', 0, $wgFeedLimit );
00075 return $opts;
00076 }
00077
00081 public function getOptions() {
00082 if ( $this->rcOptions === null ) {
00083 global $wgRequest;
00084 $feedFormat = $wgRequest->getVal( 'feed' );
00085 $this->rcOptions = $feedFormat ? $this->feedSetup() : $this->setup( $this->rcSubpage );
00086 }
00087 return $this->rcOptions;
00088 }
00089
00090
00096 public function execute( $subpage ) {
00097 global $wgRequest, $wgOut;
00098 $this->rcSubpage = $subpage;
00099 $feedFormat = $wgRequest->getVal( 'feed' );
00100
00101 # 10 seconds server-side caching max
00102 $wgOut->setSquidMaxage( 10 );
00103 # Check if the client has a cached version
00104 $lastmod = $this->checkLastModified( $feedFormat );
00105 if( $lastmod === false ) {
00106 return;
00107 }
00108
00109 $opts = $this->getOptions();
00110 $this->setHeaders();
00111 $this->outputHeader();
00112
00113
00114 $conds = $this->buildMainQueryConds( $opts );
00115 $rows = $this->doMainQuery( $conds, $opts );
00116 if( $rows === false ){
00117 if( !$this->including() ) {
00118 $this->doHeader( $opts );
00119 }
00120 return;
00121 }
00122
00123 if( !$feedFormat ) {
00124 $batch = new LinkBatch;
00125 foreach( $rows as $row ) {
00126 $batch->add( NS_USER, $row->rc_user_text );
00127 $batch->add( NS_USER_TALK, $row->rc_user_text );
00128 $batch->add( $row->rc_namespace, $row->rc_title );
00129 }
00130 $batch->execute();
00131 }
00132 if( $feedFormat ) {
00133 list( $changesFeed, $formatter ) = $this->getFeedObject( $feedFormat );
00134 $changesFeed->execute( $formatter, $rows, $lastmod, $opts );
00135 } else {
00136 $this->webOutput( $rows, $opts );
00137 }
00138
00139 $rows->free();
00140 }
00141
00147 public function getFeedObject( $feedFormat ){
00148 $changesFeed = new ChangesFeed( $feedFormat, 'rcfeed' );
00149 $formatter = $changesFeed->getFeedObject(
00150 wfMsgForContent( 'recentchanges' ),
00151 wfMsgForContent( 'recentchanges-feed-description' )
00152 );
00153 return array( $changesFeed, $formatter );
00154 }
00155
00163 public function parseParameters( $par, FormOptions $opts ) {
00164 $bits = preg_split( '/\s*,\s*/', trim( $par ) );
00165 foreach( $bits as $bit ) {
00166 if( 'hidebots' === $bit ) $opts['hidebots'] = true;
00167 if( 'bots' === $bit ) $opts['hidebots'] = false;
00168 if( 'hideminor' === $bit ) $opts['hideminor'] = true;
00169 if( 'minor' === $bit ) $opts['hideminor'] = false;
00170 if( 'hideliu' === $bit ) $opts['hideliu'] = true;
00171 if( 'hidepatrolled' === $bit ) $opts['hidepatrolled'] = true;
00172 if( 'hideanons' === $bit ) $opts['hideanons'] = true;
00173 if( 'hidemyself' === $bit ) $opts['hidemyself'] = true;
00174
00175 if( is_numeric( $bit ) ) $opts['limit'] = $bit;
00176
00177 $m = array();
00178 if( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) $opts['limit'] = $m[1];
00179 if( preg_match( '/^days=(\d+)$/', $bit, $m ) ) $opts['days'] = $m[1];
00180 }
00181 }
00182
00191 public function checkLastModified( $feedFormat ) {
00192 global $wgUseRCPatrol, $wgOut;
00193 $dbr = wfGetDB( DB_SLAVE );
00194 $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __METHOD__ );
00195 if( $feedFormat || !$wgUseRCPatrol ) {
00196 if( $lastmod && $wgOut->checkLastModified( $lastmod ) ) {
00197 # Client cache fresh and headers sent, nothing more to do.
00198 return false;
00199 }
00200 }
00201 return $lastmod;
00202 }
00203
00210 public function buildMainQueryConds( FormOptions $opts ) {
00211 global $wgUser;
00212
00213 $dbr = wfGetDB( DB_SLAVE );
00214 $conds = array();
00215
00216 # It makes no sense to hide both anons and logged-in users
00217 # Where this occurs, force anons to be shown
00218 $forcebot = false;
00219 if( $opts['hideanons'] && $opts['hideliu'] ){
00220 # Check if the user wants to show bots only
00221 if( $opts['hidebots'] ){
00222 $opts['hideanons'] = false;
00223 } else {
00224 $forcebot = true;
00225 $opts['hidebots'] = false;
00226 }
00227 }
00228
00229
00230 $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
00231 $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400);
00232 $cutoff = $dbr->timestamp( $cutoff_unixtime );
00233
00234 $fromValid = preg_match('/^[0-9]{14}$/', $opts['from']);
00235 if( $fromValid && $opts['from'] > wfTimestamp(TS_MW,$cutoff) ) {
00236 $cutoff = $dbr->timestamp($opts['from']);
00237 } else {
00238 $opts->reset( 'from' );
00239 }
00240
00241 $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff );
00242
00243
00244 $hidePatrol = $wgUser->useRCPatrol() && $opts['hidepatrolled'];
00245 $hideLoggedInUsers = $opts['hideliu'] && !$forcebot;
00246 $hideAnonymousUsers = $opts['hideanons'] && !$forcebot;
00247
00248 if( $opts['hideminor'] ) $conds['rc_minor'] = 0;
00249 if( $opts['hidebots'] ) $conds['rc_bot'] = 0;
00250 if( $hidePatrol ) $conds['rc_patrolled'] = 0;
00251 if( $forcebot ) $conds['rc_bot'] = 1;
00252 if( $hideLoggedInUsers ) $conds[] = 'rc_user = 0';
00253 if( $hideAnonymousUsers ) $conds[] = 'rc_user != 0';
00254
00255 if( $opts['hidemyself'] ) {
00256 if( $wgUser->getId() ) {
00257 $conds[] = 'rc_user != ' . $dbr->addQuotes( $wgUser->getId() );
00258 } else {
00259 $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $wgUser->getName() );
00260 }
00261 }
00262
00263 # Namespace filtering
00264 if( $opts['namespace'] !== '' ) {
00265 if( !$opts['invert'] ) {
00266 $conds[] = 'rc_namespace = ' . $dbr->addQuotes( $opts['namespace'] );
00267 } else {
00268 $conds[] = 'rc_namespace != ' . $dbr->addQuotes( $opts['namespace'] );
00269 }
00270 }
00271
00272 return $conds;
00273 }
00274
00282 public function doMainQuery( $conds, $opts ) {
00283 global $wgUser;
00284
00285 $tables = array( 'recentchanges' );
00286 $join_conds = array();
00287 $query_options = array( 'USE INDEX' => array('recentchanges' => 'rc_timestamp') );
00288
00289 $uid = $wgUser->getId();
00290 $dbr = wfGetDB( DB_SLAVE );
00291 $limit = $opts['limit'];
00292 $namespace = $opts['namespace'];
00293 $invert = $opts['invert'];
00294
00295
00296 if( $uid ) {
00297 $tables[] = 'watchlist';
00298 $join_conds['watchlist'] = array('LEFT JOIN',
00299 "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace");
00300 }
00301 if ($wgUser->isAllowed("rollback")) {
00302 $tables[] = 'page';
00303 $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id');
00304 }
00305
00306 $fields = array();
00307
00308 ChangeTags::modifyDisplayQuery(
00309 $tables, $fields, $conds, $join_conds, $query_options, $opts['tagfilter']
00310 );
00311
00312 if ( !wfRunHooks( 'SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts, &$query_options ) ) )
00313 return false;
00314
00315
00316
00317
00318
00319
00320 if( is_null($namespace)
00321 || $invert
00322 || $opts['tagfilter'] != ''
00323 || !$dbr->unionSupportsOrderAndLimit() )
00324 {
00325 $res = $dbr->select( $tables, '*', $conds, __METHOD__,
00326 array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit ) +
00327 $query_options,
00328 $join_conds );
00329
00330 } else {
00331
00332 $sqlNew = $dbr->selectSQLText( $tables, '*',
00333 array( 'rc_new' => 1 ) + $conds,
00334 __METHOD__,
00335 array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit,
00336 'USE INDEX' => array('recentchanges' => 'rc_timestamp') ),
00337 $join_conds );
00338
00339 $sqlOld = $dbr->selectSQLText( $tables, '*',
00340 array( 'rc_new' => 0 ) + $conds,
00341 __METHOD__,
00342 array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit,
00343 'USE INDEX' => array('recentchanges' => 'rc_timestamp') ),
00344 $join_conds );
00345 # Join the two fast queries, and sort the result set
00346 $sql = $dbr->unionQueries(array($sqlNew, $sqlOld), false).' ORDER BY rc_timestamp DESC';
00347 $sql = $dbr->limitResult($sql, $limit, false);
00348 $res = $dbr->query( $sql, __METHOD__ );
00349 }
00350
00351 return $res;
00352 }
00353
00360 public function webOutput( $rows, $opts ) {
00361 global $wgOut, $wgUser, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
00362 global $wgAllowCategorizedRecentChanges;
00363
00364 $limit = $opts['limit'];
00365
00366 if( !$this->including() ) {
00367
00368 $this->doHeader( $opts );
00369 }
00370
00371
00372 $wgOut->setFeedAppendQuery( $this->getFeedQuery() );
00373
00374 if( $wgAllowCategorizedRecentChanges ) {
00375 $this->filterByCategories( $rows, $opts );
00376 }
00377
00378 $showWatcherCount = $wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' );
00379 $watcherCache = array();
00380
00381 $dbr = wfGetDB( DB_SLAVE );
00382
00383 $counter = 1;
00384 $list = ChangesList::newFromUser( $wgUser );
00385
00386 $s = $list->beginRecentChangesList();
00387 foreach( $rows as $obj ) {
00388 if( $limit == 0 ) break;
00389 $rc = RecentChange::newFromRow( $obj );
00390 $rc->counter = $counter++;
00391 # Check if the page has been updated since the last visit
00392 if( $wgShowUpdatedMarker && !empty($obj->wl_notificationtimestamp) ) {
00393 $rc->notificationtimestamp = ($obj->rc_timestamp >= $obj->wl_notificationtimestamp);
00394 } else {
00395 $rc->notificationtimestamp = false;
00396 }
00397 # Check the number of users watching the page
00398 $rc->numberofWatchingusers = 0;
00399 if( $showWatcherCount && $obj->rc_namespace >= 0 ) {
00400 if( !isset($watcherCache[$obj->rc_namespace][$obj->rc_title]) ) {
00401 $watcherCache[$obj->rc_namespace][$obj->rc_title] =
00402 $dbr->selectField( 'watchlist',
00403 'COUNT(*)',
00404 array(
00405 'wl_namespace' => $obj->rc_namespace,
00406 'wl_title' => $obj->rc_title,
00407 ),
00408 __METHOD__ . '-watchers' );
00409 }
00410 $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
00411 }
00412 $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter );
00413 --$limit;
00414 }
00415 $s .= $list->endRecentChangesList();
00416 $wgOut->addHTML( $s );
00417 }
00418
00423 public function getFeedQuery() {
00424 return false;
00425 }
00426
00433 public function doHeader( $opts ) {
00434 global $wgScript, $wgOut;
00435
00436 $this->setTopText( $wgOut, $opts );
00437
00438 $defaults = $opts->getAllValues();
00439 $nondefaults = $opts->getChangedValues();
00440 $opts->consumeValues( array( 'namespace', 'invert', 'tagfilter' ) );
00441
00442 $panel = array();
00443 $panel[] = $this->optionsPanel( $defaults, $nondefaults );
00444 $panel[] = '<hr />';
00445
00446 $extraOpts = $this->getExtraOptions( $opts );
00447 $extraOptsCount = count( $extraOpts );
00448 $count = 0;
00449 $submit = ' ' . Xml::submitbutton( wfMsg( 'allpagessubmit' ) );
00450
00451 $out = Xml::openElement( 'table', array( 'class' => 'mw-recentchanges-table' ) );
00452 foreach( $extraOpts as $optionRow ) {
00453 # Add submit button to the last row only
00454 ++$count;
00455 $addSubmit = $count === $extraOptsCount ? $submit : '';
00456
00457 $out .= Xml::openElement( 'tr' );
00458 if( is_array( $optionRow ) ) {
00459 $out .= Xml::tags( 'td', array( 'class' => 'mw-label' ), $optionRow[0] );
00460 $out .= Xml::tags( 'td', array( 'class' => 'mw-input' ), $optionRow[1] . $addSubmit );
00461 } else {
00462 $out .= Xml::tags( 'td', array( 'class' => 'mw-input', 'colspan' => 2 ), $optionRow . $addSubmit );
00463 }
00464 $out .= Xml::closeElement( 'tr' );
00465 }
00466 $out .= Xml::closeElement( 'table' );
00467
00468 $unconsumed = $opts->getUnconsumedValues();
00469 foreach( $unconsumed as $key => $value ) {
00470 $out .= Xml::hidden( $key, $value );
00471 }
00472
00473 $t = $this->getTitle();
00474 $out .= Xml::hidden( 'title', $t->getPrefixedText() );
00475 $form = Xml::tags( 'form', array( 'action' => $wgScript ), $out );
00476 $panel[] = $form;
00477 $panelString = implode( "\n", $panel );
00478
00479 $wgOut->addHTML(
00480 Xml::fieldset( wfMsg( 'recentchanges-legend' ), $panelString, array( 'class' => 'rcoptions' ) )
00481 );
00482
00483 $wgOut->addHTML( ChangesList::flagLegend() );
00484
00485 $this->setBottomText( $wgOut, $opts );
00486 }
00487
00494 function getExtraOptions( $opts ){
00495 $extraOpts = array();
00496 $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
00497
00498 global $wgAllowCategorizedRecentChanges;
00499 if( $wgAllowCategorizedRecentChanges ) {
00500 $extraOpts['category'] = $this->categoryFilterForm( $opts );
00501 }
00502
00503 $tagFilter = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] );
00504 if ( count($tagFilter) )
00505 $extraOpts['tagfilter'] = $tagFilter;
00506
00507 wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
00508 return $extraOpts;
00509 }
00510
00517 function setTopText( OutputPage $out, FormOptions $opts ){
00518 $out->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) );
00519 }
00520
00528 function setBottomText( OutputPage $out, FormOptions $opts ){}
00529
00536 protected function namespaceFilterForm( FormOptions $opts ) {
00537 $nsSelect = Xml::namespaceSelector( $opts['namespace'], '' );
00538 $nsLabel = Xml::label( wfMsg('namespace'), 'namespace' );
00539 $invert = Xml::checkLabel( wfMsg('invert'), 'invert', 'nsinvert', $opts['invert'] );
00540 return array( $nsLabel, "$nsSelect $invert" );
00541 }
00542
00549 protected function categoryFilterForm( FormOptions $opts ) {
00550 list( $label, $input ) = Xml::inputLabelSep( wfMsg('rc_categories'),
00551 'categories', 'mw-categories', false, $opts['categories'] );
00552
00553 $input .= ' ' . Xml::checkLabel( wfMsg('rc_categories_any'),
00554 'categories_any', 'mw-categories_any', $opts['categories_any'] );
00555
00556 return array( $label, $input );
00557 }
00558
00565 function filterByCategories( &$rows, FormOptions $opts ) {
00566 $categories = array_map( 'trim', explode( "|" , $opts['categories'] ) );
00567
00568 if( empty($categories) ) {
00569 return;
00570 }
00571
00572 # Filter categories
00573 $cats = array();
00574 foreach( $categories as $cat ) {
00575 $cat = trim( $cat );
00576 if( $cat == "" ) continue;
00577 $cats[] = $cat;
00578 }
00579
00580 # Filter articles
00581 $articles = array();
00582 $a2r = array();
00583 foreach( $rows AS $k => $r ) {
00584 $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
00585 $id = $nt->getArticleID();
00586 if( $id == 0 ) continue; # Page might have been deleted...
00587 if( !in_array($id, $articles) ) {
00588 $articles[] = $id;
00589 }
00590 if( !isset($a2r[$id]) ) {
00591 $a2r[$id] = array();
00592 }
00593 $a2r[$id][] = $k;
00594 }
00595
00596 # Shortcut?
00597 if( !count($articles) || !count($cats) )
00598 return ;
00599
00600 # Look up
00601 $c = new Categoryfinder ;
00602 $c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ;
00603 $match = $c->run();
00604
00605 # Filter
00606 $newrows = array();
00607 foreach( $match AS $id ) {
00608 foreach( $a2r[$id] AS $rev ) {
00609 $k = $rev;
00610 $newrows[$k] = $rows[$k];
00611 }
00612 }
00613 $rows = $newrows;
00614 }
00615
00622 function makeOptionsLink( $title, $override, $options, $active = false ) {
00623 global $wgUser;
00624 $sk = $wgUser->getSkin();
00625 $params = $override + $options;
00626 if ( $active ) {
00627 return $sk->link( $this->getTitle(), '<strong>' . htmlspecialchars( $title ) . '</strong>',
00628 array(), $params, array( 'known' ) );
00629 } else {
00630 return $sk->link( $this->getTitle(), htmlspecialchars( $title ), array() , $params, array( 'known' ) );
00631 }
00632 }
00633
00639 function optionsPanel( $defaults, $nondefaults ) {
00640 global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays;
00641
00642 $options = $nondefaults + $defaults;
00643
00644 $note = '';
00645 if( !wfEmptyMsg( 'rclegend', wfMsg('rclegend') ) ) {
00646 $note .= '<div class="mw-rclegend">' . wfMsgExt( 'rclegend', array('parseinline') ) . "</div>\n";
00647 }
00648 if( $options['from'] ) {
00649 $note .= wfMsgExt( 'rcnotefrom', array( 'parseinline' ),
00650 $wgLang->formatNum( $options['limit'] ),
00651 $wgLang->timeanddate( $options['from'], true ),
00652 $wgLang->date( $options['from'], true ),
00653 $wgLang->time( $options['from'], true ) ) . '<br />';
00654 }
00655
00656 # Sort data for display and make sure it's unique after we've added user data.
00657 $wgRCLinkLimits[] = $options['limit'];
00658 $wgRCLinkDays[] = $options['days'];
00659 sort( $wgRCLinkLimits );
00660 sort( $wgRCLinkDays );
00661 $wgRCLinkLimits = array_unique( $wgRCLinkLimits );
00662 $wgRCLinkDays = array_unique( $wgRCLinkDays );
00663
00664
00665 foreach( $wgRCLinkLimits as $value ) {
00666 $cl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ),
00667 array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ;
00668 }
00669 $cl = $wgLang->pipeList( $cl );
00670
00671
00672 foreach( $wgRCLinkDays as $value ) {
00673 $dl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ),
00674 array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ;
00675 }
00676 $dl = $wgLang->pipeList( $dl );
00677
00678
00679
00680 $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' ) );
00681 $minorLink = $this->makeOptionsLink( $showhide[1-$options['hideminor']],
00682 array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults);
00683 $botLink = $this->makeOptionsLink( $showhide[1-$options['hidebots']],
00684 array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults);
00685 $anonsLink = $this->makeOptionsLink( $showhide[ 1 - $options['hideanons'] ],
00686 array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults );
00687 $liuLink = $this->makeOptionsLink( $showhide[1-$options['hideliu']],
00688 array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults);
00689 $patrLink = $this->makeOptionsLink( $showhide[1-$options['hidepatrolled']],
00690 array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults);
00691 $myselfLink = $this->makeOptionsLink( $showhide[1-$options['hidemyself']],
00692 array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults);
00693
00694 $links[] = wfMsgHtml( 'rcshowhideminor', $minorLink );
00695 $links[] = wfMsgHtml( 'rcshowhidebots', $botLink );
00696 $links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink );
00697 $links[] = wfMsgHtml( 'rcshowhideliu', $liuLink );
00698 if( $wgUser->useRCPatrol() )
00699 $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink );
00700 $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink );
00701 $hl = $wgLang->pipeList( $links );
00702
00703
00704 $now = $wgLang->timeanddate( wfTimestampNow(), true );
00705 $tl = $this->makeOptionsLink( $now, array( 'from' => wfTimestampNow() ), $nondefaults );
00706
00707 $rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter' ),
00708 $cl, $dl, $hl );
00709 $rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter' ), $tl );
00710 return "{$note}$rclinks<br />$rclistfrom";
00711 }
00712 }