MediaWiki
REL1_23
|
00001 <?php 00026 class LogEventsList extends ContextSource { 00027 const NO_ACTION_LINK = 1; 00028 const NO_EXTRA_USER_LINKS = 2; 00029 const USE_REVDEL_CHECKBOXES = 4; 00030 00031 public $flags; 00032 00036 protected $mDefaultQuery; 00037 00049 public function __construct( $context, $unused = null, $flags = 0 ) { 00050 if ( $context instanceof IContextSource ) { 00051 $this->setContext( $context ); 00052 } else { 00053 // Old parameters, $context should be a Skin object 00054 $this->setContext( $context->getContext() ); 00055 } 00056 00057 $this->flags = $flags; 00058 } 00059 00066 public function getDisplayTitle() { 00067 wfDeprecated( __METHOD__, '1.20' ); 00068 return $this->getTitle(); 00069 } 00070 00076 public function showHeader( $type ) { 00077 wfDeprecated( __METHOD__, '1.19' ); 00078 // If only one log type is used, then show a special message... 00079 $headerType = count( $type ) == 1 ? $type[0] : ''; 00080 $out = $this->getOutput(); 00081 if ( LogPage::isLogType( $headerType ) ) { 00082 $page = new LogPage( $headerType ); 00083 $out->setPageTitle( $page->getName()->text() ); 00084 $out->addHTML( $page->getDescription()->parseAsBlock() ); 00085 } else { 00086 $out->addHTML( $this->msg( 'alllogstext' )->parse() ); 00087 } 00088 } 00089 00102 public function showOptions( $types = array(), $user = '', $page = '', $pattern = '', $year = 0, 00103 $month = 0, $filter = null, $tagFilter = '' 00104 ) { 00105 global $wgScript, $wgMiserMode; 00106 00107 $title = SpecialPage::getTitleFor( 'Log' ); 00108 00109 // For B/C, we take strings, but make sure they are converted... 00110 $types = ( $types === '' ) ? array() : (array)$types; 00111 00112 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); 00113 00114 $html = Html::hidden( 'title', $title->getPrefixedDBkey() ); 00115 00116 // Basic selectors 00117 $html .= $this->getTypeMenu( $types ) . "\n"; 00118 $html .= $this->getUserInput( $user ) . "\n"; 00119 $html .= $this->getTitleInput( $page ) . "\n"; 00120 $html .= $this->getExtraInputs( $types ) . "\n"; 00121 00122 // Title pattern, if allowed 00123 if ( !$wgMiserMode ) { 00124 $html .= $this->getTitlePattern( $pattern ) . "\n"; 00125 } 00126 00127 // date menu 00128 $html .= Xml::tags( 'p', null, Xml::dateMenu( (int)$year, (int)$month ) ); 00129 00130 // Tag filter 00131 if ( $tagSelector ) { 00132 $html .= Xml::tags( 'p', null, implode( ' ', $tagSelector ) ); 00133 } 00134 00135 // Filter links 00136 if ( $filter ) { 00137 $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) ); 00138 } 00139 00140 // Submit button 00141 $html .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ); 00142 00143 // Fieldset 00144 $html = Xml::fieldset( $this->msg( 'log' )->text(), $html ); 00145 00146 // Form wrapping 00147 $html = Xml::tags( 'form', array( 'action' => $wgScript, 'method' => 'get' ), $html ); 00148 00149 $this->getOutput()->addHTML( $html ); 00150 } 00151 00156 private function getFilterLinks( $filter ) { 00157 // show/hide links 00158 $messages = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() ); 00159 // Option value -> message mapping 00160 $links = array(); 00161 $hiddens = ''; // keep track for "go" button 00162 foreach ( $filter as $type => $val ) { 00163 // Should the below assignment be outside the foreach? 00164 // Then it would have to be copied. Not certain what is more expensive. 00165 $query = $this->getDefaultQuery(); 00166 $queryKey = "hide_{$type}_log"; 00167 00168 $hideVal = 1 - intval( $val ); 00169 $query[$queryKey] = $hideVal; 00170 00171 $link = Linker::linkKnown( 00172 $this->getTitle(), 00173 $messages[$hideVal], 00174 array(), 00175 $query 00176 ); 00177 00178 // Message: log-show-hide-patrol 00179 $links[$type] = $this->msg( "log-show-hide-{$type}" )->rawParams( $link )->escaped(); 00180 $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n"; 00181 } 00182 00183 // Build links 00184 return '<small>' . $this->getLanguage()->pipeList( $links ) . '</small>' . $hiddens; 00185 } 00186 00187 private function getDefaultQuery() { 00188 if ( !isset( $this->mDefaultQuery ) ) { 00189 $this->mDefaultQuery = $this->getRequest()->getQueryValues(); 00190 unset( $this->mDefaultQuery['title'] ); 00191 unset( $this->mDefaultQuery['dir'] ); 00192 unset( $this->mDefaultQuery['offset'] ); 00193 unset( $this->mDefaultQuery['limit'] ); 00194 unset( $this->mDefaultQuery['order'] ); 00195 unset( $this->mDefaultQuery['month'] ); 00196 unset( $this->mDefaultQuery['year'] ); 00197 } 00198 00199 return $this->mDefaultQuery; 00200 } 00201 00206 private function getTypeMenu( $queryTypes ) { 00207 $queryType = count( $queryTypes ) == 1 ? $queryTypes[0] : ''; 00208 $selector = $this->getTypeSelector(); 00209 $selector->setDefault( $queryType ); 00210 00211 return $selector->getHtml(); 00212 } 00213 00219 public function getTypeSelector() { 00220 $typesByName = array(); // Temporary array 00221 // First pass to load the log names 00222 foreach ( LogPage::validTypes() as $type ) { 00223 $page = new LogPage( $type ); 00224 $restriction = $page->getRestriction(); 00225 if ( $this->getUser()->isAllowed( $restriction ) ) { 00226 $typesByName[$type] = $page->getName()->text(); 00227 } 00228 } 00229 00230 // Second pass to sort by name 00231 asort( $typesByName ); 00232 00233 // Always put "All public logs" on top 00234 $public = $typesByName['']; 00235 unset( $typesByName[''] ); 00236 $typesByName = array( '' => $public ) + $typesByName; 00237 00238 $select = new XmlSelect( 'type' ); 00239 foreach ( $typesByName as $type => $name ) { 00240 $select->addOption( $name, $type ); 00241 } 00242 00243 return $select; 00244 } 00245 00250 private function getUserInput( $user ) { 00251 $label = Xml::inputLabel( 00252 $this->msg( 'specialloguserlabel' )->text(), 00253 'user', 00254 'mw-log-user', 00255 15, 00256 $user 00257 ); 00258 00259 return '<span style="white-space: nowrap">' . $label . '</span>'; 00260 } 00261 00266 private function getTitleInput( $title ) { 00267 $label = Xml::inputLabel( 00268 $this->msg( 'speciallogtitlelabel' )->text(), 00269 'page', 00270 'mw-log-page', 00271 20, 00272 $title 00273 ); 00274 00275 return '<span style="white-space: nowrap">' . $label . '</span>'; 00276 } 00277 00282 private function getTitlePattern( $pattern ) { 00283 return '<span style="white-space: nowrap">' . 00284 Xml::checkLabel( $this->msg( 'log-title-wildcard' )->text(), 'pattern', 'pattern', $pattern ) . 00285 '</span>'; 00286 } 00287 00292 private function getExtraInputs( $types ) { 00293 $offender = $this->getRequest()->getVal( 'offender' ); 00294 $user = User::newFromName( $offender, false ); 00295 if ( !$user || ( $user->getId() == 0 && !IP::isIPAddress( $offender ) ) ) { 00296 $offender = ''; // Blank field if invalid 00297 } 00298 if ( count( $types ) == 1 && $types[0] == 'suppress' ) { 00299 return Xml::inputLabel( $this->msg( 'revdelete-offender' )->text(), 'offender', 00300 'mw-log-offender', 20, $offender ); 00301 } 00302 00303 return ''; 00304 } 00305 00309 public function beginLogEventsList() { 00310 return "<ul>\n"; 00311 } 00312 00316 public function endLogEventsList() { 00317 return "</ul>\n"; 00318 } 00319 00324 public function logLine( $row ) { 00325 $entry = DatabaseLogEntry::newFromRow( $row ); 00326 $formatter = LogFormatter::newFromEntry( $entry ); 00327 $formatter->setContext( $this->getContext() ); 00328 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) ); 00329 00330 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate( 00331 $entry->getTimestamp(), $this->getUser() ) ); 00332 00333 $action = $formatter->getActionText(); 00334 00335 if ( $this->flags & self::NO_ACTION_LINK ) { 00336 $revert = ''; 00337 } else { 00338 $revert = $formatter->getActionLinks(); 00339 if ( $revert != '' ) { 00340 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>'; 00341 } 00342 } 00343 00344 $comment = $formatter->getComment(); 00345 00346 // Some user can hide log items and have review links 00347 $del = $this->getShowHideLinks( $row ); 00348 00349 // Any tags... 00350 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' ); 00351 $classes = array_merge( 00352 array( 'mw-logline-' . $entry->getType() ), 00353 $newClasses 00354 ); 00355 00356 return Html::rawElement( 'li', array( 'class' => $classes ), 00357 "$del $time $action $comment $revert $tagDisplay" ) . "\n"; 00358 } 00359 00364 private function getShowHideLinks( $row ) { 00365 // We don't want to see the links and 00366 // no one can hide items from the suppress log. 00367 if ( ( $this->flags == self::NO_ACTION_LINK ) 00368 || $row->log_type == 'suppress' 00369 ) { 00370 return ''; 00371 } 00372 $del = ''; 00373 $user = $this->getUser(); 00374 // Don't show useless checkbox to people who cannot hide log entries 00375 if ( $user->isAllowed( 'deletedhistory' ) ) { 00376 $canHide = $user->isAllowed( 'deletelogentry' ); 00377 if ( $row->log_deleted || $canHide ) { 00378 // Show checkboxes instead of links. 00379 if ( $canHide && $this->flags & self::USE_REVDEL_CHECKBOXES ) { 00380 // If event was hidden from sysops 00381 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { 00382 $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) ); 00383 } else { 00384 $del = Xml::check( 00385 'showhiderevisions', 00386 false, 00387 array( 'name' => 'ids[' . $row->log_id . ']' ) 00388 ); 00389 } 00390 } else { 00391 // If event was hidden from sysops 00392 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { 00393 $del = Linker::revDeleteLinkDisabled( $canHide ); 00394 } else { 00395 $query = array( 00396 'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(), 00397 'type' => 'logging', 00398 'ids' => $row->log_id, 00399 ); 00400 $del = Linker::revDeleteLink( 00401 $query, 00402 self::isDeleted( $row, LogPage::DELETED_RESTRICTED ), 00403 $canHide 00404 ); 00405 } 00406 } 00407 } 00408 } 00409 00410 return $del; 00411 } 00412 00420 public static function typeAction( $row, $type, $action, $right = '' ) { 00421 $match = is_array( $type ) ? 00422 in_array( $row->log_type, $type ) : $row->log_type == $type; 00423 if ( $match ) { 00424 $match = is_array( $action ) ? 00425 in_array( $row->log_action, $action ) : $row->log_action == $action; 00426 if ( $match && $right ) { 00427 global $wgUser; 00428 $match = $wgUser->isAllowed( $right ); 00429 } 00430 } 00431 00432 return $match; 00433 } 00434 00444 public static function userCan( $row, $field, User $user = null ) { 00445 return self::userCanBitfield( $row->log_deleted, $field, $user ); 00446 } 00447 00457 public static function userCanBitfield( $bitfield, $field, User $user = null ) { 00458 if ( $bitfield & $field ) { 00459 if ( $bitfield & LogPage::DELETED_RESTRICTED ) { 00460 $permission = 'suppressrevision'; 00461 } else { 00462 $permission = 'deletedhistory'; 00463 } 00464 wfDebug( "Checking for $permission due to $field match on $bitfield\n" ); 00465 if ( $user === null ) { 00466 global $wgUser; 00467 $user = $wgUser; 00468 } 00469 00470 return $user->isAllowed( $permission ); 00471 } 00472 00473 return true; 00474 } 00475 00481 public static function isDeleted( $row, $field ) { 00482 return ( $row->log_deleted & $field ) == $field; 00483 } 00484 00508 public static function showLogExtract( 00509 &$out, $types = array(), $page = '', $user = '', $param = array() 00510 ) { 00511 $defaultParameters = array( 00512 'lim' => 25, 00513 'conds' => array(), 00514 'showIfEmpty' => true, 00515 'msgKey' => array( '' ), 00516 'wrap' => "$1", 00517 'flags' => 0, 00518 'useRequestParams' => false, 00519 'useMaster' => false, 00520 ); 00521 # The + operator appends elements of remaining keys from the right 00522 # handed array to the left handed, whereas duplicated keys are NOT overwritten. 00523 $param += $defaultParameters; 00524 # Convert $param array to individual variables 00525 $lim = $param['lim']; 00526 $conds = $param['conds']; 00527 $showIfEmpty = $param['showIfEmpty']; 00528 $msgKey = $param['msgKey']; 00529 $wrap = $param['wrap']; 00530 $flags = $param['flags']; 00531 $useRequestParams = $param['useRequestParams']; 00532 if ( !is_array( $msgKey ) ) { 00533 $msgKey = array( $msgKey ); 00534 } 00535 00536 if ( $out instanceof OutputPage ) { 00537 $context = $out->getContext(); 00538 } else { 00539 $context = RequestContext::getMain(); 00540 } 00541 00542 # Insert list of top 50 (or top $lim) items 00543 $loglist = new LogEventsList( $context, null, $flags ); 00544 $pager = new LogPager( $loglist, $types, $user, $page, '', $conds ); 00545 if ( !$useRequestParams ) { 00546 # Reset vars that may have been taken from the request 00547 $pager->mLimit = 50; 00548 $pager->mDefaultLimit = 50; 00549 $pager->mOffset = ""; 00550 $pager->mIsBackwards = false; 00551 } 00552 00553 if ( $param['useMaster'] ) { 00554 $pager->mDb = wfGetDB( DB_MASTER ); 00555 } 00556 if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset 00557 $pager->setOffset( $param['offset'] ); 00558 } 00559 00560 if ( $lim > 0 ) { 00561 $pager->mLimit = $lim; 00562 } 00563 00564 $logBody = $pager->getBody(); 00565 $s = ''; 00566 00567 if ( $logBody ) { 00568 if ( $msgKey[0] ) { 00569 $dir = $context->getLanguage()->getDir(); 00570 $lang = $context->getLanguage()->getCode(); 00571 00572 $s = Xml::openElement( 'div', array( 00573 'class' => "mw-warning-with-logexcerpt mw-content-$dir", 00574 'dir' => $dir, 00575 'lang' => $lang, 00576 ) ); 00577 00578 if ( count( $msgKey ) == 1 ) { 00579 $s .= $context->msg( $msgKey[0] )->parseAsBlock(); 00580 } else { // Process additional arguments 00581 $args = $msgKey; 00582 array_shift( $args ); 00583 $s .= $context->msg( $msgKey[0], $args )->parseAsBlock(); 00584 } 00585 } 00586 $s .= $loglist->beginLogEventsList() . 00587 $logBody . 00588 $loglist->endLogEventsList(); 00589 } elseif ( $showIfEmpty ) { 00590 $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ), 00591 $context->msg( 'logempty' )->parse() ); 00592 } 00593 00594 if ( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link 00595 $urlParam = array(); 00596 if ( $page instanceof Title ) { 00597 $urlParam['page'] = $page->getPrefixedDBkey(); 00598 } elseif ( $page != '' ) { 00599 $urlParam['page'] = $page; 00600 } 00601 00602 if ( $user != '' ) { 00603 $urlParam['user'] = $user; 00604 } 00605 00606 if ( !is_array( $types ) ) { # Make it an array, if it isn't 00607 $types = array( $types ); 00608 } 00609 00610 # If there is exactly one log type, we can link to Special:Log?type=foo 00611 if ( count( $types ) == 1 ) { 00612 $urlParam['type'] = $types[0]; 00613 } 00614 00615 $s .= Linker::link( 00616 SpecialPage::getTitleFor( 'Log' ), 00617 $context->msg( 'log-fulllog' )->escaped(), 00618 array(), 00619 $urlParam 00620 ); 00621 } 00622 00623 if ( $logBody && $msgKey[0] ) { 00624 $s .= '</div>'; 00625 } 00626 00627 if ( $wrap != '' ) { // Wrap message in html 00628 $s = str_replace( '$1', $s, $wrap ); 00629 } 00630 00631 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */ 00632 if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) { 00633 // $out can be either an OutputPage object or a String-by-reference 00634 if ( $out instanceof OutputPage ) { 00635 $out->addHTML( $s ); 00636 } else { 00637 $out = $s; 00638 } 00639 } 00640 00641 return $pager->getNumRows(); 00642 } 00643 00652 public static function getExcludeClause( $db, $audience = 'public', User $user = null ) { 00653 global $wgLogRestrictions; 00654 00655 if ( $audience != 'public' && $user === null ) { 00656 global $wgUser; 00657 $user = $wgUser; 00658 } 00659 00660 // Reset the array, clears extra "where" clauses when $par is used 00661 $hiddenLogs = array(); 00662 00663 // Don't show private logs to unprivileged users 00664 foreach ( $wgLogRestrictions as $logType => $right ) { 00665 if ( $audience == 'public' || !$user->isAllowed( $right ) ) { 00666 $hiddenLogs[] = $logType; 00667 } 00668 } 00669 if ( count( $hiddenLogs ) == 1 ) { 00670 return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] ); 00671 } elseif ( $hiddenLogs ) { 00672 return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) . ')'; 00673 } 00674 00675 return false; 00676 } 00677 }