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;
12use MediaWiki\Revision\RevisionRecord;
13use MessageIndex;
14use Title;
16use Wikimedia\Rdbms\ILoadBalancer;
17
26 private $messageIndex;
28 private $jobQueue;
30 private $revTagStore;
32 private $loadBalancer;
34 private $translatableBundleStatusStore;
35
36 public function __construct(
37 MessageIndex $messageIndex,
38 JobQueueGroup $jobQueue,
39 RevTagStore $revTagStore,
40 ILoadBalancer $loadBalancer,
41 TranslatableBundleStatusStore $translatableBundleStatusStore
42 ) {
43 $this->messageIndex = $messageIndex;
44 $this->jobQueue = $jobQueue;
45 $this->revTagStore = $revTagStore;
46 $this->loadBalancer = $loadBalancer;
47 $this->translatableBundleStatusStore = $translatableBundleStatusStore;
48 }
49
50 public function move( Title $oldName, Title $newName ): void {
51 $oldTranslatablePage = TranslatablePage::newFromTitle( $oldName );
52 $newTranslatablePage = TranslatablePage::newFromTitle( $newName );
53 $oldGroupId = $oldTranslatablePage->getMessageGroupId();
54 $newGroupId = $newTranslatablePage->getMessageGroupId();
55
56 TranslateMetadata::moveMetadata( $oldGroupId, $newGroupId, TranslatablePage::METADATA_KEYS );
57
58 $this->moveMetadata( $oldGroupId, $newGroupId );
59
60 TranslatablePage::clearSourcePageCache();
61
62 // Re-render the pages to get everything in sync
63 MessageGroups::singleton()->recache();
64 // Update message index now so that, when after this job the MoveTranslationUnits hook
65 // runs in deferred updates, it will not run MessageIndexRebuildJob (T175834).
66 $this->messageIndex->rebuild();
67
68 $job = UpdateTranslatablePageJob::newFromPage( TranslatablePage::newFromTitle( $newName ) );
69 $this->jobQueue->push( $job );
70 }
71
72 public function handleNullRevisionInsert( TranslatableBundle $bundle, RevisionRecord $revision ): void {
73 if ( !$bundle instanceof TranslatablePage ) {
74 throw new InvalidArgumentException(
75 'Expected $bundle to be of type TranslatablePage, got ' . get_class( $bundle )
76 );
77 }
78
79 $this->revTagStore->replaceTag( $bundle->getTitle(), RevTagStore::TP_READY_TAG, $revision->getId() );
80 TranslatablePage::clearSourcePageCache();
81 }
82
84 public function delete( Title $title ): void {
85 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
86 $dbw->delete( 'translate_sections', [ 'trs_page' => $title->getArticleID() ], __METHOD__ );
87
88 $this->unmark( $title );
89 }
90
92 public function unmark( Title $title ): void {
93 $translatablePage = TranslatablePage::newFromTitle( $title );
94 $translatablePage->getTranslationPercentages();
95 foreach ( $translatablePage->getTranslationPages() as $page ) {
96 $page->invalidateCache();
97 }
98
99 $groupId = $translatablePage->getMessageGroupId();
100 TranslateMetadata::clearMetadata( $groupId, TranslatablePage::METADATA_KEYS );
101 $this->removeFromAggregateGroups( $groupId );
102
103 // Remove tags after all group related work is done in order to avoid breaking calls to
104 // TranslatablePage::getMessageGroup incase the group cache is not populated
105 $this->revTagStore->removeTags( $title, RevTagStore::TP_MARK_TAG, RevTagStore::TP_READY_TAG );
106 $this->translatableBundleStatusStore->removeStatus( $title->getArticleID() );
107
108 MessageGroups::singleton()->recache();
109 $this->messageIndex->rebuild();
110
111 TranslatablePage::clearSourcePageCache();
112 }
113
115 public function performStatusUpdate( Title $title ): void {
116 DeferredUpdates::addCallableUpdate(
117 function () use ( $title ) {
118 $this->updateStatus( $title );
119 }
120 );
121 }
122
124 public function updateStatus( Title $title ): ?TranslatableBundleStatus {
125 $revTags = $this->revTagStore->getLatestRevisionsForTags(
126 $title,
127 RevTagStore::TP_MARK_TAG,
128 RevTagStore::TP_READY_TAG
129 );
130
131 $status = TranslatablePage::determineStatus(
132 $revTags[RevTagStore::TP_READY_TAG] ?? null,
133 $revTags[RevTagStore::TP_MARK_TAG] ?? null,
134 $title->getLatestRevID( Title::READ_LATEST )
135 );
136
137 if ( $status ) {
138 $this->translatableBundleStatusStore->setStatus(
139 $title, $status, TranslatablePage::class
140 );
141 }
142
143 return $status;
144 }
145
146 private function moveMetadata( string $oldGroupId, string $newGroupId ): void {
147 // Make the changes in aggregate groups metadata, if present in any of them.
148 $aggregateGroups = MessageGroups::getGroupsByType( AggregateMessageGroup::class );
149 TranslateMetadata::preloadGroups( array_keys( $aggregateGroups ), __METHOD__ );
150
151 foreach ( $aggregateGroups as $id => $group ) {
152 $subgroups = TranslateMetadata::get( $id, 'subgroups' );
153 if ( $subgroups === false ) {
154 continue;
155 }
156
157 $subgroups = explode( ',', $subgroups );
158 $subgroups = array_flip( $subgroups );
159 if ( isset( $subgroups[$oldGroupId] ) ) {
160 $subgroups[$newGroupId] = $subgroups[$oldGroupId];
161 unset( $subgroups[$oldGroupId] );
162 $subgroups = array_flip( $subgroups );
163 TranslateMetadata::set(
164 $group->getId(),
165 'subgroups',
166 implode( ',', $subgroups )
167 );
168 }
169 }
170
171 // Move discouraged status
172 $priority = MessageGroups::getPriority( $oldGroupId );
173 if ( $priority !== '' ) {
174 MessageGroups::setPriority( $newGroupId, $priority );
175 MessageGroups::setPriority( $oldGroupId, '' );
176 }
177 }
178
179 private function removeFromAggregateGroups( string $groupId ): void {
180 // remove the page from aggregate groups, if present in any of them.
181 $aggregateGroups = MessageGroups::getGroupsByType( AggregateMessageGroup::class );
182 TranslateMetadata::preloadGroups( array_keys( $aggregateGroups ), __METHOD__ );
183 foreach ( $aggregateGroups as $group ) {
184 $subgroups = TranslateMetadata::get( $group->getId(), 'subgroups' );
185 if ( $subgroups !== false ) {
186 $subgroups = explode( ',', $subgroups );
187 $subgroups = array_flip( $subgroups );
188 if ( isset( $subgroups[$groupId] ) ) {
189 unset( $subgroups[$groupId] );
190 $subgroups = array_flip( $subgroups );
192 $group->getId(),
193 'subgroups',
194 implode( ',', $subgroups )
195 );
196 }
197 }
198 }
199 }
200}
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.
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( $group, $key, $value)
Set a metadata value for the given group and metadata key.
static get( $group, $key)
Get a metadata value for the given group and key.
static preloadGroups(array $groups, string $caller)