MediaWiki
REL1_24
|
00001 <?php 00023 class EnhancedChangesList extends ChangesList { 00024 00028 protected $cacheEntryFactory; 00029 00033 protected $rc_cache; 00034 00038 public function __construct( $obj ) { 00039 if ( $obj instanceof Skin ) { 00040 // @todo: deprecate constructing with Skin 00041 $context = $obj->getContext(); 00042 } else { 00043 if ( !$obj instanceof IContextSource ) { 00044 throw new MWException( 'EnhancedChangesList must be constructed with a ' 00045 . 'context source or skin.' ); 00046 } 00047 00048 $context = $obj; 00049 } 00050 00051 parent::__construct( $context ); 00052 00053 // message is set by the parent ChangesList class 00054 $this->cacheEntryFactory = new RCCacheEntryFactory( 00055 $context, 00056 $this->message 00057 ); 00058 } 00059 00064 public function beginRecentChangesList() { 00065 $this->rc_cache = array(); 00066 $this->rcMoveIndex = 0; 00067 $this->rcCacheIndex = 0; 00068 $this->lastdate = ''; 00069 $this->rclistOpen = false; 00070 $this->getOutput()->addModuleStyles( array( 00071 'mediawiki.special.changeslist', 00072 'mediawiki.special.changeslist.enhanced', 00073 ) ); 00074 $this->getOutput()->addModules( array( 00075 'jquery.makeCollapsible', 00076 'mediawiki.icon', 00077 ) ); 00078 00079 return '<div class="mw-changeslist">'; 00080 } 00081 00090 public function recentChangesLine( &$baseRC, $watched = false ) { 00091 wfProfileIn( __METHOD__ ); 00092 00093 $date = $this->getLanguage()->userDate( 00094 $baseRC->mAttribs['rc_timestamp'], 00095 $this->getUser() 00096 ); 00097 00098 $ret = ''; 00099 00100 # If it's a new day, add the headline and flush the cache 00101 if ( $date != $this->lastdate ) { 00102 # Process current cache 00103 $ret = $this->recentChangesBlock(); 00104 $this->rc_cache = array(); 00105 $ret .= Xml::element( 'h4', null, $date ) . "\n"; 00106 $this->lastdate = $date; 00107 } 00108 00109 $cacheEntry = $this->cacheEntryFactory->newFromRecentChange( $baseRC, $watched ); 00110 $this->addCacheEntry( $cacheEntry ); 00111 00112 wfProfileOut( __METHOD__ ); 00113 00114 return $ret; 00115 } 00116 00123 protected function addCacheEntry( RCCacheEntry $cacheEntry ) { 00124 $cacheGroupingKey = $this->makeCacheGroupingKey( $cacheEntry ); 00125 00126 if ( !isset( $this->rc_cache[$cacheGroupingKey] ) ) { 00127 $this->rc_cache[$cacheGroupingKey] = array(); 00128 } 00129 00130 array_push( $this->rc_cache[$cacheGroupingKey], $cacheEntry ); 00131 } 00132 00140 protected function makeCacheGroupingKey( RCCacheEntry $cacheEntry ) { 00141 $title = $cacheEntry->getTitle(); 00142 $cacheGroupingKey = $title->getPrefixedDBkey(); 00143 00144 $type = $cacheEntry->mAttribs['rc_type']; 00145 00146 if ( $type == RC_LOG ) { 00147 // Group by log type 00148 $cacheGroupingKey = SpecialPage::getTitleFor( 00149 'Log', 00150 $cacheEntry->mAttribs['rc_log_type'] 00151 )->getPrefixedDBkey(); 00152 } 00153 00154 return $cacheGroupingKey; 00155 } 00156 00162 protected function recentChangesBlockGroup( $block ) { 00163 wfProfileIn( __METHOD__ ); 00164 00165 # Add the namespace and title of the block as part of the class 00166 $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' ); 00167 if ( $block[0]->mAttribs['rc_log_type'] ) { 00168 # Log entry 00169 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' 00170 . $block[0]->mAttribs['rc_log_type'] ); 00171 } else { 00172 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' 00173 . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] ); 00174 } 00175 $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched 00176 ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; 00177 $r = Html::openElement( 'table', array( 'class' => $classes ) ) . 00178 Html::openElement( 'tr' ); 00179 00180 # Collate list of users 00181 $userlinks = array(); 00182 # Other properties 00183 $unpatrolled = false; 00184 $isnew = false; 00185 $allBots = true; 00186 $allMinors = true; 00187 $curId = $currentRevision = 0; 00188 # Some catalyst variables... 00189 $namehidden = true; 00190 $allLogs = true; 00191 $oldid = ''; 00192 $RCShowChangedSize = $this->getConfig()->get( 'RCShowChangedSize' ); 00193 foreach ( $block as $rcObj ) { 00194 $oldid = $rcObj->mAttribs['rc_last_oldid']; 00195 if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) { 00196 $isnew = true; 00197 } 00198 // If all log actions to this page were hidden, then don't 00199 // give the name of the affected page for this block! 00200 if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) { 00201 $namehidden = false; 00202 } 00203 $u = $rcObj->userlink; 00204 if ( !isset( $userlinks[$u] ) ) { 00205 $userlinks[$u] = 0; 00206 } 00207 if ( $rcObj->unpatrolled ) { 00208 $unpatrolled = true; 00209 } 00210 if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) { 00211 $allLogs = false; 00212 } 00213 # Get the latest entry with a page_id and oldid 00214 # since logs may not have these. 00215 if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) { 00216 $curId = $rcObj->mAttribs['rc_cur_id']; 00217 } 00218 if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) { 00219 $currentRevision = $rcObj->mAttribs['rc_this_oldid']; 00220 } 00221 00222 if ( !$rcObj->mAttribs['rc_bot'] ) { 00223 $allBots = false; 00224 } 00225 if ( !$rcObj->mAttribs['rc_minor'] ) { 00226 $allMinors = false; 00227 } 00228 00229 $userlinks[$u]++; 00230 } 00231 00232 # Sort the list and convert to text 00233 krsort( $userlinks ); 00234 asort( $userlinks ); 00235 $users = array(); 00236 foreach ( $userlinks as $userlink => $count ) { 00237 $text = $userlink; 00238 $text .= $this->getLanguage()->getDirMark(); 00239 if ( $count > 1 ) { 00240 // @todo FIXME: Hardcoded '×'. Should be a message. 00241 $formattedCount = $this->getLanguage()->formatNum( $count ) . '×'; 00242 $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $formattedCount )->escaped(); 00243 } 00244 array_push( $users, $text ); 00245 } 00246 00247 $users = ' <span class="changedby">' 00248 . $this->msg( 'brackets' )->rawParams( 00249 implode( $this->message['semicolon-separator'], $users ) 00250 )->escaped() . '</span>'; 00251 00252 $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow ' . 00253 'mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>'; 00254 $r .= "<td>$tl</td>"; 00255 00256 # Main line 00257 $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array( 00258 'newpage' => $isnew, # show, when one have this flag 00259 'minor' => $allMinors, # show only, when all have this flag 00260 'unpatrolled' => $unpatrolled, # show, when one have this flag 00261 'bot' => $allBots, # show only, when all have this flag 00262 ) ); 00263 00264 # Timestamp 00265 $r .= ' ' . $block[0]->timestamp . ' </td><td>'; 00266 00267 # Article link 00268 if ( $namehidden ) { 00269 $r .= ' <span class="history-deleted">' . 00270 $this->msg( 'rev-deleted-event' )->escaped() . '</span>'; 00271 } elseif ( $allLogs ) { 00272 $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched ); 00273 } else { 00274 $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched ); 00275 } 00276 00277 $r .= $this->getLanguage()->getDirMark(); 00278 00279 $queryParams['curid'] = $curId; 00280 00281 # Changes message 00282 static $nchanges = array(); 00283 static $sinceLastVisitMsg = array(); 00284 00285 $n = count( $block ); 00286 if ( !isset( $nchanges[$n] ) ) { 00287 $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped(); 00288 } 00289 00290 $sinceLast = 0; 00291 $unvisitedOldid = null; 00293 foreach ( $block as $rcObj ) { 00294 // Same logic as below inside main foreach 00295 if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) { 00296 $sinceLast++; 00297 $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid']; 00298 } 00299 } 00300 if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) { 00301 $sinceLastVisitMsg[$sinceLast] = 00302 $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped(); 00303 } 00304 00305 # Total change link 00306 $r .= ' '; 00307 $logtext = ''; 00309 $block0 = $block[0]; 00310 if ( !$allLogs ) { 00311 if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { 00312 $logtext .= $nchanges[$n]; 00313 } elseif ( $isnew ) { 00314 $logtext .= $nchanges[$n]; 00315 } else { 00316 $logtext .= Linker::link( 00317 $block0->getTitle(), 00318 $nchanges[$n], 00319 array(), 00320 $queryParams + array( 00321 'diff' => $currentRevision, 00322 'oldid' => $oldid, 00323 ), 00324 array( 'known', 'noclasses' ) 00325 ); 00326 if ( $sinceLast > 0 && $sinceLast < $n ) { 00327 $logtext .= $this->message['pipe-separator'] . Linker::link( 00328 $block0->getTitle(), 00329 $sinceLastVisitMsg[$sinceLast], 00330 array(), 00331 $queryParams + array( 00332 'diff' => $currentRevision, 00333 'oldid' => $unvisitedOldid, 00334 ), 00335 array( 'known', 'noclasses' ) 00336 ); 00337 } 00338 } 00339 } 00340 00341 # History 00342 if ( $allLogs ) { 00343 // don't show history link for logs 00344 } elseif ( $namehidden || !$block0->getTitle()->exists() ) { 00345 $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history']; 00346 } else { 00347 $params = $queryParams; 00348 $params['action'] = 'history'; 00349 00350 $logtext .= $this->message['pipe-separator'] . 00351 Linker::linkKnown( 00352 $block0->getTitle(), 00353 $this->message['enhancedrc-history'], 00354 array(), 00355 $params 00356 ); 00357 } 00358 00359 if ( $logtext !== '' ) { 00360 $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped(); 00361 } 00362 00363 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 00364 00365 # Character difference (does not apply if only log items) 00366 if ( $RCShowChangedSize && !$allLogs ) { 00367 $last = 0; 00368 $first = count( $block ) - 1; 00369 # Some events (like logs) have an "empty" size, so we need to skip those... 00370 while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) { 00371 $last++; 00372 } 00373 while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) { 00374 $first--; 00375 } 00376 # Get net change 00377 $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] ); 00378 00379 if ( $chardiff == '' ) { 00380 $r .= ' '; 00381 } else { 00382 $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> '; 00383 } 00384 } 00385 00386 $r .= $users; 00387 $r .= $this->numberofWatchingusers( $block0->numberofWatchingusers ); 00388 $r .= '</td></tr>'; 00389 00390 # Sub-entries 00391 foreach ( $block as $rcObj ) { 00392 # Classes to apply -- TODO implement 00393 $classes = array(); 00394 $type = $rcObj->mAttribs['rc_type']; 00395 00396 $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched 00397 ? ' class="mw-enhanced-watched"' : ''; 00398 00399 $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">'; 00400 $r .= $this->recentChangesFlags( array( 00401 'newpage' => $type == RC_NEW, 00402 'minor' => $rcObj->mAttribs['rc_minor'], 00403 'unpatrolled' => $rcObj->unpatrolled, 00404 'bot' => $rcObj->mAttribs['rc_bot'], 00405 ) ); 00406 $r .= ' </td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">'; 00407 00408 $params = $queryParams; 00409 00410 if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) { 00411 $params['oldid'] = $rcObj->mAttribs['rc_this_oldid']; 00412 } 00413 00414 # Log timestamp 00415 if ( $type == RC_LOG ) { 00416 $link = $rcObj->timestamp; 00417 # Revision link 00418 } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { 00419 $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> '; 00420 } else { 00421 00422 $link = Linker::linkKnown( 00423 $rcObj->getTitle(), 00424 $rcObj->timestamp, 00425 array(), 00426 $params 00427 ); 00428 if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) { 00429 $link = '<span class="history-deleted">' . $link . '</span> '; 00430 } 00431 } 00432 $r .= $link . '</span>'; 00433 00434 if ( !$type == RC_LOG || $type == RC_NEW ) { 00435 $r .= ' ' . $this->msg( 'parentheses' )->rawParams( 00436 $rcObj->curlink . 00437 $this->message['pipe-separator'] . 00438 $rcObj->lastlink 00439 )->escaped(); 00440 } 00441 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 00442 00443 # Character diff 00444 if ( $RCShowChangedSize ) { 00445 $cd = $this->formatCharacterDifference( $rcObj ); 00446 if ( $cd !== '' ) { 00447 $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> '; 00448 } 00449 } 00450 00451 if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) { 00452 $r .= $this->insertLogEntry( $rcObj ); 00453 } else { 00454 # User links 00455 $r .= $rcObj->userlink; 00456 $r .= $rcObj->usertalklink; 00457 $r .= $this->insertComment( $rcObj ); 00458 } 00459 00460 # Rollback 00461 $this->insertRollback( $r, $rcObj ); 00462 # Tags 00463 $this->insertTags( $r, $rcObj, $classes ); 00464 00465 $r .= "</td></tr>\n"; 00466 } 00467 $r .= "</table>\n"; 00468 00469 $this->rcCacheIndex++; 00470 00471 wfProfileOut( __METHOD__ ); 00472 00473 return $r; 00474 } 00475 00482 protected function recentChangesBlockLine( $rcObj ) { 00483 wfProfileIn( __METHOD__ ); 00484 $query['curid'] = $rcObj->mAttribs['rc_cur_id']; 00485 00486 $type = $rcObj->mAttribs['rc_type']; 00487 $logType = $rcObj->mAttribs['rc_log_type']; 00488 $classes = array( 'mw-enhanced-rc' ); 00489 if ( $logType ) { 00490 # Log entry 00491 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType ); 00492 } else { 00493 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' . 00494 $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] ); 00495 } 00496 $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched 00497 ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; 00498 $r = Html::openElement( 'table', array( 'class' => $classes ) ) . 00499 Html::openElement( 'tr' ); 00500 00501 $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>'; 00502 # Flag and Timestamp 00503 $r .= $this->recentChangesFlags( array( 00504 'newpage' => $type == RC_NEW, 00505 'minor' => $rcObj->mAttribs['rc_minor'], 00506 'unpatrolled' => $rcObj->unpatrolled, 00507 'bot' => $rcObj->mAttribs['rc_bot'], 00508 ) ); 00509 $r .= ' ' . $rcObj->timestamp . ' </td><td>'; 00510 # Article or log link 00511 if ( $logType ) { 00512 $logPage = new LogPage( $logType ); 00513 $logTitle = SpecialPage::getTitleFor( 'Log', $logType ); 00514 $logName = $logPage->getName()->escaped(); 00515 $r .= $this->msg( 'parentheses' ) 00516 ->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped(); 00517 } else { 00518 $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched ); 00519 } 00520 # Diff and hist links 00521 if ( $type != RC_LOG ) { 00522 $query['action'] = 'history'; 00523 $r .= ' ' . $this->msg( 'parentheses' ) 00524 ->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown( 00525 $rcObj->getTitle(), 00526 $this->message['hist'], 00527 array(), 00528 $query 00529 ) )->escaped(); 00530 } 00531 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 00532 # Character diff 00533 if ( $this->getConfig()->get( 'RCShowChangedSize' ) ) { 00534 $cd = $this->formatCharacterDifference( $rcObj ); 00535 if ( $cd !== '' ) { 00536 $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> '; 00537 } 00538 } 00539 00540 if ( $type == RC_LOG ) { 00541 $r .= $this->insertLogEntry( $rcObj ); 00542 } else { 00543 $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink; 00544 $r .= $this->insertComment( $rcObj ); 00545 $this->insertRollback( $r, $rcObj ); 00546 } 00547 00548 # Tags 00549 $this->insertTags( $r, $rcObj, $classes ); 00550 # Show how many people are watching this if enabled 00551 $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers ); 00552 00553 $r .= "</td></tr></table>\n"; 00554 00555 wfProfileOut( __METHOD__ ); 00556 00557 return $r; 00558 } 00559 00566 protected function recentChangesBlock() { 00567 if ( count( $this->rc_cache ) == 0 ) { 00568 return ''; 00569 } 00570 00571 wfProfileIn( __METHOD__ ); 00572 00573 $blockOut = ''; 00574 foreach ( $this->rc_cache as $block ) { 00575 if ( count( $block ) < 2 ) { 00576 $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) ); 00577 } else { 00578 $blockOut .= $this->recentChangesBlockGroup( $block ); 00579 } 00580 } 00581 00582 wfProfileOut( __METHOD__ ); 00583 00584 return '<div>' . $blockOut . '</div>'; 00585 } 00586 00592 public function endRecentChangesList() { 00593 return $this->recentChangesBlock() . '</div>'; 00594 } 00595 }