MediaWiki
REL1_24
|
00001 <?php 00029 class LogPager extends ReverseChronologicalPager { 00031 private $types = array(); 00032 00034 private $performer = ''; 00035 00037 private $title = ''; 00038 00040 private $pattern = ''; 00041 00043 private $typeCGI = ''; 00044 00046 public $mLogEventsList; 00047 00061 public function __construct( $list, $types = array(), $performer = '', $title = '', $pattern = '', 00062 $conds = array(), $year = false, $month = false, $tagFilter = '' ) { 00063 parent::__construct( $list->getContext() ); 00064 $this->mConds = $conds; 00065 00066 $this->mLogEventsList = $list; 00067 00068 $this->limitType( $types ); // also excludes hidden types 00069 $this->limitPerformer( $performer ); 00070 $this->limitTitle( $title, $pattern ); 00071 $this->getDateCond( $year, $month ); 00072 $this->mTagFilter = $tagFilter; 00073 00074 $this->mDb = wfGetDB( DB_SLAVE, 'logpager' ); 00075 } 00076 00077 public function getDefaultQuery() { 00078 $query = parent::getDefaultQuery(); 00079 $query['type'] = $this->typeCGI; // arrays won't work here 00080 $query['user'] = $this->performer; 00081 $query['month'] = $this->mMonth; 00082 $query['year'] = $this->mYear; 00083 00084 return $query; 00085 } 00086 00087 // Call ONLY after calling $this->limitType() already! 00088 public function getFilterParams() { 00089 global $wgFilterLogTypes; 00090 $filters = array(); 00091 if ( count( $this->types ) ) { 00092 return $filters; 00093 } 00094 foreach ( $wgFilterLogTypes as $type => $default ) { 00095 // Avoid silly filtering 00096 if ( $type !== 'patrol' || $this->getUser()->useNPPatrol() ) { 00097 $hide = $this->getRequest()->getInt( "hide_{$type}_log", $default ); 00098 $filters[$type] = $hide; 00099 if ( $hide ) { 00100 $this->mConds[] = 'log_type != ' . $this->mDb->addQuotes( $type ); 00101 } 00102 } 00103 } 00104 00105 return $filters; 00106 } 00107 00115 private function limitType( $types ) { 00116 global $wgLogRestrictions; 00117 00118 $user = $this->getUser(); 00119 // If $types is not an array, make it an array 00120 $types = ( $types === '' ) ? array() : (array)$types; 00121 // Don't even show header for private logs; don't recognize it... 00122 $needReindex = false; 00123 foreach ( $types as $type ) { 00124 if ( isset( $wgLogRestrictions[$type] ) 00125 && !$user->isAllowed( $wgLogRestrictions[$type] ) 00126 ) { 00127 $needReindex = true; 00128 $types = array_diff( $types, array( $type ) ); 00129 } 00130 } 00131 if ( $needReindex ) { 00132 // Lots of this code makes assumptions that 00133 // the first entry in the array is $types[0]. 00134 $types = array_values( $types ); 00135 } 00136 $this->types = $types; 00137 // Don't show private logs to unprivileged users. 00138 // Also, only show them upon specific request to avoid suprises. 00139 $audience = $types ? 'user' : 'public'; 00140 $hideLogs = LogEventsList::getExcludeClause( $this->mDb, $audience, $user ); 00141 if ( $hideLogs !== false ) { 00142 $this->mConds[] = $hideLogs; 00143 } 00144 if ( count( $types ) ) { 00145 $this->mConds['log_type'] = $types; 00146 // Set typeCGI; used in url param for paging 00147 if ( count( $types ) == 1 ) { 00148 $this->typeCGI = $types[0]; 00149 } 00150 } 00151 } 00152 00159 private function limitPerformer( $name ) { 00160 if ( $name == '' ) { 00161 return; 00162 } 00163 $usertitle = Title::makeTitleSafe( NS_USER, $name ); 00164 if ( is_null( $usertitle ) ) { 00165 return; 00166 } 00167 /* Fetch userid at first, if known, provides awesome query plan afterwards */ 00168 $userid = User::idFromName( $name ); 00169 if ( !$userid ) { 00170 $this->mConds['log_user_text'] = IP::sanitizeIP( $name ); 00171 } else { 00172 $this->mConds['log_user'] = $userid; 00173 } 00174 // Paranoia: avoid brute force searches (bug 17342) 00175 $user = $this->getUser(); 00176 if ( !$user->isAllowed( 'deletedhistory' ) ) { 00177 $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0'; 00178 } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { 00179 $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_USER ) . 00180 ' != ' . LogPage::SUPPRESSED_USER; 00181 } 00182 00183 $this->performer = $usertitle->getText(); 00184 } 00185 00194 private function limitTitle( $page, $pattern ) { 00195 global $wgMiserMode; 00196 00197 if ( $page instanceof Title ) { 00198 $title = $page; 00199 } else { 00200 $title = Title::newFromText( $page ); 00201 if ( strlen( $page ) == 0 || !$title instanceof Title ) { 00202 return; 00203 } 00204 } 00205 00206 $this->title = $title->getPrefixedText(); 00207 $ns = $title->getNamespace(); 00208 $db = $this->mDb; 00209 00210 $doUserRightsLogLike = false; 00211 if ( $this->types == array( 'rights' ) ) { 00212 global $wgUserrightsInterwikiDelimiter; 00213 $parts = explode( $wgUserrightsInterwikiDelimiter, $title->getDBKey() ); 00214 if ( count( $parts ) == 2 ) { 00215 list( $name, $database ) = array_map( 'trim', $parts ); 00216 if ( strstr( $database, '*' ) ) { // Search for wildcard in database name 00217 $doUserRightsLogLike = true; 00218 } 00219 } 00220 } 00221 00222 # Using the (log_namespace, log_title, log_timestamp) index with a 00223 # range scan (LIKE) on the first two parts, instead of simple equality, 00224 # makes it unusable for sorting. Sorted retrieval using another index 00225 # would be possible, but then we might have to scan arbitrarily many 00226 # nodes of that index. Therefore, we need to avoid this if $wgMiserMode 00227 # is on. 00228 # 00229 # This is not a problem with simple title matches, because then we can 00230 # use the page_time index. That should have no more than a few hundred 00231 # log entries for even the busiest pages, so it can be safely scanned 00232 # in full to satisfy an impossible condition on user or similar. 00233 $this->mConds['log_namespace'] = $ns; 00234 if ( $doUserRightsLogLike ) { 00235 $params = array( $name . $wgUserrightsInterwikiDelimiter ); 00236 foreach ( explode( '*', $database ) as $databasepart ) { 00237 $params[] = $databasepart; 00238 $params[] = $db->anyString(); 00239 } 00240 array_pop( $params ); // Get rid of the last % we added. 00241 $this->mConds[] = 'log_title' . $db->buildLike( $params ); 00242 } elseif ( $pattern && !$wgMiserMode ) { 00243 $this->mConds[] = 'log_title' . $db->buildLike( $title->getDBkey(), $db->anyString() ); 00244 $this->pattern = $pattern; 00245 } else { 00246 $this->mConds['log_title'] = $title->getDBkey(); 00247 } 00248 // Paranoia: avoid brute force searches (bug 17342) 00249 $user = $this->getUser(); 00250 if ( !$user->isAllowed( 'deletedhistory' ) ) { 00251 $this->mConds[] = $db->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0'; 00252 } elseif ( !$user->isAllowed( 'suppressrevision' ) ) { 00253 $this->mConds[] = $db->bitAnd( 'log_deleted', LogPage::SUPPRESSED_ACTION ) . 00254 ' != ' . LogPage::SUPPRESSED_ACTION; 00255 } 00256 } 00257 00263 public function getQueryInfo() { 00264 $basic = DatabaseLogEntry::getSelectQueryData(); 00265 00266 $tables = $basic['tables']; 00267 $fields = $basic['fields']; 00268 $conds = $basic['conds']; 00269 $options = $basic['options']; 00270 $joins = $basic['join_conds']; 00271 00272 $index = array(); 00273 # Add log_search table if there are conditions on it. 00274 # This filters the results to only include log rows that have 00275 # log_search records with the specified ls_field and ls_value values. 00276 if ( array_key_exists( 'ls_field', $this->mConds ) ) { 00277 $tables[] = 'log_search'; 00278 $index['log_search'] = 'ls_field_val'; 00279 $index['logging'] = 'PRIMARY'; 00280 if ( !$this->hasEqualsClause( 'ls_field' ) 00281 || !$this->hasEqualsClause( 'ls_value' ) 00282 ) { 00283 # Since (ls_field,ls_value,ls_logid) is unique, if the condition is 00284 # to match a specific (ls_field,ls_value) tuple, then there will be 00285 # no duplicate log rows. Otherwise, we need to remove the duplicates. 00286 $options[] = 'DISTINCT'; 00287 } 00288 } 00289 if ( count( $index ) ) { 00290 $options['USE INDEX'] = $index; 00291 } 00292 # Don't show duplicate rows when using log_search 00293 $joins['log_search'] = array( 'INNER JOIN', 'ls_log_id=log_id' ); 00294 00295 $info = array( 00296 'tables' => $tables, 00297 'fields' => $fields, 00298 'conds' => array_merge( $conds, $this->mConds ), 00299 'options' => $options, 00300 'join_conds' => $joins, 00301 ); 00302 # Add ChangeTags filter query 00303 ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], 00304 $info['join_conds'], $info['options'], $this->mTagFilter ); 00305 00306 return $info; 00307 } 00308 00314 protected function hasEqualsClause( $field ) { 00315 return ( 00316 array_key_exists( $field, $this->mConds ) && 00317 ( !is_array( $this->mConds[$field] ) || count( $this->mConds[$field] ) == 1 ) 00318 ); 00319 } 00320 00321 function getIndexField() { 00322 return 'log_timestamp'; 00323 } 00324 00325 public function getStartBody() { 00326 wfProfileIn( __METHOD__ ); 00327 # Do a link batch query 00328 if ( $this->getNumRows() > 0 ) { 00329 $lb = new LinkBatch; 00330 foreach ( $this->mResult as $row ) { 00331 $lb->add( $row->log_namespace, $row->log_title ); 00332 $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) ); 00333 $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) ); 00334 $formatter = LogFormatter::newFromRow( $row ); 00335 foreach ( $formatter->getPreloadTitles() as $title ) { 00336 $lb->addObj( $title ); 00337 } 00338 } 00339 $lb->execute(); 00340 $this->mResult->seek( 0 ); 00341 } 00342 wfProfileOut( __METHOD__ ); 00343 00344 return ''; 00345 } 00346 00347 public function formatRow( $row ) { 00348 return $this->mLogEventsList->logLine( $row ); 00349 } 00350 00351 public function getType() { 00352 return $this->types; 00353 } 00354 00358 public function getPerformer() { 00359 return $this->performer; 00360 } 00361 00365 public function getPage() { 00366 return $this->title; 00367 } 00368 00369 public function getPattern() { 00370 return $this->pattern; 00371 } 00372 00373 public function getYear() { 00374 return $this->mYear; 00375 } 00376 00377 public function getMonth() { 00378 return $this->mMonth; 00379 } 00380 00381 public function getTagFilter() { 00382 return $this->mTagFilter; 00383 } 00384 00385 public function doQuery() { 00386 // Workaround MySQL optimizer bug 00387 $this->mDb->setBigSelects(); 00388 parent::doQuery(); 00389 $this->mDb->setBigSelects( 'default' ); 00390 } 00391 }