Index: trunk/phase3/includes/logging/LogPager.php |
— | — | @@ -0,0 +1,352 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Contain classes to list log entries |
| 5 | + * |
| 6 | + * Copyright © 2004 Brion Vibber <brion@pobox.com>, 2008 Aaron Schulz |
| 7 | + * http://www.mediawiki.org/ |
| 8 | + * |
| 9 | + * This program is free software; you can redistribute it and/or modify |
| 10 | + * it under the terms of the GNU General Public License as published by |
| 11 | + * the Free Software Foundation; either version 2 of the License, or |
| 12 | + * (at your option) any later version. |
| 13 | + * |
| 14 | + * This program is distributed in the hope that it will be useful, |
| 15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | + * GNU General Public License for more details. |
| 18 | + * |
| 19 | + * You should have received a copy of the GNU General Public License along |
| 20 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 22 | + * http://www.gnu.org/copyleft/gpl.html |
| 23 | + * |
| 24 | + * @file |
| 25 | + */ |
| 26 | + |
| 27 | +/** |
| 28 | + * @ingroup Pager |
| 29 | + */ |
| 30 | +class LogPager extends ReverseChronologicalPager { |
| 31 | + private $types = array(), $performer = '', $title = '', $pattern = ''; |
| 32 | + private $typeCGI = ''; |
| 33 | + public $mLogEventsList; |
| 34 | + |
| 35 | + /** |
| 36 | + * Constructor |
| 37 | + * |
| 38 | + * @param $list LogEventsList |
| 39 | + * @param $types String or Array: log types to show |
| 40 | + * @param $performer String: the user who made the log entries |
| 41 | + * @param $title String|Title: the page title the log entries are for |
| 42 | + * @param $pattern String: do a prefix search rather than an exact title match |
| 43 | + * @param $conds Array: extra conditions for the query |
| 44 | + * @param $year Integer: the year to start from |
| 45 | + * @param $month Integer: the month to start from |
| 46 | + * @param $tagFilter String: tag |
| 47 | + */ |
| 48 | + public function __construct( $list, $types = array(), $performer = '', $title = '', $pattern = '', |
| 49 | + $conds = array(), $year = false, $month = false, $tagFilter = '' ) { |
| 50 | + parent::__construct( $list->getContext() ); |
| 51 | + $this->mConds = $conds; |
| 52 | + |
| 53 | + $this->mLogEventsList = $list; |
| 54 | + |
| 55 | + $this->limitType( $types ); // also excludes hidden types |
| 56 | + $this->limitPerformer( $performer ); |
| 57 | + $this->limitTitle( $title, $pattern ); |
| 58 | + $this->getDateCond( $year, $month ); |
| 59 | + $this->mTagFilter = $tagFilter; |
| 60 | + } |
| 61 | + |
| 62 | + public function getDefaultQuery() { |
| 63 | + $query = parent::getDefaultQuery(); |
| 64 | + $query['type'] = $this->typeCGI; // arrays won't work here |
| 65 | + $query['user'] = $this->performer; |
| 66 | + $query['month'] = $this->mMonth; |
| 67 | + $query['year'] = $this->mYear; |
| 68 | + return $query; |
| 69 | + } |
| 70 | + |
| 71 | + // Call ONLY after calling $this->limitType() already! |
| 72 | + public function getFilterParams() { |
| 73 | + global $wgFilterLogTypes; |
| 74 | + $filters = array(); |
| 75 | + if( count($this->types) ) { |
| 76 | + return $filters; |
| 77 | + } |
| 78 | + foreach( $wgFilterLogTypes as $type => $default ) { |
| 79 | + // Avoid silly filtering |
| 80 | + if( $type !== 'patrol' || $this->getUser()->useNPPatrol() ) { |
| 81 | + $hide = $this->getRequest()->getInt( "hide_{$type}_log", $default ); |
| 82 | + $filters[$type] = $hide; |
| 83 | + if( $hide ) |
| 84 | + $this->mConds[] = 'log_type != ' . $this->mDb->addQuotes( $type ); |
| 85 | + } |
| 86 | + } |
| 87 | + return $filters; |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * Set the log reader to return only entries of the given type. |
| 92 | + * Type restrictions enforced here |
| 93 | + * |
| 94 | + * @param $types String or array: Log types ('upload', 'delete', etc); |
| 95 | + * empty string means no restriction |
| 96 | + */ |
| 97 | + private function limitType( $types ) { |
| 98 | + global $wgLogRestrictions; |
| 99 | + // If $types is not an array, make it an array |
| 100 | + $types = ($types === '') ? array() : (array)$types; |
| 101 | + // Don't even show header for private logs; don't recognize it... |
| 102 | + $needReindex = false; |
| 103 | + foreach ( $types as $type ) { |
| 104 | + if( isset( $wgLogRestrictions[$type] ) |
| 105 | + && !$this->getUser()->isAllowed($wgLogRestrictions[$type]) |
| 106 | + ) { |
| 107 | + $needReindex = true; |
| 108 | + $types = array_diff( $types, array( $type ) ); |
| 109 | + } |
| 110 | + } |
| 111 | + if ( $needReindex ) { |
| 112 | + // Lots of this code makes assumptions that |
| 113 | + // the first entry in the array is $types[0]. |
| 114 | + $types = array_values( $types ); |
| 115 | + } |
| 116 | + $this->types = $types; |
| 117 | + // Don't show private logs to unprivileged users. |
| 118 | + // Also, only show them upon specific request to avoid suprises. |
| 119 | + $audience = $types ? 'user' : 'public'; |
| 120 | + $hideLogs = LogEventsList::getExcludeClause( $this->mDb, $audience ); |
| 121 | + if( $hideLogs !== false ) { |
| 122 | + $this->mConds[] = $hideLogs; |
| 123 | + } |
| 124 | + if( count($types) ) { |
| 125 | + $this->mConds['log_type'] = $types; |
| 126 | + // Set typeCGI; used in url param for paging |
| 127 | + if( count($types) == 1 ) $this->typeCGI = $types[0]; |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Set the log reader to return only entries by the given user. |
| 133 | + * |
| 134 | + * @param $name String: (In)valid user name |
| 135 | + */ |
| 136 | + private function limitPerformer( $name ) { |
| 137 | + if( $name == '' ) { |
| 138 | + return false; |
| 139 | + } |
| 140 | + $usertitle = Title::makeTitleSafe( NS_USER, $name ); |
| 141 | + if( is_null($usertitle) ) { |
| 142 | + return false; |
| 143 | + } |
| 144 | + /* Fetch userid at first, if known, provides awesome query plan afterwards */ |
| 145 | + $userid = User::idFromName( $name ); |
| 146 | + if( !$userid ) { |
| 147 | + /* It should be nicer to abort query at all, |
| 148 | + but for now it won't pass anywhere behind the optimizer */ |
| 149 | + $this->mConds[] = "NULL"; |
| 150 | + } else { |
| 151 | + $this->mConds['log_user'] = $userid; |
| 152 | + // Paranoia: avoid brute force searches (bug 17342) |
| 153 | + $user = $this->getUser(); |
| 154 | + if( !$user->isAllowed( 'deletedhistory' ) ) { |
| 155 | + $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::DELETED_USER) . ' = 0'; |
| 156 | + } elseif( !$user->isAllowed( 'suppressrevision' ) ) { |
| 157 | + $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::SUPPRESSED_USER) . |
| 158 | + ' != ' . LogPage::SUPPRESSED_USER; |
| 159 | + } |
| 160 | + $this->performer = $usertitle->getText(); |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Set the log reader to return only entries affecting the given page. |
| 166 | + * (For the block and rights logs, this is a user page.) |
| 167 | + * |
| 168 | + * @param $page String or Title object: Title name |
| 169 | + * @param $pattern String |
| 170 | + */ |
| 171 | + private function limitTitle( $page, $pattern ) { |
| 172 | + global $wgMiserMode; |
| 173 | + |
| 174 | + if ( $page instanceof Title ) { |
| 175 | + $title = $page; |
| 176 | + } else { |
| 177 | + $title = Title::newFromText( $page ); |
| 178 | + if( strlen( $page ) == 0 || !$title instanceof Title ) { |
| 179 | + return false; |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + $this->title = $title->getPrefixedText(); |
| 184 | + $ns = $title->getNamespace(); |
| 185 | + $db = $this->mDb; |
| 186 | + |
| 187 | + # Using the (log_namespace, log_title, log_timestamp) index with a |
| 188 | + # range scan (LIKE) on the first two parts, instead of simple equality, |
| 189 | + # makes it unusable for sorting. Sorted retrieval using another index |
| 190 | + # would be possible, but then we might have to scan arbitrarily many |
| 191 | + # nodes of that index. Therefore, we need to avoid this if $wgMiserMode |
| 192 | + # is on. |
| 193 | + # |
| 194 | + # This is not a problem with simple title matches, because then we can |
| 195 | + # use the page_time index. That should have no more than a few hundred |
| 196 | + # log entries for even the busiest pages, so it can be safely scanned |
| 197 | + # in full to satisfy an impossible condition on user or similar. |
| 198 | + if( $pattern && !$wgMiserMode ) { |
| 199 | + $this->mConds['log_namespace'] = $ns; |
| 200 | + $this->mConds[] = 'log_title ' . $db->buildLike( $title->getDBkey(), $db->anyString() ); |
| 201 | + $this->pattern = $pattern; |
| 202 | + } else { |
| 203 | + $this->mConds['log_namespace'] = $ns; |
| 204 | + $this->mConds['log_title'] = $title->getDBkey(); |
| 205 | + } |
| 206 | + // Paranoia: avoid brute force searches (bug 17342) |
| 207 | + $user = $this->getUser(); |
| 208 | + if( !$user->isAllowed( 'deletedhistory' ) ) { |
| 209 | + $this->mConds[] = $db->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0'; |
| 210 | + } elseif( !$user->isAllowed( 'suppressrevision' ) ) { |
| 211 | + $this->mConds[] = $db->bitAnd('log_deleted', LogPage::SUPPRESSED_ACTION) . |
| 212 | + ' != ' . LogPage::SUPPRESSED_ACTION; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * Constructs the most part of the query. Extra conditions are sprinkled in |
| 218 | + * all over this class. |
| 219 | + * @return array |
| 220 | + */ |
| 221 | + public function getQueryInfo() { |
| 222 | + $basic = DatabaseLogEntry::getSelectQueryData(); |
| 223 | + |
| 224 | + $tables = $basic['tables']; |
| 225 | + $fields = $basic['fields']; |
| 226 | + $conds = $basic['conds']; |
| 227 | + $options = $basic['options']; |
| 228 | + $joins = $basic['join_conds']; |
| 229 | + |
| 230 | + $index = array(); |
| 231 | + # Add log_search table if there are conditions on it. |
| 232 | + # This filters the results to only include log rows that have |
| 233 | + # log_search records with the specified ls_field and ls_value values. |
| 234 | + if( array_key_exists( 'ls_field', $this->mConds ) ) { |
| 235 | + $tables[] = 'log_search'; |
| 236 | + $index['log_search'] = 'ls_field_val'; |
| 237 | + $index['logging'] = 'PRIMARY'; |
| 238 | + if ( !$this->hasEqualsClause( 'ls_field' ) |
| 239 | + || !$this->hasEqualsClause( 'ls_value' ) ) |
| 240 | + { |
| 241 | + # Since (ls_field,ls_value,ls_logid) is unique, if the condition is |
| 242 | + # to match a specific (ls_field,ls_value) tuple, then there will be |
| 243 | + # no duplicate log rows. Otherwise, we need to remove the duplicates. |
| 244 | + $options[] = 'DISTINCT'; |
| 245 | + } |
| 246 | + # Avoid usage of the wrong index by limiting |
| 247 | + # the choices of available indexes. This mainly |
| 248 | + # avoids site-breaking filesorts. |
| 249 | + } elseif( $this->title || $this->pattern || $this->performer ) { |
| 250 | + $index['logging'] = array( 'page_time', 'user_time' ); |
| 251 | + if( count($this->types) == 1 ) { |
| 252 | + $index['logging'][] = 'log_user_type_time'; |
| 253 | + } |
| 254 | + } elseif( count($this->types) == 1 ) { |
| 255 | + $index['logging'] = 'type_time'; |
| 256 | + } else { |
| 257 | + $index['logging'] = 'times'; |
| 258 | + } |
| 259 | + $options['USE INDEX'] = $index; |
| 260 | + # Don't show duplicate rows when using log_search |
| 261 | + $joins['log_search'] = array( 'INNER JOIN', 'ls_log_id=log_id' ); |
| 262 | + |
| 263 | + $info = array( |
| 264 | + 'tables' => $tables, |
| 265 | + 'fields' => $fields, |
| 266 | + 'conds' => array_merge( $conds, $this->mConds ), |
| 267 | + 'options' => $options, |
| 268 | + 'join_conds' => $joins, |
| 269 | + ); |
| 270 | + # Add ChangeTags filter query |
| 271 | + ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], |
| 272 | + $info['join_conds'], $info['options'], $this->mTagFilter ); |
| 273 | + return $info; |
| 274 | + } |
| 275 | + |
| 276 | + /** |
| 277 | + * Checks if $this->mConds has $field matched to a *single* value |
| 278 | + * @param $field |
| 279 | + * @return bool |
| 280 | + */ |
| 281 | + protected function hasEqualsClause( $field ) { |
| 282 | + return ( |
| 283 | + array_key_exists( $field, $this->mConds ) && |
| 284 | + ( !is_array( $this->mConds[$field] ) || count( $this->mConds[$field] ) == 1 ) |
| 285 | + ); |
| 286 | + } |
| 287 | + |
| 288 | + function getIndexField() { |
| 289 | + return 'log_timestamp'; |
| 290 | + } |
| 291 | + |
| 292 | + public function getStartBody() { |
| 293 | + wfProfileIn( __METHOD__ ); |
| 294 | + # Do a link batch query |
| 295 | + if( $this->getNumRows() > 0 ) { |
| 296 | + $lb = new LinkBatch; |
| 297 | + foreach ( $this->mResult as $row ) { |
| 298 | + $lb->add( $row->log_namespace, $row->log_title ); |
| 299 | + $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) ); |
| 300 | + $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) ); |
| 301 | + } |
| 302 | + $lb->execute(); |
| 303 | + $this->mResult->seek( 0 ); |
| 304 | + } |
| 305 | + wfProfileOut( __METHOD__ ); |
| 306 | + return ''; |
| 307 | + } |
| 308 | + |
| 309 | + public function formatRow( $row ) { |
| 310 | + return $this->mLogEventsList->logLine( $row ); |
| 311 | + } |
| 312 | + |
| 313 | + public function getType() { |
| 314 | + return $this->types; |
| 315 | + } |
| 316 | + |
| 317 | + /** |
| 318 | + * @return string |
| 319 | + */ |
| 320 | + public function getPerformer() { |
| 321 | + return $this->performer; |
| 322 | + } |
| 323 | + |
| 324 | + /** |
| 325 | + * @return string |
| 326 | + */ |
| 327 | + public function getPage() { |
| 328 | + return $this->title; |
| 329 | + } |
| 330 | + |
| 331 | + public function getPattern() { |
| 332 | + return $this->pattern; |
| 333 | + } |
| 334 | + |
| 335 | + public function getYear() { |
| 336 | + return $this->mYear; |
| 337 | + } |
| 338 | + |
| 339 | + public function getMonth() { |
| 340 | + return $this->mMonth; |
| 341 | + } |
| 342 | + |
| 343 | + public function getTagFilter() { |
| 344 | + return $this->mTagFilter; |
| 345 | + } |
| 346 | + |
| 347 | + public function doQuery() { |
| 348 | + // Workaround MySQL optimizer bug |
| 349 | + $this->mDb->setBigSelects(); |
| 350 | + parent::doQuery(); |
| 351 | + $this->mDb->setBigSelects( 'default' ); |
| 352 | + } |
| 353 | +} |
Property changes on: trunk/phase3/includes/logging/LogPager.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 354 | + native |
Index: trunk/phase3/includes/logging/LogEventsList.php |
— | — | @@ -750,332 +750,3 @@ |
751 | 751 | return false; |
752 | 752 | } |
753 | 753 | } |
754 | | - |
755 | | -/** |
756 | | - * @ingroup Pager |
757 | | - */ |
758 | | -class LogPager extends ReverseChronologicalPager { |
759 | | - private $types = array(), $performer = '', $title = '', $pattern = ''; |
760 | | - private $typeCGI = ''; |
761 | | - public $mLogEventsList; |
762 | | - |
763 | | - /** |
764 | | - * Constructor |
765 | | - * |
766 | | - * @param $list LogEventsList |
767 | | - * @param $types String or Array: log types to show |
768 | | - * @param $performer String: the user who made the log entries |
769 | | - * @param $title String|Title: the page title the log entries are for |
770 | | - * @param $pattern String: do a prefix search rather than an exact title match |
771 | | - * @param $conds Array: extra conditions for the query |
772 | | - * @param $year Integer: the year to start from |
773 | | - * @param $month Integer: the month to start from |
774 | | - * @param $tagFilter String: tag |
775 | | - */ |
776 | | - public function __construct( $list, $types = array(), $performer = '', $title = '', $pattern = '', |
777 | | - $conds = array(), $year = false, $month = false, $tagFilter = '' ) { |
778 | | - parent::__construct( $list->getContext() ); |
779 | | - $this->mConds = $conds; |
780 | | - |
781 | | - $this->mLogEventsList = $list; |
782 | | - |
783 | | - $this->limitType( $types ); // also excludes hidden types |
784 | | - $this->limitPerformer( $performer ); |
785 | | - $this->limitTitle( $title, $pattern ); |
786 | | - $this->getDateCond( $year, $month ); |
787 | | - $this->mTagFilter = $tagFilter; |
788 | | - } |
789 | | - |
790 | | - public function getDefaultQuery() { |
791 | | - $query = parent::getDefaultQuery(); |
792 | | - $query['type'] = $this->typeCGI; // arrays won't work here |
793 | | - $query['user'] = $this->performer; |
794 | | - $query['month'] = $this->mMonth; |
795 | | - $query['year'] = $this->mYear; |
796 | | - return $query; |
797 | | - } |
798 | | - |
799 | | - // Call ONLY after calling $this->limitType() already! |
800 | | - public function getFilterParams() { |
801 | | - global $wgFilterLogTypes; |
802 | | - $filters = array(); |
803 | | - if( count($this->types) ) { |
804 | | - return $filters; |
805 | | - } |
806 | | - foreach( $wgFilterLogTypes as $type => $default ) { |
807 | | - // Avoid silly filtering |
808 | | - if( $type !== 'patrol' || $this->getUser()->useNPPatrol() ) { |
809 | | - $hide = $this->getRequest()->getInt( "hide_{$type}_log", $default ); |
810 | | - $filters[$type] = $hide; |
811 | | - if( $hide ) |
812 | | - $this->mConds[] = 'log_type != ' . $this->mDb->addQuotes( $type ); |
813 | | - } |
814 | | - } |
815 | | - return $filters; |
816 | | - } |
817 | | - |
818 | | - /** |
819 | | - * Set the log reader to return only entries of the given type. |
820 | | - * Type restrictions enforced here |
821 | | - * |
822 | | - * @param $types String or array: Log types ('upload', 'delete', etc); |
823 | | - * empty string means no restriction |
824 | | - */ |
825 | | - private function limitType( $types ) { |
826 | | - global $wgLogRestrictions; |
827 | | - // If $types is not an array, make it an array |
828 | | - $types = ($types === '') ? array() : (array)$types; |
829 | | - // Don't even show header for private logs; don't recognize it... |
830 | | - $needReindex = false; |
831 | | - foreach ( $types as $type ) { |
832 | | - if( isset( $wgLogRestrictions[$type] ) |
833 | | - && !$this->getUser()->isAllowed($wgLogRestrictions[$type]) |
834 | | - ) { |
835 | | - $needReindex = true; |
836 | | - $types = array_diff( $types, array( $type ) ); |
837 | | - } |
838 | | - } |
839 | | - if ( $needReindex ) { |
840 | | - // Lots of this code makes assumptions that |
841 | | - // the first entry in the array is $types[0]. |
842 | | - $types = array_values( $types ); |
843 | | - } |
844 | | - $this->types = $types; |
845 | | - // Don't show private logs to unprivileged users. |
846 | | - // Also, only show them upon specific request to avoid suprises. |
847 | | - $audience = $types ? 'user' : 'public'; |
848 | | - $hideLogs = LogEventsList::getExcludeClause( $this->mDb, $audience ); |
849 | | - if( $hideLogs !== false ) { |
850 | | - $this->mConds[] = $hideLogs; |
851 | | - } |
852 | | - if( count($types) ) { |
853 | | - $this->mConds['log_type'] = $types; |
854 | | - // Set typeCGI; used in url param for paging |
855 | | - if( count($types) == 1 ) $this->typeCGI = $types[0]; |
856 | | - } |
857 | | - } |
858 | | - |
859 | | - /** |
860 | | - * Set the log reader to return only entries by the given user. |
861 | | - * |
862 | | - * @param $name String: (In)valid user name |
863 | | - */ |
864 | | - private function limitPerformer( $name ) { |
865 | | - if( $name == '' ) { |
866 | | - return false; |
867 | | - } |
868 | | - $usertitle = Title::makeTitleSafe( NS_USER, $name ); |
869 | | - if( is_null($usertitle) ) { |
870 | | - return false; |
871 | | - } |
872 | | - /* Fetch userid at first, if known, provides awesome query plan afterwards */ |
873 | | - $userid = User::idFromName( $name ); |
874 | | - if( !$userid ) { |
875 | | - /* It should be nicer to abort query at all, |
876 | | - but for now it won't pass anywhere behind the optimizer */ |
877 | | - $this->mConds[] = "NULL"; |
878 | | - } else { |
879 | | - $this->mConds['log_user'] = $userid; |
880 | | - // Paranoia: avoid brute force searches (bug 17342) |
881 | | - $user = $this->getUser(); |
882 | | - if( !$user->isAllowed( 'deletedhistory' ) ) { |
883 | | - $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::DELETED_USER) . ' = 0'; |
884 | | - } elseif( !$user->isAllowed( 'suppressrevision' ) ) { |
885 | | - $this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::SUPPRESSED_USER) . |
886 | | - ' != ' . LogPage::SUPPRESSED_USER; |
887 | | - } |
888 | | - $this->performer = $usertitle->getText(); |
889 | | - } |
890 | | - } |
891 | | - |
892 | | - /** |
893 | | - * Set the log reader to return only entries affecting the given page. |
894 | | - * (For the block and rights logs, this is a user page.) |
895 | | - * |
896 | | - * @param $page String or Title object: Title name |
897 | | - * @param $pattern String |
898 | | - */ |
899 | | - private function limitTitle( $page, $pattern ) { |
900 | | - global $wgMiserMode; |
901 | | - |
902 | | - if ( $page instanceof Title ) { |
903 | | - $title = $page; |
904 | | - } else { |
905 | | - $title = Title::newFromText( $page ); |
906 | | - if( strlen( $page ) == 0 || !$title instanceof Title ) { |
907 | | - return false; |
908 | | - } |
909 | | - } |
910 | | - |
911 | | - $this->title = $title->getPrefixedText(); |
912 | | - $ns = $title->getNamespace(); |
913 | | - $db = $this->mDb; |
914 | | - |
915 | | - # Using the (log_namespace, log_title, log_timestamp) index with a |
916 | | - # range scan (LIKE) on the first two parts, instead of simple equality, |
917 | | - # makes it unusable for sorting. Sorted retrieval using another index |
918 | | - # would be possible, but then we might have to scan arbitrarily many |
919 | | - # nodes of that index. Therefore, we need to avoid this if $wgMiserMode |
920 | | - # is on. |
921 | | - # |
922 | | - # This is not a problem with simple title matches, because then we can |
923 | | - # use the page_time index. That should have no more than a few hundred |
924 | | - # log entries for even the busiest pages, so it can be safely scanned |
925 | | - # in full to satisfy an impossible condition on user or similar. |
926 | | - if( $pattern && !$wgMiserMode ) { |
927 | | - $this->mConds['log_namespace'] = $ns; |
928 | | - $this->mConds[] = 'log_title ' . $db->buildLike( $title->getDBkey(), $db->anyString() ); |
929 | | - $this->pattern = $pattern; |
930 | | - } else { |
931 | | - $this->mConds['log_namespace'] = $ns; |
932 | | - $this->mConds['log_title'] = $title->getDBkey(); |
933 | | - } |
934 | | - // Paranoia: avoid brute force searches (bug 17342) |
935 | | - $user = $this->getUser(); |
936 | | - if( !$user->isAllowed( 'deletedhistory' ) ) { |
937 | | - $this->mConds[] = $db->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0'; |
938 | | - } elseif( !$user->isAllowed( 'suppressrevision' ) ) { |
939 | | - $this->mConds[] = $db->bitAnd('log_deleted', LogPage::SUPPRESSED_ACTION) . |
940 | | - ' != ' . LogPage::SUPPRESSED_ACTION; |
941 | | - } |
942 | | - } |
943 | | - |
944 | | - /** |
945 | | - * Constructs the most part of the query. Extra conditions are sprinkled in |
946 | | - * all over this class. |
947 | | - * @return array |
948 | | - */ |
949 | | - public function getQueryInfo() { |
950 | | - $basic = DatabaseLogEntry::getSelectQueryData(); |
951 | | - |
952 | | - $tables = $basic['tables']; |
953 | | - $fields = $basic['fields']; |
954 | | - $conds = $basic['conds']; |
955 | | - $options = $basic['options']; |
956 | | - $joins = $basic['join_conds']; |
957 | | - |
958 | | - $index = array(); |
959 | | - # Add log_search table if there are conditions on it. |
960 | | - # This filters the results to only include log rows that have |
961 | | - # log_search records with the specified ls_field and ls_value values. |
962 | | - if( array_key_exists( 'ls_field', $this->mConds ) ) { |
963 | | - $tables[] = 'log_search'; |
964 | | - $index['log_search'] = 'ls_field_val'; |
965 | | - $index['logging'] = 'PRIMARY'; |
966 | | - if ( !$this->hasEqualsClause( 'ls_field' ) |
967 | | - || !$this->hasEqualsClause( 'ls_value' ) ) |
968 | | - { |
969 | | - # Since (ls_field,ls_value,ls_logid) is unique, if the condition is |
970 | | - # to match a specific (ls_field,ls_value) tuple, then there will be |
971 | | - # no duplicate log rows. Otherwise, we need to remove the duplicates. |
972 | | - $options[] = 'DISTINCT'; |
973 | | - } |
974 | | - # Avoid usage of the wrong index by limiting |
975 | | - # the choices of available indexes. This mainly |
976 | | - # avoids site-breaking filesorts. |
977 | | - } elseif( $this->title || $this->pattern || $this->performer ) { |
978 | | - $index['logging'] = array( 'page_time', 'user_time' ); |
979 | | - if( count($this->types) == 1 ) { |
980 | | - $index['logging'][] = 'log_user_type_time'; |
981 | | - } |
982 | | - } elseif( count($this->types) == 1 ) { |
983 | | - $index['logging'] = 'type_time'; |
984 | | - } else { |
985 | | - $index['logging'] = 'times'; |
986 | | - } |
987 | | - $options['USE INDEX'] = $index; |
988 | | - # Don't show duplicate rows when using log_search |
989 | | - $joins['log_search'] = array( 'INNER JOIN', 'ls_log_id=log_id' ); |
990 | | - |
991 | | - $info = array( |
992 | | - 'tables' => $tables, |
993 | | - 'fields' => $fields, |
994 | | - 'conds' => array_merge( $conds, $this->mConds ), |
995 | | - 'options' => $options, |
996 | | - 'join_conds' => $joins, |
997 | | - ); |
998 | | - # Add ChangeTags filter query |
999 | | - ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], |
1000 | | - $info['join_conds'], $info['options'], $this->mTagFilter ); |
1001 | | - return $info; |
1002 | | - } |
1003 | | - |
1004 | | - /** |
1005 | | - * Checks if $this->mConds has $field matched to a *single* value |
1006 | | - * @param $field |
1007 | | - * @return bool |
1008 | | - */ |
1009 | | - protected function hasEqualsClause( $field ) { |
1010 | | - return ( |
1011 | | - array_key_exists( $field, $this->mConds ) && |
1012 | | - ( !is_array( $this->mConds[$field] ) || count( $this->mConds[$field] ) == 1 ) |
1013 | | - ); |
1014 | | - } |
1015 | | - |
1016 | | - function getIndexField() { |
1017 | | - return 'log_timestamp'; |
1018 | | - } |
1019 | | - |
1020 | | - public function getStartBody() { |
1021 | | - wfProfileIn( __METHOD__ ); |
1022 | | - # Do a link batch query |
1023 | | - if( $this->getNumRows() > 0 ) { |
1024 | | - $lb = new LinkBatch; |
1025 | | - foreach ( $this->mResult as $row ) { |
1026 | | - $lb->add( $row->log_namespace, $row->log_title ); |
1027 | | - $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) ); |
1028 | | - $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) ); |
1029 | | - } |
1030 | | - $lb->execute(); |
1031 | | - $this->mResult->seek( 0 ); |
1032 | | - } |
1033 | | - wfProfileOut( __METHOD__ ); |
1034 | | - return ''; |
1035 | | - } |
1036 | | - |
1037 | | - public function formatRow( $row ) { |
1038 | | - return $this->mLogEventsList->logLine( $row ); |
1039 | | - } |
1040 | | - |
1041 | | - public function getType() { |
1042 | | - return $this->types; |
1043 | | - } |
1044 | | - |
1045 | | - /** |
1046 | | - * @return string |
1047 | | - */ |
1048 | | - public function getPerformer() { |
1049 | | - return $this->performer; |
1050 | | - } |
1051 | | - |
1052 | | - /** |
1053 | | - * @return string |
1054 | | - */ |
1055 | | - public function getPage() { |
1056 | | - return $this->title; |
1057 | | - } |
1058 | | - |
1059 | | - public function getPattern() { |
1060 | | - return $this->pattern; |
1061 | | - } |
1062 | | - |
1063 | | - public function getYear() { |
1064 | | - return $this->mYear; |
1065 | | - } |
1066 | | - |
1067 | | - public function getMonth() { |
1068 | | - return $this->mMonth; |
1069 | | - } |
1070 | | - |
1071 | | - public function getTagFilter() { |
1072 | | - return $this->mTagFilter; |
1073 | | - } |
1074 | | - |
1075 | | - public function doQuery() { |
1076 | | - // Workaround MySQL optimizer bug |
1077 | | - $this->mDb->setBigSelects(); |
1078 | | - parent::doQuery(); |
1079 | | - $this->mDb->setBigSelects( 'default' ); |
1080 | | - } |
1081 | | -} |
1082 | | - |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -593,7 +593,7 @@ |
594 | 594 | 'LogEntryBase' => 'includes/logging/LogEntry.php', |
595 | 595 | 'LogFormatter' => 'includes/logging/LogFormatter.php', |
596 | 596 | 'LogPage' => 'includes/logging/LogPage.php', |
597 | | - 'LogPager' => 'includes/logging/LogEventsList.php', |
| 597 | + 'LogPager' => 'includes/logging/LogPager.php', |
598 | 598 | 'ManualLogEntry' => 'includes/logging/LogEntry.php', |
599 | 599 | 'MoveLogFormatter' => 'includes/logging/LogFormatter.php', |
600 | 600 | 'NewUsersLogFormatter' => 'includes/logging/LogFormatter.php', |