MediaWiki
master
|
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 00048 public function __construct( $context, $unused = null, $flags = 0 ) { 00049 if ( $context instanceof IContextSource ) { 00050 $this->setContext( $context ); 00051 } else { 00052 // Old parameters, $context should be a Skin object 00053 $this->setContext( $context->getContext() ); 00054 } 00055 00056 $this->flags = $flags; 00057 } 00058 00065 public function getDisplayTitle() { 00066 return $this->getTitle(); 00067 } 00068 00074 public function showHeader( $type ) { 00075 wfDeprecated( __METHOD__, '1.19' ); 00076 // If only one log type is used, then show a special message... 00077 $headerType = (count($type) == 1) ? $type[0] : ''; 00078 $out = $this->getOutput(); 00079 if( LogPage::isLogType( $headerType ) ) { 00080 $page = new LogPage( $headerType ); 00081 $out->setPageTitle( $page->getName()->text() ); 00082 $out->addHTML( $page->getDescription()->parseAsBlock() ); 00083 } else { 00084 $out->addHTML( $this->msg( 'alllogstext' )->parse() ); 00085 } 00086 } 00087 00100 public function showOptions( $types=array(), $user='', $page='', $pattern='', $year='', 00101 $month = '', $filter = null, $tagFilter='' ) { 00102 global $wgScript, $wgMiserMode; 00103 00104 $title = SpecialPage::getTitleFor( 'Log' ); 00105 00106 // For B/C, we take strings, but make sure they are converted... 00107 $types = ($types === '') ? array() : (array)$types; 00108 00109 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); 00110 00111 $html = Html::hidden( 'title', $title->getPrefixedDBkey() ); 00112 00113 // Basic selectors 00114 $html .= $this->getTypeMenu( $types ) . "\n"; 00115 $html .= $this->getUserInput( $user ) . "\n"; 00116 $html .= $this->getTitleInput( $page ) . "\n"; 00117 $html .= $this->getExtraInputs( $types ) . "\n"; 00118 00119 // Title pattern, if allowed 00120 if (!$wgMiserMode) { 00121 $html .= $this->getTitlePattern( $pattern ) . "\n"; 00122 } 00123 00124 // date menu 00125 $html .= Xml::tags( 'p', null, Xml::dateMenu( $year, $month ) ); 00126 00127 // Tag filter 00128 if ($tagSelector) { 00129 $html .= Xml::tags( 'p', null, implode( ' ', $tagSelector ) ); 00130 } 00131 00132 // Filter links 00133 if ($filter) { 00134 $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) ); 00135 } 00136 00137 // Submit button 00138 $html .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ); 00139 00140 // Fieldset 00141 $html = Xml::fieldset( $this->msg( 'log' )->text(), $html ); 00142 00143 // Form wrapping 00144 $html = Xml::tags( 'form', array( 'action' => $wgScript, 'method' => 'get' ), $html ); 00145 00146 $this->getOutput()->addHTML( $html ); 00147 } 00148 00153 private function getFilterLinks( $filter ) { 00154 // show/hide links 00155 $messages = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() ); 00156 // Option value -> message mapping 00157 $links = array(); 00158 $hiddens = ''; // keep track for "go" button 00159 foreach( $filter as $type => $val ) { 00160 // Should the below assignment be outside the foreach? 00161 // Then it would have to be copied. Not certain what is more expensive. 00162 $query = $this->getDefaultQuery(); 00163 $queryKey = "hide_{$type}_log"; 00164 00165 $hideVal = 1 - intval($val); 00166 $query[$queryKey] = $hideVal; 00167 00168 $link = Linker::linkKnown( 00169 $this->getTitle(), 00170 $messages[$hideVal], 00171 array(), 00172 $query 00173 ); 00174 00175 $links[$type] = $this->msg( "log-show-hide-{$type}" )->rawParams( $link )->escaped(); 00176 $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n"; 00177 } 00178 // Build links 00179 return '<small>'.$this->getLanguage()->pipeList( $links ) . '</small>' . $hiddens; 00180 } 00181 00182 private function getDefaultQuery() { 00183 if ( !isset( $this->mDefaultQuery ) ) { 00184 $this->mDefaultQuery = $this->getRequest()->getQueryValues(); 00185 unset( $this->mDefaultQuery['title'] ); 00186 unset( $this->mDefaultQuery['dir'] ); 00187 unset( $this->mDefaultQuery['offset'] ); 00188 unset( $this->mDefaultQuery['limit'] ); 00189 unset( $this->mDefaultQuery['order'] ); 00190 unset( $this->mDefaultQuery['month'] ); 00191 unset( $this->mDefaultQuery['year'] ); 00192 } 00193 return $this->mDefaultQuery; 00194 } 00195 00200 private function getTypeMenu( $queryTypes ) { 00201 $queryType = count($queryTypes) == 1 ? $queryTypes[0] : ''; 00202 $selector = $this->getTypeSelector(); 00203 $selector->setDefault( $queryType ); 00204 return $selector->getHtml(); 00205 } 00206 00212 public function getTypeSelector() { 00213 $typesByName = array(); // Temporary array 00214 // First pass to load the log names 00215 foreach( LogPage::validTypes() as $type ) { 00216 $page = new LogPage( $type ); 00217 $restriction = $page->getRestriction(); 00218 if ( $this->getUser()->isAllowed( $restriction ) ) { 00219 $typesByName[$type] = $page->getName()->text(); 00220 } 00221 } 00222 00223 // Second pass to sort by name 00224 asort($typesByName); 00225 00226 // Always put "All public logs" on top 00227 $public = $typesByName['']; 00228 unset( $typesByName[''] ); 00229 $typesByName = array( '' => $public ) + $typesByName; 00230 00231 $select = new XmlSelect( 'type' ); 00232 foreach( $typesByName as $type => $name ) { 00233 $select->addOption( $name, $type ); 00234 } 00235 00236 return $select; 00237 } 00238 00243 private function getUserInput( $user ) { 00244 return '<span style="white-space: nowrap">' . 00245 Xml::inputLabel( $this->msg( 'specialloguserlabel' )->text(), 'user', 'mw-log-user', 15, $user ) . 00246 '</span>'; 00247 } 00248 00253 private function getTitleInput( $title ) { 00254 return '<span style="white-space: nowrap">' . 00255 Xml::inputLabel( $this->msg( 'speciallogtitlelabel' )->text(), 'page', 'mw-log-page', 20, $title ) . 00256 '</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 return ''; 00284 } 00285 00289 public function beginLogEventsList() { 00290 return "<ul>\n"; 00291 } 00292 00296 public function endLogEventsList() { 00297 return "</ul>\n"; 00298 } 00299 00304 public function logLine( $row ) { 00305 $entry = DatabaseLogEntry::newFromRow( $row ); 00306 $formatter = LogFormatter::newFromEntry( $entry ); 00307 $formatter->setContext( $this->getContext() ); 00308 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) ); 00309 00310 $title = $entry->getTarget(); 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 if( ( $this->flags == self::NO_ACTION_LINK ) // we don't want to see the links 00347 || $row->log_type == 'suppress' ) { // no one can hide items from the suppress log 00348 return ''; 00349 } 00350 $del = ''; 00351 $user = $this->getUser(); 00352 // Don't show useless checkbox to people who cannot hide log entries 00353 if( $user->isAllowed( 'deletedhistory' ) ) { 00354 $canHide = $user->isAllowed( 'deletelogentry' ); 00355 if( $row->log_deleted || $canHide ) { 00356 if ( $canHide && $this->flags & self::USE_REVDEL_CHECKBOXES ) { // Show checkboxes instead of links. 00357 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { // If event was hidden from sysops 00358 $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) ); 00359 } else { 00360 $del = Xml::check( 'showhiderevisions', false, array( 'name' => 'ids[' . $row->log_id . ']' ) ); 00361 } 00362 } else { 00363 if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { // If event was hidden from sysops 00364 $del = Linker::revDeleteLinkDisabled( $canHide ); 00365 } else { 00366 $query = array( 00367 'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(), 00368 'type' => 'logging', 00369 'ids' => $row->log_id, 00370 ); 00371 $del = Linker::revDeleteLink( $query, self::isDeleted( $row, LogPage::DELETED_RESTRICTED ), $canHide ); 00372 } 00373 } 00374 } 00375 } 00376 return $del; 00377 } 00378 00386 public static function typeAction( $row, $type, $action, $right='' ) { 00387 $match = is_array($type) ? 00388 in_array( $row->log_type, $type ) : $row->log_type == $type; 00389 if( $match ) { 00390 $match = is_array( $action ) ? 00391 in_array( $row->log_action, $action ) : $row->log_action == $action; 00392 if( $match && $right ) { 00393 global $wgUser; 00394 $match = $wgUser->isAllowed( $right ); 00395 } 00396 } 00397 return $match; 00398 } 00399 00409 public static function userCan( $row, $field, User $user = null ) { 00410 return self::userCanBitfield( $row->log_deleted, $field, $user ); 00411 } 00412 00422 public static function userCanBitfield( $bitfield, $field, User $user = null ) { 00423 if( $bitfield & $field ) { 00424 if ( $bitfield & LogPage::DELETED_RESTRICTED ) { 00425 $permission = 'suppressrevision'; 00426 } else { 00427 $permission = 'deletedhistory'; 00428 } 00429 wfDebug( "Checking for $permission due to $field match on $bitfield\n" ); 00430 if ( $user === null ) { 00431 global $wgUser; 00432 $user = $wgUser; 00433 } 00434 return $user->isAllowed( $permission ); 00435 } else { 00436 return true; 00437 } 00438 } 00439 00445 public static function isDeleted( $row, $field ) { 00446 return ( $row->log_deleted & $field ) == $field; 00447 } 00448 00470 public static function showLogExtract( 00471 &$out, $types=array(), $page='', $user='', $param = array() 00472 ) { 00473 $defaultParameters = array( 00474 'lim' => 25, 00475 'conds' => array(), 00476 'showIfEmpty' => true, 00477 'msgKey' => array(''), 00478 'wrap' => "$1", 00479 'flags' => 0 00480 ); 00481 # The + operator appends elements of remaining keys from the right 00482 # handed array to the left handed, whereas duplicated keys are NOT overwritten. 00483 $param += $defaultParameters; 00484 # Convert $param array to individual variables 00485 $lim = $param['lim']; 00486 $conds = $param['conds']; 00487 $showIfEmpty = $param['showIfEmpty']; 00488 $msgKey = $param['msgKey']; 00489 $wrap = $param['wrap']; 00490 $flags = $param['flags']; 00491 if ( !is_array( $msgKey ) ) { 00492 $msgKey = array( $msgKey ); 00493 } 00494 00495 if ( $out instanceof OutputPage ) { 00496 $context = $out->getContext(); 00497 } else { 00498 $context = RequestContext::getMain(); 00499 } 00500 00501 # Insert list of top 50 (or top $lim) items 00502 $loglist = new LogEventsList( $context, null, $flags ); 00503 $pager = new LogPager( $loglist, $types, $user, $page, '', $conds ); 00504 if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset 00505 $pager->setOffset( $param['offset'] ); 00506 } 00507 if( $lim > 0 ) $pager->mLimit = $lim; 00508 $logBody = $pager->getBody(); 00509 $s = ''; 00510 if( $logBody ) { 00511 if ( $msgKey[0] ) { 00512 $s = '<div class="mw-warning-with-logexcerpt">'; 00513 00514 if ( count( $msgKey ) == 1 ) { 00515 $s .= $context->msg( $msgKey[0] )->parseAsBlock(); 00516 } else { // Process additional arguments 00517 $args = $msgKey; 00518 array_shift( $args ); 00519 $s .= $context->msg( $msgKey[0], $args )->parseAsBlock(); 00520 } 00521 } 00522 $s .= $loglist->beginLogEventsList() . 00523 $logBody . 00524 $loglist->endLogEventsList(); 00525 } else { 00526 if ( $showIfEmpty ) { 00527 $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ), 00528 $context->msg( 'logempty' )->parse() ); 00529 } 00530 } 00531 if( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link 00532 $urlParam = array(); 00533 if ( $page instanceof Title ) { 00534 $urlParam['page'] = $page->getPrefixedDBkey(); 00535 } elseif ( $page != '' ) { 00536 $urlParam['page'] = $page; 00537 } 00538 if ( $user != '') 00539 $urlParam['user'] = $user; 00540 if ( !is_array( $types ) ) # Make it an array, if it isn't 00541 $types = array( $types ); 00542 # If there is exactly one log type, we can link to Special:Log?type=foo 00543 if ( count( $types ) == 1 ) 00544 $urlParam['type'] = $types[0]; 00545 $s .= Linker::link( 00546 SpecialPage::getTitleFor( 'Log' ), 00547 $context->msg( 'log-fulllog' )->escaped(), 00548 array(), 00549 $urlParam 00550 ); 00551 } 00552 if ( $logBody && $msgKey[0] ) { 00553 $s .= '</div>'; 00554 } 00555 00556 if ( $wrap != '' ) { // Wrap message in html 00557 $s = str_replace( '$1', $s, $wrap ); 00558 } 00559 00560 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */ 00561 if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) { 00562 // $out can be either an OutputPage object or a String-by-reference 00563 if ( $out instanceof OutputPage ){ 00564 $out->addHTML( $s ); 00565 } else { 00566 $out = $s; 00567 } 00568 } 00569 00570 return $pager->getNumRows(); 00571 } 00572 00580 public static function getExcludeClause( $db, $audience = 'public' ) { 00581 global $wgLogRestrictions, $wgUser; 00582 // Reset the array, clears extra "where" clauses when $par is used 00583 $hiddenLogs = array(); 00584 // Don't show private logs to unprivileged users 00585 foreach( $wgLogRestrictions as $logType => $right ) { 00586 if( $audience == 'public' || !$wgUser->isAllowed($right) ) { 00587 $safeType = $db->strencode( $logType ); 00588 $hiddenLogs[] = $safeType; 00589 } 00590 } 00591 if( count($hiddenLogs) == 1 ) { 00592 return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] ); 00593 } elseif( $hiddenLogs ) { 00594 return 'log_type NOT IN (' . $db->makeList($hiddenLogs) . ')'; 00595 } 00596 return false; 00597 } 00598 }