MediaWiki  REL1_23
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 
00076     public function showHeader( $type ) {
00077         wfDeprecated( __METHOD__, '1.19' );
00078         // If only one log type is used, then show a special message...
00079         $headerType = count( $type ) == 1 ? $type[0] : '';
00080         $out = $this->getOutput();
00081         if ( LogPage::isLogType( $headerType ) ) {
00082             $page = new LogPage( $headerType );
00083             $out->setPageTitle( $page->getName()->text() );
00084             $out->addHTML( $page->getDescription()->parseAsBlock() );
00085         } else {
00086             $out->addHTML( $this->msg( 'alllogstext' )->parse() );
00087         }
00088     }
00089 
00102     public function showOptions( $types = array(), $user = '', $page = '', $pattern = '', $year = 0,
00103         $month = 0, $filter = null, $tagFilter = ''
00104     ) {
00105         global $wgScript, $wgMiserMode;
00106 
00107         $title = SpecialPage::getTitleFor( 'Log' );
00108 
00109         // For B/C, we take strings, but make sure they are converted...
00110         $types = ( $types === '' ) ? array() : (array)$types;
00111 
00112         $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter );
00113 
00114         $html = Html::hidden( 'title', $title->getPrefixedDBkey() );
00115 
00116         // Basic selectors
00117         $html .= $this->getTypeMenu( $types ) . "\n";
00118         $html .= $this->getUserInput( $user ) . "\n";
00119         $html .= $this->getTitleInput( $page ) . "\n";
00120         $html .= $this->getExtraInputs( $types ) . "\n";
00121 
00122         // Title pattern, if allowed
00123         if ( !$wgMiserMode ) {
00124             $html .= $this->getTitlePattern( $pattern ) . "\n";
00125         }
00126 
00127         // date menu
00128         $html .= Xml::tags( 'p', null, Xml::dateMenu( (int)$year, (int)$month ) );
00129 
00130         // Tag filter
00131         if ( $tagSelector ) {
00132             $html .= Xml::tags( 'p', null, implode( '&#160;', $tagSelector ) );
00133         }
00134 
00135         // Filter links
00136         if ( $filter ) {
00137             $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) );
00138         }
00139 
00140         // Submit button
00141         $html .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() );
00142 
00143         // Fieldset
00144         $html = Xml::fieldset( $this->msg( 'log' )->text(), $html );
00145 
00146         // Form wrapping
00147         $html = Xml::tags( 'form', array( 'action' => $wgScript, 'method' => 'get' ), $html );
00148 
00149         $this->getOutput()->addHTML( $html );
00150     }
00151 
00156     private function getFilterLinks( $filter ) {
00157         // show/hide links
00158         $messages = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() );
00159         // Option value -> message mapping
00160         $links = array();
00161         $hiddens = ''; // keep track for "go" button
00162         foreach ( $filter as $type => $val ) {
00163             // Should the below assignment be outside the foreach?
00164             // Then it would have to be copied. Not certain what is more expensive.
00165             $query = $this->getDefaultQuery();
00166             $queryKey = "hide_{$type}_log";
00167 
00168             $hideVal = 1 - intval( $val );
00169             $query[$queryKey] = $hideVal;
00170 
00171             $link = Linker::linkKnown(
00172                 $this->getTitle(),
00173                 $messages[$hideVal],
00174                 array(),
00175                 $query
00176             );
00177 
00178             // Message: log-show-hide-patrol
00179             $links[$type] = $this->msg( "log-show-hide-{$type}" )->rawParams( $link )->escaped();
00180             $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n";
00181         }
00182 
00183         // Build links
00184         return '<small>' . $this->getLanguage()->pipeList( $links ) . '</small>' . $hiddens;
00185     }
00186 
00187     private function getDefaultQuery() {
00188         if ( !isset( $this->mDefaultQuery ) ) {
00189             $this->mDefaultQuery = $this->getRequest()->getQueryValues();
00190             unset( $this->mDefaultQuery['title'] );
00191             unset( $this->mDefaultQuery['dir'] );
00192             unset( $this->mDefaultQuery['offset'] );
00193             unset( $this->mDefaultQuery['limit'] );
00194             unset( $this->mDefaultQuery['order'] );
00195             unset( $this->mDefaultQuery['month'] );
00196             unset( $this->mDefaultQuery['year'] );
00197         }
00198 
00199         return $this->mDefaultQuery;
00200     }
00201 
00206     private function getTypeMenu( $queryTypes ) {
00207         $queryType = count( $queryTypes ) == 1 ? $queryTypes[0] : '';
00208         $selector = $this->getTypeSelector();
00209         $selector->setDefault( $queryType );
00210 
00211         return $selector->getHtml();
00212     }
00213 
00219     public function getTypeSelector() {
00220         $typesByName = array(); // Temporary array
00221         // First pass to load the log names
00222         foreach ( LogPage::validTypes() as $type ) {
00223             $page = new LogPage( $type );
00224             $restriction = $page->getRestriction();
00225             if ( $this->getUser()->isAllowed( $restriction ) ) {
00226                 $typesByName[$type] = $page->getName()->text();
00227             }
00228         }
00229 
00230         // Second pass to sort by name
00231         asort( $typesByName );
00232 
00233         // Always put "All public logs" on top
00234         $public = $typesByName[''];
00235         unset( $typesByName[''] );
00236         $typesByName = array( '' => $public ) + $typesByName;
00237 
00238         $select = new XmlSelect( 'type' );
00239         foreach ( $typesByName as $type => $name ) {
00240             $select->addOption( $name, $type );
00241         }
00242 
00243         return $select;
00244     }
00245 
00250     private function getUserInput( $user ) {
00251         $label = Xml::inputLabel(
00252             $this->msg( 'specialloguserlabel' )->text(),
00253             'user',
00254             'mw-log-user',
00255             15,
00256             $user
00257         );
00258 
00259         return '<span style="white-space: nowrap">' . $label . '</span>';
00260     }
00261 
00266     private function getTitleInput( $title ) {
00267         $label = Xml::inputLabel(
00268             $this->msg( 'speciallogtitlelabel' )->text(),
00269             'page',
00270             'mw-log-page',
00271             20,
00272             $title
00273         );
00274 
00275         return '<span style="white-space: nowrap">' . $label .  '</span>';
00276     }
00277 
00282     private function getTitlePattern( $pattern ) {
00283         return '<span style="white-space: nowrap">' .
00284             Xml::checkLabel( $this->msg( 'log-title-wildcard' )->text(), 'pattern', 'pattern', $pattern ) .
00285             '</span>';
00286     }
00287 
00292     private function getExtraInputs( $types ) {
00293         $offender = $this->getRequest()->getVal( 'offender' );
00294         $user = User::newFromName( $offender, false );
00295         if ( !$user || ( $user->getId() == 0 && !IP::isIPAddress( $offender ) ) ) {
00296             $offender = ''; // Blank field if invalid
00297         }
00298         if ( count( $types ) == 1 && $types[0] == 'suppress' ) {
00299             return Xml::inputLabel( $this->msg( 'revdelete-offender' )->text(), 'offender',
00300                 'mw-log-offender', 20, $offender );
00301         }
00302 
00303         return '';
00304     }
00305 
00309     public function beginLogEventsList() {
00310         return "<ul>\n";
00311     }
00312 
00316     public function endLogEventsList() {
00317         return "</ul>\n";
00318     }
00319 
00324     public function logLine( $row ) {
00325         $entry = DatabaseLogEntry::newFromRow( $row );
00326         $formatter = LogFormatter::newFromEntry( $entry );
00327         $formatter->setContext( $this->getContext() );
00328         $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) );
00329 
00330         $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
00331             $entry->getTimestamp(), $this->getUser() ) );
00332 
00333         $action = $formatter->getActionText();
00334 
00335         if ( $this->flags & self::NO_ACTION_LINK ) {
00336             $revert = '';
00337         } else {
00338             $revert = $formatter->getActionLinks();
00339             if ( $revert != '' ) {
00340                 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
00341             }
00342         }
00343 
00344         $comment = $formatter->getComment();
00345 
00346         // Some user can hide log items and have review links
00347         $del = $this->getShowHideLinks( $row );
00348 
00349         // Any tags...
00350         list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
00351         $classes = array_merge(
00352             array( 'mw-logline-' . $entry->getType() ),
00353             $newClasses
00354         );
00355 
00356         return Html::rawElement( 'li', array( 'class' => $classes ),
00357             "$del $time $action $comment $revert $tagDisplay" ) . "\n";
00358     }
00359 
00364     private function getShowHideLinks( $row ) {
00365         // We don't want to see the links and
00366         // no one can hide items from the suppress log.
00367         if ( ( $this->flags == self::NO_ACTION_LINK )
00368             || $row->log_type == 'suppress'
00369         ) {
00370             return '';
00371         }
00372         $del = '';
00373         $user = $this->getUser();
00374         // Don't show useless checkbox to people who cannot hide log entries
00375         if ( $user->isAllowed( 'deletedhistory' ) ) {
00376             $canHide = $user->isAllowed( 'deletelogentry' );
00377             if ( $row->log_deleted || $canHide ) {
00378                 // Show checkboxes instead of links.
00379                 if ( $canHide && $this->flags & self::USE_REVDEL_CHECKBOXES ) {
00380                     // If event was hidden from sysops
00381                     if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) {
00382                         $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) );
00383                     } else {
00384                         $del = Xml::check(
00385                             'showhiderevisions',
00386                             false,
00387                             array( 'name' => 'ids[' . $row->log_id . ']' )
00388                         );
00389                     }
00390                 } else {
00391                     // If event was hidden from sysops
00392                     if ( !self::userCan( $row, LogPage::DELETED_RESTRICTED, $user ) ) {
00393                         $del = Linker::revDeleteLinkDisabled( $canHide );
00394                     } else {
00395                         $query = array(
00396                             'target' => SpecialPage::getTitleFor( 'Log', $row->log_type )->getPrefixedDBkey(),
00397                             'type' => 'logging',
00398                             'ids' => $row->log_id,
00399                         );
00400                         $del = Linker::revDeleteLink(
00401                             $query,
00402                             self::isDeleted( $row, LogPage::DELETED_RESTRICTED ),
00403                             $canHide
00404                         );
00405                     }
00406                 }
00407             }
00408         }
00409 
00410         return $del;
00411     }
00412 
00420     public static function typeAction( $row, $type, $action, $right = '' ) {
00421         $match = is_array( $type ) ?
00422             in_array( $row->log_type, $type ) : $row->log_type == $type;
00423         if ( $match ) {
00424             $match = is_array( $action ) ?
00425                 in_array( $row->log_action, $action ) : $row->log_action == $action;
00426             if ( $match && $right ) {
00427                 global $wgUser;
00428                 $match = $wgUser->isAllowed( $right );
00429             }
00430         }
00431 
00432         return $match;
00433     }
00434 
00444     public static function userCan( $row, $field, User $user = null ) {
00445         return self::userCanBitfield( $row->log_deleted, $field, $user );
00446     }
00447 
00457     public static function userCanBitfield( $bitfield, $field, User $user = null ) {
00458         if ( $bitfield & $field ) {
00459             if ( $bitfield & LogPage::DELETED_RESTRICTED ) {
00460                 $permission = 'suppressrevision';
00461             } else {
00462                 $permission = 'deletedhistory';
00463             }
00464             wfDebug( "Checking for $permission due to $field match on $bitfield\n" );
00465             if ( $user === null ) {
00466                 global $wgUser;
00467                 $user = $wgUser;
00468             }
00469 
00470             return $user->isAllowed( $permission );
00471         }
00472 
00473         return true;
00474     }
00475 
00481     public static function isDeleted( $row, $field ) {
00482         return ( $row->log_deleted & $field ) == $field;
00483     }
00484 
00508     public static function showLogExtract(
00509         &$out, $types = array(), $page = '', $user = '', $param = array()
00510     ) {
00511         $defaultParameters = array(
00512             'lim' => 25,
00513             'conds' => array(),
00514             'showIfEmpty' => true,
00515             'msgKey' => array( '' ),
00516             'wrap' => "$1",
00517             'flags' => 0,
00518             'useRequestParams' => false,
00519             'useMaster' => false,
00520         );
00521         # The + operator appends elements of remaining keys from the right
00522         # handed array to the left handed, whereas duplicated keys are NOT overwritten.
00523         $param += $defaultParameters;
00524         # Convert $param array to individual variables
00525         $lim = $param['lim'];
00526         $conds = $param['conds'];
00527         $showIfEmpty = $param['showIfEmpty'];
00528         $msgKey = $param['msgKey'];
00529         $wrap = $param['wrap'];
00530         $flags = $param['flags'];
00531         $useRequestParams = $param['useRequestParams'];
00532         if ( !is_array( $msgKey ) ) {
00533             $msgKey = array( $msgKey );
00534         }
00535 
00536         if ( $out instanceof OutputPage ) {
00537             $context = $out->getContext();
00538         } else {
00539             $context = RequestContext::getMain();
00540         }
00541 
00542         # Insert list of top 50 (or top $lim) items
00543         $loglist = new LogEventsList( $context, null, $flags );
00544         $pager = new LogPager( $loglist, $types, $user, $page, '', $conds );
00545         if ( !$useRequestParams ) {
00546             # Reset vars that may have been taken from the request
00547             $pager->mLimit = 50;
00548             $pager->mDefaultLimit = 50;
00549             $pager->mOffset = "";
00550             $pager->mIsBackwards = false;
00551         }
00552 
00553         if ( $param['useMaster'] ) {
00554             $pager->mDb = wfGetDB( DB_MASTER );
00555         }
00556         if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset
00557             $pager->setOffset( $param['offset'] );
00558         }
00559 
00560         if ( $lim > 0 ) {
00561             $pager->mLimit = $lim;
00562         }
00563 
00564         $logBody = $pager->getBody();
00565         $s = '';
00566 
00567         if ( $logBody ) {
00568             if ( $msgKey[0] ) {
00569                 $dir = $context->getLanguage()->getDir();
00570                 $lang = $context->getLanguage()->getCode();
00571 
00572                 $s = Xml::openElement( 'div', array(
00573                     'class' => "mw-warning-with-logexcerpt mw-content-$dir",
00574                     'dir' => $dir,
00575                     'lang' => $lang,
00576                 ) );
00577 
00578                 if ( count( $msgKey ) == 1 ) {
00579                     $s .= $context->msg( $msgKey[0] )->parseAsBlock();
00580                 } else { // Process additional arguments
00581                     $args = $msgKey;
00582                     array_shift( $args );
00583                     $s .= $context->msg( $msgKey[0], $args )->parseAsBlock();
00584                 }
00585             }
00586             $s .= $loglist->beginLogEventsList() .
00587                 $logBody .
00588                 $loglist->endLogEventsList();
00589         } elseif ( $showIfEmpty ) {
00590             $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ),
00591                 $context->msg( 'logempty' )->parse() );
00592         }
00593 
00594         if ( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link
00595             $urlParam = array();
00596             if ( $page instanceof Title ) {
00597                 $urlParam['page'] = $page->getPrefixedDBkey();
00598             } elseif ( $page != '' ) {
00599                 $urlParam['page'] = $page;
00600             }
00601 
00602             if ( $user != '' ) {
00603                 $urlParam['user'] = $user;
00604             }
00605 
00606             if ( !is_array( $types ) ) { # Make it an array, if it isn't
00607                 $types = array( $types );
00608             }
00609 
00610             # If there is exactly one log type, we can link to Special:Log?type=foo
00611             if ( count( $types ) == 1 ) {
00612                 $urlParam['type'] = $types[0];
00613             }
00614 
00615             $s .= Linker::link(
00616                 SpecialPage::getTitleFor( 'Log' ),
00617                 $context->msg( 'log-fulllog' )->escaped(),
00618                 array(),
00619                 $urlParam
00620             );
00621         }
00622 
00623         if ( $logBody && $msgKey[0] ) {
00624             $s .= '</div>';
00625         }
00626 
00627         if ( $wrap != '' ) { // Wrap message in html
00628             $s = str_replace( '$1', $s, $wrap );
00629         }
00630 
00631         /* hook can return false, if we don't want the message to be emitted (Wikia BugId:7093) */
00632         if ( wfRunHooks( 'LogEventsListShowLogExtract', array( &$s, $types, $page, $user, $param ) ) ) {
00633             // $out can be either an OutputPage object or a String-by-reference
00634             if ( $out instanceof OutputPage ) {
00635                 $out->addHTML( $s );
00636             } else {
00637                 $out = $s;
00638             }
00639         }
00640 
00641         return $pager->getNumRows();
00642     }
00643 
00652     public static function getExcludeClause( $db, $audience = 'public', User $user = null ) {
00653         global $wgLogRestrictions;
00654 
00655         if ( $audience != 'public' && $user === null ) {
00656             global $wgUser;
00657             $user = $wgUser;
00658         }
00659 
00660         // Reset the array, clears extra "where" clauses when $par is used
00661         $hiddenLogs = array();
00662 
00663         // Don't show private logs to unprivileged users
00664         foreach ( $wgLogRestrictions as $logType => $right ) {
00665             if ( $audience == 'public' || !$user->isAllowed( $right ) ) {
00666                 $hiddenLogs[] = $logType;
00667             }
00668         }
00669         if ( count( $hiddenLogs ) == 1 ) {
00670             return 'log_type != ' . $db->addQuotes( $hiddenLogs[0] );
00671         } elseif ( $hiddenLogs ) {
00672             return 'log_type NOT IN (' . $db->makeList( $hiddenLogs ) . ')';
00673         }
00674 
00675         return false;
00676     }
00677 }