MediaWiki  master
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                 $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  }