21 private const MAX_ITEMS_PER_QUERY = 2000;
23 private array $cache = [];
24 private ?array $priorityCache =
null;
25 private IConnectionProvider $dbProvider;
27 public function __construct( IConnectionProvider $dbProvider ) {
28 $this->dbProvider = $dbProvider;
31 public function preloadGroups( array $groups,
string $caller ):
void {
32 $dbGroupIds = array_map( [ $this,
'getGroupIdForDatabase' ], $groups );
33 $missing = array_keys( array_diff_key( array_flip( $dbGroupIds ), $this->cache ) );
38 $functionName = __METHOD__ .
" (for $caller)";
40 $this->cache += array_fill_keys( $missing,
null );
43 $dbr = Utilities::getSafeReadDB();
44 $chunks = array_chunk( $missing, self::MAX_ITEMS_PER_QUERY );
45 foreach ( $chunks as $chunk ) {
46 $res = $dbr->newSelectQueryBuilder()
47 ->select( [
'tmd_group',
'tmd_key',
'tmd_value' ] )
48 ->from(
'translate_metadata' )
49 ->where( [
'tmd_group' => array_map(
'strval', $chunk ) ] )
50 ->caller( $functionName )
52 foreach ( $res as $row ) {
53 $this->cache[$row->tmd_group][$row->tmd_key] = $row->tmd_value;
64 public function get(
string $group,
string $key ) {
65 $this->preloadGroups( [ $group ], __METHOD__ );
66 return $this->cache[$this->getGroupIdForDatabase( $group )][$key] ??
false;
74 $value = $this->
get( $group, $key );
75 return $value ===
false ? $defaultValue : $value;
85 public function set(
string $groupId,
string $key, $value ): void {
86 $dbw = $this->dbProvider->getPrimaryDatabase();
87 $dbGroupId = $this->getGroupIdForDatabase( $groupId );
88 $data = [
'tmd_group' => $dbGroupId,
'tmd_key' => $key,
'tmd_value' => $value ];
89 if ( $value ===
false ) {
90 unset( $data[
'tmd_value'] );
91 $dbw->newDeleteQueryBuilder()
92 ->deleteFrom(
'translate_metadata' )
94 ->caller( __METHOD__ )
96 unset( $this->cache[$dbGroupId][$key] );
98 $dbw->newReplaceQueryBuilder()
99 ->replaceInto(
'translate_metadata' )
100 ->uniqueIndexFields( [
'tmd_group',
'tmd_key' ] )
102 ->caller( __METHOD__ )
104 $this->cache[$dbGroupId][$key] = $value;
107 $this->priorityCache =
null;
115 $groups = $this->get( $groupId,
'subgroups' );
116 if ( is_string( $groups ) ) {
117 if ( str_contains( $groups,
'|' ) ) {
118 $groups = explode(
'|', $groups );
120 $groups = array_map(
'trim', explode(
',', $groups ) );
123 foreach ( $groups as $index => $id ) {
124 if ( trim( $id ) ===
'' ) {
125 unset( $groups[$index] );
136 public function setSubgroups(
string $groupId, array $subgroupIds ): void {
137 $subgroups = implode(
'|', $subgroupIds );
138 $this->
set( $groupId,
'subgroups', $subgroups );
143 $dbw = $this->dbProvider->getPrimaryDatabase();
145 $dbGroupId = $this->getGroupIdForDatabase( $groupId );
146 $dbw->newDeleteQueryBuilder()
147 ->deleteFrom(
'translate_metadata' )
148 ->where( [
'tmd_group' => $dbGroupId ] )
149 ->caller( __METHOD__ )
151 $this->cache[ $dbGroupId ] =
null;
152 unset( $this->priorityCache[ $dbGroupId ] );
155 public function isExcluded(
string $groupId,
string $code ): bool {
156 if ( $this->priorityCache === null ) {
158 $db = Utilities::getSafeReadDB();
159 $res = $db->newSelectQueryBuilder()
161 'group' =>
'a.tmd_group',
162 'langs' =>
'b.tmd_value',
164 ->from(
'translate_metadata',
'a' )
165 ->leftJoin(
'translate_metadata',
'b', [
166 'a.tmd_group = b.tmd_group',
167 'b.tmd_key' =>
'prioritylangs',
170 'a.tmd_key' =>
'priorityforce',
171 'a.tmd_value' =>
'on'
173 ->caller( __METHOD__ )
176 $this->priorityCache = [];
177 foreach ( $res as $row ) {
178 if ( isset( $row->langs ) ) {
179 $this->priorityCache[ $row->group ] = array_flip( explode(
',', $row->langs ) );
181 $this->priorityCache[ $row->group ] = [];
186 $dbGroupId = $this->getGroupIdForDatabase( $groupId );
187 $isDiscouraged = MessageGroups::getPriority( $groupId ) ===
'discouraged';
188 $hasLimitedLanguages = isset( $this->priorityCache[$dbGroupId] );
189 $isLanguageIncluded = isset( $this->priorityCache[$dbGroupId][$code] );
191 return $isDiscouraged || ( $hasLimitedLanguages && !$isLanguageIncluded );
205 foreach ( $groupIds as $groupId ) {
206 $dbGroupIdMap[ $this->getGroupIdForDatabase( $groupId ) ] = $groupId;
209 $res = $db->newSelectQueryBuilder()
210 ->select( [
'tmd_group',
'tmd_key',
'tmd_value' ] )
211 ->from(
'translate_metadata' )
213 'tmd_group' => array_keys( $dbGroupIdMap ),
216 ->caller( __METHOD__ )
220 foreach ( $res as $row ) {
221 $groupId = $row->tmd_group;
223 $ret[ $dbGroupIdMap[ $groupId ] ][ $row->tmd_key ] = $row->tmd_value;
229 public function moveMetadata(
232 array $metadataKeysToMove
234 $this->preloadGroups( [ $oldGroupId, $newGroupId ], __METHOD__ );
235 foreach ( $metadataKeysToMove as $type ) {
236 $value = $this->
get( $oldGroupId, $type );
237 if ( $value !==
false ) {
238 $this->
set( $oldGroupId, $type, false );
239 $this->
set( $newGroupId, $type, $value );
248 public function clearMetadata(
string $groupId, array $metadataKeys ): void {
250 foreach ( $metadataKeys as $type ) {
251 $this->
set( $groupId, $type, false );
262 return $db->newSelectQueryBuilder()
263 ->select(
'tmd_group' )
264 ->from(
'translate_metadata' )
265 ->where( [
'tmd_key' =>
'subgroups' ] )
266 ->caller( __METHOD__ )
267 ->fetchFieldValues();
270 private function getGroupIdForDatabase(
string $groupId ): string {
272 if ( strlen( $groupId ) <= 200 ) {
276 $hash = hash(
'md5', $groupId );
278 return mb_strcut( $groupId, 0, 160 ) .
'||' . $hash;