Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 68 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
UpdateTranslatablePageJob | |
0.00% |
0 / 68 |
|
0.00% |
0 / 5 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
newFromPage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
run | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
30 | |||
getTranslationUnitJobs | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
getRenderJobs | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
42 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\PageTranslation; |
5 | |
6 | use MediaWiki\Extension\Translate\Jobs\GenericTranslateJob; |
7 | use MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroups; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\Title\Title; |
10 | use MessageGroupStats; |
11 | use MessageIndexRebuildJob; |
12 | use MessageUpdateJob; |
13 | use RunnableJob; |
14 | |
15 | /** |
16 | * Job for updating translation units and translation pages when |
17 | * a translatable page is marked for translation. |
18 | */ |
19 | class UpdateTranslatablePageJob extends GenericTranslateJob { |
20 | /** @inheritDoc */ |
21 | public function __construct( Title $title, array $params = [] ) { |
22 | parent::__construct( 'UpdateTranslatablePageJob', $title, $params ); |
23 | } |
24 | |
25 | /** |
26 | * Create a job that updates a translation page. |
27 | * |
28 | * If a list of sections is provided, then the job will also update translation |
29 | * unit pages. |
30 | * |
31 | * @param TranslatablePage $page |
32 | * @param TranslationUnit[] $sections |
33 | */ |
34 | public static function newFromPage( TranslatablePage $page, array $sections = [] ): self { |
35 | $params = []; |
36 | $params[ 'sections' ] = []; |
37 | foreach ( $sections as $section ) { |
38 | $params[ 'sections' ][] = $section->serializeToArray(); |
39 | } |
40 | |
41 | return new self( $page->getTitle(), $params ); |
42 | } |
43 | |
44 | public function run(): bool { |
45 | // WARNING: Nothing here must not depend on message index being up to date. |
46 | // For performance reasons, message index rebuild is run a separate job after |
47 | // everything else is updated. |
48 | |
49 | // START: This section does not care about replication lag |
50 | $this->logInfo( 'Starting UpdateTranslatablePageJob' ); |
51 | |
52 | $sections = $this->params[ 'sections' ]; |
53 | foreach ( $sections as $index => $section ) { |
54 | // Old jobs stored sections as objects because they were serialized and |
55 | // unserialized transparently. That is no longer supported, so we |
56 | // convert manually to primitive types first (to an PHP array). |
57 | if ( is_array( $section ) ) { |
58 | $sections[ $index ] = TranslationUnit::unserializeFromArray( $section ); |
59 | } |
60 | } |
61 | |
62 | /** |
63 | * Units should be updated before the render jobs are run so that the |
64 | * latest changes can take effect on the translation pages. |
65 | */ |
66 | $page = TranslatablePage::newFromTitle( $this->title ); |
67 | $unitJobs = self::getTranslationUnitJobs( $page, $sections ); |
68 | foreach ( $unitJobs as $job ) { |
69 | $job->run(); |
70 | } |
71 | |
72 | $this->logInfo( |
73 | 'Finished running ' . count( $unitJobs ) . ' MessageUpdate jobs for ' |
74 | . count( $sections ) . ' sections' |
75 | ); |
76 | // END: This section does not care about replication lag |
77 | $mwServices = MediaWikiServices::getInstance(); |
78 | $lb = $mwServices->getDBLoadBalancerFactory(); |
79 | if ( !$lb->waitForReplication() ) { |
80 | $this->logWarning( 'Continuing despite replication lag' ); |
81 | } |
82 | |
83 | // Ensure we are using the latest group definitions. This is needed so |
84 | // that in long running scripts we do see the page which was just |
85 | // marked for translation. Otherwise getMessageGroup in the next line |
86 | // returns null. There is no need to regenerate the global cache. |
87 | MessageGroups::singleton()->clearProcessCache(); |
88 | // Ensure fresh definitions for stats |
89 | $page->getMessageGroup()->clearCaches(); |
90 | |
91 | $this->logInfo( 'Cleared caches' ); |
92 | |
93 | // Refresh translations statistics, we want these to be up to date for the |
94 | // RenderJobs, for displaying up to date statistics on the translation pages. |
95 | $id = $page->getMessageGroupId(); |
96 | MessageGroupStats::forGroup( |
97 | $id, |
98 | MessageGroupStats::FLAG_NO_CACHE | MessageGroupStats::FLAG_IMMEDIATE_WRITES |
99 | ); |
100 | $this->logInfo( 'Updated the message group stats' ); |
101 | |
102 | // Try to avoid stale statistics on the base page |
103 | $wikiPage = $mwServices->getWikiPageFactory()->newFromTitle( $page->getTitle() ); |
104 | $wikiPage->doPurge(); |
105 | $this->logInfo( 'Finished purging' ); |
106 | |
107 | // These can be run independently and in parallel if possible |
108 | $jobQueueGroup = $mwServices->getJobQueueGroup(); |
109 | $renderJobs = self::getRenderJobs( $page ); |
110 | $jobQueueGroup->push( $renderJobs ); |
111 | $this->logInfo( 'Added ' . count( $renderJobs ) . ' RenderJobs to the queue' ); |
112 | |
113 | // Schedule message index update. Thanks to front caching, it is okay if this takes |
114 | // a while (and on large wikis it does take a while!). Running it as a separate job |
115 | // also allows de-duplication in case multiple translatable pages are being marked |
116 | // for translation in a short period of time. |
117 | $job = MessageIndexRebuildJob::newJob(); |
118 | $jobQueueGroup->push( $job ); |
119 | |
120 | $this->logInfo( 'Finished UpdateTranslatablePageJob' ); |
121 | |
122 | return true; |
123 | } |
124 | |
125 | /** |
126 | * Creates jobs needed to create or update all translation unit definition pages. |
127 | * @param TranslatablePage $page |
128 | * @param TranslationUnit[] $units |
129 | * @return RunnableJob[] |
130 | */ |
131 | private static function getTranslationUnitJobs( TranslatablePage $page, array $units ): array { |
132 | $jobs = []; |
133 | |
134 | $code = $page->getSourceLanguageCode(); |
135 | $prefix = $page->getTitle()->getPrefixedText(); |
136 | |
137 | foreach ( $units as $unit ) { |
138 | $unitName = $unit->id; |
139 | $title = Title::makeTitle( NS_TRANSLATIONS, "$prefix/$unitName/$code" ); |
140 | |
141 | $fuzzy = $unit->type === 'changed'; |
142 | $jobs[] = MessageUpdateJob::newJob( $title, $unit->getTextWithVariables(), $fuzzy ); |
143 | } |
144 | |
145 | return $jobs; |
146 | } |
147 | |
148 | /** |
149 | * Creates jobs needed to create or update all translation pages. |
150 | * @return RunnableJob[] |
151 | */ |
152 | public static function getRenderJobs( TranslatablePage $page, bool $nonPrioritizedJobs = false ): array { |
153 | $documentationLanguageCode = MediaWikiServices::getInstance() |
154 | ->getMainConfig() |
155 | ->get( 'TranslateDocumentationLanguageCode' ); |
156 | |
157 | $jobs = []; |
158 | |
159 | $jobTitles = $page->getTranslationPages(); |
160 | // Ensure that we create the source language page when page is marked for translation. |
161 | $jobTitles[] = $page->getTitle()->getSubpage( $page->getSourceLanguageCode() ); |
162 | // In some cases translation page may be missing even though translations exist. One such case |
163 | // is when FuzzyBot makes edits, which suppresses render jobs. There may also be bugs with the |
164 | // render jobs failing. Add jobs based on message group stats to create self-healing process. |
165 | $stats = MessageGroupStats::forGroup( $page->getMessageGroupId() ); |
166 | foreach ( $stats as $languageCode => $languageStats ) { |
167 | if ( $languageStats[MessageGroupStats::TRANSLATED] > 0 && $languageCode !== $documentationLanguageCode ) { |
168 | $jobTitles[] = $page->getTitle()->getSubpage( $languageCode ); |
169 | } |
170 | } |
171 | |
172 | // These jobs can be deduplicated by the job queue as well, but it's simple to do it here ourselves. |
173 | // Titles have __toString method that returns the prefixed text so array_unique should work. |
174 | $jobTitles = array_unique( $jobTitles ); |
175 | foreach ( $jobTitles as $t ) { |
176 | if ( $nonPrioritizedJobs ) { |
177 | $jobs[] = RenderTranslationPageJob::newNonPrioritizedJob( $t ); |
178 | } else { |
179 | $jobs[] = RenderTranslationPageJob::newJob( $t ); |
180 | } |
181 | |
182 | } |
183 | |
184 | return $jobs; |
185 | } |
186 | |
187 | } |