Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 83 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
MessageBundleStore | |
0.00% |
0 / 83 |
|
0.00% |
0 / 6 |
506 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
move | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
handleNullRevisionInsert | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
delete | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
validate | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
110 | |||
save | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\MessageBundleTranslation; |
5 | |
6 | use InvalidArgumentException; |
7 | use JobQueueGroup; |
8 | use MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroups; |
9 | use MediaWiki\Extension\Translate\MessageGroupProcessing\RevTagStore; |
10 | use MediaWiki\Extension\Translate\MessageGroupProcessing\TranslatableBundle; |
11 | use MediaWiki\Extension\Translate\MessageGroupProcessing\TranslatableBundleStore; |
12 | use MediaWiki\Extension\Translate\MessageLoading\MessageIndex; |
13 | use MediaWiki\Extension\Translate\MessageProcessing\MessageGroupMetadata; |
14 | use MediaWiki\Languages\LanguageNameUtils; |
15 | use MediaWiki\Revision\RevisionRecord; |
16 | use MediaWiki\Title\Title; |
17 | use Message; |
18 | use RequestContext; |
19 | use SpecialPageLanguage; |
20 | |
21 | /** |
22 | * @author Abijeet Patro |
23 | * @author Niklas Laxström |
24 | * @since 2022.04 |
25 | * @license GPL-2.0-or-later |
26 | */ |
27 | class MessageBundleStore implements TranslatableBundleStore { |
28 | private RevTagStore $revTagStore; |
29 | private JobQueueGroup $jobQueue; |
30 | private LanguageNameUtils $languageNameUtils; |
31 | private MessageIndex $messageIndex; |
32 | private MessageGroupMetadata $messageGroupMetadata; |
33 | private const METADATA_KEYS_DB = [ |
34 | 'priorityforce', |
35 | 'prioritylangs' |
36 | ]; |
37 | |
38 | public function __construct( |
39 | RevTagStore $revTagStore, |
40 | JobQueueGroup $jobQueue, |
41 | LanguageNameUtils $languageNameUtils, |
42 | MessageIndex $messageIndex, |
43 | MessageGroupMetadata $messageGroupMetadata |
44 | ) { |
45 | $this->revTagStore = $revTagStore; |
46 | $this->jobQueue = $jobQueue; |
47 | $this->languageNameUtils = $languageNameUtils; |
48 | $this->messageIndex = $messageIndex; |
49 | $this->messageGroupMetadata = $messageGroupMetadata; |
50 | } |
51 | |
52 | public function move( Title $oldName, Title $newName ): void { |
53 | $oldBundle = new MessageBundle( $oldName ); |
54 | $newBundle = new MessageBundle( $newName ); |
55 | |
56 | $this->messageGroupMetadata->moveMetadata( |
57 | $oldBundle->getMessageGroupId(), |
58 | $newBundle->getMessageGroupId(), |
59 | self::METADATA_KEYS_DB |
60 | ); |
61 | |
62 | MessageBundle::clearSourcePageCache(); |
63 | |
64 | // Re-render the bundles to get everything in sync |
65 | MessageGroups::singleton()->recache(); |
66 | // Update message index now so that, when after this job the MoveTranslationUnits hook |
67 | // runs in deferred updates, it will not run MessageIndexRebuildJob (T175834). |
68 | $this->messageIndex->rebuild(); |
69 | } |
70 | |
71 | public function handleNullRevisionInsert( TranslatableBundle $bundle, RevisionRecord $revision ): void { |
72 | if ( !$bundle instanceof MessageBundle ) { |
73 | throw new InvalidArgumentException( |
74 | 'Expected $bundle to be of type MessageBundle, got ' . get_class( $bundle ) |
75 | ); |
76 | } |
77 | |
78 | $this->revTagStore->replaceTag( $bundle->getTitle(), RevTagStore::MB_VALID_TAG, $revision->getId() ); |
79 | MessageBundle::clearSourcePageCache(); |
80 | } |
81 | |
82 | public function delete( Title $title ): void { |
83 | $this->revTagStore->removeTags( $title, RevTagStore::MB_VALID_TAG ); |
84 | |
85 | $bundle = new MessageBundle( $title ); |
86 | $this->messageGroupMetadata->clearMetadata( $bundle->getMessageGroupId(), self::METADATA_KEYS_DB ); |
87 | |
88 | MessageBundle::clearSourcePageCache(); |
89 | |
90 | MessageGroups::singleton()->recache(); |
91 | $this->messageIndex->rebuild(); |
92 | } |
93 | |
94 | public function validate( Title $pageTitle, MessageBundleContent $content ): void { |
95 | $content->validate(); |
96 | // Verify that the language code is valid |
97 | $metadata = $content->getMetadata(); |
98 | $sourceLanguageCode = $metadata->getSourceLanguageCode(); |
99 | if ( $sourceLanguageCode ) { |
100 | if ( !$this->languageNameUtils->isKnownLanguageTag( $sourceLanguageCode ) ) { |
101 | throw new MalformedBundle( |
102 | 'translate-messagebundle-error-invalid-sourcelanguage', [ $sourceLanguageCode ] |
103 | ); |
104 | } |
105 | |
106 | $revisionId = $this->revTagStore->getLatestRevisionWithTag( $pageTitle, RevTagStore::MB_VALID_TAG ); |
107 | // If request wants the source language to be changed after creation, then throw an exception |
108 | if ( $revisionId !== null && $sourceLanguageCode !== $pageTitle->getPageLanguage()->getCode() ) { |
109 | throw new MalformedBundle( 'translate-messagebundle-sourcelanguage-changed' ); |
110 | } |
111 | |
112 | } |
113 | |
114 | $priorityLanguageCodes = $metadata->getPriorityLanguages(); |
115 | if ( $priorityLanguageCodes ) { |
116 | $invalidLanguageCodes = []; |
117 | foreach ( $priorityLanguageCodes as $languageCode ) { |
118 | if ( !is_string( $languageCode ) ) { |
119 | throw new MalformedBundle( 'translate-messagebundle-error-invalid-prioritylanguage-format' ); |
120 | } |
121 | |
122 | if ( !$this->languageNameUtils->isKnownLanguageTag( $languageCode ) ) { |
123 | $invalidLanguageCodes[] = $languageCode; |
124 | } |
125 | } |
126 | |
127 | if ( $invalidLanguageCodes ) { |
128 | throw new MalformedBundle( |
129 | 'translate-messagebundle-error-invalid-prioritylanguage', |
130 | [ Message::listParam( $invalidLanguageCodes ), count( $invalidLanguageCodes ) ] |
131 | ); |
132 | } |
133 | } |
134 | } |
135 | |
136 | public function save( |
137 | Title $pageTitle, |
138 | RevisionRecord $revisionRecord, |
139 | MessageBundleContent $content |
140 | ): void { |
141 | // Validate the content before saving |
142 | $this->validate( $pageTitle, $content ); |
143 | |
144 | $previousRevisionId = $this->revTagStore->getLatestRevisionWithTag( $pageTitle, RevTagStore::MB_VALID_TAG ); |
145 | if ( $previousRevisionId !== null ) { |
146 | $this->revTagStore->removeTags( $pageTitle, RevTagStore::MB_VALID_TAG ); |
147 | } |
148 | |
149 | if ( $content->isValid() ) { |
150 | // Bundle is valid and contains translatable messages |
151 | $this->revTagStore->replaceTag( $pageTitle, RevTagStore::MB_VALID_TAG, $revisionRecord->getId() ); |
152 | MessageBundle::clearSourcePageCache(); |
153 | |
154 | // Defer most of the heavy work to the job queue |
155 | $job = UpdateMessageBundleJob::newJob( $pageTitle, $revisionRecord->getId(), $previousRevisionId ); |
156 | |
157 | $this->jobQueue->push( $job ); |
158 | |
159 | // A new message bundle, set the source language. |
160 | $definedLanguageCode = $content->getMetadata()->getSourceLanguageCode(); |
161 | $pageLanguageCode = $pageTitle->getPageLanguage()->getCode(); |
162 | if ( $previousRevisionId === null ) { |
163 | if ( $definedLanguageCode !== $pageLanguageCode ) { |
164 | $context = RequestContext::getMain(); |
165 | SpecialPageLanguage::changePageLanguage( |
166 | $context, |
167 | $pageTitle, |
168 | $definedLanguageCode, |
169 | wfMessage( 'translate-messagebundle-change-sourcelanguage' )->inContentLanguage() |
170 | ); |
171 | } |
172 | } |
173 | |
174 | // Save the metadata |
175 | $messageBundle = new MessageBundle( $pageTitle ); |
176 | $groupId = $messageBundle->getMessageGroupId(); |
177 | |
178 | $metadata = $content->getMetadata(); |
179 | $priorityForce = $metadata->areOnlyPriorityLanguagesAllowed() ? 'on' : false; |
180 | $priorityLanguages = $metadata->getPriorityLanguages(); |
181 | $priorityLanguages = $priorityLanguages ? implode( ',', $priorityLanguages ) : false; |
182 | |
183 | $this->messageGroupMetadata->set( $groupId, 'prioritylangs', $priorityLanguages ); |
184 | $this->messageGroupMetadata->set( $groupId, 'priorityforce', $priorityForce ); |
185 | |
186 | $description = $metadata->getDescription(); |
187 | $this->messageGroupMetadata->set( $groupId, 'description', $description ?? false ); |
188 | |
189 | $label = $metadata->getLabel(); |
190 | $this->messageGroupMetadata->set( $groupId, 'label', $label ?? false ); |
191 | } |
192 | |
193 | // What should we do if there are no messages? Use the previous version? Remove the group? |
194 | // Currently, the bundle is removed from translation. |
195 | } |
196 | } |