Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MoveTranslatableBundleSpecialPage.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
6use ErrorPageError;
7use HTMLForm;
8use InvalidArgumentException;
9use MediaWiki\CommentStore\CommentStore;
13use MediaWiki\Html\Html;
14use MediaWiki\Language\FormatterFactory;
15use MediaWiki\Permissions\PermissionManager;
16use MediaWiki\Permissions\PermissionStatus;
17use MediaWiki\SpecialPage\UnlistedSpecialPage;
18use MediaWiki\Title\Title;
19use Message;
20use OutputPage;
21use PermissionsError;
22use ReadOnlyError;
23use SplObjectStorage;
24use ThrottledError;
25use Wikimedia\ObjectFactory\ObjectFactory;
26
35class MoveTranslatableBundleSpecialPage extends UnlistedSpecialPage {
36 // Form parameters both as text and as titles
37 private string $oldText;
38 private string $reason;
39 private bool $moveTalkpages = true;
40 private bool $moveSubpages = true;
41 private bool $leaveRedirect = true;
42 private ObjectFactory $objectFactory;
43 private TranslatableBundleMover $bundleMover;
44 private PermissionManager $permissionManager;
45 private TranslatableBundleFactory $bundleFactory;
46 private FormatterFactory $formatterFactory;
47 private $movePageSpec;
48 // Other
49 private ?Title $oldTitle;
50
51 public function __construct(
52 ObjectFactory $objectFactory,
53 PermissionManager $permissionManager,
54 TranslatableBundleMover $bundleMover,
55 TranslatableBundleFactory $bundleFactory,
56 FormatterFactory $formatterFactory,
57 $movePageSpec
58 ) {
59 parent::__construct( 'Movepage' );
60 $this->objectFactory = $objectFactory;
61 $this->permissionManager = $permissionManager;
62 $this->bundleMover = $bundleMover;
63 $this->bundleFactory = $bundleFactory;
64 $this->formatterFactory = $formatterFactory;
65 $this->movePageSpec = $movePageSpec;
66 }
67
68 public function doesWrites(): bool {
69 return true;
70 }
71
72 protected function getGroupName(): string {
73 return 'pagetools';
74 }
75
77 public function execute( $par ) {
78 $request = $this->getRequest();
79 $user = $this->getUser();
80 $this->addHelpLink( 'Help:Extension:Translate/Move_translatable_page' );
81 $out = $this->getOutput();
82 $out->addModuleStyles( 'ext.translate.special.movetranslatablebundles.styles' );
83
84 $this->oldText = $request->getText(
85 'wpOldTitle',
86 $request->getText( 'target', $par ?? '' )
87 );
88 $newText = $request->getText( 'wpNewTitle' );
89
90 $this->oldTitle = Title::newFromText( $this->oldText );
91 $newTitle = Title::newFromText( $newText );
92 // Normalize input
93 if ( $this->oldTitle ) {
94 $this->oldText = $this->oldTitle->getPrefixedText();
95 }
96
97 $this->reason = $request->getText( 'reason' );
98
99 // This will throw exceptions if there is an error.
100 $this->doBasicChecks();
101
102 // Real stuff starts here
103 $bundle = $this->bundleFactory->getBundle( $this->oldTitle );
104 if ( $bundle && $bundle->isMoveable() ) {
105 $this->getOutput()->setPageTitleMsg( $this->getSpecialPageTitle( $bundle ) );
106
107 if ( !$user->isAllowed( 'pagetranslation' ) ) {
108 throw new PermissionsError( 'pagetranslation' );
109 }
110
111 $subaction = $this->getSubactionFromRequest( $request->getText( 'subaction' ) );
112
113 $isValidPostRequest = $this->checkToken() && $request->wasPosted();
114 if ( $isValidPostRequest && $subaction === 'check' ) {
115 try {
116 $pageCollection = $this->bundleMover->getPageMoveCollection(
117 $this->oldTitle,
118 $newTitle,
119 $user,
120 $this->reason,
121 $this->moveSubpages,
122 $this->moveTalkpages,
123 $this->leaveRedirect
124 );
125 } catch ( ImpossiblePageMove $e ) {
126 $this->showErrors( $e->getBlockers() );
127 $this->showForm( $bundle );
128 return;
129 }
130
131 $this->showConfirmation( $pageCollection, $bundle );
132 } elseif ( $isValidPostRequest && $subaction === 'perform' ) {
133 $this->moveSubpages = $request->getBool( 'subpages' );
134 $this->moveTalkpages = $request->getBool( 'talkpages' );
135 $this->leaveRedirect = $request->getBool( 'redirect' );
136
137 // This will throw exceptions if there is an error.
138 $this->authorizeMove();
139
140 $this->bundleMover->moveAsynchronously(
141 $this->oldTitle,
142 $newTitle,
143 $this->moveSubpages,
144 $this->getUser(),
145 $this->reason,
146 $this->moveTalkpages,
147 $this->leaveRedirect
148 );
149 $this->getOutput()->addWikiMsg(
150 'pt-movepage-started',
151 $this->getLogPageWikiLink( $this->bundleFactory->getValidBundle( $this->oldTitle ) )
152 );
153 } else {
154 $this->showForm( $bundle );
155 }
156 } else {
157 // Delegate... don't want to reimplement this
158 $sp = $this->objectFactory->createObject( $this->movePageSpec );
159 $sp->execute( $par );
160 }
161 }
162
168 protected function doBasicChecks(): void {
169 $this->checkReadOnly();
170
171 if ( $this->oldTitle === null ) {
172 throw new ErrorPageError( 'notargettitle', 'notargettext' );
173 }
174
175 if ( !$this->oldTitle->exists() ) {
176 throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
177 }
178
179 // Since MW 1.36, Authority should be used to check permissions
180 $status = PermissionStatus::newEmpty();
181 if ( !$this->getAuthority()
182 ->definitelyCan( 'move', $this->oldTitle, $status )
183 ) {
184 throw new PermissionsError( 'move', $status );
185 }
186 }
187
189 protected function authorizeMove(): void {
190 $status = PermissionStatus::newEmpty();
191 $this->getAuthority()
192 ->authorizeWrite( 'move', $this->oldTitle, $status );
193
194 if ( !$status->isOK() ) {
195 if ( $status->isRateLimitExceeded() ) {
196 throw new ThrottledError;
197 } else {
198 throw new PermissionsError( 'move', $status );
199 }
200 }
201 }
202
204 protected function checkToken(): bool {
205 return $this->getContext()->getCsrfTokenSet()->matchTokenField( 'wpEditToken' );
206 }
207
209 protected function showErrors( SplObjectStorage $errors ): void {
210 // If there are many errors, for performance reasons we must parse them all at once
211 $s = '';
212 $context = 'pt-movepage-error-placeholder';
213 foreach ( $errors as $title ) {
214 $titleText = $title->getPrefixedText();
215 $s .= "'''$titleText'''\n\n";
216 $s .= $errors[ $title ]->getWikiText( false, $context );
217 }
218
219 $out = $this->getOutput();
220 $out->addHTML(
221 Html::errorBox(
222 $out->msg(
223 'pt-movepage-blockers',
224 $this->getLanguage()->formatNum( count( $errors ) )
225 )->parseAsBlock() .
226 $out->parseAsContent( $s )
227 )
228 );
229 }
230
232 public function showForm( TranslatableBundle $bundle ) {
233 $this->getOutput()->addBacklinkSubtitle( $this->oldTitle );
234 $this->getOutput()->addWikiMsg(
235 'pt-movepage-intro',
236 $this->getLogPageWikiLink(
237 $this->bundleFactory->getBundle( Title::newFromText( $this->oldText ) )
238 )
239 );
240
241 HTMLForm::factory( 'ooui', $this->getCommonFormFields(), $this->getContext() )
242 ->setMethod( 'post' )
243 ->setAction( $this->getPageTitle( $this->oldText )->getLocalURL() )
244 ->setSubmitName( 'subaction' )
245 ->setSubmitTextMsg( 'pt-movepage-action-check' )
246 ->setWrapperLegendMsg(
247 $bundle instanceof MessageBundle ? 'pt-movepage-messagebundle-legend' : 'pt-movepage-legend'
248 )
249 ->prepareForm()
250 ->displayForm( false );
251 }
252
257 protected function showConfirmation( PageMoveCollection $pageCollection, TranslatableBundle $bundle ): void {
258 $out = $this->getOutput();
259 $out->addBacklinkSubtitle( $this->oldTitle );
260 $out->addWikiMsg(
261 'pt-movepage-intro',
262 $this->getLogPageWikiLink(
263 $this->bundleFactory->getBundle( Title::newFromText( $this->oldText ) )
264 )
265 );
266
267 $count = 0;
268 $subpagesCount = 0;
269 $talkpagesCount = 0;
270
272 $pagesToMove = [
273 'pt-movepage-list-source' => [ $pageCollection->getTranslatablePage() ],
274 'pt-movepage-list-translation' => $pageCollection->getTranslationPagesPair(),
275 'pt-movepage-list-section' => $pageCollection->getUnitPagesPair()
276 ];
277
278 $subpages = $pageCollection->getSubpagesPair();
279 if ( $subpages ) {
280 $pagesToMove[ 'pt-movepage-list-other'] = $subpages;
281 }
282
283 $out->wrapWikiMsg( '== $1 ==', [ 'pt-movepage-list-pages' ] );
284
285 foreach ( $pagesToMove as $type => $pages ) {
286 $this->addSectionHeaderAndMessage( $out, $type, $pages );
287
288 if ( !$pages ) {
289 continue;
290 }
291
292 $lines = [];
293
294 foreach ( $pages as $pagePairs ) {
295 $count++;
296
297 if ( $type === 'pt-movepage-list-other' ) {
298 $subpagesCount++;
299 }
300
301 $old = $pagePairs->getOldTitle();
302 $new = $pagePairs->getNewTitle();
303 $line = '* ' . $old->getPrefixedText() . ' → ' . $new->getPrefixedText();
304 if ( $pagePairs->hasTalkpage() ) {
305 $count++;
306 $talkpagesCount++;
307 $line .= ' ' . $this->msg( 'pt-movepage-talkpage-exists' )->text();
308 }
309
310 $lines[] = $line;
311 }
312
313 $out->addWikiTextAsInterface( implode( "\n", $lines ) );
314 }
315
316 $nonMovableSubpages = $pageCollection->getNonMovableSubpages();
317 if ( $nonMovableSubpages ) {
318 $this->addSectionHeaderAndMessage( $out, 'pt-movepage-list-nonmovable', $nonMovableSubpages );
319 $lines = [];
320 $out->wrapWikiMsg( "'''$1'''", $this->msg( 'pt-movepage-list-nonmovable-note' ) );
321 foreach ( $nonMovableSubpages as $page => $status ) {
322 $invalidityReason = $this->formatterFactory
323 ->getStatusFormatter( $this->getContext() )
324 ->getWikiText( $status );
325 $lines[] = '* ' . $page . ' (' . str_replace( "\n", " ", $invalidityReason ) . ')';
326 }
327
328 $out->addWikiTextAsInterface( implode( "\n", $lines ) );
329 }
330
331 $translatableSubpages = $pageCollection->getTranslatableSubpages();
332 $sectionType = 'pt-movepage-list-translatable';
333 $this->addSectionHeaderAndMessage( $out, $sectionType, $translatableSubpages );
334 if ( $translatableSubpages ) {
335 $lines = [];
336 $out->wrapWikiMsg( "'''$1'''", $this->msg( 'pt-movepage-list-translatable-note' ) );
337 foreach ( $translatableSubpages as $page ) {
338 $lines[] = '* ' . $page->getPrefixedText();
339 }
340 $out->addWikiTextAsInterface( implode( "\n", $lines ) );
341 }
342
343 $out->addWikiTextAsInterface( "----\n" );
344 $out->addWikiMsg(
345 'pt-movepage-list-count',
346 $this->getLanguage()->formatNum( $count ),
347 $this->getLanguage()->formatNum( $subpagesCount ),
348 $this->getLanguage()->formatNum( $talkpagesCount )
349 );
350
351 $formDescriptor = array_merge(
352 $this->getCommonFormFields(),
353 [
354 'subpages' => [
355 'type' => 'check',
356 'name' => 'subpages',
357 'id' => 'mw-subpages',
358 'label-message' => 'pt-movepage-subpages',
359 'default' => $this->moveSubpages && !count( $nonMovableSubpages ),
360 'disabled' => count( $nonMovableSubpages )
361 ],
362 'redirect' => [
363 'type' => 'check',
364 'name' => 'redirect',
365 'id' => 'mw-leave-redirect',
366 'label-message' => 'pt-leave-redirect',
367 'default' => $this->leaveRedirect,
368 ],
369 'talkpages' => [
370 'type' => 'check',
371 'name' => 'talkpages',
372 'id' => 'mw-talkpages',
373 'label-message' => 'pt-movepage-talkpages',
374 'default' => $this->moveTalkpages
375 ]
376 ]
377 );
378
379 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
380 $htmlForm
381 ->addButton( [
382 'name' => 'subaction',
383 'value' => $this->msg( 'pt-movepage-action-other' )->text(),
384 ] )
385 ->setMethod( 'post' )
386 ->setAction( $this->getPageTitle( $this->oldText )->getLocalURL() )
387 ->setSubmitName( 'subaction' )
388 ->setSubmitTextMsg( 'pt-movepage-action-perform' )
389 ->setWrapperLegendMsg(
390 $bundle instanceof MessageBundle ? 'pt-movepage-messagebundle-legend' : 'pt-movepage-legend'
391 )
392 ->prepareForm()
393 ->displayForm( false );
394 }
395
397 private function addSectionHeaderAndMessage( OutputPage $out, string $type, array $pages ): void {
398 $leaveRedirect = TranslatableBundleMover::shouldLeaveRedirect( $type, $this->leaveRedirect );
399
400 $pageCount = count( $pages );
401 if ( $leaveRedirect ) {
402 $headingRedirectLabel = $this->msg(
403 'pt-leave-redirect-label',
404 $this->msg( $type, $pageCount )->text()
405 )->text();
406 $out->addWikiTextAsInterface( "<h3 class=\"mw-translate-leave-redirect\">$headingRedirectLabel</h3>" );
407 } else {
408 $out->wrapWikiMsg( '=== $1 ===', [ $type, $pageCount ] );
409 }
410
411 if ( !$pageCount ) {
412 $out->addWikiMsg( 'pt-movepage-list-no-pages' );
413 }
414 }
415
416 private function getSubactionFromRequest( string $subactionText ): string {
417 switch ( $subactionText ) {
418 case $this->msg( 'pt-movepage-action-check' )->text():
419 return 'check';
420 case $this->msg( 'pt-movepage-action-perform' )->text():
421 return 'perform';
422 case $this->msg( 'pt-movepage-action-other' )->text():
423 return 'show-form';
424 default:
425 return 'show-form';
426 }
427 }
428
429 private function getCommonFormFields(): array {
430 return [
431 'wpOldTitle' => [
432 'type' => 'text',
433 'name' => 'wpOldTitle',
434 'label-message' => 'pt-movepage-current',
435 'default' => $this->oldText,
436 'readonly' => true,
437 ],
438 'wpNewTitle' => [
439 'type' => 'text',
440 'name' => 'wpNewTitle',
441 'label-message' => 'pt-movepage-new',
442 ],
443 'reason' => [
444 'type' => 'text',
445 'name' => 'reason',
446 'label-message' => 'pt-movepage-reason',
447 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
448 'default' => $this->reason,
449 ],
450 'redirect' => [
451 'type' => 'hidden',
452 'name' => 'redirect',
453 'id' => 'mw-leave-redirect',
454 'label-message' => 'pt-leave-redirect',
455 'default' => $this->leaveRedirect,
456 ],
457 'subpages' => [
458 'type' => 'hidden',
459 'name' => 'subpages',
460 'default' => $this->moveSubpages,
461 ],
462 'talkpages' => [
463 'type' => 'hidden',
464 'name' => 'talkpages',
465 'default' => $this->moveTalkpages
466 ]
467 ];
468 }
469
470 private function getSpecialPageTitle( TranslatableBundle $bundle ): Message {
471 if ( $bundle instanceof TranslatablePage ) {
472 return $this->msg( 'pt-movepage-title', $this->oldText );
473 } elseif ( $bundle instanceof MessageBundle ) {
474 return $this->msg( 'pt-movepage-messagebundle-title', $this->oldText );
475 }
476
477 throw new InvalidArgumentException( 'TranslatableBundle is neither a TranslatablePage or MessageBundle' );
478 }
479
480 private function getLogPageWikiLink( ?TranslatableBundle $bundle ): string {
481 if ( $bundle instanceof MessageBundle ) {
482 return 'Special:Log/messagebundle';
483 }
484
485 // Default to page translation log in case of errors
486 return 'Special:Log/pagetranslation';
487 }
488}
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager( $services->getTitleFactory());}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getDBLoadBalancer());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance( 'Translate.MessageGroupSubscription'), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getDBLoadBalancerFactory());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance( 'Translate'), $services->getMainObjectStash(), $services->getDBLoadBalancerFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getDBLoadBalancer());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup(), $services->getNamespaceInfo(), $services->getTitleFactory());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getDBLoadBalancerFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getDBLoadBalancer(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'));}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getDBLoadBalancerFactory(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getDBLoadBalancer(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
Create instances of various classes based on the type of TranslatableBundle.
Translatable bundle represents a message group where its translatable content is defined on a wiki pa...
Exception thrown when a translatable page move is not possible.
Replacement for Special:Movepage to allow renaming a translatable bundle and all pages associated wit...
showConfirmation(PageMoveCollection $pageCollection, TranslatableBundle $bundle)
The second form, which still allows changing some things.
doBasicChecks()
Do the basic checks whether moving is possible and whether the input looks anywhere near sane.
Collection of pages potentially affected by a page move operation.
getNonMovableSubpages()
Get list of sub-pages which cannot be moved for various reasons (e.g.
Contains the core logic to validate and move translatable bundles.