MediaWiki  REL1_19
LogEventsList.php
Go to the documentation of this file.
00001 <?php
00026 class LogEventsList {
00027         const NO_ACTION_LINK = 1;
00028         const NO_EXTRA_USER_LINKS = 2;
00029 
00033         private $skin;
00034 
00038         private $out;
00039         public $flags;
00040 
00044         protected $message;
00045 
00049         protected $mDefaultQuery;
00050 
00051         public function __construct( $skin, $out, $flags = 0 ) {
00052                 $this->skin = $skin;
00053                 $this->out = $out;
00054                 $this->flags = $flags;
00055                 $this->preCacheMessages();
00056         }
00057 
00062         private function preCacheMessages() {
00063                 // Precache various messages
00064                 if( !isset( $this->message ) ) {
00065                         $messages = array( 'revertmerge', 'protect_change', 'unblocklink', 'change-blocklink',
00066                                 'revertmove', 'undeletelink', 'undeleteviewlink', 'revdel-restore', 'hist', 'diff',
00067                                 'pipe-separator', 'revdel-restore-deleted', 'revdel-restore-visible' );
00068                         foreach( $messages as $msg ) {
00069                                 $this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) );
00070                         }
00071                 }
00072         }
00073 
00079         public function showHeader( $type ) {
00080                 wfDeprecated( __METHOD__, '1.19' );
00081                 // If only one log type is used, then show a special message...
00082                 $headerType = (count($type) == 1) ? $type[0] : '';
00083                 if( LogPage::isLogType( $headerType ) ) {
00084                         $page = new LogPage( $headerType );
00085                         $this->out->setPageTitle( $page->getName()->text() );
00086                         $this->out->addHTML( $page->getDescription()->parseAsBlock() );
00087                 } else {
00088                         $this->out->addHTML( wfMsgExt('alllogstext',array('parseinline')) );
00089                 }
00090         }
00091 
00104         public function showOptions( $types=array(), $user='', $page='', $pattern='', $year='',
00105                 $month = '', $filter = null, $tagFilter='' ) {
00106                 global $wgScript, $wgMiserMode;
00107 
00108                 $action = $wgScript;
00109                 $title = SpecialPage::getTitleFor( 'Log' );
00110                 $special = $title->getPrefixedDBkey();
00111 
00112                 // For B/C, we take strings, but make sure they are converted...
00113                 $types = ($types === '') ? array() : (array)$types;
00114 
00115                 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter );
00116 
00117                 $html = Html::hidden( 'title', $special );
00118 
00119                 // Basic selectors
00120                 $html .= $this->getTypeMenu( $types ) . "\n";
00121                 $html .= $this->getUserInput( $user ) . "\n";
00122                 $html .= $this->getTitleInput( $page ) . "\n";
00123                 $html .= $this->getExtraInputs( $types ) . "\n";
00124 
00125                 // Title pattern, if allowed
00126                 if (!$wgMiserMode) {
00127                         $html .= $this->getTitlePattern( $pattern ) . "\n";
00128                 }
00129 
00130                 // date menu
00131                 $html .= Xml::tags( 'p', null, Xml::dateMenu( $year, $month ) );
00132 
00133                 // Tag filter
00134                 if ($tagSelector) {
00135                         $html .= Xml::tags( 'p', null, implode( '&#160;', $tagSelector ) );
00136                 }
00137 
00138                 // Filter links
00139                 if ($filter) {
00140                         $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) );
00141                 }
00142 
00143                 // Submit button
00144                 $html .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
00145 
00146                 // Fieldset
00147                 $html = Xml::fieldset( wfMsg( 'log' ), $html );
00148 
00149                 // Form wrapping
00150                 $html = Xml::tags( 'form', array( 'action' => $action, 'method' => 'get' ), $html );
00151 
00152                 $this->out->addHTML( $html );
00153         }
00154 
00159         private function getFilterLinks( $filter ) {
00160                 global $wgLang;
00161                 // show/hide links
00162                 $messages = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
00163                 // Option value -> message mapping
00164                 $links = array();
00165                 $hiddens = ''; // keep track for "go" button
00166                 foreach( $filter as $type => $val ) {
00167                         // Should the below assignment be outside the foreach?
00168                         // Then it would have to be copied. Not certain what is more expensive.
00169                         $query = $this->getDefaultQuery();
00170                         $queryKey = "hide_{$type}_log";
00171 
00172                         $hideVal = 1 - intval($val);
00173                         $query[$queryKey] = $hideVal;
00174 
00175                         $link = Linker::link(
00176                                 $this->getDisplayTitle(),
00177                                 $messages[$hideVal],
00178                                 array(),
00179                                 $query,
00180                                 array( 'known', 'noclasses' )
00181                         );
00182 
00183                         $links[$type] = wfMsgHtml( "log-show-hide-{$type}", $link );
00184                         $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n";
00185                 }
00186                 // Build links
00187                 return '<small>'.$wgLang->pipeList( $links ) . '</small>' . $hiddens;
00188         }
00189 
00190         private function getDefaultQuery() {
00191                 global $wgRequest;
00192 
00193                 if ( !isset( $this->mDefaultQuery ) ) {
00194                         $this->mDefaultQuery = $wgRequest->getQueryValues();
00195                         unset( $this->mDefaultQuery['title'] );
00196                         unset( $this->mDefaultQuery['dir'] );
00197                         unset( $this->mDefaultQuery['offset'] );
00198                         unset( $this->mDefaultQuery['limit'] );
00199                         unset( $this->mDefaultQuery['order'] );
00200                         unset( $this->mDefaultQuery['month'] );
00201                         unset( $this->mDefaultQuery['year'] );
00202                 }
00203                 return $this->mDefaultQuery;
00204         }
00205 
00212         public function getDisplayTitle() {
00213                 return $this->out->getTitle();
00214         }
00215 
00216         public function getContext() {
00217                 return $this->out->getContext();
00218         }
00219 
00224         private function getTypeMenu( $queryTypes ) {
00225                 $queryType = count($queryTypes) == 1 ? $queryTypes[0] : '';
00226                 $selector = $this->getTypeSelector();
00227                 $selector->setDefault( $queryType );
00228                 return $selector->getHtml();
00229         }
00230 
00236         public function getTypeSelector() {
00237                 global $wgUser;
00238 
00239                 $typesByName = array(); // Temporary array
00240                 // First pass to load the log names
00241                 foreach(  LogPage::validTypes() as $type ) {
00242                         $page = new LogPage( $type );
00243                         $restriction = $page->getRestriction();
00244                         if ( $wgUser->isAllowed( $restriction ) ) {
00245                                 $typesByName[$type] = $page->getName()->text();
00246                         }
00247                 }
00248 
00249                 // Second pass to sort by name
00250                 asort($typesByName);
00251 
00252                 // Always put "All public logs" on top
00253                 $public = $typesByName[''];
00254                 unset( $typesByName[''] );
00255                 $typesByName = array( '' => $public ) + $typesByName;
00256 
00257                 $select = new XmlSelect( 'type' );
00258                 foreach( $typesByName as $type => $name ) {
00259                         $select->addOption( $name, $type );
00260                 }
00261 
00262                 return $select;
00263         }
00264 
00269         private function getUserInput( $user ) {
00270                 return '<span style="white-space: nowrap">' .
00271                         Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'mw-log-user', 15, $user ) .
00272                         '</span>';
00273         }
00274 
00279         private function getTitleInput( $title ) {
00280                 return '<span style="white-space: nowrap">' .
00281                         Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'mw-log-page', 20, $title ) .
00282                         '</span>';
00283         }
00284 
00289         private function getTitlePattern( $pattern ) {
00290                 return '<span style="white-space: nowrap">' .
00291                         Xml::checkLabel( wfMsg( 'log-title-wildcard' ), 'pattern', 'pattern', $pattern ) .
00292                         '</span>';
00293         }
00294 
00299         private function getExtraInputs( $types ) {
00300                 global $wgRequest;
00301                 $offender = $wgRequest->getVal('offender');
00302                 $user = User::newFromName( $offender, false );
00303                 if( !$user || ($user->getId() == 0 && !IP::isIPAddress($offender) ) ) {
00304                         $offender = ''; // Blank field if invalid
00305                 }
00306                 if( count($types) == 1 && $types[0] == 'suppress' ) {
00307                         return Xml::inputLabel( wfMsg('revdelete-offender'), 'offender',
00308                                 'mw-log-offender', 20, $offender );
00309                 }
00310                 return '';
00311         }
00312 
00316         public function beginLogEventsList() {
00317                 return "<ul>\n";
00318         }
00319 
00323         public function endLogEventsList() {
00324                 return "</ul>\n";
00325         }
00326 
00331         public function logLine( $row ) {
00332                 $entry = DatabaseLogEntry::newFromRow( $row );
00333                 $formatter = LogFormatter::newFromEntry( $entry );
00334                 $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) );
00335 
00336                 $action = $formatter->getActionText();
00337                 $comment = $formatter->getComment();
00338 
00339                 $classes = array( 'mw-logline-' . $entry->getType() );
00340                 $title = $entry->getTarget();
00341                 $time = $this->logTimestamp( $entry );
00342 
00343                 // Extract extra parameters
00344                 $paramArray = LogPage::extractParams( $row->log_params );
00345                 // Add review/revert links and such...
00346                 $revert = $this->logActionLinks( $row, $title, $paramArray, $comment );
00347 
00348                 // Some user can hide log items and have review links
00349                 $del = $this->getShowHideLinks( $row );
00350                 if( $del != '' ) $del .= ' ';
00351 
00352                 // Any tags...
00353                 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
00354                 $classes = array_merge( $classes, $newClasses );
00355 
00356                 return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ),
00357                         $del . "$time $action $comment $revert $tagDisplay" ) . "\n";
00358         }
00359 
00360         private function logTimestamp( LogEntry $entry ) {
00361                 global $wgLang;
00362                 $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $entry->getTimestamp() ), true );
00363                 return htmlspecialchars( $time );
00364         }
00365 
00375         private function logActionLinks( $row, $title, $paramArray, &$comment ) {
00376                 global $wgUser;
00377                 if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the action
00378                         || self::isDeleted( $row, LogPage::DELETED_ACTION ) ) // action is hidden
00379                 {
00380                         return '';
00381                 }
00382                 $revert = '';
00383                 if( self::typeAction( $row, 'move', 'move', 'move' ) && !empty( $paramArray[0] ) ) {
00384                         $destTitle = Title::newFromText( $paramArray[0] );
00385                         if( $destTitle ) {
00386                                 $revert = '(' . Linker::link(
00387                                         SpecialPage::getTitleFor( 'Movepage' ),
00388                                         $this->message['revertmove'],
00389                                         array(),
00390                                         array(
00391                                                 'wpOldTitle' => $destTitle->getPrefixedDBkey(),
00392                                                 'wpNewTitle' => $title->getPrefixedDBkey(),
00393                                                 'wpReason'   => wfMsgForContent( 'revertmove' ),
00394                                                 'wpMovetalk' => 0
00395                                         ),
00396                                         array( 'known', 'noclasses' )
00397                                 ) . ')';
00398                         }
00399                 // Show undelete link
00400                 } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'delete', 'deletedhistory' ) ) {
00401                         if( !$wgUser->isAllowed( 'undelete' ) ) {
00402                                 $viewdeleted = $this->message['undeleteviewlink'];
00403                         } else {
00404                                 $viewdeleted = $this->message['undeletelink'];
00405                         }
00406                         $revert = '(' . Linker::link(
00407                                 SpecialPage::getTitleFor( 'Undelete' ),
00408                                 $viewdeleted,
00409                                 array(),
00410                                 array( 'target' => $title->getPrefixedDBkey() ),
00411                                 array( 'known', 'noclasses' )
00412                          ) . ')';
00413                 // Show unblock/change block link
00414                 } elseif( self::typeAction( $row, array( 'block', 'suppress' ), array( 'block', 'reblock' ), 'block' ) ) {
00415                         $revert = '(' .
00416                                 Linker::link(
00417                                         SpecialPage::getTitleFor( 'Unblock', $row->log_title ),
00418                                         $this->message['unblocklink'],
00419                                         array(),
00420                                         array(),
00421                                         'known'
00422                                 ) .
00423                                 $this->message['pipe-separator'] .
00424                                 Linker::link(
00425                                         SpecialPage::getTitleFor( 'Block', $row->log_title ),
00426                                         $this->message['change-blocklink'],
00427                                         array(),
00428                                         array(),
00429                                         'known'
00430                                 ) .
00431                                 ')';
00432                 // Show change protection link
00433                 } elseif( self::typeAction( $row, 'protect', array( 'modify', 'protect', 'unprotect' ) ) ) {
00434                         $revert .= ' (' .
00435                                 Linker::link( $title,
00436                                         $this->message['hist'],
00437                                         array(),
00438                                         array(
00439                                                 'action' => 'history',
00440                                                 'offset' => $row->log_timestamp
00441                                         )
00442                                 );
00443                         if( $wgUser->isAllowed( 'protect' ) ) {
00444                                 $revert .= $this->message['pipe-separator'] .
00445                                         Linker::link( $title,
00446                                                 $this->message['protect_change'],
00447                                                 array(),
00448                                                 array( 'action' => 'protect' ),
00449                                                 'known' );
00450                         }
00451                         $revert .= ')';
00452                 // Show unmerge link
00453                 } elseif( self::typeAction( $row, 'merge', 'merge', 'mergehistory' ) ) {
00454                         $revert = '(' . Linker::link(
00455                                 SpecialPage::getTitleFor( 'MergeHistory' ),
00456                                 $this->message['revertmerge'],
00457                                 array(),
00458                                 array(
00459                                         'target' => $paramArray[0],
00460                                         'dest' => $title->getPrefixedDBkey(),
00461                                         'mergepoint' => $paramArray[1]
00462                                 ),
00463                                 array( 'known', 'noclasses' )
00464                         ) . ')';
00465                 // If an edit was hidden from a page give a review link to the history
00466                 } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'revision', 'deletedhistory' ) ) {
00467                         $revert = RevisionDeleter::getLogLinks( $title, $paramArray,
00468                                                                 $this->message );
00469                 // Hidden log items, give review link
00470                 } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'event', 'deletedhistory' ) ) {
00471                         if( count($paramArray) >= 1 ) {
00472                                 $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
00473                                 // $paramArray[1] is a CSV of the IDs
00474                                 $query = $paramArray[0];
00475                                 // Link to each hidden object ID, $paramArray[1] is the url param
00476                                 $revert = '(' . Linker::link(
00477                                         $revdel,
00478                                         $this->message['revdel-restore'],
00479                                         array(),
00480                                         array(
00481                                                 'target' => $title->getPrefixedText(),
00482                                                 'type' => 'logging',
00483                                                 'ids' => $query
00484                                         ),
00485                                         array( 'known', 'noclasses' )
00486                                 ) . ')';
00487                         }
00488                 // Do nothing. The implementation is handled by the hook modifiying the passed-by-ref parameters.
00489                 } else {
00490                         wfRunHooks( 'LogLine', array( $row->log_type, $row->log_action, $title, $paramArray,
00491                                 &$comment, &$revert, $row->log_timestamp ) );
00492                 }
00493                 if( $revert != '' ) {
00494                         $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
00495                 }
00496                 return $revert;
00497         }
00498 
00503         private function getShowHideLinks( $row ) {
00504                 global $wgUser;
00505                 if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the links
00506                         || $row->log_type == 'suppress' ) { // no one can hide items from the suppress log
00507                         return '';
00508                 }
00509                 $del = '';
00510                 // Don't show useless link to people who cannot hide revisions
00511                 if( $wgUser->isAllowed( 'deletedhistory' ) ) {
00512                         if( $row->log_deleted || $wgUser->isAllowed( 'deleterevision' ) ) {
00513                                 $canHide = $wgUser->isAllowed( 'deleterevision' );
00514                                 // If event was hidden from sysops
00515                                 if( !self::userCan( $row, LogPage::DELETED_RESTRICTED ) ) {
00516                                         $del = Linker::revDeleteLinkDisabled( $canHide );
00517                                 } else {
00518                                         $target = SpecialPage::getTitleFor( 'Log', $row->log_type );
00519                                         $query = array(
00520                                                 'target' => $target->getPrefixedDBkey(),
00521                                                 'type'   => 'logging',
00522                                                 'ids'    => $row->log_id,
00523                                         );
00524                                         $del = Linker::revDeleteLink( $query,
00525                                                 self::isDeleted( $row, LogPage::DELETED_RESTRICTED ), $canHide );
00526                                 }
00527                         }
00528                 }
00529                 return $del;
00530         }
00531 
00539         public static function typeAction( $row, $type, $action, $right='' ) {
00540                 $match = is_array($type) ?
00541                         in_array( $row->log_type, $type ) : $row->log_type == $type;
00542                 if( $match ) {
00543                         $match = is_array( $action ) ?
00544                                 in_array( $row->log_action, $action ) : $row->log_action == $action;
00545                         if( $match && $right ) {
00546                                 global $wgUser;
00547                                 $match = $wgUser->isAllowed( $right );
00548                         }
00549                 }
00550                 return $match;
00551         }
00552 
00562         public static function userCan( $row, $field, User $user = null ) {
00563                 return self::userCanBitfield( $row->log_deleted, $field, $user );
00564         }
00565 
00575         public static function userCanBitfield( $bitfield, $field, User $user = null ) {
00576                 if( $bitfield & $field ) {
00577                         if ( $bitfield & LogPage::DELETED_RESTRICTED ) {
00578                                 $permission = 'suppressrevision';
00579                         } else {
00580                                 $permission = 'deletedhistory';
00581                         }
00582                         wfDebug( "Checking for $permission due to $field match on $bitfield\n" );
00583                         if ( $user === null ) {
00584                                 global $wgUser;
00585                                 $user = $wgUser;
00586                         }
00587                         return $user->isAllowed( $permission );
00588                 } else {
00589                         return true;
00590                 }
00591         }
00592 
00598         public static function isDeleted( $row, $field ) {
00599                 return ( $row->log_deleted & $field ) == $field;
00600         }
00601 
00623         public static function showLogExtract(
00624                 &$out, $types=array(), $page='', $user='', $param = array()
00625         ) {
00626                 $defaultParameters = array(
00627                         'lim' => 25,
00628                         'conds' => array(),
00629                         'showIfEmpty' => true,
00630                         'msgKey' => array(''),
00631                         'wrap' => "$1",
00632                         'flags' => 0
00633                 );
00634                 # The + operator appends elements of remaining keys from the right
00635                 # handed array to the left handed, whereas duplicated keys are NOT overwritten.
00636                 $param += $defaultParameters;
00637                 # Convert $param array to individual variables
00638                 $lim = $param['lim'];
00639                 $conds = $param['conds'];
00640                 $showIfEmpty = $param['showIfEmpty'];
00641                 $msgKey = $param['msgKey'];
00642                 $wrap = $param['wrap'];
00643                 $flags = $param['flags'];
00644                 if ( !is_array( $msgKey ) ) {
00645                         $msgKey = array( $msgKey );
00646                 }
00647 
00648                 if ( $out instanceof OutputPage ) {
00649                         $context = $out->getContext();
00650                 } else {
00651                         $context = RequestContext::getMain();
00652                 }
00653 
00654                 # Insert list of top 50 (or top $lim) items
00655                 $loglist = new LogEventsList( $context->getSkin(), $context->getOutput(), $flags );
00656                 $pager = new LogPager( $loglist, $types, $user, $page, '', $conds );
00657                 if ( isset( $param['offset'] ) ) { # Tell pager to ignore $wgRequest offset
00658                         $pager->setOffset( $param['offset'] );
00659                 }
00660                 if( $lim > 0 ) $pager->mLimit = $lim;
00661                 $logBody = $pager->getBody();
00662                 $s = '';
00663                 if( $logBody ) {
00664                         if ( $msgKey[0] ) {
00665                                 $s = '<div class="mw-warning-with-logexcerpt">';
00666 
00667                                 if ( count( $msgKey ) == 1 ) {
00668                                         $s .= wfMsgExt( $msgKey[0], array( 'parse' ) );
00669                                 } else { // Process additional arguments
00670                                         $args = $msgKey;
00671                                         array_shift( $args );
00672                                         $s .= wfMsgExt( $msgKey[0], array( 'parse' ), $args );
00673                                 }
00674                         }
00675                         $s .= $loglist->beginLogEventsList() .
00676                                  $logBody .
00677                                  $loglist->endLogEventsList();
00678                 } else {
00679                         if ( $showIfEmpty ) {
00680                                 $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ),
00681                                         wfMsgExt( 'logempty', array( 'parseinline' ) ) );
00682                         }
00683                 }
00684                 if( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link
00685                         $urlParam = array();
00686                         if ( $page instanceof Title ) {
00687                                 $urlParam['page'] = $page->getPrefixedDBkey();
00688                         } elseif ( $page != '' ) {
00689                                 $urlParam['page'] = $page;
00690                         }
00691                         if ( $user != '')
00692                                 $urlParam['user'] = $user;
00693                         if ( !is_array( $types ) ) # Make it an array, if it isn't
00694                                 $types = array( $types );
00695                         # If there is exactly one log type, we can link to Special:Log?type=foo
00696                         if ( count( $types ) == 1 )
00697                                 $urlParam['type'] = $types[0];
00698                         $s .= Linker::link(
00699                                 SpecialPage::getTitleFor( 'Log' ),
00700                                 wfMsgHtml( 'log-fulllog' ),
00701                                 array(),
00702                                 $urlParam
00703                         );
00704                 }
00705                 if ( $logBody && $msgKey[0] ) {
00706                         $s .= '</div>';
00707                 }
00708 
00709                 if ( $wrap != '' ) { // Wrap message in html
00710                         $s = str_replace( '$1', $s, $wrap );
00711                 }
00712 
00713                 /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */
00714                 if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) {
00715                         // $out can be either an OutputPage object or a String-by-reference
00716                         if ( $out instanceof OutputPage ){
00717                                 $out->addHTML( $s );
00718                         } else {
00719                                 $out = $s;
00720                         }
00721                 }
00722 
00723                 return $pager->getNumRows();
00724         }
00725 
00733         public static function getExcludeClause( $db, $audience = 'public' ) {
00734                 global $wgLogRestrictions, $wgUser;
00735                 // Reset the array, clears extra "where" clauses when $par is used
00736                 $hiddenLogs = array();
00737                 // Don't show private logs to unprivileged users
00738                 foreach( $wgLogRestrictions as $logType => $right ) {
00739                         if( $audience == 'public' || !$wgUser->isAllowed($right) ) {
00740                                 $safeType = $db->strencode( $logType );
00741                                 $hiddenLogs[] = $safeType;
00742                         }
00743                 }
00744                 if( count($hiddenLogs) == 1 ) {
00745                         return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] );
00746                 } elseif( $hiddenLogs ) {
00747                         return 'log_type NOT IN (' . $db->makeList($hiddenLogs) . ')';
00748                 }
00749                 return false;
00750         }
00751  }