Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslatablePageStore.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
7use DeferredUpdates;
8use InvalidArgumentException;
9use JobQueueGroup;
13use MediaWiki\Page\PageIdentity;
14use MediaWiki\Revision\RevisionRecord;
15use MediaWiki\Revision\SlotRecord;
16use MessageIndex;
17use RuntimeException;
18use TextContent;
19use Title;
21use Wikimedia\Rdbms\ILoadBalancer;
22
30 private MessageIndex $messageIndex;
31 private JobQueueGroup $jobQueue;
32 private RevTagStore $revTagStore;
33 private ILoadBalancer $loadBalancer;
34 private TranslatableBundleStatusStore $translatableBundleStatusStore;
35 private TranslatablePageParser $translatablePageParser;
36
37 public function __construct(
38 MessageIndex $messageIndex,
39 JobQueueGroup $jobQueue,
40 RevTagStore $revTagStore,
41 ILoadBalancer $loadBalancer,
42 TranslatableBundleStatusStore $translatableBundleStatusStore,
43 TranslatablePageParser $translatablePageParser
44 ) {
45 $this->messageIndex = $messageIndex;
46 $this->jobQueue = $jobQueue;
47 $this->revTagStore = $revTagStore;
48 $this->loadBalancer = $loadBalancer;
49 $this->translatableBundleStatusStore = $translatableBundleStatusStore;
50 $this->translatablePageParser = $translatablePageParser;
51 }
52
53 public function move( Title $oldName, Title $newName ): void {
54 $oldTranslatablePage = TranslatablePage::newFromTitle( $oldName );
55 $newTranslatablePage = TranslatablePage::newFromTitle( $newName );
56 $oldGroupId = $oldTranslatablePage->getMessageGroupId();
57 $newGroupId = $newTranslatablePage->getMessageGroupId();
58
59 TranslateMetadata::moveMetadata( $oldGroupId, $newGroupId, TranslatablePage::METADATA_KEYS );
60
61 $this->moveMetadata( $oldGroupId, $newGroupId );
62
63 TranslatablePage::clearSourcePageCache();
64
65 // Re-render the pages to get everything in sync
66 MessageGroups::singleton()->recache();
67 // Update message index now so that, when after this job the MoveTranslationUnits hook
68 // runs in deferred updates, it will not run MessageIndexRebuildJob (T175834).
69 $this->messageIndex->rebuild();
70
71 $job = UpdateTranslatablePageJob::newFromPage( TranslatablePage::newFromTitle( $newName ) );
72 $this->jobQueue->push( $job );
73 }
74
75 public function handleNullRevisionInsert( TranslatableBundle $bundle, RevisionRecord $revision ): void {
76 if ( !$bundle instanceof TranslatablePage ) {
77 throw new InvalidArgumentException(
78 'Expected $bundle to be of type TranslatablePage, got ' . get_class( $bundle )
79 );
80 }
81
82 $pageContent = $revision->getContent( SlotRecord::MAIN );
83 if ( !$pageContent instanceof TextContent ) {
84 throw new RuntimeException( "Translatable page {$bundle->getTitle()} has non-textual content." );
85 }
86
87 // Check if the revision still has the <translate> tag
88 $pageText = $pageContent->getText();
89 if ( $this->translatablePageParser->containsMarkup( $pageText ) ) {
90 $this->revTagStore->replaceTag( $bundle->getTitle(), RevTagStore::TP_READY_TAG, $revision->getId() );
91 TranslatablePage::clearSourcePageCache();
92 }
93 }
94
96 public function delete( Title $title ): void {
97 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
98 $dbw->delete( 'translate_sections', [ 'trs_page' => $title->getArticleID() ], __METHOD__ );
99
100 $this->unmark( $title );
101 }
102
104 public function unmark( PageIdentity $title ): void {
105 $translatablePage = TranslatablePage::newFromTitle( $title );
106 $translatablePage->getTranslationPercentages();
107 foreach ( $translatablePage->getTranslationPages() as $page ) {
108 $page->invalidateCache();
109 }
110
111 $groupId = $translatablePage->getMessageGroupId();
112 TranslateMetadata::clearMetadata( $groupId, TranslatablePage::METADATA_KEYS );
113 $this->removeFromAggregateGroups( $groupId );
114
115 // Remove tags after all group related work is done in order to avoid breaking calls to
116 // TranslatablePage::getMessageGroup incase the group cache is not populated
117 $this->revTagStore->removeTags( $title, RevTagStore::TP_MARK_TAG, RevTagStore::TP_READY_TAG );
118 $this->translatableBundleStatusStore->removeStatus( $title->getId() );
119
120 MessageGroups::singleton()->recache();
121 $this->messageIndex->rebuild();
122
123 TranslatablePage::clearSourcePageCache();
124 }
125
127 public function performStatusUpdate( Title $title ): void {
128 DeferredUpdates::addCallableUpdate(
129 function () use ( $title ) {
130 $this->updateStatus( $title );
131 }
132 );
133 }
134
136 public function updateStatus( Title $title ): ?TranslatableBundleStatus {
137 $revTags = $this->revTagStore->getLatestRevisionsForTags(
138 $title,
139 RevTagStore::TP_MARK_TAG,
140 RevTagStore::TP_READY_TAG
141 );
142
143 $status = TranslatablePage::determineStatus(
144 $revTags[RevTagStore::TP_READY_TAG] ?? null,
145 $revTags[RevTagStore::TP_MARK_TAG] ?? null,
146 $title->getLatestRevID( Title::READ_LATEST )
147 );
148
149 if ( $status ) {
150 $this->translatableBundleStatusStore->setStatus(
151 $title, $status, TranslatablePage::class
152 );
153 }
154
155 return $status;
156 }
157
158 private function moveMetadata( string $oldGroupId, string $newGroupId ): void {
159 // Make the changes in aggregate groups metadata, if present in any of them.
160 $aggregateGroups = MessageGroups::getGroupsByType( AggregateMessageGroup::class );
161 TranslateMetadata::preloadGroups( array_keys( $aggregateGroups ), __METHOD__ );
162
163 foreach ( $aggregateGroups as $id => $group ) {
164 $subgroups = TranslateMetadata::get( $id, 'subgroups' );
165 if ( $subgroups === false ) {
166 continue;
167 }
168
169 $subgroups = explode( ',', $subgroups );
170 $subgroups = array_flip( $subgroups );
171 if ( isset( $subgroups[$oldGroupId] ) ) {
172 $subgroups[$newGroupId] = $subgroups[$oldGroupId];
173 unset( $subgroups[$oldGroupId] );
174 $subgroups = array_flip( $subgroups );
175 TranslateMetadata::set(
176 $group->getId(),
177 'subgroups',
178 implode( ',', $subgroups )
179 );
180 }
181 }
182
183 // Move discouraged status
184 $priority = MessageGroups::getPriority( $oldGroupId );
185 if ( $priority !== '' ) {
186 MessageGroups::setPriority( $newGroupId, $priority );
187 MessageGroups::setPriority( $oldGroupId, '' );
188 }
189 }
190
191 private function removeFromAggregateGroups( string $groupId ): void {
192 // remove the page from aggregate groups, if present in any of them.
193 $aggregateGroups = MessageGroups::getGroupsByType( AggregateMessageGroup::class );
194 TranslateMetadata::preloadGroups( array_keys( $aggregateGroups ), __METHOD__ );
195 foreach ( $aggregateGroups as $group ) {
196 $subgroups = TranslateMetadata::get( $group->getId(), 'subgroups' );
197 if ( $subgroups !== false ) {
198 $subgroups = explode( ',', $subgroups );
199 $subgroups = array_flip( $subgroups );
200 if ( isset( $subgroups[$groupId] ) ) {
201 unset( $subgroups[$groupId] );
202 $subgroups = array_flip( $subgroups );
204 $group->getId(),
205 'subgroups',
206 implode( ',', $subgroups )
207 );
208 }
209 }
210 }
211 }
212}
Groups multiple message groups together as one group.
Class to manage revision tags for translatable bundles.
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.
Store service for looking up and storing status for translatable bundle status.
Translatable bundle represents a message group where its translatable content is defined on a wiki pa...
getTitle()
Return the title of the page where the translatable bundle is defined.
performStatusUpdate(Title $title)
Queues an update for the status of the translatable page.
Generates ParserOutput from text or removes all tags from a text.
Mixed bag of methods related to translatable pages.
Job for updating translation units and translation pages when a translatable page is marked for trans...
Creates a database of keys in all groups, so that namespace and key can be used to get the groups the...
static set(string $groupId, string $key, $value)
Set a metadata value for the given group and metadata key.
static get(string $group, string $key)
Get a metadata value for the given group and key.
static preloadGroups(array $groups, string $caller)