Merge "Code to read from change_tag_def table instead of valid_tag"
[lhc/web/wiklou.git] / includes / changetags / ChangeTags.php
index dd29c10..8dc63e5 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\NameTableAccessException;
 use Wikimedia\Rdbms\Database;
 
 class ChangeTags {
@@ -87,6 +88,7 @@ class ChangeTags {
         * @return array Array with two items: (html, classes)
         *   - html: String: HTML for displaying the tags (empty string when param $tags is empty)
         *   - classes: Array of strings: CSS classes used in the generated html, one class for each tag
+        * @return-taint onlysafefor_htmlnoent
         */
        public static function formatSummaryRow( $tags, $page, IContextSource $context = null ) {
                if ( !$tags ) {
@@ -342,11 +344,10 @@ class ChangeTags {
                }
 
                // insert a row into change_tag for each new tag
+               $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
                if ( count( $tagsToAdd ) ) {
                        $changeTagMapping = [];
                        if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_OLD ) {
-                               $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
-
                                foreach ( $tagsToAdd as $tag ) {
                                        $changeTagMapping[$tag] = $changeTagDefStore->acquireId( $tag );
                                }
@@ -361,13 +362,18 @@ class ChangeTags {
 
                        $tagsRows = [];
                        foreach ( $tagsToAdd as $tag ) {
+                               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                                       $tagName = null;
+                               } else {
+                                       $tagName = $tag;
+                               }
                                // Filter so we don't insert NULLs as zero accidentally.
                                // Keep in mind that $rc_id === null means "I don't care/know about the
                                // rc_id, just delete $tag on this revision/log entry". It doesn't
                                // mean "only delete tags on this revision/log WHERE rc_id IS NULL".
                                $tagsRows[] = array_filter(
                                        [
-                                               'ct_tag' => $tag,
+                                               'ct_tag' => $tagName,
                                                'ct_rc_id' => $rc_id,
                                                'ct_log_id' => $log_id,
                                                'ct_rev_id' => $rev_id,
@@ -384,12 +390,20 @@ class ChangeTags {
                // delete from change_tag
                if ( count( $tagsToRemove ) ) {
                        foreach ( $tagsToRemove as $tag ) {
+                               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                                       $tagName = null;
+                                       $tagId = $changeTagDefStore->getId( $tag );
+                               } else {
+                                       $tagName = $tag;
+                                       $tagId = null;
+                               }
                                $conds = array_filter(
                                        [
-                                               'ct_tag' => $tag,
+                                               'ct_tag' => $tagName,
                                                'ct_rc_id' => $rc_id,
                                                'ct_log_id' => $log_id,
-                                               'ct_rev_id' => $rev_id
+                                               'ct_rev_id' => $rev_id,
+                                               'ct_tag_id' => $tagId,
                                        ]
                                );
                                $dbw->delete( 'change_tag', $conds, __METHOD__ );
@@ -769,7 +783,7 @@ class ChangeTags {
        public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
                &$join_conds, &$options, $filter_tag = ''
        ) {
-               global $wgUseTagFilter;
+               global $wgUseTagFilter, $wgChangeTagsSchemaMigrationStage;
 
                // Normalize to arrays
                $tables = (array)$tables;
@@ -790,8 +804,18 @@ class ChangeTags {
                        throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
                }
 
+               $tagTables[] = 'change_tag';
+               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       $tagTables[] = 'change_tag_def';
+                       $join_cond_ts_tags = [ $join_cond, 'ct_tag_id=ctd_id' ];
+                       $field = 'ctd_name';
+               } else {
+                       $field = 'ct_tag';
+                       $join_cond_ts_tags = $join_cond;
+               }
+
                $fields['ts_tags'] = wfGetDB( DB_REPLICA )->buildGroupConcatField(
-                       ',', 'change_tag', 'ct_tag', $join_cond
+                       ',', $tagTables, $field, $join_cond_ts_tags
                );
 
                if ( $wgUseTagFilter && $filter_tag ) {
@@ -800,7 +824,23 @@ class ChangeTags {
 
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
-                       $conds['ct_tag'] = $filter_tag;
+                       if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                               $filterTagIds = [];
+                               $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
+                               foreach ( (array)$filter_tag as $filterTagName ) {
+                                       try {
+                                               $filterTagIds[] = $changeTagDefStore->getId( $filterTagName );
+                                       } catch ( NameTableAccessException $exception ) {
+                                               // Return nothing.
+                                               $conds[] = '0';
+                                               break;
+                                       };
+                               }
+                               $conds['ct_tag_id'] = $filterTagIds;
+                       } else {
+                               $conds['ct_tag'] = $filter_tag;
+                       }
+
                        if (
                                is_array( $filter_tag ) && count( $filter_tag ) > 1 &&
                                !in_array( 'DISTINCT', $options )
@@ -888,13 +928,14 @@ class ChangeTags {
                        );
                }
 
-               $dbw->replace(
-                       'valid_tag',
-                       [ 'vt_tag' ],
-                       [ 'vt_tag' => $tag ],
-                       __METHOD__
-               );
-
+               if ( $wgChangeTagsSchemaMigrationStage < MIGRATION_NEW ) {
+                       $dbw->replace(
+                               'valid_tag',
+                               [ 'vt_tag' ],
+                               [ 'vt_tag' => $tag ],
+                               __METHOD__
+                       );
+               }
                // clear the memcache of defined tags
                self::purgeTagCacheAll();
        }
@@ -927,7 +968,9 @@ class ChangeTags {
                        );
                }
 
-               $dbw->delete( 'valid_tag', [ 'vt_tag' => $tag ], __METHOD__ );
+               if ( $wgChangeTagsSchemaMigrationStage < MIGRATION_NEW ) {
+                       $dbw->delete( 'valid_tag', [ 'vt_tag' => $tag ], __METHOD__ );
+               }
 
                // clear the memcache of defined tags
                self::purgeTagCacheAll();
@@ -1236,10 +1279,17 @@ class ChangeTags {
                // delete from valid_tag and/or set ctd_user_defined = 0
                self::undefineTag( $tag );
 
+               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       $tagId = MediaWikiServices::getInstance()->getChangeTagDefStore()->getId( $tag );
+                       $conditions = [ 'ct_tag_id' => $tagId ];
+               } else {
+                       $conditions = [ 'ct_tag' => $tag ];
+               }
+
                // find out which revisions use this tag, so we can delete from tag_summary
                $result = $dbw->select( 'change_tag',
-                       [ 'ct_rc_id', 'ct_log_id', 'ct_rev_id', 'ct_tag' ],
-                       [ 'ct_tag' => $tag ],
+                       [ 'ct_rc_id', 'ct_log_id', 'ct_rev_id' ],
+                       $conditions,
                        __METHOD__ );
                foreach ( $result as $row ) {
                        // remove the tag from the relevant row of tag_summary
@@ -1250,7 +1300,12 @@ class ChangeTags {
                }
 
                // delete from change_tag
-               $dbw->delete( 'change_tag', [ 'ct_tag' => $tag ], __METHOD__ );
+               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       $tagId = MediaWikiServices::getInstance()->getChangeTagDefStore()->getId( $tag );
+                       $dbw->delete( 'change_tag', [ 'ct_tag_id' => $tagId ], __METHOD__ );
+               } else {
+                       $dbw->delete( 'change_tag', [ 'ct_tag' => $tag ], __METHOD__ );
+               }
 
                if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_OLD ) {
                        $dbw->delete( 'change_tag_def', [ 'ctd_name' => $tag ], __METHOD__ );
@@ -1406,7 +1461,7 @@ class ChangeTags {
        /**
         * Lists tags explicitly defined in the `valid_tag` table of the database.
         * Tags in table 'change_tag' which are not in table 'valid_tag' are not
-        * included.
+        * included. In case of new backend loads the data from `change_tag_def` table.
         *
         * Tries memcached first.
         *
@@ -1421,11 +1476,16 @@ class ChangeTags {
                        $cache->makeKey( 'valid-tags-db' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
+                               global $wgChangeTagsSchemaMigrationStage;
                                $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
-                               $tags = $dbr->selectFieldValues( 'valid_tag', 'vt_tag', [], $fname );
+                               if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                                       $tags = self::listExplicitlyDefinedTagsNewBackend();
+                               } else {
+                                       $tags = $dbr->selectFieldValues( 'valid_tag', 'vt_tag', [], $fname );
+                               }
 
                                return array_filter( array_unique( $tags ) );
                        },
@@ -1437,6 +1497,22 @@ class ChangeTags {
                );
        }
 
+       /**
+        * Lists tags explicitly user defined tags. When ctd_user_defined is true.
+        *
+        * @return string[] Array of strings: tags
+        * @since 1.25
+        */
+       private static function listExplicitlyDefinedTagsNewBackend() {
+               $dbr = wfGetDB( DB_REPLICA );
+               return $dbr->selectFieldValues(
+                       'change_tag_def',
+                       'ctd_name',
+                       [ 'ctd_user_defined' => 1 ],
+                       __METHOD__
+               );
+       }
+
        /**
         * Lists tags defined by core or extensions using the ListDefinedTags hook.
         * Extensions need only define those tags they deem to be in active use.