[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Generates a list of changes using an Enhanced system (uses javascript). 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 */ 22 23 class EnhancedChangesList extends ChangesList { 24 25 /** 26 * @var RCCacheEntryFactory 27 */ 28 protected $cacheEntryFactory; 29 30 /** 31 * @var array Array of array of RCCacheEntry 32 */ 33 protected $rc_cache; 34 35 /** 36 * @param IContextSource|Skin $obj 37 */ 38 public function __construct( $obj ) { 39 if ( $obj instanceof Skin ) { 40 // @todo: deprecate constructing with Skin 41 $context = $obj->getContext(); 42 } else { 43 if ( !$obj instanceof IContextSource ) { 44 throw new MWException( 'EnhancedChangesList must be constructed with a ' 45 . 'context source or skin.' ); 46 } 47 48 $context = $obj; 49 } 50 51 parent::__construct( $context ); 52 53 // message is set by the parent ChangesList class 54 $this->cacheEntryFactory = new RCCacheEntryFactory( 55 $context, 56 $this->message 57 ); 58 } 59 60 /** 61 * Add the JavaScript file for enhanced changeslist 62 * @return string 63 */ 64 public function beginRecentChangesList() { 65 $this->rc_cache = array(); 66 $this->rcMoveIndex = 0; 67 $this->rcCacheIndex = 0; 68 $this->lastdate = ''; 69 $this->rclistOpen = false; 70 $this->getOutput()->addModuleStyles( array( 71 'mediawiki.special.changeslist', 72 'mediawiki.special.changeslist.enhanced', 73 ) ); 74 $this->getOutput()->addModules( array( 75 'jquery.makeCollapsible', 76 'mediawiki.icon', 77 ) ); 78 79 return '<div class="mw-changeslist">'; 80 } 81 82 /** 83 * Format a line for enhanced recentchange (aka with javascript and block of lines). 84 * 85 * @param RecentChange $baseRC 86 * @param bool $watched 87 * 88 * @return string 89 */ 90 public function recentChangesLine( &$baseRC, $watched = false ) { 91 wfProfileIn( __METHOD__ ); 92 93 $date = $this->getLanguage()->userDate( 94 $baseRC->mAttribs['rc_timestamp'], 95 $this->getUser() 96 ); 97 98 $ret = ''; 99 100 # If it's a new day, add the headline and flush the cache 101 if ( $date != $this->lastdate ) { 102 # Process current cache 103 $ret = $this->recentChangesBlock(); 104 $this->rc_cache = array(); 105 $ret .= Xml::element( 'h4', null, $date ) . "\n"; 106 $this->lastdate = $date; 107 } 108 109 $cacheEntry = $this->cacheEntryFactory->newFromRecentChange( $baseRC, $watched ); 110 $this->addCacheEntry( $cacheEntry ); 111 112 wfProfileOut( __METHOD__ ); 113 114 return $ret; 115 } 116 117 /** 118 * Put accumulated information into the cache, for later display. 119 * Page moves go on their own line. 120 * 121 * @param RCCacheEntry $cacheEntry 122 */ 123 protected function addCacheEntry( RCCacheEntry $cacheEntry ) { 124 $cacheGroupingKey = $this->makeCacheGroupingKey( $cacheEntry ); 125 126 if ( !isset( $this->rc_cache[$cacheGroupingKey] ) ) { 127 $this->rc_cache[$cacheGroupingKey] = array(); 128 } 129 130 array_push( $this->rc_cache[$cacheGroupingKey], $cacheEntry ); 131 } 132 133 /** 134 * @todo use rc_source to group, if set; fallback to rc_type 135 * 136 * @param RCCacheEntry $cacheEntry 137 * 138 * @return string 139 */ 140 protected function makeCacheGroupingKey( RCCacheEntry $cacheEntry ) { 141 $title = $cacheEntry->getTitle(); 142 $cacheGroupingKey = $title->getPrefixedDBkey(); 143 144 $type = $cacheEntry->mAttribs['rc_type']; 145 146 if ( $type == RC_LOG ) { 147 // Group by log type 148 $cacheGroupingKey = SpecialPage::getTitleFor( 149 'Log', 150 $cacheEntry->mAttribs['rc_log_type'] 151 )->getPrefixedDBkey(); 152 } 153 154 return $cacheGroupingKey; 155 } 156 157 /** 158 * Enhanced RC group 159 * @param RCCacheEntry[] $block 160 * @return string 161 */ 162 protected function recentChangesBlockGroup( $block ) { 163 wfProfileIn( __METHOD__ ); 164 165 # Add the namespace and title of the block as part of the class 166 $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' ); 167 if ( $block[0]->mAttribs['rc_log_type'] ) { 168 # Log entry 169 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' 170 . $block[0]->mAttribs['rc_log_type'] ); 171 } else { 172 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' 173 . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] ); 174 } 175 $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched 176 ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; 177 $r = Html::openElement( 'table', array( 'class' => $classes ) ) . 178 Html::openElement( 'tr' ); 179 180 # Collate list of users 181 $userlinks = array(); 182 # Other properties 183 $unpatrolled = false; 184 $isnew = false; 185 $allBots = true; 186 $allMinors = true; 187 $curId = $currentRevision = 0; 188 # Some catalyst variables... 189 $namehidden = true; 190 $allLogs = true; 191 $oldid = ''; 192 $RCShowChangedSize = $this->getConfig()->get( 'RCShowChangedSize' ); 193 foreach ( $block as $rcObj ) { 194 $oldid = $rcObj->mAttribs['rc_last_oldid']; 195 if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) { 196 $isnew = true; 197 } 198 // If all log actions to this page were hidden, then don't 199 // give the name of the affected page for this block! 200 if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) { 201 $namehidden = false; 202 } 203 $u = $rcObj->userlink; 204 if ( !isset( $userlinks[$u] ) ) { 205 $userlinks[$u] = 0; 206 } 207 if ( $rcObj->unpatrolled ) { 208 $unpatrolled = true; 209 } 210 if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) { 211 $allLogs = false; 212 } 213 # Get the latest entry with a page_id and oldid 214 # since logs may not have these. 215 if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) { 216 $curId = $rcObj->mAttribs['rc_cur_id']; 217 } 218 if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) { 219 $currentRevision = $rcObj->mAttribs['rc_this_oldid']; 220 } 221 222 if ( !$rcObj->mAttribs['rc_bot'] ) { 223 $allBots = false; 224 } 225 if ( !$rcObj->mAttribs['rc_minor'] ) { 226 $allMinors = false; 227 } 228 229 $userlinks[$u]++; 230 } 231 232 # Sort the list and convert to text 233 krsort( $userlinks ); 234 asort( $userlinks ); 235 $users = array(); 236 foreach ( $userlinks as $userlink => $count ) { 237 $text = $userlink; 238 $text .= $this->getLanguage()->getDirMark(); 239 if ( $count > 1 ) { 240 // @todo FIXME: Hardcoded '×'. Should be a message. 241 $formattedCount = $this->getLanguage()->formatNum( $count ) . '×'; 242 $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $formattedCount )->escaped(); 243 } 244 array_push( $users, $text ); 245 } 246 247 $users = ' <span class="changedby">' 248 . $this->msg( 'brackets' )->rawParams( 249 implode( $this->message['semicolon-separator'], $users ) 250 )->escaped() . '</span>'; 251 252 $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow ' . 253 'mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>'; 254 $r .= "<td>$tl</td>"; 255 256 # Main line 257 $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array( 258 'newpage' => $isnew, # show, when one have this flag 259 'minor' => $allMinors, # show only, when all have this flag 260 'unpatrolled' => $unpatrolled, # show, when one have this flag 261 'bot' => $allBots, # show only, when all have this flag 262 ) ); 263 264 # Timestamp 265 $r .= ' ' . $block[0]->timestamp . ' </td><td>'; 266 267 # Article link 268 if ( $namehidden ) { 269 $r .= ' <span class="history-deleted">' . 270 $this->msg( 'rev-deleted-event' )->escaped() . '</span>'; 271 } elseif ( $allLogs ) { 272 $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched ); 273 } else { 274 $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched ); 275 } 276 277 $r .= $this->getLanguage()->getDirMark(); 278 279 $queryParams['curid'] = $curId; 280 281 # Changes message 282 static $nchanges = array(); 283 static $sinceLastVisitMsg = array(); 284 285 $n = count( $block ); 286 if ( !isset( $nchanges[$n] ) ) { 287 $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped(); 288 } 289 290 $sinceLast = 0; 291 $unvisitedOldid = null; 292 /** @var $rcObj RCCacheEntry */ 293 foreach ( $block as $rcObj ) { 294 // Same logic as below inside main foreach 295 if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) { 296 $sinceLast++; 297 $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid']; 298 } 299 } 300 if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) { 301 $sinceLastVisitMsg[$sinceLast] = 302 $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped(); 303 } 304 305 # Total change link 306 $r .= ' '; 307 $logtext = ''; 308 /** @var $block0 RecentChange */ 309 $block0 = $block[0]; 310 if ( !$allLogs ) { 311 if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { 312 $logtext .= $nchanges[$n]; 313 } elseif ( $isnew ) { 314 $logtext .= $nchanges[$n]; 315 } else { 316 $logtext .= Linker::link( 317 $block0->getTitle(), 318 $nchanges[$n], 319 array(), 320 $queryParams + array( 321 'diff' => $currentRevision, 322 'oldid' => $oldid, 323 ), 324 array( 'known', 'noclasses' ) 325 ); 326 if ( $sinceLast > 0 && $sinceLast < $n ) { 327 $logtext .= $this->message['pipe-separator'] . Linker::link( 328 $block0->getTitle(), 329 $sinceLastVisitMsg[$sinceLast], 330 array(), 331 $queryParams + array( 332 'diff' => $currentRevision, 333 'oldid' => $unvisitedOldid, 334 ), 335 array( 'known', 'noclasses' ) 336 ); 337 } 338 } 339 } 340 341 # History 342 if ( $allLogs ) { 343 // don't show history link for logs 344 } elseif ( $namehidden || !$block0->getTitle()->exists() ) { 345 $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history']; 346 } else { 347 $params = $queryParams; 348 $params['action'] = 'history'; 349 350 $logtext .= $this->message['pipe-separator'] . 351 Linker::linkKnown( 352 $block0->getTitle(), 353 $this->message['enhancedrc-history'], 354 array(), 355 $params 356 ); 357 } 358 359 if ( $logtext !== '' ) { 360 $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped(); 361 } 362 363 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 364 365 # Character difference (does not apply if only log items) 366 if ( $RCShowChangedSize && !$allLogs ) { 367 $last = 0; 368 $first = count( $block ) - 1; 369 # Some events (like logs) have an "empty" size, so we need to skip those... 370 while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) { 371 $last++; 372 } 373 while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) { 374 $first--; 375 } 376 # Get net change 377 $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] ); 378 379 if ( $chardiff == '' ) { 380 $r .= ' '; 381 } else { 382 $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> '; 383 } 384 } 385 386 $r .= $users; 387 $r .= $this->numberofWatchingusers( $block0->numberofWatchingusers ); 388 $r .= '</td></tr>'; 389 390 # Sub-entries 391 foreach ( $block as $rcObj ) { 392 # Classes to apply -- TODO implement 393 $classes = array(); 394 $type = $rcObj->mAttribs['rc_type']; 395 396 $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched 397 ? ' class="mw-enhanced-watched"' : ''; 398 399 $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">'; 400 $r .= $this->recentChangesFlags( array( 401 'newpage' => $type == RC_NEW, 402 'minor' => $rcObj->mAttribs['rc_minor'], 403 'unpatrolled' => $rcObj->unpatrolled, 404 'bot' => $rcObj->mAttribs['rc_bot'], 405 ) ); 406 $r .= ' </td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">'; 407 408 $params = $queryParams; 409 410 if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) { 411 $params['oldid'] = $rcObj->mAttribs['rc_this_oldid']; 412 } 413 414 # Log timestamp 415 if ( $type == RC_LOG ) { 416 $link = $rcObj->timestamp; 417 # Revision link 418 } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { 419 $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> '; 420 } else { 421 422 $link = Linker::linkKnown( 423 $rcObj->getTitle(), 424 $rcObj->timestamp, 425 array(), 426 $params 427 ); 428 if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) { 429 $link = '<span class="history-deleted">' . $link . '</span> '; 430 } 431 } 432 $r .= $link . '</span>'; 433 434 if ( !$type == RC_LOG || $type == RC_NEW ) { 435 $r .= ' ' . $this->msg( 'parentheses' )->rawParams( 436 $rcObj->curlink . 437 $this->message['pipe-separator'] . 438 $rcObj->lastlink 439 )->escaped(); 440 } 441 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 442 443 # Character diff 444 if ( $RCShowChangedSize ) { 445 $cd = $this->formatCharacterDifference( $rcObj ); 446 if ( $cd !== '' ) { 447 $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> '; 448 } 449 } 450 451 if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) { 452 $r .= $this->insertLogEntry( $rcObj ); 453 } else { 454 # User links 455 $r .= $rcObj->userlink; 456 $r .= $rcObj->usertalklink; 457 $r .= $this->insertComment( $rcObj ); 458 } 459 460 # Rollback 461 $this->insertRollback( $r, $rcObj ); 462 # Tags 463 $this->insertTags( $r, $rcObj, $classes ); 464 465 $r .= "</td></tr>\n"; 466 } 467 $r .= "</table>\n"; 468 469 $this->rcCacheIndex++; 470 471 wfProfileOut( __METHOD__ ); 472 473 return $r; 474 } 475 476 /** 477 * Enhanced RC ungrouped line. 478 * 479 * @param RecentChange|RCCacheEntry $rcObj 480 * @return string A HTML formatted line (generated using $r) 481 */ 482 protected function recentChangesBlockLine( $rcObj ) { 483 wfProfileIn( __METHOD__ ); 484 $query['curid'] = $rcObj->mAttribs['rc_cur_id']; 485 486 $type = $rcObj->mAttribs['rc_type']; 487 $logType = $rcObj->mAttribs['rc_log_type']; 488 $classes = array( 'mw-enhanced-rc' ); 489 if ( $logType ) { 490 # Log entry 491 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType ); 492 } else { 493 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' . 494 $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] ); 495 } 496 $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched 497 ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; 498 $r = Html::openElement( 'table', array( 'class' => $classes ) ) . 499 Html::openElement( 'tr' ); 500 501 $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>'; 502 # Flag and Timestamp 503 $r .= $this->recentChangesFlags( array( 504 'newpage' => $type == RC_NEW, 505 'minor' => $rcObj->mAttribs['rc_minor'], 506 'unpatrolled' => $rcObj->unpatrolled, 507 'bot' => $rcObj->mAttribs['rc_bot'], 508 ) ); 509 $r .= ' ' . $rcObj->timestamp . ' </td><td>'; 510 # Article or log link 511 if ( $logType ) { 512 $logPage = new LogPage( $logType ); 513 $logTitle = SpecialPage::getTitleFor( 'Log', $logType ); 514 $logName = $logPage->getName()->escaped(); 515 $r .= $this->msg( 'parentheses' ) 516 ->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped(); 517 } else { 518 $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched ); 519 } 520 # Diff and hist links 521 if ( $type != RC_LOG ) { 522 $query['action'] = 'history'; 523 $r .= ' ' . $this->msg( 'parentheses' ) 524 ->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown( 525 $rcObj->getTitle(), 526 $this->message['hist'], 527 array(), 528 $query 529 ) )->escaped(); 530 } 531 $r .= ' <span class="mw-changeslist-separator">. .</span> '; 532 # Character diff 533 if ( $this->getConfig()->get( 'RCShowChangedSize' ) ) { 534 $cd = $this->formatCharacterDifference( $rcObj ); 535 if ( $cd !== '' ) { 536 $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> '; 537 } 538 } 539 540 if ( $type == RC_LOG ) { 541 $r .= $this->insertLogEntry( $rcObj ); 542 } else { 543 $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink; 544 $r .= $this->insertComment( $rcObj ); 545 $this->insertRollback( $r, $rcObj ); 546 } 547 548 # Tags 549 $this->insertTags( $r, $rcObj, $classes ); 550 # Show how many people are watching this if enabled 551 $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers ); 552 553 $r .= "</td></tr></table>\n"; 554 555 wfProfileOut( __METHOD__ ); 556 557 return $r; 558 } 559 560 /** 561 * If enhanced RC is in use, this function takes the previously cached 562 * RC lines, arranges them, and outputs the HTML 563 * 564 * @return string 565 */ 566 protected function recentChangesBlock() { 567 if ( count( $this->rc_cache ) == 0 ) { 568 return ''; 569 } 570 571 wfProfileIn( __METHOD__ ); 572 573 $blockOut = ''; 574 foreach ( $this->rc_cache as $block ) { 575 if ( count( $block ) < 2 ) { 576 $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) ); 577 } else { 578 $blockOut .= $this->recentChangesBlockGroup( $block ); 579 } 580 } 581 582 wfProfileOut( __METHOD__ ); 583 584 return '<div>' . $blockOut . '</div>'; 585 } 586 587 /** 588 * Returns text for the end of RC 589 * If enhanced RC is in use, returns pretty much all the text 590 * @return string 591 */ 592 public function endRecentChangesList() { 593 return $this->recentChangesBlock() . '</div>'; 594 } 595 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |