X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Fspecials%2Fpagers%2FContribsPager.php;h=979460cf8aa5d09c0bc870625393f2c9aa50c5bb;hp=d7819c42b3405362864db11c70358156c26657e3;hb=7dbe3faa67045255d092c6a407b7b5ad46c01db1;hpb=a10b14d78a4589fbdfe33bc9c9c0a8ab85bd626e diff --git a/includes/specials/pagers/ContribsPager.php b/includes/specials/pagers/ContribsPager.php index d7819c42b3..979460cf8a 100644 --- a/includes/specials/pagers/ContribsPager.php +++ b/includes/specials/pagers/ContribsPager.php @@ -87,6 +87,10 @@ class ContribsPager extends RangeChronologicalPager { } $this->getDateRangeCond( $startTimestamp, $endTimestamp ); + // This property on IndexPager is set by $this->getIndexField() in parent::__construct(). + // We need to reassign it here so that it is used when the actual query is ran. + $this->mIndexField = $this->getIndexField(); + // Most of this code will use the 'contributions' group DB, which can map to replica DBs // with extra user based indexes or partioning by user. The additional metadata // queries should use a regular replica DB since the lookup pattern is not all by user. @@ -207,6 +211,12 @@ class ContribsPager extends RangeChronologicalPager { 'join_conds' => $join_cond ]; + // For IPv6, we use ipc_rev_timestamp on ip_changes as the index field, + // which will be referenced when parsing the results of a query. + if ( self::isQueryableRange( $this->target ) ) { + $queryInfo['fields'][] = 'ipc_rev_timestamp'; + } + ChangeTags::modifyDisplayQuery( $queryInfo['tables'], $queryInfo['fields'], @@ -257,8 +267,18 @@ class ContribsPager extends RangeChronologicalPager { $condition['rev_user'] = $uid; $index = 'user_timestamp'; } else { - $condition['rev_user_text'] = $this->target; - $index = 'usertext_timestamp'; + $ipRangeConds = $this->getIpRangeConds( $this->mDb, $this->target ); + + if ( $ipRangeConds ) { + $tables[] = 'ip_changes'; + $join_conds['ip_changes'] = [ + 'LEFT JOIN', [ 'ipc_rev_id = rev_id' ] + ]; + $condition[] = $ipRangeConds; + } else { + $condition['rev_user_text'] = $this->target; + $index = 'usertext_timestamp'; + } } } @@ -305,8 +325,57 @@ class ContribsPager extends RangeChronologicalPager { return []; } - function getIndexField() { - return 'rev_timestamp'; + /** + * Get SQL conditions for an IP range, if applicable + * @param IDatabase $db + * @param string $ip The IP address or CIDR + * @return string|false SQL for valid IP ranges, false if invalid + */ + private function getIpRangeConds( $db, $ip ) { + // First make sure it is a valid range and they are not outside the CIDR limit + if ( !$this->isQueryableRange( $ip ) ) { + return false; + } + + list( $start, $end ) = IP::parseRange( $ip ); + + return 'ipc_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end ); + } + + /** + * Is the given IP a range and within the CIDR limit? + * + * @param string $ipRange + * @return bool True if it is valid + * @since 1.30 + */ + public function isQueryableRange( $ipRange ) { + $limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' ); + + $bits = IP::parseCIDR( $ipRange )[1]; + if ( + ( $bits === false ) || + ( IP::isIPv4( $ipRange ) && $bits < $limits['IPv4'] ) || + ( IP::isIPv6( $ipRange ) && $bits < $limits['IPv6'] ) + ) { + return false; + } + + return true; + } + + /** + * Override of getIndexField() in IndexPager. + * For IP ranges, it's faster to use the replicated ipc_rev_timestamp + * on the `ip_changes` table than the rev_timestamp on the `revision` table. + * @return string Name of field + */ + public function getIndexField() { + if ( $this->isQueryableRange( $this->target ) ) { + return 'ipc_rev_timestamp'; + } else { + return 'rev_timestamp'; + } } function doBatchLookups() { @@ -315,6 +384,7 @@ class ContribsPager extends RangeChronologicalPager { $parentRevIds = []; $this->mParentLens = []; $batch = new LinkBatch(); + $isIpRange = $this->isQueryableRange( $this->target ); # Give some pointers to make (last) links foreach ( $this->mResult as $row ) { if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) { @@ -325,6 +395,9 @@ class ContribsPager extends RangeChronologicalPager { if ( $this->contribs === 'newbie' ) { // multiple users $batch->add( NS_USER, $row->user_name ); $batch->add( NS_USER_TALK, $row->user_name ); + } elseif ( $isIpRange ) { + // If this is an IP range, batch the IP's talk page + $batch->add( NS_USER_TALK, $row->rev_user_text ); } $batch->add( $row->page_namespace, $row->page_title ); } @@ -400,6 +473,7 @@ class ContribsPager extends RangeChronologicalPager { # Mark current revisions $topmarktext = ''; $user = $this->getUser(); + if ( $row->rev_id === $row->page_latest ) { $topmarktext .= '' . $this->messages['uctop'] . ''; $classes[] = 'mw-contributions-current'; @@ -473,8 +547,10 @@ class ContribsPager extends RangeChronologicalPager { # Show user names for /newbies as there may be different users. # Note that only unprivileged users have rows with hidden user names excluded. + # When querying for an IP range, we want to always show user and user talk links. $userlink = ''; - if ( $this->contribs == 'newbie' && !$rev->isDeleted( Revision::DELETED_USER ) ) { + if ( ( $this->contribs == 'newbie' && !$rev->isDeleted( Revision::DELETED_USER ) ) + || $this->isQueryableRange( $this->target ) ) { $userlink = ' . . ' . $lang->getDirMark() . Linker::userLink( $rev->getUser(), $rev->getUserText() ); $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(