MediaWiki
REL1_24
|
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 00083 public function showOptions( $types = array(), $user = '', $page = '', $pattern = '', $year = 0, 00084 $month = 0, $filter = null, $tagFilter = '' 00085 ) { 00086 global $wgScript, $wgMiserMode; 00087 00088 $title = SpecialPage::getTitleFor( 'Log' ); 00089 00090 // For B/C, we take strings, but make sure they are converted... 00091 $types = ( $types === '' ) ? array() : (array)$types; 00092 00093 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); 00094 00095 $html = Html::hidden( 'title', $title->getPrefixedDBkey() ); 00096 00097 // Basic selectors 00098 $html .= $this->getTypeMenu( $types ) . "\n"; 00099 $html .= $this->getUserInput( $user ) . "\n"; 00100 $html .= $this->getTitleInput( $page ) . "\n"; 00101 $html .= $this->getExtraInputs( $types ) . "\n"; 00102 00103 // Title pattern, if allowed 00104 if ( !$wgMiserMode ) { 00105 $html .= $this->getTitlePattern( $pattern ) . "\n"; 00106 } 00107 00108 // date menu 00109 $html .= Xml::tags( 'p', null, Xml::dateMenu( (int)$year, (int)$month ) ); 00110 00111 // Tag filter 00112 if ( $tagSelector ) { 00113 $html .= Xml::tags( 'p', null, implode( ' ', $tagSelector ) ); 00114 } 00115 00116 // Filter links 00117 if ( $filter ) { 00118 $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) ); 00119 } 00120 00121 // Submit button 00122 $html .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ); 00123 00124 // Fieldset 00125 $html = Xml::fieldset( $this->msg( 'log' )->text(), $html ); 00126 00127 // Form wrapping 00128 $html = Xml::tags( 'form', array( 'action' => $wgScript, 'method' => 'get' ), $html ); 00129 00130 $this->getOutput()->addHTML( $html ); 00131 } 00132 00137 private function getFilterLinks( $filter ) { 00138 // show/hide links 00139 $messages = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() ); 00140 // Option value -> message mapping 00141 $links = array(); 00142 $hiddens = ''; // keep track for "go" button 00143 foreach ( $filter as $type => $val ) { 00144 // Should the below assignment be outside the foreach? 00145 // Then it would have to be copied. Not certain what is more expensive. 00146 $query = $this->getDefaultQuery(); 00147 $queryKey = "hide_{$type}_log"; 00148 00149 $hideVal = 1 - intval( $val ); 00150 $query[$queryKey] = $hideVal; 00151 00152 $link = Linker::linkKnown( 00153 $this->getTitle(), 00154 $messages[$hideVal], 00155 array(), 00156 $query 00157 ); 00158 00159 // Message: log-show-hide-patrol 00160 $links[$type] = $this->msg( "log-show-hide-{$type}" )->rawParams( $link )->escaped(); 00161 $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n"; 00162 } 00163 00164 // Build links 00165 return '<small>' . $this->getLanguage()->pipeList( $links ) . '</small>' . $hiddens; 00166 } 00167 00168 private function getDefaultQuery() { 00169 if ( !isset( $this->mDefaultQuery ) ) { 00170 $this->mDefaultQuery = $this->getRequest()->getQueryValues(); 00171 unset( $this->mDefaultQuery['title'] ); 00172 unset( $this->mDefaultQuery['dir'] ); 00173 unset( $this->mDefaultQuery['offset'] ); 00174 unset( $this->mDefaultQuery['limit'] ); 00175 unset( $this->mDefaultQuery['order'] ); 00176 unset( $this->mDefaultQuery['month'] ); 00177 unset( $this->mDefaultQuery['year'] ); 00178 } 00179 00180 return $this->mDefaultQuery; 00181 } 00182 00187 private function getTypeMenu( $queryTypes ) { 00188 $queryType = count( $queryTypes ) == 1 ? $queryTypes[0] : ''; 00189 $selector = $this->getTypeSelector(); 00190 $selector->setDefault( $queryType ); 00191 00192 return $selector->getHtml(); 00193 } 00194 00200 public function getTypeSelector() { 00201 $typesByName = array(); // Temporary array 00202 // First pass to load the log names 00203 foreach ( LogPage::validTypes() as $type ) { 00204 $page = new LogPage( $type ); 00205 $restriction = $page->getRestriction(); 00206 if ( $this->getUser()->isAllowed( $restriction ) ) { 00207 $typesByName[$type] = $page->getName()->text(); 00208 } 00209 } 00210 00211 // Second pass to sort by name 00212 asort( $typesByName ); 00213 00214 // Always put "All public logs" on top 00215 $public = $typesByName['']; 00216 unset( $typesByName[''] ); 00217 $typesByName = array( '' => $public ) + $typesByName; 00218 00219 $select = new XmlSelect( 'type' ); 00220 foreach ( $typesByName as $type => $name ) { 00221 $select->addOption( $name, $type ); 00222 } 00223 00224 return $select; 00225 } 00226 00231 private function getUserInput( $user ) { 00232 $label = Xml::inputLabel( 00233 $this->msg( 'specialloguserlabel' )->text(), 00234 'user', 00235 'mw-log-user', 00236 15, 00237 $user 00238 ); 00239 00240 return '<span style="white-space: nowrap">' . $label . '</span>'; 00241 } 00242 00247 private function getTitleInput( $title ) { 00248 $label = Xml::inputLabel( 00249 $this->msg( 'speciallogtitlelabel' )->text(), 00250 'page', 00251 'mw-log-page', 00252 20, 00253 $title 00254 ); 00255 00256 return '<span style="white-space: nowrap">' . $label . '</span>'; 00257 } 00258 00263 private function getTitlePattern( $pattern ) { 00264 return '<span style="white-space: nowrap">' . 00265 Xml::checkLabel( $this->msg( 'log-title-wildcard' )->text(), 'pattern', 'pattern', $pattern ) . 00266 '</span>'; 00267 } 00268 00273 private function getExtraInputs( $types ) { 00274 $offender = $this->getRequest()->getVal( 'offender' ); 00275 $user = User::newFromName( $offender, false ); 00276 if ( !$user || ( $user->getId() == 0 && !IP::isIPAddress( $offender ) ) ) { 00277 $offender = ''; // Blank field if invalid 00278 } 00279 if ( count( $types ) == 1 && $types[0] == 'suppress' ) { 00280 return Xml::inputLabel( $this->msg( 'revdelete-offender' )->text(), 'offender', 00281 'mw-log-offender', 20, $offender ); 00282 } 00283 00284 return ''; 00285 } 00286 00290 public function beginLogEventsList() { 00291 return "<ul>\n"; 00292 } 00293 00297 public function endLogEventsList() { 00298 return "</ul>\n"; 00299 } 00300 00305 public function logLine( $row ) { 00306 $entry = DatabaseLogEntry::newFromRow( $row ); 00307 $formatter = LogFormatter::newFromEntry( $entry ); 00308 $formatter->setContext( $this->getContext() ); 00309 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) ); 00310 00311 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate( 00312 $entry->getTimestamp(), $this->getUser() ) ); 00313 00314 $action = $formatter->getActionText(); 00315 00316 if ( $this->flags & self::NO_ACTION_LINK ) { 00317 $revert = ''; 00318 } else { 00319 $revert = $formatter->getActionLinks(); 00320 if ( $revert != '' ) { 00321 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>'; 00322 } 00323 } 00324 00325 $comment = $formatter->getComment(); 00326 00327 // Some user can hide log items and have review links 00328 $del = $this->getShowHideLinks( $row ); 00329 00330 // Any tags... 00331 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' ); 00332 $classes = array_merge( 00333 array( 'mw-logline-' . $entry->getType() ), 00334 $newClasses 00335 ); 00336 00337 return Html::rawElement( 'li', array( 'class' => $classes ), 00338 "$del $time $action $comment $revert $tagDisplay" ) . "\n"; 00339 } 00340 00345 private function getShowHideLinks( $row ) { 00346 // We don't want to see the links and 00347 // no one can hide items from the suppress log. 00348 if ( ( $this->flags == self::NO_ACTION_LINK ) 00349 || $row->log_type == 'suppress' 00350 ) { 00351 return ''; 00352 } 00353 $del = ''; 00354 $user = $this->getUser(); 00355 // Don't show useless checkbox to people who cannot hide log entries 00356 if ( $user->isAllowed( 'deletedhistory' ) ) { 00357 $canHide = $user->isAllowed( 'deletelogentry' ); 00358 $canViewSuppressedOnly = $user->isAllowed( 'viewsuppressed' ) && 00359 !$user->isAllowed( 'suppressrevision' ); 00360 $entryIsSuppressed = self::isDeleted( $row, LogPage::DELETED_RESTRICTED ); 00361 $canViewThisSuppressedEntry = $canViewSuppressedOnly && $entryIsSuppressed; 00362 if ( $row->log_deleted || $canHide ) { 00363 // Show checkboxes instead of links. 00364 if ( $canHide && $this->flags & self::USE_REVDEL_CHECKBOXES && !$canViewThisSuppressedEntry ) { 00365 // If event was hidden from sysops 00366 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { 00367 $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) ); 00368 } else { 00369 $del = Xml::check( 00370 'showhiderevisions', 00371 false, 00372 array( 'name' => 'ids[' . $row->log_id . ']' ) 00373 ); 00374 } 00375 } else { 00376 // If event was hidden from sysops 00377 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { 00378 $del = Linker::revDeleteLinkDisabled( $canHide ); 00379 } else { 00380 $query = array( 00381 'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(), 00382 'type' => 'logging', 00383 'ids' => $row->log_id, 00384 ); 00385 $del = Linker::revDeleteLink( 00386 $query, 00387 $entryIsSuppressed, 00388 $canHide && !$canViewThisSuppressedEntry 00389 ); 00390 } 00391 } 00392 } 00393 } 00394 00395 return $del; 00396 } 00397 00405 public static function typeAction( $row, $type, $action, $right = '' ) { 00406 $match = is_array( $type ) ? 00407 in_array( $row->log_type, $type ) : $row->log_type == $type; 00408 if ( $match ) { 00409 $match = is_array( $action ) ? 00410 in_array( $row->log_action, $action ) : $row->log_action == $action; 00411 if ( $match && $right ) { 00412 global $wgUser; 00413 $match = $wgUser->isAllowed( $right ); 00414 } 00415 } 00416 00417 return $match; 00418 } 00419 00429 public static function userCan( $row, $field, User $user = null ) { 00430 return self::userCanBitfield( $row->log_deleted, $field, $user ); 00431 } 00432 00442 public static function userCanBitfield( $bitfield, $field, User $user = null ) { 00443 if ( $bitfield & $field ) { 00444 if ( $user === null ) { 00445 global $wgUser; 00446 $user = $wgUser; 00447 } 00448 if ( $bitfield & LogPage::DELETED_RESTRICTED ) { 00449 $permissions = array( 'suppressrevision', 'viewsuppressed' ); 00450 } else { 00451 $permissions = array( 'deletedhistory' ); 00452 } 00453 $permissionlist = implode( ', ', $permissions ); 00454 wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" ); 00455 return call_user_func_array( array( $user, 'isAllowedAny' ), $permissions ); 00456 } 00457 return true; 00458 } 00459 00465 public static function isDeleted( $row, $field ) { 00466 return ( $row->log_deleted & $field ) == $field; 00467 } 00468 00492 public static function showLogExtract( 00493 &$out, $types = array(), $page = '', $user = '', $param = array() 00494 ) { 00495 $defaultParameters = array( 00496 'lim' => 25, 00497 'conds' => array(), 00498 'showIfEmpty' => true, 00499 'msgKey' => array( '' ), 00500 'wrap' => "$1", 00501 'flags' => 0, 00502 'useRequestParams' => false, 00503 'useMaster' => false, 00504 ); 00505 # The + operator appends elements of remaining keys from the right 00506 # handed array to the left handed, whereas duplicated keys are NOT overwritten. 00507 $param += $defaultParameters; 00508 # Convert $param array to individual variables 00509 $lim = $param['lim']; 00510 $conds = $param['conds']; 00511 $showIfEmpty = $param['showIfEmpty']; 00512 $msgKey = $param['msgKey']; 00513 $wrap = $param['wrap']; 00514 $flags = $param['flags']; 00515 $useRequestParams = $param['useRequestParams']; 00516 if ( !is_array( $msgKey ) ) { 00517 $msgKey = array( $msgKey ); 00518 } 00519 00520 if ( $out instanceof OutputPage ) { 00521 $context = $out->getContext(); 00522 } else { 00523 $context = RequestContext::getMain(); 00524 } 00525 00526 # Insert list of top 50 (or top $lim) items 00527 $loglist = new LogEventsList( $context, null, $flags ); 00528 $pager = new LogPager( $loglist, $types, $user, $page, '', $conds ); 00529 if ( !$useRequestParams ) { 00530 # Reset vars that may have been taken from the request 00531 $pager->mLimit = 50; 00532 $pager->mDefaultLimit = 50; 00533 $pager->mOffset = ""; 00534 $pager->mIsBackwards = false; 00535 } 00536 00537 if ( $param['useMaster'] ) { 00538 $pager->mDb = wfGetDB( DB_MASTER ); 00539 } 00540 if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset 00541 $pager->setOffset( $param['offset'] ); 00542 } 00543 00544 if ( $lim > 0 ) { 00545 $pager->mLimit = $lim; 00546 } 00547 00548 $logBody = $pager->getBody(); 00549 $s = ''; 00550 00551 if ( $logBody ) { 00552 if ( $msgKey[0] ) { 00553 $dir = $context->getLanguage()->getDir(); 00554 $lang = $context->getLanguage()->getCode(); 00555 00556 $s = Xml::openElement( 'div', array( 00557 'class' => "mw-warning-with-logexcerpt mw-content-$dir", 00558 'dir' => $dir, 00559 'lang' => $lang, 00560 ) ); 00561 00562 if ( count( $msgKey ) == 1 ) { 00563 $s .= $context->msg( $msgKey[0] )->parseAsBlock(); 00564 } else { // Process additional arguments 00565 $args = $msgKey; 00566 array_shift( $args ); 00567 $s .= $context->msg( $msgKey[0], $args )->parseAsBlock(); 00568 } 00569 } 00570 $s .= $loglist->beginLogEventsList() . 00571 $logBody . 00572 $loglist->endLogEventsList(); 00573 } elseif ( $showIfEmpty ) { 00574 $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ), 00575 $context->msg( 'logempty' )->parse() ); 00576 } 00577 00578 if ( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link 00579 $urlParam = array(); 00580 if ( $page instanceof Title ) { 00581 $urlParam['page'] = $page->getPrefixedDBkey(); 00582 } elseif ( $page != '' ) { 00583 $urlParam['page'] = $page; 00584 } 00585 00586 if ( $user != '' ) { 00587 $urlParam['user'] = $user; 00588 } 00589 00590 if ( !is_array( $types ) ) { # Make it an array, if it isn't 00591 $types = array( $types ); 00592 } 00593 00594 # If there is exactly one log type, we can link to Special:Log?type=foo 00595 if ( count( $types ) == 1 ) { 00596 $urlParam['type'] = $types[0]; 00597 } 00598 00599 $s .= Linker::link( 00600 SpecialPage::getTitleFor( 'Log' ), 00601 $context->msg( 'log-fulllog' )->escaped(), 00602 array(), 00603 $urlParam 00604 ); 00605 } 00606 00607 if ( $logBody && $msgKey[0] ) { 00608 $s .= '</div>'; 00609 } 00610 00611 if ( $wrap != '' ) { // Wrap message in html 00612 $s = str_replace( '$1', $s, $wrap ); 00613 } 00614 00615 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */ 00616 if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) { 00617 // $out can be either an OutputPage object or a String-by-reference 00618 if ( $out instanceof OutputPage ) { 00619 $out->addHTML( $s ); 00620 } else { 00621 $out = $s; 00622 } 00623 } 00624 00625 return $pager->getNumRows(); 00626 } 00627 00636 public static function getExcludeClause( $db, $audience = 'public', User $user = null ) { 00637 global $wgLogRestrictions; 00638 00639 if ( $audience != 'public' && $user === null ) { 00640 global $wgUser; 00641 $user = $wgUser; 00642 } 00643 00644 // Reset the array, clears extra "where" clauses when $par is used 00645 $hiddenLogs = array(); 00646 00647 // Don't show private logs to unprivileged users 00648 foreach ( $wgLogRestrictions as $logType => $right ) { 00649 if ( $audience == 'public' || !$user->isAllowed( $right ) ) { 00650 $hiddenLogs[] = $logType; 00651 } 00652 } 00653 if ( count( $hiddenLogs ) == 1 ) { 00654 return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] ); 00655 } elseif ( $hiddenLogs ) { 00656 return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) . ')'; 00657 } 00658 00659 return false; 00660 } 00661 }