Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslateEditAddons.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorInterface;
5
6use DeferredUpdates;
7use ManualLogEntry;
8use MediaWiki\CommentStore\CommentStoreComment;
15use MediaWiki\MediaWikiServices;
16use MediaWiki\Revision\RevisionRecord;
17use MediaWiki\Storage\EditResult;
18use MediaWiki\Title\Title;
19use MediaWiki\User\User;
20use MediaWiki\User\UserIdentity;
21use ParserOptions;
22use TextContent;
23use TTMServer;
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 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()->getDBLoadBalancer()->getConnection( DB_PRIMARY );
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->replace( 'revtag', [ $index ], $conds, __METHOD__ );
228 } else {
229 $dbw->delete( 'revtag', $conds, __METHOD__ );
230 }
231 }
232
239 public static function updateTransverTag(
240 MessageHandle $handle,
241 int $revision,
242 string $text,
243 User $user
244 ): bool {
245 if ( $user->isAllowed( 'bot' ) ) {
246 return false;
247 }
248
249 $group = $handle->getGroup();
250
251 $title = $handle->getTitle();
252 $name = $handle->getKey() . '/' . $group->getSourceLanguage();
253 $definitionTitle = Title::makeTitleSafe( $title->getNamespace(), $name );
254 if ( !$definitionTitle || !$definitionTitle->exists() ) {
255 return true;
256 }
257
258 $definitionRevision = $definitionTitle->getLatestRevID();
259 $dbw = MediaWikiServices::getInstance()
260 ->getDBLoadBalancer()
261 ->getConnection( DB_PRIMARY );
262
263 $conds = [
264 'rt_page' => $title->getArticleID(),
265 'rt_type' => RevTagStore::TRANSVER_PROP,
266 'rt_revision' => $revision,
267 'rt_value' => $definitionRevision,
268 ];
269 $index = [ 'rt_type', 'rt_page', 'rt_revision' ];
270 $dbw->replace( 'revtag', [ $index ], $conds, __METHOD__ );
271
272 return true;
273 }
274
276 public static function disablePreSaveTransform(
277 WikiPage $wikiPage,
278 ParserOptions $popts
279 ): void {
280 global $wgTranslateUsePreSaveTransform;
281
282 if ( !$wgTranslateUsePreSaveTransform ) {
283 $handle = new MessageHandle( $wikiPage->getTitle() );
284 if ( $handle->isMessageNamespace() && !$handle->isDoc() ) {
285 $popts->setPreSaveTransform( false );
286 }
287 }
288 }
289}
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:382
Minimal service container.
Definition Services.php:58
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.
Some general static methods for instantiating TTMServer and helpers.
Definition TTMServer.php:20
static onChange(MessageHandle $handle)
Called from TranslateEditAddons::onSave.
Definition TTMServer.php:55