diff options
author | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
---|---|---|
committer | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
commit | fc7369835258467bf97eb64f184b93691f9a9fd5 (patch) | |
tree | daabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/Translate/TranslateHooks.php |
first commit
Diffstat (limited to 'www/wiki/extensions/Translate/TranslateHooks.php')
-rw-r--r-- | www/wiki/extensions/Translate/TranslateHooks.php | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/TranslateHooks.php b/www/wiki/extensions/Translate/TranslateHooks.php new file mode 100644 index 00000000..dae7fa85 --- /dev/null +++ b/www/wiki/extensions/Translate/TranslateHooks.php @@ -0,0 +1,846 @@ +<?php +/** + * Contains class with basic non-feature specific hooks. + * + * @file + * @author Niklas Laxström + * @license GPL-2.0-or-later + */ + +/** + * Some hooks for Translate extension. + */ +class TranslateHooks { + /** + * Any user of this list should make sure that the tables + * actually exist, since they may be optional + * + * @var array + */ + private static $userMergeTables = [ + 'translate_stash' => 'ts_user', + 'translate_reviews' => 'trr_user', + ]; + + /** + * Hook: ResourceLoaderTestModules + * @param array &$modules + */ + public static function onResourceLoaderTestModules( array &$modules ) { + $modules['qunit']['ext.translate.parsers.test'] = [ + 'scripts' => [ 'tests/qunit/ext.translate.parsers.test.js' ], + 'dependencies' => [ 'ext.translate.parsers' ], + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Translate', + ]; + + $modules['qunit']['ext.translate.special.pagemigration.test'] = [ + 'scripts' => [ 'tests/qunit/ext.translate.special.pagemigration.test.js' ], + 'dependencies' => [ 'ext.translate.special.pagemigration' ], + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Translate', + ]; + } + + /** + * Initialises the extension. + * Does late-initialization that is not possible at file level, + * because it depends on user configuration. + */ + public static function setupTranslate() { + global $wgPageTranslationNamespace; + if ( isset( $wgPageTranslationNamespace ) && + $wgPageTranslationNamespace !== NS_TRANSLATIONS ) { + throw new MWException( + '$wgPageTranslationNamespace is no longer supported. Instead, define ' . + 'NS_TRANSLATIONS and NS_TRANSLATIONS_TALK in LocalSettings.php before loading ' . + 'Translate.' + ); + } + + global $wgTranslatePHPlot, $wgAutoloadClasses, $wgHooks; + + if ( $wgTranslatePHPlot ) { + $wgAutoloadClasses['PHPlot'] = $wgTranslatePHPlot; + } + + // Page translation setup check and init if enabled. + global $wgEnablePageTranslation; + if ( $wgEnablePageTranslation ) { + // Special page and the right to use it + global $wgSpecialPages, $wgAvailableRights; + $wgSpecialPages['PageTranslation'] = 'SpecialPageTranslation'; + $wgSpecialPages['PageTranslationDeletePage'] = 'SpecialPageTranslationDeletePage'; + + // right-pagetranslation action-pagetranslation + $wgAvailableRights[] = 'pagetranslation'; + + $wgSpecialPages['PageMigration'] = 'SpecialPageMigration'; + $wgSpecialPages['PagePreparation'] = 'SpecialPagePreparation'; + + global $wgActionFilteredLogs, $wgLogActionsHandlers, $wgLogTypes; + + // log-description-pagetranslation log-name-pagetranslation logentry-pagetranslation-mark + // logentry-pagetranslation-unmark logentry-pagetranslation-moveok + // logentry-pagetranslation-movenok logentry-pagetranslation-deletefok + // logentry-pagetranslation-deletefnok logentry-pagetranslation-deletelok + // logentry-pagetranslation-deletelnok logentry-pagetranslation-encourage + // logentry-pagetranslation-discourage logentry-pagetranslation-prioritylanguages + // logentry-pagetranslation-associate logentry-pagetranslation-dissociate + $wgLogTypes[] = 'pagetranslation'; + $wgLogActionsHandlers['pagetranslation/mark'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/unmark'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/moveok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/movenok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/deletelok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/deletefok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/deletelnok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/deletefnok'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/encourage'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/discourage'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/prioritylanguages'] = + 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/associate'] = 'PageTranslationLogFormatter'; + $wgLogActionsHandlers['pagetranslation/dissociate'] = 'PageTranslationLogFormatter'; + $wgActionFilteredLogs['pagetranslation'] = [ + 'mark' => [ 'mark' ], + 'unmark' => [ 'unmark' ], + 'move' => [ 'moveok', 'movenok' ], + 'delete' => [ 'deletefok', 'deletefnok', 'deletelok', 'deletelnok' ], + 'encourage' => [ 'encourage' ], + 'discourage' => [ 'discourage' ], + 'prioritylanguages' => [ 'prioritylanguages' ], + 'aggregategroups' => [ 'associate', 'dissociate' ], + ]; + + global $wgJobClasses; + $wgJobClasses['TranslateRenderJob'] = 'TranslateRenderJob'; + $wgJobClasses['RenderJob'] = 'TranslateRenderJob'; + $wgJobClasses['TranslatablePageMoveJob'] = 'TranslatablePageMoveJob'; + $wgJobClasses['TranslateMoveJob'] = 'TranslateMoveJob'; + $wgJobClasses['MoveJob'] = 'TranslateMoveJob'; + $wgJobClasses['TranslateDeleteJob'] = 'TranslateDeleteJob'; + $wgJobClasses['DeleteJob'] = 'TranslateDeleteJob'; + $wgJobClasses['TranslationsUpdateJob'] = 'TranslationsUpdateJob'; + + // Namespaces + global $wgNamespacesWithSubpages, $wgNamespaceProtection; + global $wgTranslateMessageNamespaces; + + $wgNamespacesWithSubpages[NS_TRANSLATIONS] = true; + $wgNamespacesWithSubpages[NS_TRANSLATIONS_TALK] = true; + + // Standard protection and register it for filtering + $wgNamespaceProtection[NS_TRANSLATIONS] = [ 'translate' ]; + $wgTranslateMessageNamespaces[] = NS_TRANSLATIONS; + + /// Page translation hooks + + /// @todo Register our css, is there a better place for this? + $wgHooks['OutputPageBeforeHTML'][] = 'PageTranslationHooks::injectCss'; + + // Check syntax for \<translate> + $wgHooks['PageContentSave'][] = 'PageTranslationHooks::tpSyntaxCheck'; + $wgHooks['EditFilterMergedContent'][] = + 'PageTranslationHooks::tpSyntaxCheckForEditContent'; + + // Add transtag to page props for discovery + $wgHooks['PageContentSaveComplete'][] = 'PageTranslationHooks::addTranstag'; + $wgHooks['RevisionInsertComplete'][] = + 'PageTranslationHooks::updateTranstagOnNullRevisions'; + + // Register different ways to show language links + $wgHooks['ParserFirstCallInit'][] = 'TranslateHooks::setupParserHooks'; + $wgHooks['LanguageLinks'][] = 'PageTranslationHooks::addLanguageLinks'; + $wgHooks['SkinTemplateGetLanguageLink'][] = 'PageTranslationHooks::formatLanguageLink'; + + // Strip \<translate> tags etc. from source pages when rendering + $wgHooks['ParserBeforeStrip'][] = 'PageTranslationHooks::renderTagPage'; + $wgHooks['ParserOutputPostCacheTransform'][] = + 'PageTranslationHooks::onParserOutputPostCacheTransform'; + + // Set the page content language + $wgHooks['PageContentLanguage'][] = 'PageTranslationHooks::onPageContentLanguage'; + + // Prevent editing of certain pages in translations namespace + $wgHooks['getUserPermissionsErrorsExpensive'][] = + 'PageTranslationHooks::onGetUserPermissionsErrorsExpensive'; + // Prevent editing of translation pages directly + $wgHooks['getUserPermissionsErrorsExpensive'][] = + 'PageTranslationHooks::preventDirectEditing'; + // Prevent patroling of translation pages + $wgHooks['getUserPermissionsErrors'][] = + 'PageTranslationHooks::preventPatrolling'; + + // Our custom header for translation pages + $wgHooks['ArticleViewHeader'][] = 'PageTranslationHooks::translatablePageHeader'; + + // Edit notice shown on translatable pages + $wgHooks['TitleGetEditNotices'][] = 'PageTranslationHooks::onTitleGetEditNotices'; + + // Custom move page that can move all the associated pages too + $wgHooks['SpecialPage_initList'][] = 'PageTranslationHooks::replaceMovePage'; + // Locking during page moves + $wgHooks['getUserPermissionsErrorsExpensive'][] = + 'PageTranslationHooks::lockedPagesCheck'; + // Disable action=delete + $wgHooks['ArticleConfirmDelete'][] = 'PageTranslationHooks::disableDelete'; + + // Replace subpage logic behavior + $wgHooks['SkinSubPageSubtitle'][] = 'PageTranslationHooks::replaceSubtitle'; + + // Replaced edit tab with translation tab for translation pages + $wgHooks['SkinTemplateNavigation'][] = 'PageTranslationHooks::translateTab'; + + // Update translated page when translation unit is moved + $wgHooks['TitleMoveComplete'][] = 'PageTranslationHooks::onMoveTranslationUnits'; + + // Update translated page when translation unit is deleted + $wgHooks['ArticleDeleteComplete'][] = 'PageTranslationHooks::onDeleteTranslationUnit'; + } + + global $wgTranslateUseSandbox; + if ( $wgTranslateUseSandbox ) { + global $wgSpecialPages, $wgAvailableRights, $wgDefaultUserOptions; + + $wgSpecialPages['ManageTranslatorSandbox'] = 'SpecialManageTranslatorSandbox'; + $wgSpecialPages['TranslationStash'] = 'SpecialTranslationStash'; + $wgDefaultUserOptions['translate-sandbox'] = ''; + // right-translate-sandboxmanage action-translate-sandboxmanage + $wgAvailableRights[] = 'translate-sandboxmanage'; + + $wgHooks['GetPreferences'][] = 'TranslateSandbox::onGetPreferences'; + $wgHooks['UserGetRights'][] = 'TranslateSandbox::enforcePermissions'; + $wgHooks['ApiCheckCanExecute'][] = 'TranslateSandbox::onApiCheckCanExecute'; + $wgHooks['UserGetRights'][] = 'TranslateSandbox::allowAccountCreation'; + + global $wgLogTypes, $wgLogActionsHandlers; + // log-name-translatorsandbox log-description-translatorsandbox + $wgLogTypes[] = 'translatorsandbox'; + // logentry-translatorsandbox-promoted logentry-translatorsandbox-rejected + $wgLogActionsHandlers['translatorsandbox/promoted'] = 'TranslateLogFormatter'; + $wgLogActionsHandlers['translatorsandbox/rejected'] = 'TranslateLogFormatter'; + + // This is no longer used for new entries since 2016.07. + // logentry-newusers-tsbpromoted + $wgLogActionsHandlers['newusers/tsbpromoted'] = 'LogFormatter'; + + global $wgJobClasses; + $wgJobClasses['TranslateSandboxEmailJob'] = 'TranslateSandboxEmailJob'; + + global $wgAPIModules; + $wgAPIModules['translationstash'] = 'ApiTranslationStash'; + $wgAPIModules['translatesandbox'] = 'ApiTranslateSandbox'; + } + + // Back compatibility for MediaWiki <= 1.31 + global $wgVersion, $wgResourceModules; + if ( version_compare( $wgVersion, '1.32', '<' ) ) { + $wgResourceModules['ext.translate.editor']['dependencies'][] = 'mediawiki.api.parse'; + $wgResourceModules['ext.translate.special.translate']['dependencies'][] = 'mediawiki.api.parse'; + } + + global $wgNamespaceRobotPolicies; + $wgNamespaceRobotPolicies[NS_TRANSLATIONS] = 'noindex'; + } + + /** + * Hook: UserGetReservedNames + * Prevents anyone from registering or logging in as FuzzyBot + * + * @param array &$names + */ + public static function onUserGetReservedNames( array &$names ) { + global $wgTranslateFuzzyBotName; + $names[] = $wgTranslateFuzzyBotName; + } + + /** + * Used for setting an AbuseFilter variable. + * + * @param AbuseFilterVariableHolder &$vars + * @param Title|null $title + */ + public static function onAbuseFilterFilterAction( &$vars, $title ) { + if ( !$title instanceof Title ) { + wfDebugLog( 'T143073', 'Got non-Title in ' . wfGetAllCallers( 5 ) ); + return; + } + + $handle = new MessageHandle( $title ); + + // Only set this variable if we are in a proper namespace to avoid + // unnecessary overhead in non-translation pages + if ( $handle->isMessageNamespace() ) { + $vars->setLazyLoadVar( + 'translate_source_text', + 'translate-get-source', + [ 'handle' => $handle ] + ); + } + } + + /** + * Computes the translate_source_text AbuseFilter variable + * @param string $method + * @param AbuseFilterVariableHolder $vars + * @param array $parameters + * @param null &$result + * @return bool + */ + public static function onAbuseFilterComputeVariable( $method, $vars, $parameters, &$result ) { + if ( $method !== 'translate-get-source' ) { + return true; + } + + $handle = $parameters['handle']; + $source = ''; + if ( $handle->isValid() ) { + $group = $handle->getGroup(); + $source = $group->getMessage( $handle->getKey(), $group->getSourceLanguage() ); + } + + $result = $source; + + return false; + } + + /** + * Register AbuseFilter variables provided by Translate. + * @param array &$builderValues + */ + public static function onAbuseFilterBuilder( array &$builderValues ) { + // Uses: 'abusefilter-edit-builder-vars-translate-source-text' + $builderValues['vars']['translate_source_text'] = 'translate-source-text'; + } + + /** + * Hook: ParserFirstCallInit + * Registers \<languages> tag with the parser. + * + * @param Parser $parser + */ + public static function setupParserHooks( Parser $parser ) { + // For nice language list in-page + $parser->setHook( 'languages', [ 'PageTranslationHooks', 'languages' ] ); + } + + /** + * Hook: UnitTestsList + * + * @param array &$files + */ + public static function setupUnitTests( array &$files ) { + $dir = __DIR__ . '/tests/phpunit'; + $directoryIterator = new RecursiveDirectoryIterator( $dir ); + $fileIterator = new RecursiveIteratorIterator( $directoryIterator ); + + /** @var SplFileInfo $fileInfo */ + foreach ( $fileIterator as $fileInfo ) { + if ( substr( $fileInfo->getFilename(), -8 ) === 'Test.php' ) { + $files[] = $fileInfo->getPathname(); + } + } + } + + /** + * Hook: LoadExtensionSchemaUpdates + * + * @param DatabaseUpdater $updater + */ + public static function schemaUpdates( DatabaseUpdater $updater ) { + $dir = __DIR__ . '/sql'; + + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_sections', + "$dir/translate_sections.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addField', + 'translate_sections', + 'trs_order', + "$dir/translate_sections-trs_order.patch.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'revtag', "$dir/revtag.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_groupstats', + "$dir/translate_groupstats.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addIndex', + 'translate_sections', + 'trs_page_order', + "$dir/translate_sections-indexchange.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'dropIndex', + 'translate_sections', + 'trs_page', + "$dir/translate_sections-indexchange2.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_reviews', + "$dir/translate_reviews.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_groupreviews', + "$dir/translate_groupreviews.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_tms', + "$dir/translate_tm.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_metadata', + "$dir/translate_metadata.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addTable', 'translate_messageindex', + "$dir/translate_messageindex.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addIndex', + 'translate_groupstats', + 'tgs_lang', + "$dir/translate_groupstats-indexchange.sql", + true + ] ); + $updater->addExtensionUpdate( [ + 'addField', 'translate_groupstats', + 'tgs_proofread', + "$dir/translate_groupstats-proofread.sql", + true + ] ); + + $updater->addExtensionUpdate( [ + 'addTable', + 'translate_stash', + "$dir/translate_stash.sql", + true + ] ); + + // This also adds a PRIMARY KEY + $updater->addExtensionUpdate( [ + 'renameIndex', + 'translate_reviews', + 'trr_user_page_revision', + 'PRIMARY', + false, + "$dir/translate_reviews-patch-01-primary-key.sql", + true + ] ); + } + + /** + * Hook: ParserTestTables + * @param array &$tables + */ + public static function parserTestTables( array &$tables ) { + $tables[] = 'revtag'; + $tables[] = 'translate_groupstats'; + $tables[] = 'translate_messageindex'; + $tables[] = 'translate_stash'; + } + + /** + * Hook: PageContentLanguage + * Set the correct page content language for translation units. + * + * @param Title $title + * @param Language|StubUserLang|string &$pageLang + */ + public static function onPageContentLanguage( Title $title, &$pageLang ) { + $handle = new MessageHandle( $title ); + if ( $handle->isMessageNamespace() ) { + $pageLang = $handle->getEffectiveLanguage(); + } + } + + /** + * Hook: LanguageGetTranslatedLanguageNames + * Hook: TranslateSupportedLanguages + * @param array &$names + * @param string $code + */ + public static function translateMessageDocumentationLanguage( array &$names, $code ) { + global $wgTranslateDocumentationLanguageCode; + if ( $wgTranslateDocumentationLanguageCode ) { + // Special case the autonyms + if ( + $wgTranslateDocumentationLanguageCode === $code || + $code === null + ) { + $code = 'en'; + } + + $names[$wgTranslateDocumentationLanguageCode] = + wfMessage( 'translate-documentation-language' )->inLanguage( $code )->plain(); + } + } + + /** + * Hook: SpecialSearchProfiles + * @param array &$profiles + */ + public static function searchProfile( array &$profiles ) { + global $wgTranslateMessageNamespaces; + $insert = []; + $insert['translation'] = [ + 'message' => 'translate-searchprofile', + 'tooltip' => 'translate-searchprofile-tooltip', + 'namespaces' => $wgTranslateMessageNamespaces, + ]; + + // Insert translations before 'all' + $index = array_search( 'all', array_keys( $profiles ) ); + + // Or just at the end if all is not found + if ( $index === false ) { + wfWarn( '"all" not found in search profiles' ); + $index = count( $profiles ); + } + + $profiles = array_merge( + array_slice( $profiles, 0, $index ), + $insert, + array_slice( $profiles, $index ) + ); + } + + /** + * Hook: SpecialSearchProfileForm + * @param SpecialSearch $search + * @param string &$form + * @param string $profile + * @param string $term + * @param array $opts + * @return bool + */ + public static function searchProfileForm( + SpecialSearch $search, + &$form, + $profile, + $term, + array $opts + ) { + if ( $profile !== 'translation' ) { + return true; + } + + $server = TTMServer::primary(); + if ( TTMServer::primary() instanceof SearchableTTMServer ) { + $href = SpecialPage::getTitleFor( 'SearchTranslations' ) + ->getFullUrl( [ 'query' => $term ] ); + $wrapper = new RawMessage( '<div class="successbox plainlinks">$1</div>' ); + $form = $wrapper + ->params( $search->msg( 'translate-searchprofile-note', $href )->plain() ) + ->parse(); + + return false; + } + + if ( !$search->getSearchEngine()->supports( 'title-suffix-filter' ) ) { + return false; + } + + $hidden = ''; + foreach ( $opts as $key => $value ) { + $hidden .= Html::hidden( $key, $value ); + } + + $context = $search->getContext(); + $code = $context->getLanguage()->getCode(); + $selected = $context->getRequest()->getVal( 'languagefilter' ); + + $languages = TranslateUtils::getLanguageNames( $code ); + ksort( $languages ); + + $selector = new XmlSelect( 'languagefilter', 'languagefilter' ); + $selector->setDefault( $selected ); + $selector->addOption( wfMessage( 'translate-search-nofilter' )->text(), '-' ); + foreach ( $languages as $code => $name ) { + $selector->addOption( "$code - $name", $code ); + } + + $selector = $selector->getHTML(); + + $label = Xml::label( + wfMessage( 'translate-search-languagefilter' )->text(), + 'languagefilter' + ) . ' '; + $params = [ 'id' => 'mw-searchoptions' ]; + + $form = Xml::fieldset( false, false, $params ) . + $hidden . $label . $selector . + Html::closeElement( 'fieldset' ); + + return false; + } + + /** + * Hook: SpecialSearchSetupEngine + * @param SpecialSearch $search + * @param string $profile + * @param SearchEngine $engine + */ + public static function searchProfileSetupEngine( + SpecialSearch $search, + $profile, + SearchEngine $engine + ) { + if ( $profile !== 'translation' ) { + return; + } + + $context = $search->getContext(); + $selected = $context->getRequest()->getVal( 'languagefilter' ); + if ( $selected !== '-' && $selected ) { + $engine->setFeatureData( 'title-suffix-filter', "/$selected" ); + $search->setExtraParam( 'languagefilter', $selected ); + } + } + + /** + * Hook: Translate:MessageGroupStats:isIncluded + * @param int $id + * @param string $code + * @return bool + */ + public static function hideDiscouragedFromStats( $id, $code ) { + // Return true to keep, false to exclude + return MessageGroups::getPriority( $id ) !== 'discouraged'; + } + + /** + * Hook: Translate:MessageGroupStats:isIncluded + * @param int $id + * @param string $code + * @return false + */ + public static function hideRestrictedFromStats( $id, $code ) { + $filterLangs = TranslateMetadata::get( $id, 'prioritylangs' ); + if ( strlen( $filterLangs ) === 0 ) { + // No restrictions, keep everything + return true; + } + + $filter = array_flip( explode( ',', $filterLangs ) ); + + // If the language is in the list, return true to not hide it + return isset( $filter[$code] ); + } + + /** + * Hook: LinksUpdate + * @param LinksUpdate $updater + */ + public static function preventCategorization( LinksUpdate $updater ) { + $handle = new MessageHandle( $updater->getTitle() ); + if ( $handle->isMessageNamespace() && !$handle->isDoc() ) { + $updater->mCategories = []; + } + } + + /** + * Hook: MakeGlobalVariablesScript + * + * Adds $wgTranslateDocumentationLanguageCode to ResourceLoader configuration + * when Special:Translate is shown. + * @param array &$vars + * @param OutputPage $out + */ + public static function addConfig( array &$vars, OutputPage $out ) { + $request = $out->getRequest(); + $title = $out->getTitle(); + list( $alias, ) = TranslateUtils::resolveSpecialPageAlias( $title->getText() ); + + if ( $title->isSpecialPage() + && ( $alias === 'Translate' + || $alias === 'TranslationStash' + || $alias === 'SearchTranslations' ) + ) { + global $wgTranslateDocumentationLanguageCode, $wgTranslatePermissionUrl, + $wgTranslateUseSandbox; + $vars['TranslateRight'] = $out->getUser()->isAllowed( 'translate' ); + $vars['TranslateMessageReviewRight'] = + $out->getUser()->isAllowed( 'translate-messagereview' ); + $vars['DeleteRight'] = $out->getUser()->isAllowed( 'delete' ); + $vars['wgTranslateDocumentationLanguageCode'] = $wgTranslateDocumentationLanguageCode; + $vars['wgTranslatePermissionUrl'] = $wgTranslatePermissionUrl; + $vars['wgTranslateUseSandbox'] = $wgTranslateUseSandbox; + } + } + + /** + * Hook: AdminLinks + * @param ALTree $tree + */ + public static function onAdminLinks( ALTree $tree ) { + global $wgTranslateUseSandbox; + + if ( $wgTranslateUseSandbox ) { + $sectionLabel = wfMessage( 'adminlinks_users' )->text(); + $row = $tree->getSection( $sectionLabel )->getRow( 'main' ); + $row->addItem( ALItem::newFromSpecialPage( 'TranslateSandbox' ) ); + } + } + + /** + * Hook: MergeAccountFromTo + * For UserMerge extension. + * + * @param User $oldUser + * @param User $newUser + */ + public static function onMergeAccountFromTo( User $oldUser, User $newUser ) { + $dbw = wfGetDB( DB_MASTER ); + + // Update the non-duplicate rows, we'll just delete + // the duplicate ones later + foreach ( self::$userMergeTables as $table => $field ) { + if ( $dbw->tableExists( $table ) ) { + $dbw->update( + $table, + [ $field => $newUser->getId() ], + [ $field => $oldUser->getId() ], + __METHOD__, + [ 'IGNORE' ] + ); + } + } + } + + /** + * Hook: DeleteAccount + * For UserMerge extension. + * + * @param User $oldUser + */ + public static function onDeleteAccount( User $oldUser ) { + $dbw = wfGetDB( DB_MASTER ); + + // Delete any remaining rows that didn't get merged + foreach ( self::$userMergeTables as $table => $field ) { + if ( $dbw->tableExists( $table ) ) { + $dbw->delete( + $table, + [ $field => $oldUser->getId() ], + __METHOD__ + ); + } + } + } + + /** + * Hook: AbortEmailNotification + * + * False aborts the email. + * @param User $editor + * @param Title $title + * @param RecentChange $rc + * @return bool + */ + public static function onAbortEmailNotificationReview( + User $editor, + Title $title, + RecentChange $rc + ) { + if ( $rc->getAttribute( 'rc_log_type' ) === 'translationreview' ) { + return false; + } + } + + /** + * Hook: TitleIsAlwaysKnown + * Make Special:MyLanguage links red if the target page doesn't exist. + * A bit hacky because the core code is not so flexible. + * + * @param Title $target + * @param bool &$isKnown + * @return bool + */ + public static function onTitleIsAlwaysKnown( Title $target, &$isKnown ) { + if ( !$target->inNamespace( NS_SPECIAL ) ) { + return true; + } + + list( $name, $subpage ) = TranslateUtils::resolveSpecialPageAlias( $target->getDBkey() ); + if ( $name !== 'MyLanguage' ) { + return true; + } + + if ( (string)$subpage === '' ) { + return true; + } + + $realTarget = Title::newFromText( $subpage ); + if ( !$realTarget || !$realTarget->exists() ) { + $isKnown = false; + + return false; + } + + return true; + } + + /** + * Hook: ParserFirstCallInit + * @param Parser $parser + */ + public static function setupTranslateParserFunction( Parser $parser ) { + $parser->setFunctionHook( 'translation', 'TranslateHooks::translateRenderParserFunction' ); + } + + /** + * @param Parser $parser + * @return string + */ + public static function translateRenderParserFunction( Parser $parser ) { + $pageTitle = $parser->getTitle(); + + $handle = new MessageHandle( $pageTitle ); + $code = $handle->getCode(); + if ( Language::isKnownLanguageTag( $code ) ) { + return '/' . $code; + } + return ''; + } + + /** + * @param ResourceLoader $resourceLoader + */ + public static function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ) { + $modules = []; + $modules['ext.translate.recentgroups'] = [ + 'scripts' => 'resources/js/ext.translate.recentgroups.js', + 'dependencies' => [ + 'mediawiki.storage' + ], + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Translate', + 'targets' => [ 'desktop', 'mobile' ], + ]; + + $resourceLoader->register( $modules ); + } +} |