X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FLogEventsList.php;h=49d4dc58f6e37e113b8a56b079999d992978f652;hb=0400c49ea2f06c7cd7a4f3cc4327fe827bc4e15a;hp=216a40e023a4658f7a9107c0b20fdab7f434e057;hpb=631fbc8029659273deec0770e33e6545e6760bc7;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/LogEventsList.php b/includes/LogEventsList.php index 216a40e023..49d4dc58f6 100644 --- a/includes/LogEventsList.php +++ b/includes/LogEventsList.php @@ -19,6 +19,7 @@ class LogEventsList { const NO_ACTION_LINK = 1; + const NO_EXTRA_USER_LINKS = 2; private $skin; private $out; @@ -39,7 +40,7 @@ class LogEventsList { // Precache various messages if( !isset( $this->message ) ) { $messages = array( 'revertmerge', 'protect_change', 'unblocklink', 'change-blocklink', - 'revertmove', 'undeletelink', 'undeleteviewlink', 'revdel-restore', 'rev-delundel', 'hist', 'diff', + 'revertmove', 'undeletelink', 'undeleteviewlink', 'revdel-restore', 'hist', 'diff', 'pipe-separator' ); foreach( $messages as $msg ) { $this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) ); @@ -190,16 +191,16 @@ class LogEventsList { // First pass to load the log names foreach( $validTypes as $type ) { $text = LogPage::logName( $type ); - $typesByName[$text] = $type; + $typesByName[$type] = $text; } // Second pass to sort by name - ksort($typesByName); + asort($typesByName); // Note the query type $queryType = count($queryTypes) == 1 ? $queryTypes[0] : ''; // Third pass generates sorted XHTML content - foreach( $typesByName as $text => $type ) { + foreach( $typesByName as $type => $text ) { $selected = ($type == $queryType); // Restricted types if ( isset($wgLogRestrictions[$type]) ) { @@ -220,7 +221,9 @@ class LogEventsList { * @return String: Formatted HTML */ private function getUserInput( $user ) { - return Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'mw-log-user', 15, $user ); + return '' . + Xml::inputLabel( wfMsg( 'specialloguserlabel' ), 'user', 'mw-log-user', 15, $user ) . + ''; } /** @@ -228,7 +231,9 @@ class LogEventsList { * @return String: Formatted HTML */ private function getTitleInput( $title ) { - return Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'mw-log-page', 20, $title ); + return '' . + Xml::inputLabel( wfMsg( 'speciallogtitlelabel' ), 'page', 'mw-log-page', 20, $title ) . + ''; } /** @@ -267,38 +272,90 @@ class LogEventsList { * @return String: Formatted HTML list item */ public function logLine( $row ) { - global $wgLang, $wgUser, $wgContLang; - + $classes = array( 'mw-logline-' . $row->log_type ); $title = Title::makeTitle( $row->log_namespace, $row->log_title ); - $classes = array( "mw-logline-{$row->log_type}" ); - $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->log_timestamp ), true ); + // Log time + $time = $this->logTimestamp( $row ); // User links + $userLink = $this->logUserLinks( $row ); + // Extract extra parameters + $paramArray = LogPage::extractParams( $row->log_params ); + // Event description + $action = $this->logAction( $row, $title, $paramArray ); + // Log comment + $comment = $this->logComment( $row ); + // Add review/revert links and such... + $revert = $this->logActionLinks( $row, $title, $paramArray, $comment ); + + // Some user can hide log items and have review links + $del = $this->getShowHideLinks( $row ); + if( $del != '' ) $del .= ' '; + + // Any tags... + list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' ); + $classes = array_merge( $classes, $newClasses ); + + return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ), + $del . "$time $userLink $action $comment $revert $tagDisplay" ) . "\n"; + } + + private function logTimestamp( $row ) { + global $wgLang; + $time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->log_timestamp ), true ); + return htmlspecialchars( $time ); + } + + private function logUserLinks( $row ) { + $userLinks = ''; if( self::isDeleted( $row, LogPage::DELETED_USER ) ) { - $userLink = '' . wfMsgHtml( 'rev-deleted-user' ) . ''; + $userLinks = '' . + wfMsgHtml( 'rev-deleted-user' ) . ''; + } else { + $userLinks = $this->skin->userLink( $row->log_user, $row->user_name ); + // Talk|Contribs links... + if( !( $this->flags & self::NO_EXTRA_USER_LINKS ) ) { + $userLinks .= $this->skin->userToolLinks( + $row->log_user, $row->user_name, true, 0, $row->user_editcount ); + } + } + return $userLinks; + } + + private function logAction( $row, $title, $paramArray ) { + $action = ''; + if( self::isDeleted( $row, LogPage::DELETED_ACTION ) ) { + $action = '' . + wfMsgHtml( 'rev-deleted-event' ) . ''; } else { - $userLink = $this->skin->userLink( $row->log_user, $row->user_name ) . - $this->skin->userToolLinks( $row->log_user, $row->user_name, true, 0, $row->user_editcount ); + $action = LogPage::actionText( + $row->log_type, $row->log_action, $title, $this->skin, $paramArray, true ); } - // Comment + return $action; + } + + private function logComment( $row ) { + global $wgContLang; + $comment = ''; if( self::isDeleted( $row, LogPage::DELETED_COMMENT ) ) { - $comment = '' . wfMsgHtml( 'rev-deleted-comment' ) . ''; + $comment = '' . + wfMsgHtml( 'rev-deleted-comment' ) . ''; } else { - $comment = $wgContLang->getDirMark() . $this->skin->commentBlock( $row->log_comment ); + $comment = $wgContLang->getDirMark() . + $this->skin->commentBlock( $row->log_comment ); } - // Extract extra parameters - $paramArray = LogPage::extractParams( $row->log_params ); - $revert = $del = ''; - // Some user can hide log items and have review links - if( !( $this->flags & self::NO_ACTION_LINK ) && $wgUser->isAllowed( 'deletedhistory' ) ) { - // Don't show useless link to people who cannot hide revisions - if( $row->log_deleted || $wgUser->isAllowed( 'deleterevision' ) ) { - $del = $this->getShowHideLinks( $row ) . ' '; - } + return $comment; + } + + // @TODO: split up! + private function logActionLinks( $row, $title, $paramArray, &$comment ) { + global $wgUser, $wgLang; + if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the action + || self::isDeleted( $row, LogPage::DELETED_ACTION ) ) // action is hidden + { + return ''; } - // Add review links and such... - if( ( $this->flags & self::NO_ACTION_LINK ) || ( $row->log_deleted & LogPage::DELETED_ACTION ) ) { - // Action text is suppressed... - } else if( self::typeAction( $row, 'move', 'move', 'move' ) && !empty( $paramArray[0] ) ) { + $revert = ''; + if( self::typeAction( $row, 'move', 'move', 'move' ) && !empty( $paramArray[0] ) ) { $destTitle = Title::newFromText( $paramArray[0] ); if( $destTitle ) { $revert = '(' . $this->skin->link( @@ -308,7 +365,7 @@ class LogEventsList { array( 'wpOldTitle' => $destTitle->getPrefixedDBkey(), 'wpNewTitle' => $title->getPrefixedDBkey(), - 'wpReason' => wfMsgForContent( 'revertmove' ), + 'wpReason' => wfMsgForContent( 'revertmove' ), 'wpMovetalk' => 0 ), array( 'known', 'noclasses' ) @@ -321,7 +378,6 @@ class LogEventsList { } else { $viewdeleted = $this->message['undeletelink']; } - $revert = '(' . $this->skin->link( SpecialPage::getTitleFor( 'Undelete' ), $viewdeleted, @@ -373,9 +429,8 @@ class LogEventsList { $revert .= ')'; // Show unmerge link } else if( self::typeAction( $row, 'merge', 'merge', 'mergehistory' ) ) { - $merge = SpecialPage::getTitleFor( 'Mergehistory' ); $revert = '(' . $this->skin->link( - $merge, + SpecialPage::getTitleFor( 'MergeHistory' ), $this->message['revertmerge'], array(), array( @@ -466,7 +521,8 @@ class LogEventsList { # Fall back to a blue contributions link $revert = $this->skin->userToolLinks( 1, $title->getDBkey() ); } - if( $time < '20080129000000' ) { + $ts = wfTimestamp( TS_UNIX, $row->log_timestamp ); + if( $ts < '20080129000000' ) { # Suppress $comment from old entries (before 2008-01-29), # not needed and can contain incorrect links $comment = ''; @@ -476,26 +532,10 @@ class LogEventsList { wfRunHooks( 'LogLine', array( $row->log_type, $row->log_action, $title, $paramArray, &$comment, &$revert, $row->log_timestamp ) ); } - // Event description - if( self::isDeleted( $row, LogPage::DELETED_ACTION ) ) { - $action = '' . wfMsgHtml( 'rev-deleted-event' ) . ''; - } else { - $action = LogPage::actionText( $row->log_type, $row->log_action, $title, - $this->skin, $paramArray, true ); - } - - // Any tags... - list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' ); - $classes = array_merge( $classes, $newClasses ); - if( $revert != '' ) { $revert = '' . $revert . ''; } - - $time = htmlspecialchars( $time ); - - return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ), - $del . $time . ' ' . $userLink . ' ' . $action . ' ' . $comment . ' ' . $revert . " $tagDisplay" ) . "\n"; + return $revert; } /** @@ -503,22 +543,31 @@ class LogEventsList { * @return string */ private function getShowHideLinks( $row ) { - // If event was hidden from sysops - if( !self::userCan( $row, LogPage::DELETED_RESTRICTED ) ) { - $del = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), - '(' . $this->message['rev-delundel'] . ')' ); - } else if( $row->log_type == 'suppress' ) { - $del = ''; // No one should be hiding from the oversight log - } else { - $target = SpecialPage::getTitleFor( 'Log', $row->log_type ); - $page = Title::makeTitle( $row->log_namespace, $row->log_title ); - $query = array( - 'target' => $target->getPrefixedDBkey(), - 'type' => 'logging', - 'ids' => $row->log_id, - ); - $del = $this->skin->revDeleteLink( $query, - self::isDeleted( $row, LogPage::DELETED_RESTRICTED ) ); + global $wgUser; + if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the links + || $row->log_type == 'suppress' ) // no one can hide items from the suppress log + { + return ''; + } + $del = ''; + // Don't show useless link to people who cannot hide revisions + if( $wgUser->isAllowed( 'deletedhistory' ) ) { + if( $row->log_deleted || $wgUser->isAllowed( 'deleterevision' ) ) { + $canHide = $wgUser->isAllowed( 'deleterevision' ); + // If event was hidden from sysops + if( !self::userCan( $row, LogPage::DELETED_RESTRICTED ) ) { + $del = $this->skin->revDeleteLinkDisabled( $canHide ); + } else { + $target = SpecialPage::getTitleFor( 'Log', $row->log_type ); + $query = array( + 'target' => $target->getPrefixedDBkey(), + 'type' => 'logging', + 'ids' => $row->log_id, + ); + $del = $this->skin->revDeleteLink( $query, + self::isDeleted( $row, LogPage::DELETED_RESTRICTED ), $canHide ); + } + } } return $del; } @@ -552,15 +601,26 @@ class LogEventsList { * @return Boolean */ public static function userCan( $row, $field ) { - if( $row->log_deleted & $field ) { + return self::userCanBitfield( $row->log_deleted, $field ); + } + + /** + * Determine if the current user is allowed to view a particular + * field of this log row, if it's marked as deleted. + * @param $bitfield Integer (current field) + * @param $field Integer + * @return Boolean + */ + public static function userCanBitfield( $bitfield, $field ) { + if( $bitfield & $field ) { global $wgUser; $permission = ''; - if ( $row->log_deleted & LogPage::DELETED_RESTRICTED ) { + if ( $bitfield & LogPage::DELETED_RESTRICTED ) { $permission = 'suppressrevision'; } else { $permission = 'deletedhistory'; } - wfDebug( "Checking for $permission due to $field match on $row->log_deleted\n" ); + wfDebug( "Checking for $permission due to $field match on $bitfield\n" ); return $wgUser->isAllowed( $permission ); } else { return true; @@ -592,35 +652,41 @@ class LogEventsList { * that are processed with wgMsgExt and option 'parse' * - offset Set to overwrite offset parameter in $wgRequest * set to '' to unset offset + * - wrap String Wrap the message in html (usually something like "
$1
"). + * - flags Integer display flags (NO_ACTION_LINK,NO_EXTRA_USER_LINKS) * @return Integer Number of total log items (not limited by $lim) */ - public static function showLogExtract( &$out, $types=array(), $page='', $user='', - $param = array() ) { - + public static function showLogExtract( + &$out, $types=array(), $page='', $user='', $param = array() + ) { + global $wgUser, $wgOut; $defaultParameters = array( 'lim' => 25, 'conds' => array(), 'showIfEmpty' => true, - 'msgKey' => array('') + 'msgKey' => array(''), + 'wrap' => "$1", + 'flags' => 0 ); - # The + operator appends elements of remaining keys from the right # handed array to the left handed, whereas duplicated keys are NOT overwritten. $param += $defaultParameters; - - global $wgUser, $wgOut; # Convert $param array to individual variables $lim = $param['lim']; $conds = $param['conds']; $showIfEmpty = $param['showIfEmpty']; $msgKey = $param['msgKey']; - if ( !is_array( $msgKey ) ) + $wrap = $param['wrap']; + $flags = $param['flags']; + if ( !is_array( $msgKey ) ) { $msgKey = array( $msgKey ); + } # Insert list of top 50 (or top $lim) items - $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 ); + $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, $flags ); $pager = new LogPager( $loglist, $types, $user, $page, '', $conds ); - if ( isset( $param['offset'] ) ) # Tell pager to ignore $wgRequest offset + if ( isset( $param['offset'] ) ) { # Tell pager to ignore $wgRequest offset $pager->setOffset( $param['offset'] ); + } if( $lim > 0 ) $pager->mLimit = $lim; $logBody = $pager->getBody(); $s = ''; @@ -641,7 +707,8 @@ class LogEventsList { $loglist->endLogEventsList(); } else { if ( $showIfEmpty ) - $s = wfMsgExt( 'logempty', array('parse') ); + $s = Html::rawElement( 'div', array( 'class' => 'mw-warning-logempty' ), + wfMsgExt( 'logempty', array( 'parseinline' ) ) ); } if( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link $urlParam = array(); @@ -660,11 +727,16 @@ class LogEventsList { array(), $urlParam ); - } - if ( $logBody && $msgKey[0] ) + if ( $logBody && $msgKey[0] ) { $s .= ''; + } + if ( $wrap!='' ) { // Wrap message in html + $s = str_replace( '$1', $s, $wrap ); + } + + // $out can be either an OutputPage object or a String-by-reference if( $out instanceof OutputPage ){ $out->addHTML( $s ); } else { @@ -773,10 +845,13 @@ class LogPager extends ReverseChronologicalPager { $types = ($types === '') ? array() : (array)$types; // Don't even show header for private logs; don't recognize it... foreach ( $types as $type ) { - if( isset( $wgLogRestrictions[$type] ) && !$wgUser->isAllowed($wgLogRestrictions[$type]) ) { + if( isset( $wgLogRestrictions[$type] ) + && !$wgUser->isAllowed($wgLogRestrictions[$type]) + ) { $types = array_diff( $types, array( $type ) ); } } + $this->types = $types; // Don't show private logs to unprivileged users. // Also, only show them upon specific request to avoid suprises. $audience = $types ? 'user' : 'public'; @@ -785,7 +860,6 @@ class LogPager extends ReverseChronologicalPager { $this->mConds[] = $hideLogs; } if( count($types) ) { - $this->types = $types; $this->mConds['log_type'] = $types; // Set typeCGI; used in url param for paging if( count($types) == 1 ) $this->typeCGI = $types[0]; @@ -839,6 +913,8 @@ class LogPager extends ReverseChronologicalPager { $this->title = $title->getPrefixedText(); $ns = $title->getNamespace(); + $db = $this->mDb; + # Using the (log_namespace, log_title, log_timestamp) index with a # range scan (LIKE) on the first two parts, instead of simple equality, # makes it unusable for sorting. Sorted retrieval using another index @@ -851,10 +927,8 @@ class LogPager extends ReverseChronologicalPager { # log entries for even the busiest pages, so it can be safely scanned # in full to satisfy an impossible condition on user or similar. if( $pattern && !$wgMiserMode ) { - # use escapeLike to avoid expensive search patterns like 't%st%' - $safetitle = $this->mDb->escapeLike( $title->getDBkey() ); $this->mConds['log_namespace'] = $ns; - $this->mConds[] = "log_title LIKE '$safetitle%'"; + $this->mConds[] = 'log_title ' . $db->buildLike( $title->getDBkey(), $db->anyString() ); $this->pattern = $pattern; } else { $this->mConds['log_namespace'] = $ns; @@ -862,33 +936,40 @@ class LogPager extends ReverseChronologicalPager { } // Paranoia: avoid brute force searches (bug 17342) if( !$wgUser->isAllowed( 'deletedhistory' ) ) { - $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0'; + $this->mConds[] = $db->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0'; } else if( !$wgUser->isAllowed( 'suppressrevision' ) ) { - $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::SUPPRESSED_ACTION) . + $this->mConds[] = $db->bitAnd('log_deleted', LogPage::SUPPRESSED_ACTION) . ' != ' . LogPage::SUPPRESSED_ACTION; } } public function getQueryInfo() { + global $wgOut; $tables = array( 'logging', 'user' ); $this->mConds[] = 'user_id = log_user'; - $groupBy = false; + $index = array(); + $options = array(); # Add log_search table if there are conditions on it if( array_key_exists('ls_field',$this->mConds) ) { $tables[] = 'log_search'; - $index = array( 'log_search' => 'ls_field_val', 'logging' => 'PRIMARY' ); - $groupBy = 'ls_log_id'; - # Don't use the wrong logging index + $index['log_search'] = 'ls_field_val'; + $index['logging'] = 'PRIMARY'; + $options[] = 'DISTINCT'; + # Avoid usage of the wrong index by limiting + # the choices of available indexes. This mainly + # avoids site-breaking filesorts. } else if( $this->title || $this->pattern || $this->user ) { - $index = array( 'logging' => array('page_time','user_time') ); - } else if( $this->types ) { - $index = array( 'logging' => 'type_time' ); + $index['logging'] = array( 'page_time', 'user_time' ); + if( count($this->types) == 1 ) { + $index['logging'][] = 'log_user_type_time'; + } + } else if( count($this->types) == 1 ) { + $index['logging'] = 'type_time'; } else { - $index = array( 'logging' => 'times' ); + $index['logging'] = 'times'; } - $options = array( 'USE INDEX' => $index ); + $options['USE INDEX'] = $index; # Don't show duplicate rows when using log_search - if( $groupBy ) $options['GROUP BY'] = $groupBy; $info = array( 'tables' => $tables, 'fields' => array( 'log_type', 'log_action', 'log_user', 'log_namespace', @@ -904,7 +985,6 @@ class LogPager extends ReverseChronologicalPager { # Add ChangeTags filter query ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], $info['join_conds'], $info['options'], $this->mTagFilter ); - return $info; }