Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
RevTagStore.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
7use MediaWiki\Page\PageIdentity;
8use Wikimedia\Rdbms\IConnectionProvider;
9use Wikimedia\Rdbms\RawSQLExpression;
10use Wikimedia\Rdbms\SelectQueryBuilder;
11
21 public const FUZZY_TAG = 'fuzzy';
23 public const TRANSVER_PROP = 'tp:transver';
25 public const TP_MARK_TAG = 'tp:mark';
27 public const TP_READY_TAG = 'tp:tag';
29 public const MB_VALID_TAG = 'mb:valid';
30
31 private array $tagCache = [];
32
33 public function __construct(
34 private readonly IConnectionProvider $dbProvider,
35 ) {
36 }
37
39 public function replaceTag(
40 PageIdentity $identity,
41 string $tag,
42 int $revisionId,
43 ?array $value = null
44 ): void {
45 if ( !$identity->exists() ) {
46 return;
47 }
48
49 $articleId = $identity->getId();
50
51 $dbw = $this->dbProvider->getPrimaryDatabase();
52 $conditions = [
53 'rt_page' => $articleId,
54 'rt_type' => $tag
55 ];
56 $dbw->newDeleteQueryBuilder()
57 ->deleteFrom( 'revtag' )
58 ->where( $conditions )
59 ->caller( __METHOD__ )
60 ->execute();
61
62 if ( $value !== null ) {
63 $conditions['rt_value'] = serialize( implode( '|', $value ) );
64 }
65
66 $conditions['rt_revision'] = $revisionId;
67 $dbw->newInsertQueryBuilder()
68 ->insertInto( 'revtag' )
69 ->row( $conditions )
70 ->caller( __METHOD__ )
71 ->execute();
72
73 $this->tagCache[$articleId][$tag] = $revisionId;
74 }
75
76 public function getLatestRevisionWithTag( PageIdentity $identity, string $tag ): ?int {
77 $response = $this->getLatestRevisionsForTags( $identity, $tag );
78 return $response[$tag] ?? null;
79 }
80
82 public function getLatestRevisionsForTags( PageIdentity $identity, string ...$tags ): ?array {
83 if ( !$identity->exists() ) {
84 return null;
85 }
86
87 $articleId = $identity->getId();
88
89 $response = [];
90 $remainingTags = [];
91
92 // ATTENTION: Cache should only be updated on POST requests.
93 foreach ( $tags as $tag ) {
94 if ( isset( $this->tagCache[$articleId][$tag] ) ) {
95 $response[$tag] = $this->tagCache[$articleId][$tag];
96 } else {
97 $remainingTags[] = $tag;
98 }
99 }
100
101 if ( !$remainingTags ) {
102 // All tags were available in the cache, no need to run any queries.
103 return $response;
104 }
105
106 $dbr = Utilities::getSafeReadDB();
107 $results = $dbr->newSelectQueryBuilder()
108 ->select( [ 'rt_revision' => 'MAX(rt_revision)', 'rt_type' ] )
109 ->from( 'revtag' )
110 ->where( [
111 'rt_page' => $articleId,
112 'rt_type' => $remainingTags
113 ] )
114 ->groupBy( 'rt_type' )
115 ->caller( __METHOD__ )
116 ->fetchResultSet();
117
118 foreach ( $results as $row ) {
119 $response[$row->rt_type] = (int)$row->rt_revision;
120 }
121
122 return $response;
123 }
124
125 public function removeTags( PageIdentity $identity, string ...$tag ): void {
126 if ( !$identity->exists() ) {
127 return;
128 }
129
130 $articleId = $identity->getId();
131
132 $dbw = $this->dbProvider->getPrimaryDatabase();
133 $dbw->newDeleteQueryBuilder()
134 ->deleteFrom( 'revtag' )
135 ->where( [
136 'rt_page' => $articleId,
137 'rt_type' => $tag,
138 ] )
139 ->caller( __METHOD__ )
140 ->execute();
141
142 unset( $this->tagCache[$articleId] );
143 }
144
145 public function isRevIdFuzzy( int $articleId, int $revisionId ): bool {
146 $dbw = $this->dbProvider->getPrimaryDatabase();
147 $res = $dbw->newSelectQueryBuilder()
148 ->select( 'rt_type' )
149 ->from( 'revtag' )
150 ->where( [
151 'rt_page' => $articleId,
152 'rt_type' => self::FUZZY_TAG,
153 'rt_revision' => $revisionId
154 ] )
155 ->caller( __METHOD__ )
156 ->fetchField();
157
158 return $res !== false;
159 }
160
173 public function getTransver( PageIdentity $identity, ?int $revid = null ): ?int {
174 $db = Utilities::getSafeReadDB();
175 $query = $db->newSelectQueryBuilder()
176 ->select( 'rt_value' )
177 ->from( 'revtag' )
178 ->where( [
179 'rt_page' => $identity->getId(),
180 'rt_type' => self::TRANSVER_PROP,
181 ] );
182 if ( $revid !== null ) {
183 $query->where( $db->expr( 'rt_revision', '<=', $revid ) );
184 }
185 $result = $query
186 ->orderBy( 'rt_revision', SelectQueryBuilder::SORT_DESC )
187 ->caller( __METHOD__ )
188 ->fetchField();
189 // The revtag database is defined to store a string in rt_value,
190 // but tp:transver is always an integer
191 return $result ? (int)$result : null;
192 }
193
201 public function setTransver( PageIdentity $identity, int $translationRevision, int $transver ): void {
202 $dbw = $this->dbProvider->getPrimaryDatabase();
203
204 $conds = [
205 'rt_page' => $identity->getId(),
206 'rt_type' => self::TRANSVER_PROP,
207 'rt_revision' => $translationRevision,
208 'rt_value' => $transver,
209 ];
210 $dbw->newReplaceQueryBuilder()
211 ->replaceInto( 'revtag' )
212 ->uniqueIndexFields( [ 'rt_type', 'rt_page', 'rt_revision' ] )
213 ->row( $conds )
214 ->caller( __METHOD__ )
215 ->execute();
216 }
217
226 public static function getTranslatableBundleIds( array $revTagsLatest, array $revTagsAny = [] ): array {
227 $dbr = Utilities::getSafeReadDB();
228 $expr = $dbr->andExpr( [
229 new RawSQLExpression( 'rt_revision = page_latest' ),
230 'rt_type' => $revTagsLatest
231 ] );
232 if ( $revTagsAny ) {
233 $expr = $dbr->orExpr( [ $expr, 'rt_type' => $revTagsAny ] );
234 }
235 $pageIds = $dbr->newSelectQueryBuilder()
236 ->select( 'rt_page' )
237 ->from( 'revtag' )
238 ->join(
239 'page',
240 null,
241 [
242 'rt_page = page_id',
243 $expr
244 ]
245 )
246 ->groupBy( 'rt_page' )
247 ->caller( __METHOD__ )
248 ->fetchFieldValues();
249 return array_map( intval( ... ), $pageIds );
250 }
251}
Class to manage revision tags for translatable bundles.
getTransver(PageIdentity $identity, ?int $revid=null)
Get the revision ID of the original message that was live the last time a non-fuzzy translation was s...
const TRANSVER_PROP
Stores the revision id of the corresponding source text.
static getTranslatableBundleIds(array $revTagsLatest, array $revTagsAny=[])
Get a list of page ids where the latest revision has one of the tags in revTagsLatest,...
const MB_VALID_TAG
Indicates a revision of a page that is a valid message bundle.
const FUZZY_TAG
Indicates that a translation is fuzzy (outdated or not passing validation).
setTransver(PageIdentity $identity, int $translationRevision, int $transver)
Sets the tp:transver revtag of the given page/revision.
replaceTag(PageIdentity $identity, string $tag, int $revisionId, ?array $value=null)
Add tag for the given revisionId, while deleting it from others.
const TP_READY_TAG
Indicates a revision of a translatable page that is marked for translation.
const TP_MARK_TAG
Indicates a revision of a page that can be marked for translation.
getLatestRevisionsForTags(PageIdentity $identity, string ... $tags)
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:29