diff options
Diffstat (limited to 'www/wiki/extensions/Translate/specials/SpecialTranslate.php')
-rw-r--r-- | www/wiki/extensions/Translate/specials/SpecialTranslate.php | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/specials/SpecialTranslate.php b/www/wiki/extensions/Translate/specials/SpecialTranslate.php new file mode 100644 index 00000000..fad30847 --- /dev/null +++ b/www/wiki/extensions/Translate/specials/SpecialTranslate.php @@ -0,0 +1,443 @@ +<?php +/** + * Contains logic for special page Special:Translate. + * + * @file + * @author Niklas Laxström + * @author Siebrand Mazeland + * @license GPL-2.0-or-later + */ + +/** + * Implements the core of Translate extension - a special page which shows + * a list of messages in a format defined by Tasks. + * + * @ingroup SpecialPage TranslateSpecialPage + */ +class SpecialTranslate extends SpecialPage { + /** @var MessageGroup */ + protected $group; + + protected $defaults; + protected $nondefaults = []; + protected $options; + + public function __construct() { + parent::__construct( 'Translate' ); + } + + public function doesWrites() { + return true; + } + + protected function getGroupName() { + return 'wiki'; + } + + /** + * Access point for this special page. + * + * @param null|string $parameters + * @throws ErrorPageError + */ + public function execute( $parameters ) { + $out = $this->getOutput(); + $out->addModuleStyles( [ + 'ext.translate.special.translate.styles', + 'jquery.uls.grid', + 'mediawiki.ui.button' + ] ); + + $this->setHeaders(); + + if ( !defined( 'ULS_VERSION' ) ) { + throw new ErrorPageError( + 'translate-ulsdep-title', + 'translate-ulsdep-body' + ); + } + + $this->setup( $parameters ); + $out->addModules( 'ext.translate.special.translate' ); + $out->addJsConfigVars( 'wgTranslateLanguages', TranslateUtils::getLanguageNames( null ) ); + + $out->addHTML( Html::openElement( 'div', [ + 'class' => 'grid ext-translate-container', + ] ) ); + + $out->addHTML( $this->tuxSettingsForm() ); + $out->addHTML( $this->messageSelector() ); + + $table = new TuxMessageTable( $this->getContext(), $this->group, $this->options['language'] ); + $output = $table->fullTable(); + + $out->addHTML( $output ); + $out->addHTML( Html::closeElement( 'div' ) ); + } + + protected function setup( $parameters ) { + $request = $this->getRequest(); + + $defaults = [ + /* str */'taction' => 'translate', + /* str */'language' => $this->getLanguage()->getCode(), + /* str */'group' => '!additions', + ]; + + // Dump everything here + $nondefaults = []; + + $parameters = array_map( 'trim', explode( ';', $parameters ) ); + $pars = []; + + foreach ( $parameters as $_ ) { + if ( $_ === '' ) { + continue; + } + + if ( strpos( $_, '=' ) !== false ) { + list( $key, $value ) = array_map( 'trim', explode( '=', $_, 2 ) ); + } else { + $key = 'group'; + $value = $_; + } + + $pars[$key] = $value; + } + + foreach ( $defaults as $v => $t ) { + if ( is_bool( $t ) ) { + $r = isset( $pars[$v] ) ? (bool)$pars[$v] : $defaults[$v]; + $r = $request->getBool( $v, $r ); + } elseif ( is_int( $t ) ) { + $r = isset( $pars[$v] ) ? (int)$pars[$v] : $defaults[$v]; + $r = $request->getInt( $v, $r ); + } elseif ( is_string( $t ) ) { + $r = isset( $pars[$v] ) ? (string)$pars[$v] : $defaults[$v]; + $r = $request->getText( $v, $r ); + } + + if ( !isset( $r ) ) { + throw new MWException( '$r was not set' ); + } + + wfAppendToArrayIfNotDefault( $v, $r, $defaults, $nondefaults ); + } + + // Fix defaults based on what we got + if ( isset( $nondefaults['taction'] ) ) { + if ( $nondefaults['taction'] === 'export' ) { + // Redirect old export URLs to Special:ExportTranslations + $params = []; + if ( isset( $nondefaults['group'] ) ) { + $params['group'] = $nondefaults['group']; + } + if ( isset( $nondefaults['language'] ) ) { + $params['language'] = $nondefaults['language']; + } + + $export = SpecialPage::getTitleFor( 'ExportTranslations' )->getLocalURL( $params ); + $this->getOutput()->redirect( $export ); + } + } + + $this->defaults = $defaults; + $this->nondefaults = $nondefaults; + Hooks::run( 'TranslateGetSpecialTranslateOptions', [ &$defaults, &$nondefaults ] ); + + $this->options = $nondefaults + $defaults; + $this->group = MessageGroups::getGroup( $this->options['group'] ); + if ( $this->group ) { + $this->options['group'] = $this->group->getId(); + } else { + $this->group = MessageGroups::getGroup( $this->defaults['group'] ); + } + + if ( !Language::isKnownLanguageTag( $this->options['language'] ) ) { + $this->options['language'] = $this->defaults['language']; + } + + if ( MessageGroups::isDynamic( $this->group ) ) { + $this->group->setLanguage( $this->options['language'] ); + } + } + + protected function tuxSettingsForm() { + $nojs = Html::element( + 'div', + [ 'class' => 'tux-nojs errorbox' ], + $this->msg( 'tux-nojs' )->plain() + ); + + $attrs = [ 'class' => 'row tux-editor-header' ]; + $selectors = $this->tuxGroupSelector() . + $this->tuxLanguageSelector() . + $this->tuxGroupDescription() . + $this->tuxWorkflowSelector() . + $this->tuxGroupWarning(); + + return Html::rawElement( 'div', $attrs, $selectors ) . $nojs; + } + + protected function messageSelector() { + $output = Html::openElement( 'div', [ 'class' => 'row tux-messagetable-header hide' ] ); + $output .= Html::openElement( 'div', [ 'class' => 'nine columns' ] ); + $output .= Html::openElement( 'ul', [ 'class' => 'row tux-message-selector' ] ); + $userId = $this->getUser()->getId(); + $tabs = [ + 'all' => '', + 'untranslated' => '!translated', + 'outdated' => 'fuzzy', + 'translated' => 'translated', + 'unproofread' => "translated|!reviewer:$userId|!last-translator:$userId", + ]; + + $params = $this->nondefaults; + + foreach ( $tabs as $tab => $filter ) { + // Possible classes and messages, for grepping: + // tux-tab-all + // tux-tab-untranslated + // tux-tab-outdated + // tux-tab-translated + // tux-tab-unproofread + $tabClass = "tux-tab-$tab"; + $taskParams = [ 'filter' => $filter ] + $params; + ksort( $taskParams ); + $href = $this->getPageTitle()->getLocalURL( $taskParams ); + $link = Html::element( 'a', [ 'href' => $href ], $this->msg( $tabClass )->text() ); + $output .= Html::rawElement( 'li', [ + 'class' => 'column ' . $tabClass, + 'data-filter' => $filter, + 'data-title' => $tab, + ], $link ); + } + + // Check boxes for the "more" tab. + // The array keys are used as the name attribute of the checkbox. + // in the id attribute as tux-option-KEY, + // and and also for the data-filter attribute. + // The message is shown as the check box's label. + $options = [ + 'optional' => $this->msg( 'tux-message-filter-optional-messages-label' )->escaped(), + ]; + + $container = Html::openElement( 'ul', [ 'class' => 'column tux-message-selector' ] ); + foreach ( $options as $optFilter => $optLabel ) { + $container .= Html::rawElement( 'li', + [ 'class' => 'column' ], + Xml::checkLabel( + $optLabel, + $optFilter, + "tux-option-$optFilter", + isset( $this->nondefaults[$optFilter] ), + [ 'data-filter' => $optFilter ] + ) + ); + } + + $container .= Html::closeElement( 'ul' ); + + // @todo FIXME: Hard coded "ellipsis". + $output .= Html::openElement( 'li', [ 'class' => 'column more' ] ) . + '...' . + $container . + Html::closeElement( 'li' ); + + $output .= Html::closeElement( 'ul' ); + $output .= Html::closeElement( 'div' ); // close nine columns + $output .= Html::openElement( 'div', [ 'class' => 'three columns' ] ); + $output .= Html::openElement( 'div', [ 'class' => 'tux-message-filter-wrapper' ] ); + $output .= Html::element( 'input', [ + 'class' => 'tux-message-filter-box', + 'type' => 'search', + ] ); + $output .= Html::closeElement( 'div' ); // close tux-message-filter-wrapper + + $output .= Html::closeElement( 'div' ); // close three columns + + $output .= Html::closeElement( 'div' ); // close the row + + return $output; + } + + protected function tuxGroupSelector() { + $groupClass = [ 'grouptitle', 'grouplink' ]; + if ( $this->group instanceof AggregateMessageGroup ) { + $groupClass[] = 'tux-breadcrumb__item--aggregate'; + } + + // @todo FIXME The selector should have expanded parent-child lists + $output = Html::openElement( 'div', [ + 'class' => 'eight columns tux-breadcrumb', + 'data-language' => $this->options['language'], + ] ) . + Html::element( 'span', + [ 'class' => 'grouptitle' ], + $this->msg( 'translate-msggroupselector-projects' )->text() + ) . + Html::element( 'span', + [ 'class' => 'grouptitle grouplink tux-breadcrumb__item--aggregate' ], + $this->msg( 'translate-msggroupselector-search-all' )->text() + ) . + Html::element( 'span', + [ + 'class' => $groupClass, + 'data-msggroupid' => $this->group->getId(), + ], + $this->group->getLabel() + ) . + Html::closeElement( 'div' ); + + return $output; + } + + protected function tuxLanguageSelector() { + global $wgTranslateDocumentationLanguageCode; + + if ( $this->options['language'] === $wgTranslateDocumentationLanguageCode ) { + $targetLangName = $this->msg( 'translate-documentation-language' )->text(); + } else { + $targetLangName = Language::fetchLanguageName( $this->options['language'] ); + } + + $label = Html::element( + 'span', + [ 'class' => 'ext-translate-language-selector-label' ], + $this->msg( 'tux-languageselector' )->text() + ); + $value = Html::element( + 'span', + [ 'class' => 'uls' ], + $targetLangName + ); + + return Html::rawElement( + 'div', + [ 'class' => 'four columns ext-translate-language-selector' ], + "$label $value" + ); + } + + protected function tuxGroupDescription() { + // Initialize an empty warning box to be filled client-side. + return Html::rawElement( + 'div', + [ 'class' => 'twelve columns description' ], + $this->getGroupDescription( $this->group ) + ); + } + + protected function getGroupDescription( MessageGroup $group ) { + $description = $group->getDescription( $this->getContext() ); + if ( $description !== null ) { + return TranslateUtils::parseAsInterface( + $this->getOutput(), $description + ); + } + return ''; + } + + protected function tuxGroupWarning() { + if ( $this->options['group'] === '' ) { + return Html::rawElement( + 'div', + [ 'class' => 'twelve columns group-warning' ], + $this->msg( 'tux-translate-page-no-such-group' )->parse() + ); + } + + // Initialize an empty warning box to be filled client-side. + return Html::element( + 'div', + [ 'class' => 'twelve columns group-warning' ], + '' + ); + } + + protected function tuxWorkflowSelector() { + return Html::element( 'div', [ 'class' => 'tux-workflow twelve columns' ] ); + } + + /** + * Adds the task-based tabs on Special:Translate and few other special pages. + * Hook: SkinTemplateNavigation::SpecialPage + * @since 2012-02-10 + * @param Skin $skin + * @param array &$tabs + * @return true + */ + public static function tabify( Skin $skin, array &$tabs ) { + $title = $skin->getTitle(); + list( $alias, $sub ) = TranslateUtils::resolveSpecialPageAlias( $title->getText() ); + + $pagesInGroup = [ 'Translate', 'LanguageStats', 'MessageGroupStats' ]; + if ( !in_array( $alias, $pagesInGroup, true ) ) { + return true; + } + + $skin->getOutput()->addModuleStyles( 'ext.translate.tabgroup' ); + + // Extract subpage syntax, otherwise the values are not passed forward + $params = []; + if ( trim( $sub ) !== '' ) { + if ( $alias === 'Translate' || $alias === 'MessageGroupStats' ) { + $params['group'] = $sub; + } elseif ( $alias === 'LanguageStats' ) { + // Breaks if additional parameters besides language are code provided + $params['language'] = $sub; + } + } + + $request = $skin->getRequest(); + // However, query string params take precedence + $params['language'] = $request->getVal( 'language' ); + $params['group'] = $request->getVal( 'group' ); + + $taction = $request->getVal( 'taction', 'translate' ); + + $translate = SpecialPage::getTitleFor( 'Translate' ); + $languagestats = SpecialPage::getTitleFor( 'LanguageStats' ); + $messagegroupstats = SpecialPage::getTitleFor( 'MessageGroupStats' ); + + // Clear the special page tab that might be there already + $tabs['namespaces'] = []; + + $tabs['namespaces']['translate'] = [ + 'text' => wfMessage( 'translate-taction-translate' )->text(), + 'href' => $translate->getLocalURL( $params ), + 'class' => 'tux-tab', + ]; + + if ( $alias === 'Translate' && $taction === 'translate' ) { + $tabs['namespaces']['translate']['class'] .= ' selected'; + } + + $tabs['views']['lstats'] = [ + 'text' => wfMessage( 'translate-taction-lstats' )->text(), + 'href' => $languagestats->getLocalURL( $params ), + 'class' => 'tux-tab', + ]; + if ( $alias === 'LanguageStats' ) { + $tabs['views']['lstats']['class'] .= ' selected'; + } + + $tabs['views']['mstats'] = [ + 'text' => wfMessage( 'translate-taction-mstats' )->text(), + 'href' => $messagegroupstats->getLocalURL( $params ), + 'class' => 'tux-tab', + ]; + + if ( $alias === 'MessageGroupStats' ) { + $tabs['views']['mstats']['class'] .= ' selected'; + } + + $tabs['views']['export'] = [ + 'text' => wfMessage( 'translate-taction-export' )->text(), + 'href' => SpecialPage::getTitleFor( 'ExportTranslations' )->getLocalURL( $params ), + 'class' => 'tux-tab', + ]; + + return true; + } +} |