summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Translate/specials/SpecialTranslate.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/Translate/specials/SpecialTranslate.php')
-rw-r--r--www/wiki/extensions/Translate/specials/SpecialTranslate.php443
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;
+ }
+}