wikimedia/mediawiki-extensions-Translate

View on GitHub
src/TranslatorInterface/TranslationsSpecialPage.php

Summary

Maintainability
B
6 hrs
Test Coverage
<?php
declare( strict_types = 1 );

namespace MediaWiki\Extension\Translate\TranslatorInterface;

use HtmlArmor;
use MediaWiki\Extension\Translate\MessageLoading\MessageHandle;
use MediaWiki\Extension\Translate\Utilities\Utilities;
use MediaWiki\Html\Html;
use MediaWiki\HTMLForm\HTMLForm;
use MediaWiki\Language\Language;
use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\Message\Message;
use MediaWiki\SpecialPage\IncludableSpecialPage;
use MediaWiki\Title\Title;
use SearchEngineFactory;
use Wikimedia\Rdbms\ILoadBalancer;

/**
 * Implements a special page which shows all translations for a message.
 * Bits taken from SpecialPrefixindex.php and TranslateTasks.php
 *
 * @author Siebrand Mazeland
 * @author Niklas Laxstörm
 * @license GPL-2.0-or-later
 * @ingroup SpecialPage TranslateSpecialPage
 */
class TranslationsSpecialPage extends IncludableSpecialPage {
    private Language $contentLanguage;
    private LanguageNameUtils $languageNameUtils;
    private ILoadBalancer $loadBalancer;
    private SearchEngineFactory $searchEngineFactory;

    public function __construct(
        Language $contentLanguage,
        LanguageNameUtils $languageNameUtils,
        ILoadBalancer $loadBalancer,
        SearchEngineFactory $searchEngineFactory
    ) {
        parent::__construct( 'Translations' );
        $this->contentLanguage = $contentLanguage;
        $this->languageNameUtils = $languageNameUtils;
        $this->loadBalancer = $loadBalancer;
        $this->searchEngineFactory = $searchEngineFactory;
    }

    protected function getGroupName() {
        return 'translation';
    }

    public function getDescription() {
        return $this->msg( 'translations' );
    }

    public function prefixSearchSubpages( $search, $limit, $offset ) {
        return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
    }

    /**
     * Entry point : initialise variables and call subfunctions.
     * @param string|null $par Message key. Becomes "MediaWiki:Allmessages" when called like
     *             Special:Translations/MediaWiki:Allmessages (default null)
     */
    public function execute( $par ) {
        $this->setHeaders();
        $this->outputHeader();

        $out = $this->getOutput();
        $out->addModuleStyles( 'ext.translate.specialpages.styles' );

        $par = (string)$par;

        if ( $this->including() ) {
            $title = Title::newFromText( $par );
            if ( !$title ) {
                $out->addWikiMsg( 'translate-translations-including-no-param' );
            } else {
                $this->showTranslations( $title );
            }

            return;
        }

        /**
         * GET values.
         */
        $request = $this->getRequest();
        $message = $request->getText( 'message', $par );
        $namespace = $request->getInt( 'namespace', NS_MAIN );

        $title = Title::newFromText( $message, $namespace );

        $out->addHelpLink(
            'Help:Extension:Translate/Statistics_and_reporting#Translations_in_all_languages'
        );

        if ( !$title ) {
            $title = Title::makeTitle( NS_MEDIAWIKI, '' );
            $this->namespaceMessageForm( $title );
        } else {
            $this->namespaceMessageForm( $title );
            $out->addHTML( '<br />' );
            $this->showTranslations( $title );
        }
    }

    /**
     * Message input fieldset
     */
    private function namespaceMessageForm( Title $title ): void {
        $formDescriptor = [
            'textbox' => [
                'type' => 'text',
                'name' => 'message',
                'id' => 'message',
                'label-message' => 'translate-translations-messagename',
                'size' => 30,
                'default' => $title->getText(),
            ],
            'selector' => [
                'type' => 'select',
                'name' => 'namespace',
                'id' => 'namespace',
                'label-message' => 'translate-translations-project',
                'options' => $this->getSortedNamespaces(),
                'default' => $title->getNamespace(),
            ]
        ];

        HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
            ->setMethod( 'get' )
            ->setTitle( $this->getPageTitle() ) // Remove subpage
            ->setSubmitTextMsg( 'allpagessubmit' )
            ->setWrapperLegendMsg( 'translate-translations-fieldset-title' )
            ->prepareForm()
            ->displayForm( false );
    }

    /**
     * Returns sorted array of namespaces.
     *
     * @return array<string,int>
     */
    private function getSortedNamespaces(): array {
        $nslist = [];
        foreach ( $this->getConfig()->get( 'TranslateMessageNamespaces' ) as $ns ) {
            $nslist[$this->contentLanguage->getFormattedNsText( $ns )] = $ns;
        }
        ksort( $nslist );

        return $nslist;
    }

    /**
     * Builds a table with all translations of $title.
     */
    private function showTranslations( Title $title ): void {
        $handle = new MessageHandle( $title );
        $namespace = $title->getNamespace();
        $message = $handle->getKey();

        if ( !$handle->isValid() ) {
            $this->getOutput()->addWikiMsg( 'translate-translations-no-message', $title->getPrefixedText() );

            return;
        }

        $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
        /** @var string[] */
        $titles = $dbr->newSelectQueryBuilder()
            ->select( 'page_title' )
            ->from( 'page' )
            ->where( [
                'page_namespace' => $namespace,
                'page_title ' . $dbr->buildLike( "$message/", $dbr->anyString() ),
            ] )
            ->caller( __METHOD__ )
            ->orderBy( 'page_title' )
            ->fetchFieldValues();

        if ( !$titles ) {
            $this->getOutput()->addWikiMsg(
                'translate-translations-no-message',
                $title->getPrefixedText()
            );

            return;
        } else {
            $this->getOutput()->addWikiMsg(
                'translate-translations-count',
                Message::numParam( count( $titles ) )
            );
        }

        $pageInfo = Utilities::getContents( $titles, $namespace );

        $rows = [
            Html::rawElement(
                'tr',
                [],
                Html::element( 'th', [], $this->msg( 'allmessagesname' )->text() ) .
                    Html::element( 'th', [], $this->msg( 'allmessagescurrent' )->text() )
            ),
        ];

        $historyText = "\u{00A0}<sup>" .
            $this->msg( 'translate-translations-history-short' )->escaped() .
            "</sup>\u{00A0}";
        $separator = $this->msg( 'word-separator' )->plain();

        foreach ( $titles as $key ) {
            $tTitle = Title::makeTitle( $namespace, $key );
            $tHandle = new MessageHandle( $tTitle );

            $code = $tHandle->getCode();

            $text = Utilities::getLanguageName( $code, $this->getLanguage()->getCode() );
            $text .= $separator;
            $text .= $this->msg( 'parentheses' )->params( $code )->plain();
            $tools = [
                'edit' => Html::element(
                    'a',
                    [ 'href' => Utilities::getEditorUrl( $tHandle ) ],
                    $text
                ),
                'history' => $this->getLinkRenderer()->makeLink(
                    $tTitle,
                    new HtmlArmor( $historyText ),
                    [
                        'title' => $this->msg( 'history-title', $tTitle->getPrefixedDBkey() )->text()
                    ],
                    [ 'action' => 'history' ]
                ),
            ];

            $class = '';
            if ( MessageHandle::hasFuzzyString( $pageInfo[$key][0] ) || $tHandle->isFuzzy() ) {
                $class = 'mw-sp-translate-fuzzy';
            }

            $languageAttributes = [];
            if ( $this->languageNameUtils->isKnownLanguageTag( $code ) ) {
                $language = $tHandle->getEffectiveLanguage();
                $languageAttributes = [
                    'lang' => $language->getHtmlCode(),
                    'dir' => $language->getDir(),
                ];
            }

            $formattedContent = Utilities::convertWhiteSpaceToHTML( $pageInfo[$key][0] );

            $rows[] = Html::rawElement(
                'tr',
                [ 'class' => $class ],
                Html::rawElement( 'td', [], $tools['history'] . $tools['edit'] ) .
                    Html::rawElement( 'td', $languageAttributes, $formattedContent )
            );
        }

        $out = Html::rawElement(
            'table',
            [ 'class' => 'mw-sp-translate-table sortable wikitable' ],
            "\n" . implode( "\n", $rows ) . "\n"
        );
        $this->getOutput()->addHTML( $out );
    }
}