Merge "Reword 'prefixindex-strip' to "Hide the prefix in results""
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 18 Oct 2018 08:26:57 +0000 (08:26 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 18 Oct 2018 08:26:57 +0000 (08:26 +0000)
27 files changed:
RELEASE-NOTES-1.33
autoload.php
docs/hooks.txt
includes/api/ApiOptions.php
includes/api/i18n/ar.json
includes/api/i18n/de.json
includes/api/i18n/fr.json
includes/api/i18n/it.json
includes/api/i18n/ko.json
includes/api/i18n/zh-hant.json
includes/libs/rdbms/database/Database.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialRedirectExternal.php [new file with mode: 0644]
languages/i18n/bg.json
languages/i18n/en.json
languages/i18n/fa.json
languages/i18n/kjp.json
languages/i18n/lv.json
languages/i18n/qqq.json
languages/i18n/sr-ec.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/messages/MessagesEn.php
resources/src/mediawiki.feedback/feedback.js
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/specials/SpecialRedirectExternalTest.php [new file with mode: 0644]

index 59443df..00a6938 100644 (file)
@@ -34,6 +34,7 @@ production.
 * …
 
 === Action API changes in 1.33 ===
+* (T198913) Added 'ApiOptions' hook.
 * …
 
 === Action API internal changes in 1.33 ===
index 3e6b4a2..f8fc6b2 100644 (file)
@@ -1396,6 +1396,7 @@ $wgAutoloadLocalClasses = [
        'SpecialRecentChanges' => __DIR__ . '/includes/specials/SpecialRecentchanges.php',
        'SpecialRecentChangesLinked' => __DIR__ . '/includes/specials/SpecialRecentchangeslinked.php',
        'SpecialRedirect' => __DIR__ . '/includes/specials/SpecialRedirect.php',
+       'SpecialRedirectExternal' => __DIR__ . '/includes/specials/SpecialRedirectExternal.php',
        'SpecialRedirectToSpecial' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
        'SpecialRemoveCredentials' => __DIR__ . '/includes/specials/SpecialRemoveCredentials.php',
        'SpecialResetTokens' => __DIR__ . '/includes/specials/SpecialResetTokens.php',
index fd7b300..90b2b05 100644 (file)
@@ -473,6 +473,15 @@ can alter or append to the array.
       (url), 'width', 'height', 'alt', 'align'.
     - url: Url for the given title.
 
+'ApiOptions': Called by action=options before applying changes to user
+preferences.
+$apiModule: Calling ApiOptions object
+$user: User object whose preferences are being changed
+$changes: Associative array of preference name => value
+$resetKinds: Array of strings specifying which options kinds to reset.
+  See User::resetOptions() and User::getOptionKinds() for possible
+  values.
+
 'ApiParseMakeOutputPage': Called when preparing the OutputPage object for
 ApiParse. This is mainly intended for calling OutputPage::addContentOverride()
 or OutputPage::addContentOverrideCallback().
index 3ea827c..c4de31f 100644 (file)
@@ -52,9 +52,9 @@ class ApiOptions extends ApiBase {
                        $this->dieWithError( [ 'apierror-missingparam', 'optionname' ] );
                }
 
-               if ( $params['reset'] ) {
-                       $this->resetPreferences( $params['resetkinds'] );
-                       $changed = true;
+               $resetKinds = $params['resetkinds'];
+               if ( !$params['reset'] ) {
+                       $resetKinds = [];
                }
 
                $changes = [];
@@ -68,6 +68,14 @@ class ApiOptions extends ApiBase {
                        $newValue = $params['optionvalue'] ?? null;
                        $changes[$params['optionname']] = $newValue;
                }
+
+               Hooks::run( 'ApiOptions', [ $this, $user, $changes, $resetKinds ] );
+
+               if ( $resetKinds ) {
+                       $this->resetPreferences( $resetKinds );
+                       $changed = true;
+               }
+
                if ( !$changed && !count( $changes ) ) {
                        $this->dieWithError( 'apierror-nochanges' );
                }
index 7559a53..a4aa5fc 100644 (file)
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "الطابع الزمني لإشعار قائمة المراقبة لكل صفحة.",
        "apihelp-query+info-paramvalue-prop-subjectid": "معرف الصفحة للصفحة الرئيسية لكل صفحة نقاش.",
        "apihelp-query+info-paramvalue-prop-url": "يعطي مسارا كاملا، ومسارا للتعديل، ومسار الأساسي لكل صفحة.",
-       "apihelp-query+info-paramvalue-prop-readable": "ما إذا كان يمكن للمستخدم قراءة هذه الصفحة.",
+       "apihelp-query+info-paramvalue-prop-readable": "ما إذا كان يمكن للمستخدم قراءة هذه الصفحة، استخدم <kbd>intestactions=read</kbd> بدلا من ذلك.",
        "apihelp-query+info-paramvalue-prop-preload": "يعطي النص الذي تم إرجاعه بواسطة EditFormPreloadText.",
        "apihelp-query+info-paramvalue-prop-displaytitle": "يعطي الطريقة التي يتم بها عرض عنوان الصفحة بالفعل.",
        "apihelp-query+info-paramvalue-prop-varianttitles": "يعطي عنوان العرض بجميع الصيغ الخاصة بلغة محتوى الموقع.",
        "apihelp-query+info-param-testactions": "اختبر ما إذا كان المستخدم الحالي يمكنه تنفيذ إجراءات معينة على الصفحة.",
+       "apihelp-query+info-param-testactionsdetail": "مستوى التفاصيل لـ<var>$1testactions</var>، استخدم وسائط [[Special:ApiHelp/main|الوحدة الرئيسية]] <var>errorformat</var> و<var>errorlang</var> للتحكم في تنسيق الرسائل التي تم إرجاعها.",
+       "apihelp-query+info-paramvalue-testactionsdetail-boolean": "إرجاع قيمة منطقية لكل إجراء.",
+       "apihelp-query+info-paramvalue-testactionsdetail-full": "إرجاع الرسائل التي تصف سبب عدم السماح بالإجراء ، أو مصفوفة فارغة إذا كان مسموحا بها.",
+       "apihelp-query+info-paramvalue-testactionsdetail-quick": "مثل <kbd>full</kbd> ولكن تخطي المراجعات باهظة الثمن.",
        "apihelp-query+info-param-token": "استخدم [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] بدلا من ذلك.",
        "apihelp-query+info-example-simple": "الحصول على معلومات حول الصفحة <kbd>Main Page</kbd>.",
        "apihelp-query+info-example-protection": "احصل على معلومات عامة وحماية حول الصفحة <kbd>Main Page</kbd>.",
index 913e129..0eb6749 100644 (file)
        "apihelp-query+info-paramvalue-prop-watchers": "Die Anzahl der Beobachter, falls erlaubt.",
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "Der Beobachtungslisten-Benachrichtigungs-Zeitstempel jeder Seite.",
        "apihelp-query+info-paramvalue-prop-subjectid": "Die Seitenkennung der Elternseite jeder Diskussionsseite.",
-       "apihelp-query+info-paramvalue-prop-readable": "Ob der Benutzer diese Seite betrachten darf.",
+       "apihelp-query+info-paramvalue-prop-readable": "Ob der Benutzer diese Seite lesen kann. Stattdessen <kbd>intestactions=read</kbd> verwenden.",
        "apihelp-query+info-paramvalue-prop-preload": "Gibt den Text aus, der von EditFormPreloadText zurückgegeben wurde.",
        "apihelp-query+info-paramvalue-prop-displaytitle": "Gibt die Art und Weise an, in der der Seitentitel tatsächlich angezeigt wird.",
        "apihelp-query+info-paramvalue-prop-varianttitles": "Gibt den Anzeigetitel in allen Varianten der Sprache des Websiteinhalts aus.",
        "apihelp-query+info-param-testactions": "Überprüft, ob der aktuelle Benutzer gewisse Aktionen auf der Seite ausführen kann.",
+       "apihelp-query+info-paramvalue-testactionsdetail-boolean": "Gibt einen booleschen Wert für jede Aktion zurück.",
+       "apihelp-query+info-paramvalue-testactionsdetail-quick": "Wie <kbd>full</kbd>, aber mit Überspringen von Aufwandsüberprüfungen.",
        "apihelp-query+info-example-simple": "Ruft Informationen über die Seite <kbd>Hauptseite</kbd> ab.",
        "apihelp-query+iwbacklinks-summary": "Findet alle Seiten, die auf einen angegebenen Interwikilink verlinken.",
        "apihelp-query+iwbacklinks-param-prefix": "Präfix für das Interwiki.",
        "apihelp-query+redirects-paramvalue-prop-title": "Titel einer jeden Weiterleitung.",
        "apihelp-query+redirects-param-namespace": "Schließt nur Seiten in diesen Namensräumen ein.",
        "apihelp-query+redirects-param-limit": "Wie viele Weiterleitungen zurückgegeben werden sollen.",
+       "apihelp-query+revisions-summary": "Ruft Informationen zur Version ab.",
+       "apihelp-query+revisions-param-excludeuser": "Schließt Versionen nach Benutzer aus.",
        "apihelp-query+revisions-param-tag": "Listet nur Versionen auf, die mit dieser Markierung markiert sind.",
        "apihelp-query+revisions+base-param-prop": "Zurückzugebende Eigenschaften jeder Version:",
        "apihelp-query+revisions+base-paramvalue-prop-ids": "Die Kennung der Version.",
index 33c05e0..074432b 100644 (file)
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "L’horodatage de notification de la liste de suivi de chaque page.",
        "apihelp-query+info-paramvalue-prop-subjectid": "L’ID de page de la page parent de chaque page de discussion.",
        "apihelp-query+info-paramvalue-prop-url": "Fournit une URL complète, une URL de modification, et l’URL canonique de chaque page.",
-       "apihelp-query+info-paramvalue-prop-readable": "Si l’utilisateur peut lire cette page.",
+       "apihelp-query+info-paramvalue-prop-readable": "Si l’utilisateur peut lire cette page. Utiliser plutôt <kbd>intestactions=read</kbd>.",
        "apihelp-query+info-paramvalue-prop-preload": "Fournit le texte renvoyé par EditFormPreloadText.",
        "apihelp-query+info-paramvalue-prop-displaytitle": "Fournit la manière dont le titre de la page est réellement affiché.",
        "apihelp-query+info-paramvalue-prop-varianttitles": "Donne le titre affiché dans toutes les variantes de la langue de contenu du site.",
        "apihelp-query+info-param-testactions": "Tester si l’utilisateur actuel peut effectuer certaines actions sur la page.",
+       "apihelp-query+info-param-testactionsdetail": "Niveau de détail pour <var>$1testactions</var>. Utiliser les paramètres <var>errorformat</var> et <var>errorlang</var> du [[Special:ApiHelp/main|module principal]] pour contrôler la mise en forme des messages renvoyés.",
+       "apihelp-query+info-paramvalue-testactionsdetail-boolean": "Renvoyer une valeur booléenne pour chaque action.",
+       "apihelp-query+info-paramvalue-testactionsdetail-full": "Renvoyer des messages décrivant pourquoi l’action est interdite, ou un tableau vide si elle est autorisée.",
+       "apihelp-query+info-paramvalue-testactionsdetail-quick": "Comme <kbd>full</kbd> mais en sautant les contrôles coûteux.",
        "apihelp-query+info-param-token": "Utiliser plutôt [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
        "apihelp-query+info-example-simple": "Obtenir des informations sur la page <kbd>Main Page</kbd>.",
        "apihelp-query+info-example-protection": "Obtenir des informations générales et de protection sur la page <kbd>Main Page</kbd>.",
index e71f0f3..a810d21 100644 (file)
        "apihelp-query+info-summary": "Ottieni informazioni base sulla pagina.",
        "apihelp-query+info-param-prop": "Quali proprietà aggiuntive ottenere:",
        "apihelp-query+info-paramvalue-prop-visitingwatchers": "Il numero di osservatori di ogni pagina che hanno visitato le ultime modifiche alla pagina, se consentito.",
+       "apihelp-query+info-paramvalue-testactionsdetail-boolean": "Restituisce un valore booleano per ogni azione.",
        "apihelp-query+iwbacklinks-param-prefix": "Prefisso per l'interwiki.",
        "apihelp-query+iwbacklinks-param-limit": "Quante pagine totali restituire.",
        "apihelp-query+iwbacklinks-param-prop": "Quali proprietà ottenere:",
index a31655d..6149609 100644 (file)
        "apihelp-query+info-paramvalue-prop-protection": "각 문서의 보호 수준을 나열합니다.",
        "apihelp-query+info-paramvalue-prop-readable": "사용자가 이 문서를 읽을 수 있는지의 여부.",
        "apihelp-query+info-paramvalue-prop-varianttitles": "모든 종류의 사이트 내용 언어의 표시 제목을 지정합니다.",
+       "apihelp-query+info-paramvalue-testactionsdetail-boolean": "각 동작의 불리언 값을 반환합니다.",
        "apihelp-query+iwbacklinks-summary": "제시된 인터위키 링크에 연결된 모든 문서를 찾습니다.",
        "apihelp-query+iwbacklinks-param-prefix": "인터위키의 접두사.",
        "apihelp-query+iwbacklinks-param-title": "검색할 인터위키 링크. <var>$1blprefix</var>와 함께 사용해야 합니다.",
index 6fd0ee5..1521095 100644 (file)
        "apihelp-query+allpages-param-minsize": "限制頁面至少要有這樣多的位元組。",
        "apihelp-query+allpages-param-maxsize": "限制頁面最多只能這樣多的位元組。",
        "apihelp-query+allpages-param-prtype": "僅限受保護的頁面。",
+       "apihelp-query+allpages-param-prlevel": "篩選基於保護級別的保護(必須與 $1prtype= 參數一起使用)。",
        "apihelp-query+allpages-param-limit": "要回傳的頁面總數。",
        "apihelp-query+allpages-param-dir": "列出時所採用的方向。",
        "apihelp-query+allpages-example-B": "顯示以字母 <kbd>B</kbd> 為開頭的所有頁面清單。",
        "apihelp-query+allpages-example-generator": "顯示 4 個以 <kbd>T</kbd> 為開頭的頁面之資訊。",
+       "apihelp-query+allpages-example-generator-revisions": "顯示前 2 個以 <kbd>Re</kbd> 為開頭的非重新導向頁面內容。",
        "apihelp-query+allredirects-summary": "列出至命名空間的所有重新導向。",
        "apihelp-query+allredirects-param-from": "要起始列舉的重新導向標題。",
        "apihelp-query+allredirects-param-to": "要終止列舉的重新導向標題。",
        "apihelp-query+mystashedfiles-param-limit": "要取得的檔案數量。",
        "apihelp-query+alltransclusions-param-from": "要起始列舉的嵌入標題。",
        "apihelp-query+alltransclusions-param-to": "要終止列舉的嵌入標題。",
+       "apihelp-query+alltransclusions-param-prefix": "搜尋以此值為開頭的所有嵌入標題。",
        "apihelp-query+alltransclusions-param-prop": "要包含到的資訊部份:",
        "apihelp-query+alltransclusions-paramvalue-prop-ids": "添加嵌入頁面的頁面 ID(不能與 $1unique 一起使用)。",
        "apihelp-query+alltransclusions-paramvalue-prop-title": "添加嵌入的標題。",
        "apihelp-query+info-paramvalue-prop-visitingwatchers": "有訪問頁面近期編輯數的各頁面監視者數目,如有允許的話。",
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "各頁面的監視清單通知時間戳記。",
        "apihelp-query+info-paramvalue-prop-subjectid": "各對話頁的父頁面頁面 ID。",
-       "apihelp-query+info-paramvalue-prop-readable": "使用者是否可閱讀此頁面。",
+       "apihelp-query+info-paramvalue-prop-url": "替各頁面給予一個完整 URL、一個編輯 URL,以及一個規範 URL。",
+       "apihelp-query+info-paramvalue-prop-readable": "使用者是否可閱讀此頁面。請改用 <kbd>intestactions=read</kbd>。",
        "apihelp-query+info-paramvalue-prop-preload": "取得由 EditFormPreloadText 回傳的文字。",
        "apihelp-query+info-param-testactions": "測試目前使用者是否可執行頁面上的某項操作。",
        "apihelp-query+info-param-token": "請改用 [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]。",
        "apihelp-query+iwlinks-paramvalue-prop-url": "添加完整的 URL。",
        "apihelp-query+iwlinks-param-limit": "要回傳的跨 Wiki 連結數量。",
        "apihelp-query+iwlinks-param-prefix": "僅回傳帶有此前綴的跨 wiki 連結。",
+       "apihelp-query+iwlinks-param-title": "要搜尋的跨 wiki 連結。必須與 <var>$1prefix</var> 一起使用。",
        "apihelp-query+iwlinks-param-dir": "列出時所採用的方向。",
        "apihelp-query+iwlinks-example-simple": "從頁面 <kbd>Main Page</kbd> 取得跨 wiki 連結。",
        "apihelp-query+langbacklinks-summary": "找出連結至指定語言連結的所有頁面。",
        "apihelp-query+pagepropnames-summary": "列出所有在 wiki 使用的頁面屬性名稱。",
        "apihelp-query+pagepropnames-param-limit": "回傳的名稱數量上限。",
        "apihelp-query+pagepropnames-example-simple": "取得前 10 個屬性名稱。",
+       "apihelp-query+pageprops-summary": "取得定義在頁面內容的各樣頁面屬性。",
        "apihelp-query+pageprops-example-simple": "取得頁面 <kbd>Main Page</kbd> 與 <kbd>MediaWiki</kbd> 的屬性。",
        "apihelp-query+pageswithprop-summary": "列出使用到指定頁面屬性的所有頁面。",
        "apihelp-query+pageswithprop-param-prop": "要包含到的資訊部份:",
        "apihelp-query+siteinfo-paramvalue-prop-namespacealiases": "已註冊命名空間別名清單。",
        "apihelp-query+siteinfo-paramvalue-prop-specialpagealiases": "特殊頁面別名清單。",
        "apihelp-query+siteinfo-paramvalue-prop-magicwords": "魔術字及其別名清單。",
+       "apihelp-query+siteinfo-paramvalue-prop-usergroups": "回傳使用者群組以及所分配權限。",
        "apihelp-query+siteinfo-paramvalue-prop-libraries": "回傳安裝在 wiki 上的函式庫。",
        "apihelp-query+siteinfo-paramvalue-prop-extensions": "回傳安裝在 wiki 上的擴充功能。",
+       "apihelp-query+siteinfo-paramvalue-prop-extensiontags": "回傳解析擴充標籤清單。",
+       "apihelp-query+siteinfo-paramvalue-prop-defaultoptions": "回傳用於使用者偏好設定的預設值。",
+       "apihelp-query+siteinfo-paramvalue-prop-uploaddialog": "回傳上傳對話框的設置。",
+       "apihelp-query+siteinfo-param-filteriw": "僅回傳跨 wiki 地圖的本地端或非本地端項目。",
        "apihelp-query+siteinfo-param-showalldb": "列出所有資料庫伺服器,不是只有最延遲的那台。",
        "apihelp-query+siteinfo-param-numberingroup": "列出在使用者群組裡的使用者數目。",
        "apihelp-query+siteinfo-param-inlanguagecode": "用於本地化語言的語言代碼(盡可能)與外觀名稱。",
        "apihelp-query+users-example-simple": "返回使用者 <kbd>Example</kbd> 的資訊。",
        "apihelp-query+watchlist-param-start": "起始列舉的時間戳記。",
        "apihelp-query+watchlist-param-end": "結束列舉的時間戳記。",
+       "apihelp-query+watchlist-param-namespace": "篩選僅為指定命名空間的更改。",
        "apihelp-query+watchlist-param-user": "此列出由該使用者作出的更改。",
        "apihelp-query+watchlist-param-excludeuser": "不要列出由該使用者作出的更改。",
        "apihelp-query+watchlist-param-limit": "每個請求要回傳的結果總數。",
        "apihelp-query+watchlist-paramvalue-prop-patrol": "標記編輯為已巡查。",
        "apihelp-query+watchlist-paramvalue-prop-autopatrol": "標記編輯為自動巡查。",
        "apihelp-query+watchlist-paramvalue-prop-sizes": "添加頁面舊有與新的長度。",
+       "apihelp-query+watchlist-paramvalue-prop-loginfo": "在適當處添加日誌資訊。",
        "apihelp-query+watchlist-paramvalue-prop-tags": "列出項目的標籤。",
        "apihelp-query+watchlist-param-type": "要顯示的更改類型:",
        "apihelp-query+watchlist-paramvalue-type-edit": "一般頁面編輯。",
        "apihelp-resetpassword-param-email": "正被重新設定使用者的電子郵件地址。",
        "apihelp-resetpassword-example-user": "向使用者 <kbd>Example</kbd> 寄送重新設定密碼用的電子郵件。",
        "apihelp-revisiondelete-summary": "刪除和取消刪除修訂。",
+       "apihelp-revisiondelete-param-type": "正執行的修訂刪除類型。",
        "apihelp-revisiondelete-param-hide": "各修訂所要隱藏的內容。",
        "apihelp-revisiondelete-param-show": "各修訂所要取消隱藏的內容。",
        "apihelp-revisiondelete-param-suppress": "是否對管理者及其他使用者禁止資料。",
        "apihelp-stashedit-param-contentformat": "用於輸入文字的內容序列化格式。",
        "apihelp-stashedit-param-baserevid": "基本修訂的修訂 ID。",
        "apihelp-stashedit-param-summary": "更改摘要。",
+       "apihelp-tag-param-revid": "要添加或移除標籤的一個或多個修訂 ID。",
+       "apihelp-tag-param-logid": "要添加或移除標籤的一個或多個日誌項目 ID。",
        "apihelp-tag-param-reason": "變更的原因。",
        "apihelp-tokens-summary": "取得資料修改動作的密鑰。",
        "apihelp-tokens-extended-description": "此模組已因支援 [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] 而停用。",
        "apierror-assertnameduserfailed": "斷言使用者「$1」出錯。",
        "apierror-assertuserfailed": "斷言使用者已登入失敗。",
        "apierror-autoblocked": "您的 IP 位址已經被自動封鎖,因為它曾經被一名已封鎖的使用者使用過。",
+       "apierror-bad-badfilecontexttitle": "在 <var>$1badfilecontexttitle</var> 參數的無效標題。",
+       "apierror-baddiffto": "<var>$1diffto</var> 必須設定成非負值的數字、<kbd>prev</kbd>、<kbd>next</kbd>、或 <kbd>cur</kbd>。",
+       "apierror-badformat-generic": "內容模組 $2 不支援使用請求格式 $1。",
        "apierror-badgenerator-notgenerator": "模組 <kbd>$1</kbd> 不能作為產生器。",
        "apierror-badgenerator-unknown": "未知的 <kbd>generator=$1</kbd>。",
        "apierror-badip": "IP 參數無效。",
        "apierror-mustbeloggedin-linkaccounts": "您必須登入到連結帳號。",
        "apierror-mustbeloggedin-removeauth": "必須登入,才能移除身分核對資取。",
        "apierror-mustbeloggedin": "您必須登入才能$1。",
+       "apierror-nochanges": "沒有請求的更改。",
        "apierror-nodeleteablefile": "沒有這樣檔案的舊版本。",
        "apierror-noedit-anon": "匿名使用者不可編輯頁面。",
        "apierror-noedit": "您沒有權限來編輯頁面。",
        "apierror-nosuchsection": "沒有 ID 為 $1 的段落。",
        "apierror-nosuchsection-what": "在$2裡沒有段落$1。",
        "apierror-nosuchuserid": "沒有 ID 為 $1 的使用者。",
+       "apierror-notpatrollable": "因內容過舊,修訂 r$1 無法巡查。",
        "apierror-nouploadmodule": "未設定上傳模組。",
+       "apierror-opensearch-json-warnings": "警告不能以 OpenSearch JSON 格式表示。",
        "apierror-pagecannotexist": "命名空間不允許實際頁面。",
        "apierror-permissiondenied": "您沒有權限$1。",
        "apierror-permissiondenied-generic": "權限不足。",
        "apiwarn-deprecation-httpsexpected": "當應為 HTTPS 時,HTTP 要被使用。",
        "apiwarn-invalidcategory": "「$1」不是一個分類。",
        "apiwarn-invalidtitle": "「$1」不是一個有效標題。",
+       "apiwarn-invalidxmlstylesheet": "指定了無效或不存在的樣式表。",
+       "apiwarn-invalidxmlstylesheetns": "樣式表應在 {{ns:MediaWiki}} 命名空間。",
        "apiwarn-notfile": "「$1」不是一個檔案。",
+       "apiwarn-nothumb-noimagehandler": "無法建立縮圖,因為$1沒有相關的圖片處理器。",
        "apiwarn-parse-nocontentmodel": "未提供 <var>title</var> 或 <var>contentmodel</var>,應是 $1。",
        "apiwarn-tokennotallowed": "「$1」操作不允許目前的使用者。",
+       "apiwarn-unrecognizedvalues": "參數 <var>$1</var> 有無法識別的{{PLURAL:$3|值|值}}:$2。",
+       "apiwarn-unsupportedarray": "參數 <var>$1</var> 使用了不被支援的 PHP 陣列語法。",
        "apiwarn-validationfailed-badpref": "不是有效的偏好設定。",
        "apiwarn-validationfailed-cannotset": "不能透過此模組設定。",
        "apiwarn-validationfailed": "<kbd>$1</kbd>驗證錯誤:$2",
index 3d23a83..c436b64 100644 (file)
@@ -1544,14 +1544,14 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $options = [ $options ];
                }
 
-               $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
+               $res = $this->select( $table, [ 'value' => $var ], $cond, $fname, $options, $join_conds );
                if ( $res === false ) {
                        return false;
                }
 
                $values = [];
                foreach ( $res as $row ) {
-                       $values[] = $row->$var;
+                       $values[] = $row->value;
                }
 
                return $values;
index 013ceb2..f29d265 100644 (file)
@@ -202,6 +202,7 @@ class SpecialPageFactory {
                'AllMyUploads' => \SpecialAllMyUploads::class,
                'PermanentLink' => \SpecialPermanentLink::class,
                'Redirect' => \SpecialRedirect::class,
+               'RedirectExternal' => \SpecialRedirectExternal::class,
                'Revisiondelete' => \SpecialRevisionDelete::class,
                'RunJobs' => \SpecialRunJobs::class,
                'Specialpages' => \SpecialSpecialpages::class,
diff --git a/includes/specials/SpecialRedirectExternal.php b/includes/specials/SpecialRedirectExternal.php
new file mode 100644 (file)
index 0000000..41a03ed
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Implements Special:RedirectExternal.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * An unlisted special page that accepts a URL as the first argument, and redirects the user to
+ * that page. Example: Special:Redirect/https://mediawiki.org
+ *
+ * At the moment, this is intended to be used by the GrowthExperiments project in order
+ * to track outbound visits to certain external links. But it could be extended in the future to
+ * provide parameters for showing a message to the user before redirecting, or explicitly requiring
+ * a user to click on the link. This can help improve security when users follow on-wiki links to
+ * off-wiki sites.
+ */
+class SpecialRedirectExternal extends UnlistedSpecialPage {
+
+       public function __construct() {
+               parent::__construct( 'RedirectExternal' );
+       }
+
+       /**
+        * @param string $url
+        * @return bool
+        * @throws HttpError
+        */
+       public function execute( $url = '' ) {
+               $dispatch = $this->dispatch( $url );
+               if ( $dispatch->getStatusValue()->isGood() ) {
+                       $this->getOutput()->redirect( $url );
+                       return true;
+               }
+               throw new HttpError( 400, $dispatch->getMessage() );
+       }
+
+       /**
+        * @param string $url
+        * @return Status
+        */
+       public function dispatch( $url ) {
+               if ( !$url ) {
+                       return Status::newFatal( 'redirectexternal-no-url' );
+               }
+               $url = filter_var( $url, FILTER_SANITIZE_URL );
+               if ( !filter_var( $url, FILTER_VALIDATE_URL ) ) {
+                       return Status::newFatal( 'redirectexternal-invalid-url', $url );
+               }
+               return Status::newGood();
+       }
+}
index 12ffcf9..0d95656 100644 (file)
        "botpasswords-label-appid": "Име на бота:",
        "botpasswords-label-create": "Създаване",
        "botpasswords-label-update": "Обновяване",
-       "botpasswords-label-cancel": "Отказване",
+       "botpasswords-label-cancel": "Отказ",
        "botpasswords-label-delete": "Изтриване",
        "botpasswords-label-resetpassword": "Възстановяване на парола",
        "botpasswords-label-grants": "Приложими разрешения:",
        "newimages-hidepatrolled": "Скриване на проверените качвания",
        "newimages-mediatype": "Файлов тип:",
        "noimages": "Няма нищо.",
+       "gallery-slideshow-toggle": "Превключване на миниатюрите",
        "ilsubmit": "Търсене",
        "bydate": "по дата",
        "sp-newimages-showfrom": "Показване на новите файлове, като се започне от $2, $1",
        "feedback-bugcheck": "Страхотно! Само проверете дали това не сред вече [$1 докладваните грешки].",
        "feedback-bugnew": "Проверих. Докладвай за нова грешка",
        "feedback-bugornote": "Ако сте готови подробно да опишете технически проблем, моля [$1 докладвайте го тук].\nВ противен случай, можете да използвате лесния формуляр по-долу. Коментарът ви ще бъде добавен към страницата „[$3 $2]“, наред с вашето потребителско име.",
-       "feedback-cancel": "Отказване",
+       "feedback-cancel": "Отказ",
        "feedback-close": "Готово",
        "feedback-dialog-title": "Изпращане на обратна връзка",
        "feedback-error1": "Грешка: Неразпознат резултат от API",
index 5283a5b..17b7d4c 100644 (file)
        "lag-warn-normal": "Changes newer than $1 {{PLURAL:$1|second|seconds}} may not be shown in this list.",
        "lag-warn-high": "Due to high database server lag, changes newer than $1 {{PLURAL:$1|second|seconds}} may not be shown in this list.",
        "editwatchlist-summary": "",
+       "redirectexternal-summary":  "",
+       "redirectexternal-invalid-url": "$1 is not a valid URL",
+       "redirectexternal-no-url":  "No argument was provided to Special:RedirectExternal",
        "watchlistedit-normal-title": "Edit watchlist",
        "watchlistedit-normal-legend": "Remove titles from watchlist",
        "watchlistedit-normal-explain": "Titles on your watchlist are shown below.\nTo remove a title, check the box next to it, and click \"{{int:Watchlistedit-normal-submit}}\".\nYou can also [[Special:EditWatchlist/raw|edit the raw list]].",
index 1b60832..0986223 100644 (file)
@@ -86,9 +86,9 @@
        "tog-watchdefault": "صفحه‌ها و پرونده‌هایی که ویرایش می‌کنم به فهرست پیگیری‌های من افزوده شود",
        "tog-watchmoves": "صفحه‌ها و پرونده‌هایی که منتقل می‌کنم به فهرست پی‌گیری‌های من افزوده شود",
        "tog-watchdeletion": "صفحه‌ها و پرونده‌هایی که حذف می‌کنم به فهرست پی‌گیری‌های من افزوده شود",
-       "tog-watchuploads": "پرونده‌های جدیدی که بارگذاری می‌کنم به فهرست پیگیری من افزوده شود",
+       "tog-watchuploads": "پرونده‌های جدیدی که بارگذاری می‌کنم به فهرست پیگیری‌های من افزوده شود",
        "tog-watchrollback": "افزودن صفحاتی که واگردانی می‌کنم به فهرست پیگیری‌های من",
-       "tog-minordefault": "همهٔ ویرایش‌ها به طور پیش‌فرض به عنوان «جزئی» علامت زده شود",
+       "tog-minordefault": "همهٔ ویرایش‌ها به طور پیش‌فرض به عنوان «جزئی» علامت زده شوند",
        "tog-previewontop": "پیش‌نمایش بالای جعبهٔ ویرایش نمایش داده شود",
        "tog-previewonfirst": "پیش‌نمایش هنگام اولین ویرایش نمایش داده شود",
        "tog-enotifwatchlistpages": "اگر صفحه یا پرونده‌ای از فهرست پی‌گیری‌هایم ویرایش شد به من ایمیلی فرستاده شود",
        "tog-watchlisthideminor": "ویرایش‌های جزئی در فهرست پی‌گیری‌ها پنهان شود",
        "tog-watchlisthideliu": "ویرایش‌های کاربران وارد شده به سامانه در فهرست پی‌گیری‌ها پنهان شود",
        "tog-watchlistreloadautomatically": "زمانی که یک پالایه تغییر کرد فهرست پیگیری به صورت خودکار به روز شود (نیازمند جاوااسکریپت)",
-       "tog-watchlistunwatchlinks": "اÙ\81زÙ\88دÙ\86 Ù\85شخصâ\80\8cÚ©Ù\86Ù\86دÙ\87اÛ\8c Ø¹Ø¯Ù\85 Ù¾Û\8cÚ¯Û\8cرÛ\8c/Ù¾Û\8cÚ¯Û\8cرÛ\8c ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) Ø¨Ù\87 ØµÙ\81حات Ù¾Û\8cÚ¯Û\8cرÛ\8c Ø¯Ø§Ø±Ø§Û\8c ØªØºÛ\8cÛ\8cرات (جاÙ\88اسکرÛ\8cپت Ù\85Ù\85Ú©Ù\86 Ø§Ø³Øª Ù\86Û\8cاز Ø´Ù\88د)",
+       "tog-watchlistunwatchlinks": "اÙ\81زÙ\88دÙ\86 Ù\85شخصâ\80\8cÚ©Ù\86Ù\86دÙ\87اÛ\8c Ø¹Ø¯Ù\85 Ù¾Û\8cÚ¯Û\8cرÛ\8c/Ù¾Û\8cÚ¯Û\8cرÛ\8c ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) Ø¨Ù\87 ØµÙ\81حات Ù¾Û\8cÚ¯Û\8cرÛ\8c Ø¯Ø§Ø±Ø§Û\8c ØªØºÛ\8cÛ\8cرات (براÛ\8c Ø¹Ù\85Ù\84Û\8cات Ø®Ø§Ù\85Ù\88Ø´â\80\8c Ù\88 Ø±Ù\88Ø´Ù\86 Ú©Ø±Ø¯Ù\86Ø\8c Ø¬Ø§Ù\88اسکرÛ\8cپت   Ù\86Û\8cاز Ø§Ø³Øª)",
        "tog-watchlisthideanons": "ویرایش‌های کاربران ناشناس در فهرست پی‌گیری‌ها پنهان شود",
        "tog-watchlisthidepatrolled": "ویرایش‌های گشت‌خورده در فهرست پی‌گیری‌ها پنهان شود",
        "tog-watchlisthidecategorization": "نهفتن رده‌بندی صفحه‌ها",
index 19a44c0..fde7ecf 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Ninjastrikers",
-                       "Rul1902"
+                       "Rul1902",
+                       "Sawmw"
                ]
        },
        "underline-always": "ကိုဲၜၠင်",
        "searchbutton": "အင်းၰူ့",
        "go": "လေဝ်",
        "searcharticle": "လေဝ်",
-       "history": "á\80\83á\80½á\80¾á\80¬á\80\94á\80·်မေင်ႋစိင်",
+       "history": "á\80\9cá\80­á\80\99á\80±á\80¶á\81\9cá\81 á\80¬်မေင်ႋစိင်",
        "history_short": "မေင်ႋစိင်",
        "history_small": "မေင်ႋစိင်",
        "updatedmarker": "လေဝ်ယောဝ်ႋဝေ့အ်ုလါင်ခါင့်ခါ့ အင်းတင်ထဝေ့",
index 04c8c24..670d9c1 100644 (file)
        "botpasswords-updated-title": "Bota parole atjaunināta",
        "botpasswords-deleted-title": "Bota parole dzēsta",
        "botpasswords-restriction-failed": "Botu paroles ierobežojumi liedz šo pieslēgšanos.",
+       "botpasswords-not-exist": "Lietotājam \"$1\" nav bota paroles ar nosaukumu \"$2\".",
        "resetpass_forbidden": "Paroles nav iespējams nomainīt",
        "resetpass_forbidden-reason": "Paroles nav iespējams nomainīt: $1",
        "resetpass-no-info": "Jums ir nepieciešams ieiet, lai tūlīt piekļūtu šai lapai.",
        "diff-multi-otherusers": "({{PLURAL:$1|$1 starpversijas|Viena starpversija|$1 starpversijas}}, ko {{PLURAL:$2|saglabājuši|saglabājis|saglabājuši}} {{PLURAL:$2|$2 lietotāji|viens cits lietotājs|$2 lietotāji}}, nav parādīta{{PLURAL:$1||s}})",
        "diff-multi-manyusers": "({{PLURAL:$1|$1 starpversijas|$1 starpversija|$1 starpversijas}}, ko saglabājuši vairāk nekā $2 {{PLURAL:$2|lietotāji|lietotājs|lietotāji}}, nav parādīta{{PLURAL:$1||s}})",
        "searchresults": "Meklēšanas rezultāti",
+       "search-filter-title-prefix": "Meklē tikai lapas, kuru nosaukums sākas ar \"$1\"",
+       "search-filter-title-prefix-reset": "Meklēt visas lapas",
        "searchresults-title": "Meklēšanas rezultāti \"$1\"",
        "titlematches": "Rezultāti virsrakstos",
        "textmatches": "Rezultāti lapu tekstos",
        "http-timed-out": "HTTP pieprasījumam ir iestājies noilgums.",
        "http-curl-error": "Kļūda, nolasot URL: $1",
        "http-bad-status": "HTTP pieprasījuma laikā atgadījās problēma: $1 $2",
+       "http-internal-error": "HTTP iekšējā kļūda.",
        "upload-curl-error6": "URL nevarēja sasniegt",
        "upload-curl-error28": "Augšupielādes noildze",
        "license": "Licence:",
        "protectedpages-noredirect": "Paslēpt pāradresācijas",
        "protectedpages-timestamp": "Laika zīmogs",
        "protectedpages-page": "Lapa",
+       "protectedpages-expiry": "Beidzas",
+       "protectedpages-performer": "Aizsargājušais lietotājs",
        "protectedpages-params": "Aizsardzības parametri",
        "protectedpages-reason": "Iemesls",
        "protectedpages-submit": "Parādīt lapas",
index a17cfca..0c50f40 100644 (file)
        "nimagelinks": "Used on [[Special:MostLinkedFiles]] to indicate how often a specific file is used.\n\nParameters:\n* $1 - number of pages\nSee also:\n* {{msg-mw|Ntransclusions}}",
        "ntransclusions": "Used on [[Special:MostTranscludedPages]] to indicate how often a template is in use.\n\nParameters:\n* $1 - number of pages\nSee also:\n* {{msg-mw|Nimagelinks}}",
        "specialpage-empty": "Used on a special page when there is no data. For example on [[Special:Unusedimages]] when all images are used.",
+       "redirectexternal-summary":  "{{doc-specialpagessummary|redirectexternal}}",
+       "redirectexternal-invalid-url": "Error message shown when the argument to [[Special:RedirectExternal]] is an invalid URL.\n\nParameters:\n* $1 - The first URL argument to Special:RedirectExternal",
+       "redirectexternal-no-url": "Error message shown when no argument is supplied to [[Special:RedirectExternal]]",
        "lonelypages": "{{doc-special|LonelyPages}}",
        "lonelypages-summary": "{{doc-specialpagesummary|lonelypages}}",
        "lonelypagestext": "Text displayed in [[Special:LonelyPages]]",
index 635bd3c..75ed13d 100644 (file)
        "tool-link-userrights": "Промена {{GENDER:$1|корисничких}} групе",
        "tool-link-userrights-readonly": "Приказ {{GENDER:$1|корисничких}} група",
        "tool-link-emailuser": "Слање имејла {{GENDER:$1|кориснику|корисници}}",
-       "imagepage": "Погледај страницу датотеке",
-       "mediawikipage": "Погледај страницу поруке",
-       "templatepage": "Погледај страницу шаблона",
-       "viewhelppage": "Погледај страницу помоћи",
-       "categorypage": "Погледај страницу категорије",
+       "imagepage": "Прикажи страницу датотеке",
+       "mediawikipage": "Прикажи страницу поруке",
+       "templatepage": "Прикажи страницу шаблона",
+       "viewhelppage": "Прикажи страницу помоћи",
+       "categorypage": "Прикажи страницу категорије",
        "viewtalkpage": "Прикажи дискусију",
        "otherlanguages": "На другим језицима",
        "redirectedfrom": "(преусмерено са $1)",
        "privacypage": "Project:Политика приватности",
        "badaccess": "Грешка у дозволама",
        "badaccess-group0": "Није вам дозвољено да извршите радњу коју сте захтевали.",
-       "badaccess-groups": "Радња коју сте захтевали је ограничена само корисницима у {{PLURAL:$2|следећој групи|следећим групама}}: $1.",
+       "badaccess-groups": "Радња коју сте захтевали је ограничена на кориснике из {{PLURAL:$2|следеће групе|једне од следећих група}}: $1.",
        "versionrequired": "Потребна је верзија $1 Медијавикија",
        "versionrequiredtext": "Потребна је верзија $1 Медијавикија да бисте користили ову страницу.\nПогледајте страницу [[Special:Version|верзије]].",
        "ok": "У реду",
index 7734c11..d443304 100644 (file)
        "dellogpagetext": "Aşağıda en son silme işlemlerinin bir listesi bulunmaktadır.",
        "deletionlog": "silme günlüğü",
        "log-name-create": "Sayfa oluşturma günlüğü",
+       "log-description-create": "Aşağıda en son yeni sayfa oluşturma işlemlerinin bir listesi bulunmaktadır.",
        "logentry-create-create": "$1, $3 adlı sayfayı {{GENDER:$2|oluşturdu}}",
        "reverted": "Önceki sürüm geri getirildi",
        "deletecomment": "Neden:",
        "log-action-filter-upload-upload": "Yeni yükleme",
        "log-action-filter-upload-overwrite": "Yeniden yükle",
        "authmanager-authplugin-setpass-bad-domain": "Geçersiz alanadı.",
+       "authmanager-autocreate-noperm": "Otomatik kullanıcı oluşturma izni yok.",
        "authmanager-userdoesnotexist": "\"$1\" kullanıcı hesabı kayıtlı değil.",
        "authmanager-email-label": "E-posta",
        "authmanager-email-help": "E-posta adresi",
index 273d521..8da3909 100644 (file)
        "right-importupload": "імпорт сторінок через завантаження файлів",
        "right-patrol": "позначення редагувань патрульованими",
        "right-autopatrol": "автоматичне позначення редагувань патрульованими",
-       "right-patrolmarks": "Ð\9fеÑ\80еглÑ\8fд Ð¿Ð°Ñ\82Ñ\80Ñ\83лÑ\8cованиÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок у нових редагуваннях",
+       "right-patrolmarks": "пеÑ\80еглÑ\8fд Ð¿Ð¾Ð·Ð½Ð°Ñ\87ок Ð¿Ð°Ñ\82Ñ\80Ñ\83лÑ\8eваннÑ\8f у нових редагуваннях",
        "right-unwatchedpages": "перегляд списку сторінок, за якими ніхто не спостерігає",
        "right-mergehistory": "об'єднання історій редагувань сторінок",
        "right-userrights": "зміна всіх прав користувачів",
index 7a7370f..e78f003 100644 (file)
@@ -482,6 +482,7 @@ $specialPageAliases = [
        'Recentchanges'             => [ 'RecentChanges' ],
        'Recentchangeslinked'       => [ 'RecentChangesLinked', 'RelatedChanges' ],
        'Redirect'                  => [ 'Redirect' ],
+       'RedirectExternal'          => [ 'RedirectExternal' ],
        'RemoveCredentials'         => [ 'RemoveCredentials' ],
        'ResetTokens'               => [ 'ResetTokens' ],
        'Revisiondelete'            => [ 'RevisionDelete' ],
index d1fb98b..5b73e7c 100644 (file)
                                }
                        }, this )
                        .next( function () {
-                               var plainMsg, parsedMsg,
+                               var $link,
                                        settings = data.settings;
                                data.contents = data.contents || {};
 
                                this.useragentMandatory = settings.useragentCheckbox.mandatory;
                                this.useragentFieldLayout.toggle( settings.useragentCheckbox.show );
 
-                               // HACK: Setting a link in the messages doesn't work. There is already a report
-                               // about this, and the bug report offers a somewhat hacky work around that
-                               // includes setting a separate message to be parsed.
-                               // We want to make sure the user can configure both the title of the page and
-                               // a separate url, so this must be allowed to parse correctly.
-                               // See https://phabricator.wikimedia.org/T49395#490610
-                               mw.messages.set( {
-                                       'feedback-dialog-temporary-message':
-                                               '<a href="' + this.feedbackPageUrl + '" target="_blank">' + this.feedbackPageName + '</a>'
-                               } );
-                               plainMsg = mw.message( 'feedback-dialog-temporary-message' ).plain();
-                               mw.messages.set( { 'feedback-dialog-temporary-message-parsed': plainMsg } );
-                               parsedMsg = mw.message( 'feedback-dialog-temporary-message-parsed' );
+                               $link = $( '<a>' )
+                                       .attr( 'href', this.feedbackPageUrl )
+                                       .attr( 'target', '_blank' )
+                                       .text( this.feedbackPageName );
                                this.feedbackMessageLabel.setLabel(
-                                       // Double-parse
-                                       $( '<span>' )
-                                               .append( mw.message( 'feedback-dialog-intro', parsedMsg ).parse() )
+                                       mw.message( 'feedback-dialog-intro', $link ).parseDom()
                                );
 
                                this.validateFeedbackForm();
index 4488d9e..4a9603c 100644 (file)
@@ -2139,4 +2139,21 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                $this->assertLastSql( 'BEGIN; SELECT 1; ROLLBACK' );
                $this->assertEquals( 0, $this->database->trxLevel() );
        }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::selectFieldValues()
+        */
+       public function testSelectFieldValues() {
+               $this->database->forceNextResult( [
+                       (object)[ 'value' => 'row1' ],
+                       (object)[ 'value' => 'row2' ],
+                       (object)[ 'value' => 'row3' ],
+               ] );
+
+               $this->assertSame(
+                       [ 'row1', 'row2', 'row3' ],
+                       $this->database->selectFieldValues( 'table', 'table.field', 'conds', __METHOD__ )
+               );
+               $this->assertLastSql( 'SELECT table.field AS value FROM table WHERE conds' );
+       }
 }
index 762812c..bd1c112 100644 (file)
@@ -681,4 +681,5 @@ class DatabaseTest extends PHPUnit\Framework\TestCase {
                $this->assertSame( $oldPrefix, $this->db->tablePrefix() );
                $this->assertSame( $oldDomain, $this->db->getDomainId() );
        }
+
 }
diff --git a/tests/phpunit/includes/specials/SpecialRedirectExternalTest.php b/tests/phpunit/includes/specials/SpecialRedirectExternalTest.php
new file mode 100644 (file)
index 0000000..ab5b2cd
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Test class for SpecialRedirectExternal class.
+ *
+ * @license GPL-2.0-or-later
+ */
+class SpecialRedirectExternalTest extends MediaWikiTestCase {
+
+       /**
+        * @dataProvider provideDispatch
+        * @covers SpecialRedirectExternal::dispatch
+        * @covers SpecialRedirectExternal
+        * @param $url
+        * @param $expectedStatus
+        */
+       public function testDispatch( $url, $expectedStatus ) {
+               $page = new SpecialRedirectExternal();
+               $this->assertEquals( $expectedStatus, $page->dispatch( $url )->isGood() );
+       }
+
+       /**
+        * @throws HttpError
+        * @expectedException HttpError
+        * @expectedExceptionMessage asdf is not a valid URL
+        * @covers SpecialRedirectExternal::execute
+        */
+       public function testExecuteInvalidUrl() {
+               $page = new SpecialRedirectExternal();
+               $page->execute( 'asdf' );
+       }
+
+       /**
+        * @throws HttpError
+        * @covers SpecialRedirectExternal::execute
+        */
+       public function testValidUrl() {
+               $page = new SpecialRedirectExternal();
+               $this->assertTrue( $page->execute( 'https://www.mediawiki.org' ) );
+       }
+
+       public static function provideDispatch() {
+               return [
+                       [ 'asdf', false ],
+                       [ null, false ],
+                       [ 'https://www.mediawiki.org?test=1', true ],
+               ];
+       }
+}