53 private const GROUP_SYNC_INFO_WRAPPER_CLASS =
'smg-group-sync-cache-info';
54 private const RIGHT =
'translate-manage';
60 protected $hasRight =
false;
68 private $synchronizationCache;
70 private $displayGroupSyncInfo;
72 private $jobQueueGroup;
74 private $messageIndex;
76 private $linkBatchFactory;
78 public function __construct(
80 NamespaceInfo $nsInfo,
81 RevisionLookup $revLookup,
83 JobQueueGroup $jobQueueGroup,
85 LinkBatchFactory $linkBatchFactory
88 parent::__construct(
'ManageMessageGroups' );
89 $this->contLang = $contLang;
90 $this->nsInfo = $nsInfo;
91 $this->revLookup = $revLookup;
92 $this->synchronizationCache = $synchronizationCache;
94 $this->jobQueueGroup = $jobQueueGroup;
95 $this->messageIndex = $messageIndex;
96 $this->linkBatchFactory = $linkBatchFactory;
99 public function doesWrites() {
103 protected function getGroupName() {
104 return 'translation';
107 public function getDescription() {
108 return $this->msg(
'managemessagegroups' )->text();
111 public function execute( $par ) {
114 $out = $this->getOutput();
115 $out->addModuleStyles(
'ext.translate.specialpages.styles' );
116 $out->addModules(
'ext.translate.special.managegroups' );
117 $out->addHelpLink(
'Help:Extension:Translate/Group_management' );
119 $name = $par ?: MessageChangeStorage::DEFAULT_NAME;
121 $this->cdb = MessageChangeStorage::getCdbPath( $name );
122 if ( !MessageChangeStorage::isValidCdbName( $name ) || !file_exists( $this->cdb ) ) {
123 if ( $this->getConfig()->
get(
'TranslateGroupSynchronizationCache' ) ) {
125 $this->displayGroupSyncInfo->getGroupsInSyncHtml(
126 $this->synchronizationCache->getGroupsInSync(),
127 self::GROUP_SYNC_INFO_WRAPPER_CLASS
132 $this->displayGroupSyncInfo->getHtmlForGroupsWithError(
133 $this->synchronizationCache,
134 self::GROUP_SYNC_INFO_WRAPPER_CLASS,
142 $out->addWikiMsg(
'translate-smg-nochanges' );
147 $user = $this->getUser();
148 $this->hasRight = $user->isAllowed( self::RIGHT );
150 $req = $this->getRequest();
151 if ( !$req->wasPosted() ) {
152 $this->showChanges( $this->
getLimit() );
157 $block = $user->getBlock();
158 if ( $block && $block->isSitewide() ) {
159 throw new UserBlockedError(
162 $this->getLanguage(),
167 $csrfTokenSet = $this->getContext()->getCsrfTokenSet();
168 if ( !$this->hasRight || !$csrfTokenSet->matchTokenField(
'token' ) ) {
169 throw new PermissionsError( self::RIGHT );
172 $this->processSubmit();
179 ini_get(
'max_input_vars' ),
180 ini_get(
'suhosin.post.max_vars' ),
181 ini_get(
'suhosin.request.max_vars' )
184 $limits = array_filter( $limits );
185 return (
int)min( $limits );
188 protected function getLegend(): string {
189 $text = $this->diff->addHeader(
191 $this->msg(
'translate-smg-left' )->escaped(),
192 $this->msg(
'translate-smg-right' )->escaped()
195 return Html::rawElement(
'div', [
'class' =>
'mw-translate-smg-header' ], $text );
198 protected function showChanges(
int $limit ): void {
199 $diff = new DifferenceEngine( $this->getContext() );
200 $diff->showDiffStyle();
201 $diff->setReducedLineNumbers();
204 $out = $this->getOutput();
206 Html::openElement(
'form', [
'method' =>
'post' ] ) .
207 Html::hidden(
'title', $this->getPageTitle()->getPrefixedText(), [
208 'id' =>
'smgPageTitle'
210 Html::hidden(
'token', $this->getContext()->getCsrfTokenSet()->getToken() ) .
211 Html::hidden(
'changesetModifiedTime',
212 MessageChangeStorage::getLastModifiedTime( $this->cdb ) ) .
219 $groupSyncCacheEnabled = $this->getConfig()->get(
'TranslateGroupSynchronizationCache' );
220 if ( $groupSyncCacheEnabled ) {
222 $this->displayGroupSyncInfo->getGroupsInSyncHtml(
223 $this->synchronizationCache->getGroupsInSync(),
224 self::GROUP_SYNC_INFO_WRAPPER_CLASS
229 $this->displayGroupSyncInfo->getHtmlForGroupsWithError(
230 $this->synchronizationCache,
231 self::GROUP_SYNC_INFO_WRAPPER_CLASS,
237 $reader = \Cdb\Reader::open( $this->cdb );
238 $groups = $this->getGroupsFromCdb( $reader );
239 foreach ( $groups as $id => $group ) {
240 $sourceChanges = MessageSourceChange::loadModifications(
241 Utilities::deserialize( $reader->get( $id ) )
243 $out->addHTML( Html::element(
'h2', [], $group->getLabel() ) );
245 if ( $groupSyncCacheEnabled && $this->synchronizationCache->groupHasErrors( $id ) ) {
247 Html::warningBox( $this->msg(
'translate-smg-group-sync-error-warn' )->escaped(),
'center' )
252 $lb = $this->linkBatchFactory->newLinkBatch();
253 $ns = $group->getNamespace();
254 $isCap = $this->nsInfo->isCapitalized( $ns );
255 $languages = $sourceChanges->getLanguages();
257 foreach ( $languages as $language ) {
258 $languageChanges = $sourceChanges->getModificationsForLanguage( $language );
259 foreach ( $languageChanges as $type => $changes ) {
260 foreach ( $changes as $params ) {
262 $key = $params[
'key'];
264 $key = $this->contLang->ucfirst( $key );
266 $lb->add( $ns,
"$key/$language" );
272 foreach ( $languages as $language ) {
275 $changes[ MessageSourceChange::ADDITION ] = $sourceChanges->getAdditions( $language );
276 $changes[ MessageSourceChange::DELETION ] = $sourceChanges->getDeletions( $language );
277 $changes[ MessageSourceChange::CHANGE ] = $sourceChanges->getChanges( $language );
279 foreach ( $changes as $type => $messages ) {
280 foreach ( $messages as $params ) {
281 $change = $this->formatChange( $group, $sourceChanges, $language, $type, $params, $limit );
282 $out->addHTML( $change );
287 $out->wrapWikiMsg(
"<div class=warning>\n$1\n</div>",
'translate-smg-more' );
294 $this->showRenames( $group, $sourceChanges, $out, $language, $limit );
299 $button =
new ButtonInputWidget( [
301 'label' => $this->msg(
'translate-smg-submit' )->plain(),
302 'disabled' => !$this->hasRight ?
'disabled' : null,
303 'classes' => [
'mw-translate-smg-submit' ],
304 'title' => !$this->hasRight ? $this->msg(
'translate-smg-notallowed' )->plain() : null,
305 'flags' => [
'primary',
'progressive' ],
307 $out->addHTML( $button );
308 $out->addHTML( Html::closeElement(
'form' ) );
311 protected function formatChange(
313 MessageSourceChange $changes,
319 $key = $params[
'key'];
320 $title = Title::makeTitleSafe( $group->
getNamespace(),
"$key/$language" );
321 $id = self::changeId( $group->
getId(), $language, $type, $key );
323 $isReusedKey =
false;
325 if ( $title && $type ===
'addition' && $title->exists() ) {
334 $noticeHtml .= Html::warningBox( $this->msg(
'translate-manage-key-reused' )->text() );
336 } elseif ( $title && ( $type ===
'deletion' || $type ===
'change' ) && !$title->exists() ) {
343 $titleLink = $this->getLinkRenderer()->makeLink( $title );
345 if ( $type ===
'deletion' ) {
346 $content = $this->revLookup
347 ->getRevisionByTitle( $title )
348 ->getContent( SlotRecord::MAIN );
349 $wiki = ( $content instanceof TextContent ) ? $content->getText() :
'';
351 if ( $wiki ===
'' ) {
352 $noticeHtml .= Html::warningBox(
353 $this->msg(
'translate-manage-empty-content' )->text()
357 $oldContent = ContentHandler::makeContent( (
string)$wiki, $title );
358 $newContent = ContentHandler::makeContent(
'', $title );
359 $this->diff->setContent( $oldContent, $newContent );
360 $text = $this->diff->getDiff( $titleLink,
'', $noticeHtml );
361 } elseif ( $type ===
'addition' ) {
364 if ( $sourceLanguage === $language ) {
365 if ( $this->hasRight ) {
366 $menu = Html::rawElement(
369 'class' =>
'smg-rename-actions',
371 'data-group-id' => $group->
getId(),
372 'data-lang' => $language,
373 'data-msgkey' => $key,
374 'data-msgtitle' => $title->getFullText()
379 } elseif ( !self::isMessageDefinitionPresent( $group, $changes, $key ) ) {
380 $noticeHtml .= Html::warningBox(
381 $this->msg(
'translate-manage-source-message-not-found' )->text(),
382 'mw-translate-smg-notice-important'
386 $menu = Html::hidden(
"msg/$id",
'ignore', [
'id' =>
"i/$id" ] );
390 if ( $params[
'content'] ===
'' ) {
391 $noticeHtml .= Html::warningBox(
392 $this->msg(
'translate-manage-empty-content' )->text()
396 $oldContent = ContentHandler::makeContent(
'', $title );
397 $newContent = ContentHandler::makeContent( (
string)$params[
'content'], $title );
398 $this->diff->setContent( $oldContent, $newContent );
399 $text = $this->diff->getDiff(
'', $titleLink . $menu, $noticeHtml );
400 } elseif ( $type ===
'change' ) {
401 $wiki = Utilities::getContentForTitle( $title,
true );
408 $shouldFuzzy = $sourceLanguage === $language && $wiki !== $params[
'content'];
410 if ( $sourceLanguage === $language ) {
411 $label = $this->msg(
'translate-manage-action-fuzzy' )->text();
412 $actions .= Xml::radioLabel( $label,
"msg/$id",
"fuzzy",
"f/$id", $shouldFuzzy );
416 $sourceLanguage !== $language &&
418 !self::isMessageDefinitionPresent( $group, $changes, $key )
420 $noticeHtml .= Html::warningBox(
421 $this->msg(
'translate-manage-source-message-not-found' )->text(),
422 'mw-translate-smg-notice-important'
426 $actions .= Html::hidden(
"msg/$id",
'ignore', [
'id' =>
"i/$id" ] );
429 $label = $this->msg(
'translate-manage-action-import' )->text();
430 $actions .= Xml::radioLabel( $label,
"msg/$id",
"import",
"imp/$id", !$shouldFuzzy );
432 $label = $this->msg(
'translate-manage-action-ignore' )->text();
433 $actions .= Xml::radioLabel( $label,
"msg/$id",
"ignore",
"i/$id" );
437 $oldContent = ContentHandler::makeContent( (
string)$wiki, $title );
438 $newContent = ContentHandler::makeContent( (
string)$params[
'content'], $title );
440 $this->diff->setContent( $oldContent, $newContent );
441 $text .= $this->diff->getDiff( $titleLink, $actions, $noticeHtml );
444 $hidden = Html::hidden( $id, 1 );
447 $classes =
"mw-translate-smg-change smg-change-$type";
454 return Html::rawElement(
'div', [
'class' => $classes ], $text );
457 protected function processSubmit(): void {
458 $req = $this->getRequest();
459 $out = $this->getOutput();
462 $modificationJobs = $renameJobData = [];
463 $lastModifiedTime = intval( $req->getVal(
'changesetModifiedTime' ) );
465 if ( !MessageChangeStorage::isModifiedSince( $this->cdb, $lastModifiedTime ) ) {
466 $out->addWikiMsg(
'translate-smg-changeset-modified' );
470 $reader = \Cdb\Reader::open( $this->cdb );
471 $groups = $this->getGroupsFromCdb( $reader );
472 $groupSyncCacheEnabled = $this->getConfig()->get(
'TranslateGroupSynchronizationCache' );
475 foreach ( $groups as $groupId => $group ) {
478 throw new RuntimeException(
"Expected $groupId to be FileBasedMessageGroup, got "
479 . get_class( $group )
483 $changes = Utilities::deserialize( $reader->get( $groupId ) );
484 if ( $groupSyncCacheEnabled && $this->synchronizationCache->groupHasErrors( $groupId ) ) {
485 $postponed[$groupId] = $changes;
489 $sourceChanges = MessageSourceChange::loadModifications( $changes );
490 $groupModificationJobs = [];
491 $groupRenameJobData = [];
492 $languages = $sourceChanges->getLanguages();
493 foreach ( $languages as $language ) {
495 $this->handleModificationsSubmit(
501 $groupModificationJobs
505 $this->handleRenameSubmit(
512 $groupModificationJobs
515 if ( !isset( $postponed[$groupId][$language] ) ) {
516 $group->getMessageGroupCache( $language )->create();
520 if ( $groupSyncCacheEnabled && !isset( $postponed[ $groupId ] ) ) {
521 $this->synchronizationCache->markGroupAsReviewed( $groupId );
524 $modificationJobs[$groupId] = $groupModificationJobs;
525 $renameJobData[$groupId] = $groupRenameJobData;
526 }
catch ( Exception $e ) {
528 "ManageGroupsSpecialPage: Error in processSubmit. Group: $groupId\n" .
532 $errorGroups[] = $group->
getLabel();
536 $renameJobs = $this->createRenameJobs( $renameJobData );
537 $this->startSync( $modificationJobs, $renameJobs );
540 rename( $this->cdb, $this->cdb .
'-' . wfTimestamp() );
542 if ( $errorGroups ) {
543 $errorMsg = $this->getProcessingErrorMessage( $errorGroups, count( $groups ) );
547 'mw-translate-smg-submitted'
552 if ( count( $postponed ) ) {
553 $postponedSourceChanges = [];
554 foreach ( $postponed as $groupId => $changes ) {
555 $postponedSourceChanges[$groupId] = MessageSourceChange::loadModifications( $changes );
559 $this->showChanges( $this->getLimit() );
560 } elseif ( $errorGroups === [] ) {
561 $out->addWikiMsg(
'translate-smg-submitted' );
565 protected static function changeId(
571 return 'smg/' . substr( sha1(
"$groupId/$language/$type/$key" ), 0, 7 );
578 public static function tabify( Skin $skin, array &$tabs ): void {
579 $title = $skin->getTitle();
580 if ( !$title->isSpecialPage() ) {
583 $specialPageFactory = MediaWikiServices::getInstance()->getSpecialPageFactory();
584 [ $alias, ] = $specialPageFactory->resolveAlias( $title->getText() );
587 'ManageMessageGroups' =>
'namespaces',
588 'AggregateGroups' =>
'namespaces',
589 'SupportedLanguages' =>
'views',
590 'TranslationStats' =>
'views',
592 if ( !isset( $pagesInGroup[$alias] ) ) {
596 $tabs[
'namespaces'] = [];
597 foreach ( $pagesInGroup as $spName => $section ) {
598 $spClass = $specialPageFactory->getPage( $spName );
600 if ( $spClass ===
null || $spClass instanceof DisabledSpecialPage ) {
603 $spTitle = $spClass->getPageTitle();
605 $tabs[$section][strtolower( $spName )] = [
606 'text' => $spClass->getDescription(),
607 'href' => $spTitle->getLocalURL(),
608 'class' => $alias === $spName ?
'selected' :
'',
617 private static function isMessageDefinitionPresent(
619 MessageSourceChange $changes,
622 $sourceLanguage = $group->getSourceLanguage();
623 if ( $changes->findMessage( $sourceLanguage, $msgKey, [ MessageSourceChange::ADDITION ] ) ) {
628 $sourceHandle =
new MessageHandle( Title::makeTitle( $namespace, $msgKey ) );
629 return $sourceHandle->isValid();
632 private function showRenames(
634 MessageSourceChange $sourceChanges,
639 $changes = $sourceChanges->getRenames( $language );
640 foreach ( $changes as $key => $params ) {
643 if ( !isset( $changes[ $key ] ) ) {
648 $sourceChanges->isEqual( $language, $key ) ) {
656 $secondKey = $sourceChanges->getMatchedKey( $language, $key );
657 $secondMsg = $sourceChanges->getMatchedMessage( $language, $key );
660 $sourceChanges->isPreviousState(
663 [ MessageSourceChange::ADDITION, MessageSourceChange::CHANGE ]
666 $addedMsg = $firstMsg;
667 $deletedMsg = $secondMsg;
669 $addedMsg = $secondMsg;
670 $deletedMsg = $firstMsg;
673 $change = $this->formatRename(
678 $sourceChanges->isEqual( $language, $key ),
681 $out->addHTML( $change );
684 unset( $changes[$secondKey] );
689 $out->wrapWikiMsg(
"<div class=warning>\n$1\n</div>",
'translate-smg-more' );
695 private function formatRename(
703 $addedKey = $addedMsg[
'key'];
704 $deletedKey = $deletedMsg[
'key'];
707 $addedTitle = Title::makeTitleSafe( $group->
getNamespace(),
"$addedKey/$language" );
708 $deletedTitle = Title::makeTitleSafe( $group->
getNamespace(),
"$deletedKey/$language" );
709 $id = self::changeId( $group->
getId(), $language, MessageSourceChange::RENAME, $addedKey );
711 $addedTitleLink = $this->getLinkRenderer()->makeLink( $addedTitle );
712 $deletedTitleLink = $this->getLinkRenderer()->makeLink( $deletedTitle );
714 $renameSelected =
true;
717 $renameSelected =
false;
718 $label = $this->msg(
'translate-manage-action-rename-fuzzy' )->text();
719 $actions .= Xml::radioLabel( $label,
"msg/$id",
"renamefuzzy",
"rf/$id",
true );
722 $label = $this->msg(
'translate-manage-action-rename' )->text();
723 $actions .= Xml::radioLabel( $label,
"msg/$id",
"rename",
"imp/$id", $renameSelected );
725 $label = $this->msg(
'translate-manage-action-import' )->text();
726 $actions .= Xml::radioLabel( $label,
"msg/$id",
"import",
"imp/$id",
true );
731 $label = $this->msg(
'translate-manage-action-ignore-change' )->text();
732 $actions .= Xml::radioLabel( $label,
"msg/$id",
"ignore",
"i/$id" );
736 $addedContent = ContentHandler::makeContent( (
string)$addedMsg[
'content'], $addedTitle );
737 $deletedContent = ContentHandler::makeContent( (
string)$deletedMsg[
'content'], $deletedTitle );
738 $this->diff->setContent( $deletedContent, $addedContent );
743 $menu = Html::rawElement(
746 'class' =>
'smg-rename-actions',
748 'data-group-id' => $group->
getId(),
749 'data-msgkey' => $addedKey,
750 'data-msgtitle' => $addedTitle->getFullText()
755 $actions = Html::rawElement(
'div', [
'class' =>
'smg-change-import-options' ], $actions );
757 $text = $this->diff->getDiff(
759 $addedTitleLink . $menu . $actions,
760 $isEqual ? htmlspecialchars( $addedMsg[
'content'] ) :
''
763 $hidden = Html::hidden( $id, 1 );
767 return Html::rawElement(
769 [
'class' =>
'mw-translate-smg-change smg-change-rename' ],
774 private function getRenameJobParams(
776 MessageSourceChange $sourceChanges,
777 string $languageCode,
780 bool $isSourceLang =
true
782 if ( $selectedVal ===
'ignore' ) {
787 $replacementContent =
'';
788 $currentMsgKey = $currentMsg[
'key'];
789 $matchedMsg = $sourceChanges->getMatchedMessage( $languageCode, $currentMsgKey );
790 $matchedMsgKey = $matchedMsg[
'key'];
793 $sourceChanges->isPreviousState(
796 [ MessageSourceChange::ADDITION, MessageSourceChange::CHANGE ]
799 $params[
'target'] = $matchedMsgKey;
800 $params[
'replacement'] = $currentMsgKey;
801 $replacementContent = $currentMsg[
'content'];
803 $params[
'target'] = $currentMsgKey;
804 $params[
'replacement'] = $matchedMsgKey;
805 $replacementContent = $matchedMsg[
'content'];
808 $params[
'fuzzy'] = $selectedVal ===
'renamefuzzy';
810 $params[
'content'] = $replacementContent;
812 if ( $isSourceLang ) {
813 $params[
'targetTitle'] = Title::newFromText(
814 Utilities::title( $params[
'target'], $languageCode, $groupNamespace ),
817 $params[
'others'] = [];
823 private function handleRenameSubmit(
825 MessageSourceChange $sourceChanges,
830 array &$modificationJobs
832 $groupId = $group->getId();
833 $renames = $sourceChanges->getRenames( $language );
837 foreach ( $renames as $key => $params ) {
840 if ( !isset( $renames[$key] ) ) {
844 $id = self::changeId( $groupId, $language, MessageSourceChange::RENAME, $key );
846 [ $renameMissing, $isCurrentKeyPresent ] = $this->isRenameMissing(
856 if ( $renameMissing ) {
859 $postponed[$groupId][$language][MessageSourceChange::RENAME][$key] = $params;
863 if ( !$isCurrentKeyPresent ) {
868 $selectedVal = $req->getVal(
"msg/$id" );
869 $jobParams = $this->getRenameJobParams(
878 if ( $jobParams ===
null ) {
882 $targetStr = $jobParams[
'target' ];
883 if ( $isSourceLang ) {
884 $jobData[ $targetStr ] = $jobParams;
885 } elseif ( isset( $jobData[ $targetStr ] ) ) {
889 $jobData[ $targetStr ][
'others' ][ $language ] = $jobParams[
'content' ];
893 $title = Title::newFromText(
894 Utilities::title( $targetStr, $language, $groupNamespace ),
901 $matchedKey = $sourceChanges->getMatchedKey( $language, $key );
902 unset( $renames[$matchedKey] );
906 private function handleModificationsSubmit(
908 MessageSourceChange $sourceChanges,
912 array &$messageUpdateJob
914 $groupId = $group->getId();
915 $subchanges = $sourceChanges->getModificationsForLanguage( $language );
918 unset( $subchanges[ MessageSourceChange::RENAME ] );
921 foreach ( $subchanges as $type => $messages ) {
922 foreach ( $messages as $index => $params ) {
923 $key = $params[
'key'];
924 $id = self::changeId( $groupId, $language, $type, $key );
925 $title = Title::makeTitleSafe( $group->
getNamespace(),
"$key/$language" );
927 if ( !$this->isTitlePresent( $title, $type ) ) {
931 if ( !$req->getCheck( $id ) ) {
933 $postponed[$groupId][$language][$type][$index] = $params;
937 $selectedVal = $req->getVal(
"msg/$id" );
938 if ( $type === MessageSourceChange::DELETION || $selectedVal ===
'ignore' ) {
942 $fuzzy = $selectedVal ===
'fuzzy';
949 private function createRenameJobs( array $jobParams ): array {
951 foreach ( $jobParams as $groupId => $groupJobParams ) {
952 $jobs[$groupId] = $jobs[$groupId] ?? [];
953 foreach ( $groupJobParams as $params ) {
955 $params[
'targetTitle'],
957 $params[
'replacement'],
969 private function isTitlePresent( Title $title,
string $type ): bool {
972 ( $type === MessageSourceChange::DELETION || $type === MessageSourceChange::CHANGE ) &&
993 private function isRenameMissing(
995 MessageSourceChange $sourceChanges,
1002 if ( $req->getCheck( $id ) ) {
1003 return [
false, true ];
1006 $isCurrentKeyPresent =
false;
1009 $matchedKey = $sourceChanges->getMatchedKey( $language, $key );
1010 $matchedId = self::changeId( $groupId, $language, MessageSourceChange::RENAME, $matchedKey );
1011 if ( $req->getCheck( $matchedId ) ) {
1012 return [
false, $isCurrentKeyPresent ];
1018 $isSourceLang || !$sourceChanges->isEqual( $language, $matchedKey ),
1019 $isCurrentKeyPresent
1023 private function getProcessingErrorMessage( array $errorGroups,
int $totalGroupCount ): string {
1025 if ( count( $errorGroups ) < $totalGroupCount ) {
1026 $errorMsg = $this->msg(
'translate-smg-submitted-with-failure' )
1027 ->numParams( count( $errorGroups ) )
1029 $this->getLanguage()->commaList( $errorGroups ),
1030 $this->msg(
'translate-smg-submitted-others-processing' )
1034 $this->msg(
'translate-smg-submitted-with-failure' )
1035 ->numParams( count( $errorGroups ) )
1036 ->params( $this->getLanguage()->commaList( $errorGroups ),
'' )
1045 private function getGroupsFromCdb( \Cdb\Reader $reader ): array {
1047 $groupIds = Utilities::deserialize( $reader->get(
'#keys' ) );
1048 foreach ( $groupIds as $id ) {
1049 $groups[$id] = MessageGroups::getGroup( $id );
1051 return array_filter( $groups );
1059 private function startSync( array $modificationJobs, array $renameJobs ): void {
1062 $modificationGroupIds = array_keys( array_filter( $modificationJobs ) );
1063 $renameGroupIds = array_keys( array_filter( $renameJobs ) );
1064 $uniqueGroupIds = array_unique( array_merge( $modificationGroupIds, $renameGroupIds ) );
1065 $jobQueueInstance = $this->jobQueueGroup;
1067 foreach ( $uniqueGroupIds as $groupId ) {
1072 $groupRenameJobs = $renameJobs[$groupId] ?? [];
1074 foreach ( $groupRenameJobs as $job ) {
1075 $groupJobs[] = $job;
1076 $messageUpdateParam = MessageUpdateParameter::createFromJob( $job );
1077 $messages[] = $messageUpdateParam;
1080 $replacement = $messageUpdateParam->getReplacementValue();
1081 $targetTitle = Title::makeTitle( $job->getTitle()->getNamespace(), $replacement );
1082 $messageKeys[] = (
new MessageHandle( $targetTitle ) )->getKey();
1085 $groupModificationJobs = $modificationJobs[$groupId] ?? [];
1087 foreach ( $groupModificationJobs as $job ) {
1088 $groupJobs[] = $job;
1089 $messageUpdateParam = MessageUpdateParameter::createFromJob( $job );
1090 $messages[] = $messageUpdateParam;
1092 $messageKeys[] = (
new MessageHandle( $job->getTitle() ) )->getKey();
1097 $group = MessageGroups::getGroup( $groupId );
1098 $this->messageIndex->storeInterim( $group, $messageKeys );
1100 if ( $this->getConfig()->
get(
'TranslateGroupSynchronizationCache' ) ) {
1101 $this->synchronizationCache->addMessages( $groupId, ...$messages );
1102 $this->synchronizationCache->markGroupForSync( $groupId );
1104 LoggerFactory::getInstance(
'Translate.GroupSynchronization' )->info(
1105 '[' . __CLASS__ .
'] Synchronization started for {groupId} by {user}',
1107 'groupId' => $groupId,
1108 'user' => $this->getUser()->getName()
1117 DeferredUpdates::addCallableUpdate(
1118 static function () use ( $jobQueueInstance, $groupJobs ) {
1119 $jobQueueInstance->push( $groupJobs );