Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslateEditAddons.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorInterface;
5
6use ManualLogEntry;
7use MediaWiki\CommentStore\CommentStoreComment;
8use MediaWiki\Content\TextContent;
9use MediaWiki\Deferred\DeferredUpdates;
17use MediaWiki\MediaWikiServices;
18use MediaWiki\Parser\ParserOptions;
19use MediaWiki\Revision\RevisionRecord;
20use MediaWiki\Storage\EditResult;
21use MediaWiki\Title\Title;
22use MediaWiki\User\User;
23use MediaWiki\User\UserIdentity;
24use WikiPage;
25
45 public static function disallowLangTranslations(
46 Title $title,
47 User $user,
48 string $action,
49 &$result
50 ): bool {
51 if ( $action !== 'edit' ) {
52 return true;
53 }
54
55 $handle = new MessageHandle( $title );
56 if ( !$handle->isValid() ) {
57 return true;
58 }
59
60 if ( $user->isAllowed( 'translate-manage' ) ) {
61 return true;
62 }
63
64 $group = $handle->getGroup();
65 $languages = $group->getTranslatableLanguages();
66 $langCode = $handle->getCode();
67 if ( $languages !== null && $langCode && !isset( $languages[$langCode] ) ) {
68 $result = [ 'translate-language-disabled' ];
69 return false;
70 }
71
72 $groupId = $group->getId();
73 $checks = [
74 $groupId,
75 strtok( $groupId, '-' ),
76 '*'
77 ];
78
79 $disabledLanguages = Services::getInstance()->getConfigHelper()->getDisabledTargetLanguages();
80 foreach ( $checks as $check ) {
81 if ( isset( $disabledLanguages[$check][$langCode] ) ) {
82 $reason = $disabledLanguages[$check][$langCode];
83 $result = [ 'translate-page-disabled', $reason ];
84 return false;
85 }
86 }
87
88 return true;
89 }
90
95 public static function onSaveComplete(
96 WikiPage $wikiPage,
97 UserIdentity $userIdentity,
98 string $summary,
99 int $flags,
100 RevisionRecord $revisionRecord,
101 EditResult $editResult
102 ): void {
103 global $wgEnablePageTranslation;
104
105 $content = $wikiPage->getContent();
106
107 if ( !$content instanceof TextContent ) {
108 // Screw it, not interested
109 return;
110 }
111
112 $text = $content->getText();
113 $title = $wikiPage->getTitle();
114 $handle = new MessageHandle( $title );
115
116 if ( !$handle->isValid() ) {
117 return;
118 }
119
120 // Update it.
121 $revId = $revisionRecord->getId();
122 $mwServices = MediaWikiServices::getInstance();
123
124 $fuzzy = $handle->needsFuzzy( $text );
125 $parentId = $revisionRecord->getParentId();
126 if ( $editResult->isNullEdit() || $parentId == 0 ) {
127 // In this case the page_latest hasn't changed so we can rely on its fuzzy status
128 $wasFuzzy = $handle->isFuzzy();
129 } else {
130 // In this case the page_latest will (probably) have changed. The above might work by chance
131 // since it reads from a replica database which might not have gotten the update yet, but
132 // don't trust it and read the fuzzy status of the parent ID from the database instead
133 $revTagStore = Services::getInstance()->getRevTagStore();
134 $wasFuzzy = $revTagStore->isRevIdFuzzy( $title->getArticleID(), $parentId );
135 }
136 if ( !$fuzzy && $wasFuzzy ) {
137 $title = $mwServices->getTitleFactory()->castFromPageIdentity( $wikiPage );
138 $user = $mwServices->getUserFactory()->newFromUserIdentity( $userIdentity );
139
140 if ( !$mwServices->getPermissionManager()->userCan( 'unfuzzy', $user, $title ) ) {
141 // No permission to unfuzzy this unit so leave it fuzzy
142 $fuzzy = true;
143 } elseif ( $editResult->isNullEdit() ) {
144 $entry = new ManualLogEntry( 'translationreview', 'unfuzzy' );
145 // Generate a log entry and null revision for the otherwise
146 // invisible unfuzzying
147 $dbw = $mwServices->getDBLoadBalancer()->getConnection( DB_PRIMARY );
148 $nullRevision = $mwServices->getRevisionStore()->newNullRevision(
149 $dbw,
150 $wikiPage,
151 CommentStoreComment::newUnsavedComment(
152 $summary !== '' ? $summary : wfMessage( "translate-unfuzzy-comment" )
153 ),
154 false,
155 $userIdentity
156 );
157 if ( $nullRevision ) {
158 $nullRevision = $mwServices->getRevisionStore()->insertRevisionOn( $nullRevision, $dbw );
159 // Overwrite $revId so the revision ID of the null revision rather than the previous parent
160 // revision is used for any further edits
161 $revId = $nullRevision->getId();
162 $wikiPage->updateRevisionOn( $dbw, $nullRevision, $nullRevision->getParentId() );
163 $entry->setAssociatedRevId( $revId );
164 }
165
166 $entry->setPerformer( $userIdentity );
167 $entry->setTarget( $title );
168 $logId = $entry->insert();
169 $entry->publish( $logId );
170 }
171 }
172 self::updateFuzzyTag( $title, $revId, $fuzzy );
173
174 $group = $handle->getGroup();
175 // Update translation stats - source language should always be up to date
176 if ( $handle->getCode() !== $group->getSourceLanguage() ) {
177 // This will update in-process cache immediately, but the value is saved
178 // to the database in a deferred update. See MessageGroupStats::queueUpdates.
179 // In case an error happens before that, the stats may be stale, but that
180 // would be fixed by the next update or purge.
181 MessageGroupStats::clear( $handle );
182 }
183
184 // This job asks for stats, however the updated stats are written in a deferred update.
185 // To make it less likely that the job would be executed before the updated stats are
186 // written, create the job inside a deferred update too.
187 DeferredUpdates::addCallableUpdate(
188 static function () use ( $handle ) {
190 }
191 );
192 $user = $mwServices->getUserFactory()
193 ->newFromId( $userIdentity->getId() );
194
195 if ( !$fuzzy ) {
196 Services::getInstance()->getHookRunner()
197 ->onTranslate_newTranslation( $handle, $revId, $text, $user );
198 }
199
200 TtmServer::onChange( $handle );
201
202 if ( $wgEnablePageTranslation && $handle->isPageTranslation() ) {
203 // Updates for translatable pages only
204 $minor = (bool)( $flags & EDIT_MINOR );
205 PageTranslationHooks::onSectionSave( $wikiPage, $user, $content,
206 $summary, $minor, $flags, $handle );
207 }
208 }
209
215 private static function updateFuzzyTag( Title $title, int $revision, bool $fuzzy ): void {
216 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
217
218 $conds = [
219 'rt_page' => $title->getArticleID(),
220 'rt_type' => RevTagStore::FUZZY_TAG,
221 'rt_revision' => $revision
222 ];
223
224 // Replace the existing fuzzy tag, if any
225 if ( $fuzzy ) {
226 $index = array_keys( $conds );
227 $dbw->newReplaceQueryBuilder()
228 ->replaceInto( 'revtag' )
229 ->uniqueIndexFields( $index )
230 ->row( $conds )
231 ->caller( __METHOD__ )
232 ->execute();
233 } else {
234 $dbw->newDeleteQueryBuilder()
235 ->deleteFrom( 'revtag' )
236 ->where( $conds )
237 ->caller( __METHOD__ )
238 ->execute();
239 }
240 }
241
248 public static function updateTransverTag(
249 MessageHandle $handle,
250 int $revision,
251 string $text,
252 User $user
253 ): bool {
254 if ( $user->isAllowed( 'bot' ) ) {
255 return false;
256 }
257
258 $group = $handle->getGroup();
259
260 $title = $handle->getTitle();
261 $name = $handle->getKey() . '/' . $group->getSourceLanguage();
262 $definitionTitle = Title::makeTitleSafe( $title->getNamespace(), $name );
263 if ( !$definitionTitle || !$definitionTitle->exists() ) {
264 return true;
265 }
266
267 $definitionRevision = $definitionTitle->getLatestRevID();
268 $revTagStore = Services::getInstance()->getRevTagStore();
269 $revTagStore->setTransver( $title, $revision, $definitionRevision );
270
271 return true;
272 }
273
275 public static function disablePreSaveTransform(
276 WikiPage $wikiPage,
277 ParserOptions $popts
278 ): void {
279 global $wgTranslateUsePreSaveTransform;
280
281 if ( !$wgTranslateUsePreSaveTransform ) {
282 $handle = new MessageHandle( $wikiPage->getTitle() );
283 if ( $handle->isMessageNamespace() && !$handle->isDoc() ) {
284 $popts->setPreSaveTransform( false );
285 }
286 }
287 }
288}
static onChange(MessageHandle $handle)
Hook: TranslateEventTranslationReview and also on translation changes.
Class to manage revision tags for translatable bundles.
Class for pointing to messages, like Title class is for titles.
static onSectionSave(WikiPage $wikiPage, User $user, TextContent $content, $summary, $minor, $flags, MessageHandle $handle)
This is triggered after an edit to translation unit page.
Definition Hooks.php:378
Minimal service container.
Definition Services.php:59
This class aims to provide efficient mechanism for fetching translation completion stats.
Various editing enhancements to the edit page interface.
static disablePreSaveTransform(WikiPage $wikiPage, ParserOptions $popts)
Hook: ArticlePrepareTextForEdit.
static onSaveComplete(WikiPage $wikiPage, UserIdentity $userIdentity, string $summary, int $flags, RevisionRecord $revisionRecord, EditResult $editResult)
Runs message checks, adds tp:transver tags and updates statistics.
static disallowLangTranslations(Title $title, User $user, string $action, &$result)
Prevent translations to non-translatable languages for the group Hook: getUserPermissionsErrorsExpens...
static updateTransverTag(MessageHandle $handle, int $revision, string $text, User $user)
Adds tag which identifies the revision of source message at that time.