1<?php
2/**
3 * Implements Special:MyLanguage
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @author Niklas Laxström
22 * @author Siebrand Mazeland
23 * @copyright Copyright © 2010-2013 Niklas Laxström, Siebrand Mazeland
24 */
25
26use MediaWiki\Languages\LanguageNameUtils;
27use MediaWiki\Page\WikiPageFactory;
28
29/**
30 * Unlisted special page just to redirect the user to the translated version of
31 * a page, if it exists.
32 *
33 * Usage: [[Special:MyLanguage/Page name|link text]]
34 *
35 * @since 1.24
36 * @ingroup SpecialPage
37 */
38class SpecialMyLanguage extends RedirectSpecialArticle {
39
40	/** @var LanguageNameUtils */
41	private $languageNameUtils;
42
43	/** @var WikiPageFactory */
44	private $wikiPageFactory;
45
46	/**
47	 * @param LanguageNameUtils $languageNameUtils
48	 * @param WikiPageFactory $wikiPageFactory
49	 */
50	public function __construct(
51		LanguageNameUtils $languageNameUtils,
52		WikiPageFactory $wikiPageFactory
53	) {
54		parent::__construct( 'MyLanguage' );
55		$this->languageNameUtils = $languageNameUtils;
56		$this->wikiPageFactory = $wikiPageFactory;
57	}
58
59	/**
60	 * If the special page is a redirect, then get the Title object it redirects to.
61	 * False otherwise.
62	 *
63	 * @param string|null $subpage
64	 * @return Title
65	 */
66	public function getRedirect( $subpage ) {
67		$title = $this->findTitle( $subpage );
68		// Go to the main page if given invalid title.
69		if ( !$title ) {
70			$title = Title::newMainPage();
71		}
72		return $title;
73	}
74
75	/**
76	 * Assuming the user's interface language is fi. Given input Page, it
77	 * returns Page/fi if it exists, otherwise Page. Given input Page/de,
78	 * it returns Page/fi if it exists, otherwise Page/de if it exists,
79	 * otherwise Page.
80	 *
81	 * @param string|null $subpage
82	 * @return Title|null
83	 */
84	public function findTitle( $subpage ) {
85		// base = title without language code suffix
86		// provided = the title as it was given
87		$base = $provided = null;
88		if ( $subpage !== null ) {
89			$provided = Title::newFromText( $subpage );
90			$base = $provided;
91
92			if ( $provided && strpos( $subpage, '/' ) !== false ) {
93				$pos = strrpos( $subpage, '/' );
94				$basepage = substr( $subpage, 0, $pos );
95				$code = substr( $subpage, $pos + 1 );
96				if ( strlen( $code ) && $this->languageNameUtils->isKnownLanguageTag( $code ) ) {
97					$base = Title::newFromText( $basepage );
98				}
99			}
100		}
101
102		if ( !$base || !$base->canExist() ) {
103			// No subpage provided or base page does not exist
104			return null;
105		}
106
107		if ( $base->isRedirect() ) {
108			$page = $this->wikiPageFactory->newFromTitle( $base );
109			$base = $page->getRedirectTarget();
110		}
111
112		$uiLang = $this->getLanguage();
113		$contLang = $this->getContentLanguage();
114
115		if ( $uiLang->equals( $contLang ) ) {
116			// Short circuit when the current UI language is the
117			// wiki's default language to avoid unnecessary page lookups.
118			return $base;
119		}
120
121		// Check for a subpage in current UI language
122		$proposed = $base->getSubpage( $uiLang->getCode() );
123		if ( $proposed && $proposed->exists() ) {
124			return $proposed;
125		}
126
127		if ( $provided !== $base && $provided->exists() ) {
128			// Explicit language code given and the page exists
129			return $provided;
130		}
131
132		// Check for fallback languages specified by the UI language
133		$possibilities = $uiLang->getFallbackLanguages();
134		foreach ( $possibilities as $lang ) {
135			if ( $lang !== $contLang->getCode() ) {
136				$proposed = $base->getSubpage( $lang );
137				if ( $proposed && $proposed->exists() ) {
138					return $proposed;
139				}
140			}
141		}
142
143		// When all else has failed, return the base page
144		return $base;
145	}
146
147	/**
148	 * Target can identify a specific user's language preference.
149	 *
150	 * @see T109724
151	 * @since 1.27
152	 * @return bool
153	 */
154	public function personallyIdentifiableTarget() {
155		return true;
156	}
157}
158