MediaWiki  REL1_21
LogEventsList.php
Go to the documentation of this file.
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( '&#160;', $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                 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
00311                         $entry->getTimestamp(), $this->getUser() ) );
00312 
00313                 $action = $formatter->getActionText();
00314 
00315                 if ( $this->flags & self::NO_ACTION_LINK ) {
00316                         $revert = '';
00317                 } else {
00318                         $revert = $formatter->getActionLinks();
00319                         if ( $revert != '' ) {
00320                                 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
00321                         }
00322                 }
00323 
00324                 $comment = $formatter->getComment();
00325 
00326                 // Some user can hide log items and have review links
00327                 $del = $this->getShowHideLinks( $row );
00328 
00329                 // Any tags...
00330                 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
00331                 $classes = array_merge(
00332                         array( 'mw-logline-' . $entry->getType() ),
00333                         $newClasses
00334                 );
00335 
00336                 return Html::rawElement( 'li', array( 'class' => $classes ),
00337                         "$del $time $action $comment $revert $tagDisplay" ) . "\n";
00338         }
00339 
00344         private function getShowHideLinks( $row ) {
00345                 if( ( $this->flags == self::NO_ACTION_LINK ) // we don't want to see the links
00346                         || $row->log_type == 'suppress' ) { // no one can hide items from the suppress log
00347                         return '';
00348                 }
00349                 $del = '';
00350                 $user = $this->getUser();
00351                 // Don't show useless checkbox to people who cannot hide log entries
00352                 if( $user->isAllowed( 'deletedhistory' ) ) {
00353                         $canHide = $user->isAllowed( 'deletelogentry' );
00354                         if( $row->log_deleted || $canHide ) {
00355                                 if ( $canHide && $this->flags & self::USE_REVDEL_CHECKBOXES ) { // Show checkboxes instead of links.
00356                                         if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { // If event was hidden from sysops
00357                                                 $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) );
00358                                         } else {
00359                                                 $del = Xml::check( 'showhiderevisions', false, array( 'name' => 'ids[' . $row->log_id . ']' ) );
00360                                         }
00361                                 } else {
00362                                         if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) { // If event was hidden from sysops
00363                                                 $del = Linker::revDeleteLinkDisabled( $canHide );
00364                                         } else {
00365                                                 $query = array(
00366                                                         'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(),
00367                                                         'type'   => 'logging',
00368                                                         'ids'    => $row->log_id,
00369                                                 );
00370                                                 $del = Linker::revDeleteLink( $query, self::isDeleted( $row, LogPage::DELETED_RESTRICTED ), $canHide );
00371                                         }
00372                                 }
00373                         }
00374                 }
00375                 return $del;
00376         }
00377 
00385         public static function typeAction( $row, $type, $action, $right = '' ) {
00386                 $match = is_array( $type ) ?
00387                         in_array( $row->log_type, $type ) : $row->log_type == $type;
00388                 if( $match ) {
00389                         $match = is_array( $action ) ?
00390                                 in_array( $row->log_action, $action ) : $row->log_action == $action;
00391                         if( $match && $right ) {
00392                                 global $wgUser;
00393                                 $match = $wgUser->isAllowed( $right );
00394                         }
00395                 }
00396                 return $match;
00397         }
00398 
00408         public static function userCan( $row, $field, User $user = null ) {
00409                 return self::userCanBitfield( $row->log_deleted, $field, $user );
00410         }
00411 
00421         public static function userCanBitfield( $bitfield, $field, User $user = null ) {
00422                 if( $bitfield & $field ) {
00423                         if ( $bitfield & LogPage::DELETED_RESTRICTED ) {
00424                                 $permission = 'suppressrevision';
00425                         } else {
00426                                 $permission = 'deletedhistory';
00427                         }
00428                         wfDebug( "Checking for $permission due to $field match on $bitfield\n" );
00429                         if ( $user === null ) {
00430                                 global $wgUser;
00431                                 $user = $wgUser;
00432                         }
00433                         return $user->isAllowed( $permission );
00434                 } else {
00435                         return true;
00436                 }
00437         }
00438 
00444         public static function isDeleted( $row, $field ) {
00445                 return ( $row->log_deleted & $field ) == $field;
00446         }
00447 
00469         public static function showLogExtract(
00470                 &$out, $types=array(), $page = '', $user = '', $param = array()
00471         ) {
00472                 $defaultParameters = array(
00473                         'lim' => 25,
00474                         'conds' => array(),
00475                         'showIfEmpty' => true,
00476                         'msgKey' => array( '' ),
00477                         'wrap' => "$1",
00478                         'flags' => 0
00479                 );
00480                 # The + operator appends elements of remaining keys from the right
00481                 # handed array to the left handed, whereas duplicated keys are NOT overwritten.
00482                 $param += $defaultParameters;
00483                 # Convert $param array to individual variables
00484                 $lim = $param['lim'];
00485                 $conds = $param['conds'];
00486                 $showIfEmpty = $param['showIfEmpty'];
00487                 $msgKey = $param['msgKey'];
00488                 $wrap = $param['wrap'];
00489                 $flags = $param['flags'];
00490                 if ( !is_array( $msgKey ) ) {
00491                         $msgKey = array( $msgKey );
00492                 }
00493 
00494                 if ( $out instanceof OutputPage ) {
00495                         $context = $out->getContext();
00496                 } else {
00497                         $context = RequestContext::getMain();
00498                 }
00499 
00500                 # Insert list of top 50 (or top $lim) items
00501                 $loglist = new LogEventsList( $context, null, $flags );
00502                 $pager = new LogPager( $loglist, $types, $user, $page, '', $conds );
00503                 if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset
00504                         $pager->setOffset( $param['offset'] );
00505                 }
00506                 if( $lim > 0 ) $pager->mLimit = $lim;
00507                 $logBody = $pager->getBody();
00508                 $s = '';
00509                 if( $logBody ) {
00510                         if ( $msgKey[0] ) {
00511                                 $s = '<div class="mw-warning-with-logexcerpt">';
00512 
00513                                 if ( count( $msgKey ) == 1 ) {
00514                                         $s .= $context->msg( $msgKey[0] )->parseAsBlock();
00515                                 } else { // Process additional arguments
00516                                         $args = $msgKey;
00517                                         array_shift( $args );
00518                                         $s .= $context->msg( $msgKey[0], $args )->parseAsBlock();
00519                                 }
00520                         }
00521                         $s .= $loglist->beginLogEventsList() .
00522                                 $logBody .
00523                                 $loglist->endLogEventsList();
00524                 } else {
00525                         if ( $showIfEmpty ) {
00526                                 $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ),
00527                                         $context->msg( 'logempty' )->parse() );
00528                         }
00529                 }
00530                 if( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link
00531                         $urlParam = array();
00532                         if ( $page instanceof Title ) {
00533                                 $urlParam['page'] = $page->getPrefixedDBkey();
00534                         } elseif ( $page != '' ) {
00535                                 $urlParam['page'] = $page;
00536                         }
00537                         if ( $user != '' )
00538                                 $urlParam['user'] = $user;
00539                         if ( !is_array( $types ) ) # Make it an array, if it isn't
00540                                 $types = array( $types );
00541                         # If there is exactly one log type, we can link to Special:Log?type=foo
00542                         if ( count( $types ) == 1 )
00543                                 $urlParam['type'] = $types[0];
00544                         $s .= Linker::link(
00545                                 SpecialPage::getTitleFor( 'Log' ),
00546                                 $context->msg( 'log-fulllog' )->escaped(),
00547                                 array(),
00548                                 $urlParam
00549                         );
00550                 }
00551                 if ( $logBody && $msgKey[0] ) {
00552                         $s .= '</div>';
00553                 }
00554 
00555                 if ( $wrap != '' ) { // Wrap message in html
00556                         $s = str_replace( '$1', $s, $wrap );
00557                 }
00558 
00559                 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */
00560                 if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) {
00561                         // $out can be either an OutputPage object or a String-by-reference
00562                         if ( $out instanceof OutputPage ) {
00563                                 $out->addHTML( $s );
00564                         } else {
00565                                 $out = $s;
00566                         }
00567                 }
00568 
00569                 return $pager->getNumRows();
00570         }
00571 
00580         public static function getExcludeClause( $db, $audience = 'public', User $user = null ) {
00581                 global $wgLogRestrictions;
00582 
00583                 if ( $audience != 'public' && $user === null ) {
00584                         global $wgUser;
00585                         $user = $wgUser;
00586                 }
00587 
00588                 // Reset the array, clears extra "where" clauses when $par is used
00589                 $hiddenLogs = array();
00590 
00591                 // Don't show private logs to unprivileged users
00592                 foreach( $wgLogRestrictions as $logType => $right ) {
00593                         if( $audience == 'public' || !$user->isAllowed( $right ) ) {
00594                                 $hiddenLogs[] = $logType;
00595                         }
00596                 }
00597                 if( count( $hiddenLogs ) == 1 ) {
00598                         return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] );
00599                 } elseif( $hiddenLogs ) {
00600                         return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) . ')';
00601                 }
00602                 return false;
00603         }
00604 }