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