Merge "Remove Title->getSquidURLs, deprecated in 1.27, unused"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 8 Feb 2019 19:52:12 +0000 (19:52 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 8 Feb 2019 19:52:12 +0000 (19:52 +0000)
26 files changed:
.phpcs.xml
RELEASE-NOTES-1.33
autoload.php
includes/deferred/LinksUpdate.php
includes/http/HttpRequestFactory.php
includes/http/MWHttpRequest.php
includes/specials/SpecialUploadStash.php
includes/specials/exception/SpecialUploadStashTooLargeException.php [new file with mode: 0644]
includes/templates/NoLocalSettings.mustache
includes/upload/UploadFromChunks.php
includes/upload/UploadStash.php
includes/upload/exception/UploadChunkFileException.php [new file with mode: 0644]
includes/upload/exception/UploadChunkVerificationException.php [new file with mode: 0644]
includes/upload/exception/UploadChunkZeroLengthFileException.php [new file with mode: 0644]
includes/upload/exception/UploadStashBadPathException.php [new file with mode: 0644]
includes/upload/exception/UploadStashException.php [new file with mode: 0644]
includes/upload/exception/UploadStashFileException.php [new file with mode: 0644]
includes/upload/exception/UploadStashFileNotFoundException.php [new file with mode: 0644]
includes/upload/exception/UploadStashNoSuchKeyException.php [new file with mode: 0644]
includes/upload/exception/UploadStashNotLoggedInException.php [new file with mode: 0644]
includes/upload/exception/UploadStashWrongOwnerException.php [new file with mode: 0644]
includes/upload/exception/UploadStashZeroLengthFileException.php [new file with mode: 0644]
includes/user/User.php
resources/src/mediawiki.less/mediawiki.ui/variables.less
resources/src/mediawiki.ui/components/buttons.less
tests/phpunit/includes/deferred/LinksUpdateTest.php

index a525122..99afa3b 100644 (file)
                <exclude-pattern>*/includes/specials/forms/PreferencesFormLegacy\.php</exclude-pattern>
                <exclude-pattern>*/includes/specials/SpecialListusers\.php</exclude-pattern>
                <exclude-pattern>*/includes/specials/SpecialMyRedirectPages\.php</exclude-pattern>
-               <exclude-pattern>*/includes/specials/SpecialUploadStash\.php</exclude-pattern>
                <exclude-pattern>*/includes/StubObject\.php</exclude-pattern>
-               <exclude-pattern>*/includes/upload/UploadFromChunks\.php</exclude-pattern>
                <exclude-pattern>*/includes/upload/UploadStash\.php</exclude-pattern>
                <exclude-pattern>*/includes/utils/AutoloadGenerator\.php</exclude-pattern>
                <exclude-pattern>*/includes/WebResponse\.php</exclude-pattern>
index 168ccc2..4bd50c6 100644 (file)
@@ -48,6 +48,8 @@ production.
   Content::getNativeData() for text-based content models.
 * (T210814) SVGs are now by default displayed in wiki language on image
   pages.
+* (T214706) LinksUpdate::getAddedExternalLinks() and
+  LinksUpdate::getRemovedExternalLinks() were introduced.
 
 === External library changes in 1.33 ===
 
index 3b04dfc..cde24e2 100644 (file)
@@ -1434,7 +1434,7 @@ $wgAutoloadLocalClasses = [
        'SpecialUnlockdb' => __DIR__ . '/includes/specials/SpecialUnlockdb.php',
        'SpecialUpload' => __DIR__ . '/includes/specials/SpecialUpload.php',
        'SpecialUploadStash' => __DIR__ . '/includes/specials/SpecialUploadStash.php',
-       'SpecialUploadStashTooLargeException' => __DIR__ . '/includes/specials/SpecialUploadStash.php',
+       'SpecialUploadStashTooLargeException' => __DIR__ . '/includes/specials/exception/SpecialUploadStashTooLargeException.php',
        'SpecialUserLogin' => __DIR__ . '/includes/specials/SpecialUserLogin.php',
        'SpecialUserLogout' => __DIR__ . '/includes/specials/SpecialUserLogout.php',
        'SpecialVersion' => __DIR__ . '/includes/specials/SpecialVersion.php',
@@ -1534,9 +1534,9 @@ $wgAutoloadLocalClasses = [
        'UpdateSearchIndex' => __DIR__ . '/maintenance/updateSearchIndex.php',
        'UpdateSpecialPages' => __DIR__ . '/maintenance/updateSpecialPages.php',
        'UploadBase' => __DIR__ . '/includes/upload/UploadBase.php',
-       'UploadChunkFileException' => __DIR__ . '/includes/upload/UploadFromChunks.php',
-       'UploadChunkVerificationException' => __DIR__ . '/includes/upload/UploadFromChunks.php',
-       'UploadChunkZeroLengthFileException' => __DIR__ . '/includes/upload/UploadFromChunks.php',
+       'UploadChunkFileException' => __DIR__ . '/includes/upload/exception/UploadChunkFileException.php',
+       'UploadChunkVerificationException' => __DIR__ . '/includes/upload/exception/UploadChunkVerificationException.php',
+       'UploadChunkZeroLengthFileException' => __DIR__ . '/includes/upload/exception/UploadChunkZeroLengthFileException.php',
        'UploadForm' => __DIR__ . '/includes/specials/forms/UploadForm.php',
        'UploadFromChunks' => __DIR__ . '/includes/upload/UploadFromChunks.php',
        'UploadFromFile' => __DIR__ . '/includes/upload/UploadFromFile.php',
@@ -1547,15 +1547,15 @@ $wgAutoloadLocalClasses = [
        'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
        'UploadSourceField' => __DIR__ . '/includes/specials/formfields/UploadSourceField.php',
        'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashBadPathException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashException' => __DIR__ . '/includes/upload/UploadStash.php',
+       'UploadStashBadPathException' => __DIR__ . '/includes/upload/exception/UploadStashBadPathException.php',
+       'UploadStashException' => __DIR__ . '/includes/upload/exception/UploadStashException.php',
        'UploadStashFile' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashFileException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashFileNotFoundException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashNoSuchKeyException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashNotLoggedInException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashWrongOwnerException' => __DIR__ . '/includes/upload/UploadStash.php',
-       'UploadStashZeroLengthFileException' => __DIR__ . '/includes/upload/UploadStash.php',
+       'UploadStashFileException' => __DIR__ . '/includes/upload/exception/UploadStashFileException.php',
+       'UploadStashFileNotFoundException' => __DIR__ . '/includes/upload/exception/UploadStashFileNotFoundException.php',
+       'UploadStashNoSuchKeyException' => __DIR__ . '/includes/upload/exception/UploadStashNoSuchKeyException.php',
+       'UploadStashNotLoggedInException' => __DIR__ . '/includes/upload/exception/UploadStashNotLoggedInException.php',
+       'UploadStashWrongOwnerException' => __DIR__ . '/includes/upload/exception/UploadStashWrongOwnerException.php',
+       'UploadStashZeroLengthFileException' => __DIR__ . '/includes/upload/exception/UploadStashZeroLengthFileException.php',
        'UppercaseCollation' => __DIR__ . '/includes/collation/UppercaseCollation.php',
        'User' => __DIR__ . '/includes/user/User.php',
        'UserArray' => __DIR__ . '/includes/user/UserArray.php',
index 937196d..7c7cabd 100644 (file)
@@ -84,6 +84,16 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
         */
        private $linkDeletions = null;
 
+       /**
+        * @var null|array Added external links if calculated.
+        */
+       private $externalLinkInsertions = null;
+
+       /**
+        * @var null|array Deleted external links if calculated.
+        */
+       private $externalLinkDeletions = null;
+
        /**
         * @var null|array Added properties if calculated.
         */
@@ -234,11 +244,14 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
 
                # External links
                $existingEL = $this->getExistingExternals();
+               $this->externalLinkDeletions = $this->getExternalDeletions( $existingEL );
+               $this->externalLinkInsertions = $this->getExternalInsertions(
+                       $existingEL );
                $this->incrTableUpdate(
                        'externallinks',
                        'el',
-                       $this->getExternalDeletions( $existingEL ),
-                       $this->getExternalInsertions( $existingEL ) );
+                       $this->externalLinkDeletions,
+                       $this->externalLinkInsertions );
 
                # Language links
                $existingLL = $this->getExistingInterlangs();
@@ -1099,6 +1112,36 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
                return $result;
        }
 
+       /**
+        * Fetch external links added by this LinksUpdate. Only available after
+        * the update is complete.
+        * @since 1.33
+        * @return null|array Array of Strings
+        */
+       public function getAddedExternalLinks() {
+               if ( $this->externalLinkInsertions === null ) {
+                       return null;
+               }
+               $result = [];
+               foreach ( $this->externalLinkInsertions as $key => $value ) {
+                       $result[] = $value['el_to'];
+               }
+               return $result;
+       }
+
+       /**
+        * Fetch external links removed by this LinksUpdate. Only available after
+        * the update is complete.
+        * @since 1.33
+        * @return null|array Array of Strings
+        */
+       public function getRemovedExternalLinks() {
+               if ( $this->externalLinkDeletions === null ) {
+                       return null;
+               }
+               return array_keys( $this->externalLinkDeletions );
+       }
+
        /**
         * Fetch page properties added by this LinksUpdate.
         * Only available after the update is complete.
index 0faef17..a3a14d0 100644 (file)
@@ -44,7 +44,7 @@ class HttpRequestFactory {
         */
        public function create( $url, array $options = [], $caller = __METHOD__ ) {
                if ( !Http::$httpEngine ) {
-                       Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php';
+                       Http::$httpEngine = 'guzzle';
                } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
                        throw new DomainException( __METHOD__ . ': curl (https://secure.php.net/curl) is not ' .
                           'installed, but Http::$httpEngine is set to "curl"' );
index 065667d..b4ac9a7 100644 (file)
@@ -129,6 +129,8 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
                        $this->setOriginalRequest( $options['originalRequest'] );
                }
 
+               $this->setHeader( 'X-Request-Id', WebRequest::getRequestId() );
+
                $members = [ "postData", "proxy", "noProxy", "sslVerifyHost", "caInfo",
                                "method", "followRedirects", "maxRedirects", "sslVerifyCert", "callback" ];
 
index a00b031..2da9a41 100644 (file)
@@ -447,10 +447,3 @@ class SpecialUploadStash extends UnlistedSpecialPage {
                return true;
        }
 }
-
-/**
- * @ingroup SpecialPage
- * @ingroup Upload
- */
-class SpecialUploadStashTooLargeException extends UploadStashException {
-}
diff --git a/includes/specials/exception/SpecialUploadStashTooLargeException.php b/includes/specials/exception/SpecialUploadStashTooLargeException.php
new file mode 100644 (file)
index 0000000..6c2e730
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Implements SpecialUploadStashTooLargeException
+ *
+ * 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
+ * @ingroup Upload
+ */
+class SpecialUploadStashTooLargeException extends UploadStashException {
+}
index 36391f5..118f439 100644 (file)
@@ -4,7 +4,7 @@
                <meta charset="UTF-8" />
                <title>MediaWiki {{wgVersion}}</title>
                <style media="screen">
-                       html, body {
+                       body {
                                color: #000;
                                background-color: #fff;
                                font-family: sans-serif;
index 58541b2..3e88fcd 100644 (file)
@@ -409,18 +409,3 @@ class UploadFromChunks extends UploadFromFile {
                }
        }
 }
-
-class UploadChunkZeroLengthFileException extends MWException {
-}
-
-class UploadChunkFileException extends MWException {
-}
-
-class UploadChunkVerificationException extends MWException {
-       public $msg;
-       public function __construct( array $res ) {
-               $this->msg = wfMessage( ...$res );
-               parent::__construct( wfMessage( ...$res )
-                       ->inLanguage( 'en' )->useDatabase( false )->text() );
-       }
-}
index babbe3a..aa31a5b 100644 (file)
@@ -766,72 +766,3 @@ class UploadStashFile extends UnregisteredLocalFile {
                return $this->repo->fileExists( $this->path );
        }
 }
-
-/**
- * @ingroup Upload
- */
-class UploadStashException extends MWException implements ILocalizedException {
-       /** @var string|array|MessageSpecifier */
-       protected $messageSpec;
-
-       /**
-        * @param string|array|MessageSpecifier $messageSpec See Message::newFromSpecifier
-        * @param int $code Exception code
-        * @param Exception|Throwable|null $previous The previous exception used for the exception
-        *  chaining.
-        */
-       public function __construct( $messageSpec, $code = 0, $previous = null ) {
-               $this->messageSpec = $messageSpec;
-
-               $msg = $this->getMessageObject()->text();
-               $msg = preg_replace( '!</?(var|kbd|samp|code)>!', '"', $msg );
-               $msg = Sanitizer::stripAllTags( $msg );
-               parent::__construct( $msg, $code, $previous );
-       }
-
-       public function getMessageObject() {
-               return Message::newFromSpecifier( $this->messageSpec );
-       }
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashFileNotFoundException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashBadPathException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashFileException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashZeroLengthFileException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashNotLoggedInException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashWrongOwnerException extends UploadStashException {
-}
-
-/**
- * @ingroup Upload
- */
-class UploadStashNoSuchKeyException extends UploadStashException {
-}
diff --git a/includes/upload/exception/UploadChunkFileException.php b/includes/upload/exception/UploadChunkFileException.php
new file mode 100644 (file)
index 0000000..066b38c
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Implements UploadChunkFileException
+ *
+ * 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 Upload
+ */
+
+class UploadChunkFileException extends MWException {
+}
diff --git a/includes/upload/exception/UploadChunkVerificationException.php b/includes/upload/exception/UploadChunkVerificationException.php
new file mode 100644 (file)
index 0000000..cee8c03
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Implements UploadChunkVerificationException
+ *
+ * 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 Upload
+ */
+
+class UploadChunkVerificationException extends MWException {
+       public $msg;
+       public function __construct( array $res ) {
+               $this->msg = wfMessage( ...$res );
+               parent::__construct( wfMessage( ...$res )
+                       ->inLanguage( 'en' )->useDatabase( false )->text() );
+       }
+}
diff --git a/includes/upload/exception/UploadChunkZeroLengthFileException.php b/includes/upload/exception/UploadChunkZeroLengthFileException.php
new file mode 100644 (file)
index 0000000..9b937b2
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Implements UploadChunkZeroLengthFileException
+ *
+ * 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 Upload
+ */
+
+class UploadChunkZeroLengthFileException extends MWException {
+}
diff --git a/includes/upload/exception/UploadStashBadPathException.php b/includes/upload/exception/UploadStashBadPathException.php
new file mode 100644 (file)
index 0000000..470a45a
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashBadPathException
+ *
+ * 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 Upload
+ */
+class UploadStashBadPathException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashException.php b/includes/upload/exception/UploadStashException.php
new file mode 100644 (file)
index 0000000..f089643
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Implements UploadStashException
+ *
+ * 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 Upload
+ */
+class UploadStashException extends MWException implements ILocalizedException {
+       /** @var string|array|MessageSpecifier */
+       protected $messageSpec;
+
+       /**
+        * @param string|array|MessageSpecifier $messageSpec See Message::newFromSpecifier
+        * @param int $code Exception code
+        * @param Exception|Throwable|null $previous The previous exception used for the exception
+        *  chaining.
+        */
+       public function __construct( $messageSpec, $code = 0, $previous = null ) {
+               $this->messageSpec = $messageSpec;
+
+               $msg = $this->getMessageObject()->text();
+               $msg = preg_replace( '!</?(var|kbd|samp|code)>!', '"', $msg );
+               $msg = Sanitizer::stripAllTags( $msg );
+               parent::__construct( $msg, $code, $previous );
+       }
+
+       public function getMessageObject() {
+               return Message::newFromSpecifier( $this->messageSpec );
+       }
+}
diff --git a/includes/upload/exception/UploadStashFileException.php b/includes/upload/exception/UploadStashFileException.php
new file mode 100644 (file)
index 0000000..0c6e9dd
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashFileException
+ *
+ * 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 Upload
+ */
+class UploadStashFileException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashFileNotFoundException.php b/includes/upload/exception/UploadStashFileNotFoundException.php
new file mode 100644 (file)
index 0000000..a9df7e4
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashFileNotFoundException
+ *
+ * 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 Upload
+ */
+class UploadStashFileNotFoundException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashNoSuchKeyException.php b/includes/upload/exception/UploadStashNoSuchKeyException.php
new file mode 100644 (file)
index 0000000..43c63e8
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashNoSuchKeyException
+ *
+ * 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 Upload
+ */
+class UploadStashNoSuchKeyException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashNotLoggedInException.php b/includes/upload/exception/UploadStashNotLoggedInException.php
new file mode 100644 (file)
index 0000000..18a2e17
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashNotLoggedInException
+ *
+ * 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 Upload
+ */
+class UploadStashNotLoggedInException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashWrongOwnerException.php b/includes/upload/exception/UploadStashWrongOwnerException.php
new file mode 100644 (file)
index 0000000..aaa836c
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashWrongOwnerException
+ *
+ * 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 Upload
+ */
+class UploadStashWrongOwnerException extends UploadStashException {
+}
diff --git a/includes/upload/exception/UploadStashZeroLengthFileException.php b/includes/upload/exception/UploadStashZeroLengthFileException.php
new file mode 100644 (file)
index 0000000..a054d90
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Implements UploadStashZeroLengthFileException
+ *
+ * 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 Upload
+ */
+class UploadStashZeroLengthFileException extends UploadStashException {
+}
index b7b5b87..f5eee34 100644 (file)
@@ -588,14 +588,15 @@ class User implements IDBAccessObject, UserIdentity {
                $name = self::getCanonicalName( $name, $validate );
                if ( $name === false ) {
                        return false;
-               } else {
-                       // Create unloaded user object
-                       $u = new User;
-                       $u->mName = $name;
-                       $u->mFrom = 'name';
-                       $u->setItemLoaded( 'name' );
-                       return $u;
                }
+
+               // Create unloaded user object
+               $u = new User;
+               $u->mName = $name;
+               $u->mFrom = 'name';
+               $u->setItemLoaded( 'name' );
+
+               return $u;
        }
 
        /**
@@ -1125,12 +1126,12 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                // Preg yells if you try to give it an empty string
-               if ( $wgInvalidUsernameCharacters !== '' ) {
-                       if ( preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name ) ) {
-                               wfDebugLog( 'username', __METHOD__ .
-                                       ": '$name' invalid due to wgInvalidUsernameCharacters" );
-                               return false;
-                       }
+               if ( $wgInvalidUsernameCharacters !== '' &&
+                       preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name )
+               ) {
+                       wfDebugLog( 'username', __METHOD__ .
+                               ": '$name' invalid due to wgInvalidUsernameCharacters" );
+                       return false;
                }
 
                return self::isUsableName( $name );
@@ -1160,19 +1161,20 @@ class User implements IDBAccessObject, UserIdentity {
                $result = $this->checkPasswordValidity( $password );
                if ( $result->isGood() ) {
                        return true;
-               } else {
-                       $messages = [];
-                       foreach ( $result->getErrorsByType( 'error' ) as $error ) {
-                               $messages[] = $error['message'];
-                       }
-                       foreach ( $result->getErrorsByType( 'warning' ) as $warning ) {
-                               $messages[] = $warning['message'];
-                       }
-                       if ( count( $messages ) === 1 ) {
-                               return $messages[0];
-                       }
-                       return $messages;
                }
+
+               $messages = [];
+               foreach ( $result->getErrorsByType( 'error' ) as $error ) {
+                       $messages[] = $error['message'];
+               }
+               foreach ( $result->getErrorsByType( 'warning' ) as $warning ) {
+                       $messages[] = $warning['message'];
+               }
+               if ( count( $messages ) === 1 ) {
+                       return $messages[0];
+               }
+
+               return $messages;
        }
 
        /**
@@ -1213,12 +1215,14 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $result === false ) {
                        $status->merge( $upp->checkUserPassword( $this, $password ), true );
                        return $status;
-               } elseif ( $result === true ) {
+               }
+
+               if ( $result === true ) {
                        return $status;
-               } else {
-                       $status->error( $result );
-                       return $status; // the isValidPassword hook set a string $result and returned true
                }
+
+               $status->error( $result );
+               return $status; // the isValidPassword hook set a string $result and returned true
        }
 
        /**
@@ -1464,12 +1468,13 @@ class User implements IDBAccessObject, UserIdentity {
                        $this->mGroupMemberships = null; // deferred
                        $this->getEditCount(); // revalidation for nulls
                        return true;
-               } else {
-                       // Invalid user_id
-                       $this->mId = 0;
-                       $this->loadDefaults();
-                       return false;
                }
+
+               // Invalid user_id
+               $this->mId = 0;
+               $this->loadDefaults();
+
+               return false;
        }
 
        /**
@@ -1974,10 +1979,10 @@ class User implements IDBAccessObject, UserIdentity {
                                if ( $blockIsValid && $useBlockCookie ) {
                                        // Use the block.
                                        return $tmpBlock;
-                               } else {
-                                       // If the block is not valid, remove the cookie.
-                                       Block::clearCookie( $this->getRequest()->response() );
                                }
+
+                               // If the block is not valid, remove the cookie.
+                               Block::clearCookie( $this->getRequest()->response() );
                        } else {
                                // If the block doesn't exist, remove the cookie.
                                Block::clearCookie( $this->getRequest()->response() );
@@ -1996,11 +2001,9 @@ class User implements IDBAccessObject, UserIdentity {
        public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
                global $wgEnableDnsBlacklist, $wgDnsBlacklistUrls, $wgProxyWhitelist;
 
-               if ( !$wgEnableDnsBlacklist ) {
-                       return false;
-               }
-
-               if ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) ) {
+               if ( !$wgEnableDnsBlacklist ||
+                       ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) )
+               ) {
                        return false;
                }
 
@@ -2044,9 +2047,9 @@ class User implements IDBAccessObject, UserIdentity {
                                        wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
                                        $found = true;
                                        break;
-                               } else {
-                                       wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." );
                                }
+
+                               wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." );
                        }
                }
 
@@ -2166,11 +2169,9 @@ class User implements IDBAccessObject, UserIdentity {
                        if ( isset( $limits['anon'] ) ) {
                                $keys[$cache->makeKey( 'limiter', $action, 'anon' )] = $limits['anon'];
                        }
-               } else {
+               } elseif ( isset( $limits['user'] ) ) {
                        // limits for logged-in users
-                       if ( isset( $limits['user'] ) ) {
-                               $userLimit = $limits['user'];
-                       }
+                       $userLimit = $limits['user'];
                }
 
                // limits for anons and for newbie logged-in users
@@ -2460,7 +2461,9 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
                        // Special case, we know the user is anonymous
                        return 0;
-               } elseif ( !$this->isItemLoaded( 'id' ) ) {
+               }
+
+               if ( !$this->isItemLoaded( 'id' ) ) {
                        // Don't load if this was initialized from an ID
                        $this->load();
                }
@@ -2485,14 +2488,15 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $this->isItemLoaded( 'name', 'only' ) ) {
                        // Special case optimisation
                        return $this->mName;
-               } else {
-                       $this->load();
-                       if ( $this->mName === false ) {
-                               // Clean up IPs
-                               $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
-                       }
-                       return $this->mName;
                }
+
+               $this->load();
+               if ( $this->mName === false ) {
+                       // Clean up IPs
+                       $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
+               }
+
+               return $this->mName;
        }
 
        /**
@@ -2638,7 +2642,9 @@ class User implements IDBAccessObject, UserIdentity {
                $talks = [];
                if ( !Hooks::run( 'UserRetrieveNewTalks', [ &$user, &$talks ] ) ) {
                        return $talks;
-               } elseif ( !$this->getNewtalk() ) {
+               }
+
+               if ( !$this->getNewtalk() ) {
                        return [];
                }
                $utp = $this->getTalkPage();
@@ -2666,19 +2672,19 @@ class User implements IDBAccessObject, UserIdentity {
        public function getNewMessageRevisionId() {
                $newMessageRevisionId = null;
                $newMessageLinks = $this->getNewMessageLinks();
-               if ( $newMessageLinks ) {
-                       // Note: getNewMessageLinks() never returns more than a single link
-                       // and it is always for the same wiki, but we double-check here in
-                       // case that changes some time in the future.
-                       if ( count( $newMessageLinks ) === 1
-                               && WikiMap::isCurrentWikiId( $newMessageLinks[0]['wiki'] )
-                               && $newMessageLinks[0]['rev']
-                       ) {
-                               /** @var Revision $newMessageRevision */
-                               $newMessageRevision = $newMessageLinks[0]['rev'];
-                               $newMessageRevisionId = $newMessageRevision->getId();
-                       }
+
+               // Note: getNewMessageLinks() never returns more than a single link
+               // and it is always for the same wiki, but we double-check here in
+               // case that changes some time in the future.
+               if ( $newMessageLinks && count( $newMessageLinks ) === 1
+                       && WikiMap::isCurrentWikiId( $newMessageLinks[0]['wiki'] )
+                       && $newMessageLinks[0]['rev']
+               ) {
+                       /** @var Revision $newMessageRevision */
+                       $newMessageRevision = $newMessageLinks[0]['rev'];
+                       $newMessageRevisionId = $newMessageRevision->getId();
                }
+
                return $newMessageRevisionId;
        }
 
@@ -2718,10 +2724,10 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $dbw->affectedRows() ) {
                        wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
                        return true;
-               } else {
-                       wfDebug( __METHOD__ . " already set ($field, $id)\n" );
-                       return false;
                }
+
+               wfDebug( __METHOD__ . " already set ($field, $id)\n" );
+               return false;
        }
 
        /**
@@ -2738,10 +2744,10 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $dbw->affectedRows() ) {
                        wfDebug( __METHOD__ . ": killed on ($field, $id)\n" );
                        return true;
-               } else {
-                       wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
-                       return false;
                }
+
+               wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
+               return false;
        }
 
        /**
@@ -3022,25 +3028,30 @@ class User implements IDBAccessObject, UserIdentity {
                if ( !$this->mToken ) {
                        // The user doesn't have a token, return null to indicate that.
                        return null;
-               } elseif ( $this->mToken === self::INVALID_TOKEN ) {
+               }
+
+               if ( $this->mToken === self::INVALID_TOKEN ) {
                        // We return a random value here so existing token checks are very
                        // likely to fail.
                        return MWCryptRand::generateHex( self::TOKEN_LENGTH );
-               } elseif ( $wgAuthenticationTokenVersion === null ) {
+               }
+
+               if ( $wgAuthenticationTokenVersion === null ) {
                        // $wgAuthenticationTokenVersion not in use, so return the raw secret
                        return $this->mToken;
-               } else {
-                       // $wgAuthenticationTokenVersion in use, so hmac it.
-                       $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
-
-                       // The raw hash can be overly long. Shorten it up.
-                       $len = max( 32, self::TOKEN_LENGTH );
-                       if ( strlen( $ret ) < $len ) {
-                               // Should never happen, even md5 is 128 bits
-                               throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
-                       }
-                       return substr( $ret, -$len );
                }
+
+               // $wgAuthenticationTokenVersion in use, so hmac it.
+               $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
+
+               // The raw hash can be overly long. Shorten it up.
+               $len = max( 32, self::TOKEN_LENGTH );
+               if ( strlen( $ret ) < $len ) {
+                       // Should never happen, even md5 is 128 bits
+                       throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
+               }
+
+               return substr( $ret, -$len );
        }
 
        /**
@@ -3129,19 +3140,17 @@ class User implements IDBAccessObject, UserIdentity {
                $type = $oldaddr != '' ? 'changed' : 'set';
                $notificationResult = null;
 
-               if ( $wgEmailAuthentication ) {
+               if ( $wgEmailAuthentication && $type === 'changed' ) {
                        // Send the user an email notifying the user of the change in registered
                        // email address on their previous email address
-                       if ( $type == 'changed' ) {
-                               $change = $str != '' ? 'changed' : 'removed';
-                               $notificationResult = $this->sendMail(
-                                       wfMessage( 'notificationemail_subject_' . $change )->text(),
-                                       wfMessage( 'notificationemail_body_' . $change,
-                                               $this->getRequest()->getIP(),
-                                               $this->getName(),
-                                               $str )->text()
-                               );
-                       }
+                       $change = $str != '' ? 'changed' : 'removed';
+                       $notificationResult = $this->sendMail(
+                               wfMessage( 'notificationemail_subject_' . $change )->text(),
+                               wfMessage( 'notificationemail_body_' . $change,
+                                       $this->getRequest()->getIP(),
+                                       $this->getName(),
+                                       $str )->text()
+                       );
                }
 
                $this->setEmail( $str );
@@ -3211,9 +3220,9 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( array_key_exists( $oname, $this->mOptions ) ) {
                        return $this->mOptions[$oname];
-               } else {
-                       return $defaultOverride;
                }
+
+               return $defaultOverride;
        }
 
        /**
@@ -3543,14 +3552,15 @@ class User implements IDBAccessObject, UserIdentity {
                global $wgSecureLogin;
                if ( !$wgSecureLogin ) {
                        return false;
-               } else {
-                       $https = $this->getBoolOption( 'prefershttps' );
-                       Hooks::run( 'UserRequiresHTTPS', [ $this, &$https ] );
-                       if ( $https ) {
-                               $https = wfCanIPUseHTTPS( $this->getRequest()->getIP() );
-                       }
-                       return $https;
                }
+
+               $https = $this->getBoolOption( 'prefershttps' );
+               Hooks::run( 'UserRequiresHTTPS', [ $this, &$https ] );
+               if ( $https ) {
+                       $https = wfCanIPUseHTTPS( $this->getRequest()->getIP() );
+               }
+
+               return $https;
        }
 
        /**
@@ -3932,10 +3942,10 @@ class User implements IDBAccessObject, UserIdentity {
        public function getRequest() {
                if ( $this->mRequest ) {
                        return $this->mRequest;
-               } else {
-                       global $wgRequest;
-                       return $wgRequest;
                }
+
+               global $wgRequest;
+               return $wgRequest;
        }
 
        /**
@@ -4107,19 +4117,18 @@ class User implements IDBAccessObject, UserIdentity {
                $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
                $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
 
-               if (
-                       $editCount < $wgLearnerEdits ||
-                       $registration > $learnerRegistration
-               ) {
+               if ( $editCount < $wgLearnerEdits ||
+               $registration > $learnerRegistration ) {
                        return 'newcomer';
-               } elseif (
-                       $editCount > $wgExperiencedUserEdits &&
+               }
+
+               if ( $editCount > $wgExperiencedUserEdits &&
                        $registration <= $experiencedRegistration
                ) {
                        return 'experienced';
-               } else {
-                       return 'learner';
                }
+
+               return 'learner';
        }
 
        /**
@@ -4936,9 +4945,9 @@ class User implements IDBAccessObject, UserIdentity {
                                return false;
                        }
                        return true;
-               } else {
-                       return $confirmed;
                }
+
+               return $confirmed;
        }
 
        /**
@@ -5214,9 +5223,9 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $title ) {
                        return MediaWikiServices::getInstance()
                                ->getLinkRenderer()->makeLink( $title, $text );
-               } else {
-                       return htmlspecialchars( $text );
                }
+
+               return htmlspecialchars( $text );
        }
 
        /**
@@ -5239,9 +5248,9 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $title ) {
                        $page = $title->getFullText();
                        return "[[$page|$text]]";
-               } else {
-                       return $text;
                }
+
+               return $text;
        }
 
        /**
@@ -5710,9 +5719,9 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( $groups ) {
                        return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
-               } else {
-                       return Status::newFatal( 'badaccess-group0' );
                }
+
+               return Status::newFatal( 'badaccess-group0' );
        }
 
        /**
index 0d1a657..6a75db0 100644 (file)
@@ -63,7 +63,7 @@
 @colorButtonText: @colorGray2;
 @colorButtonTextHighlight: @colorGray4;
 @colorButtonTextActive: @colorGray1;
-@colorDisabledText: @colorGray12;
+@colorDisabledText: @colorGray7;
 @colorErrorText: #d33;
 @colorWarningText: #705000;
 
index b7175d0..ac7a225 100644 (file)
                box-shadow: none;
        }
 
-       &:disabled,
-       &.mw-ui-quiet.mw-ui-progressive,
-       &.mw-ui-quiet.mw-ui-destructive {
+       &:disabled {
                background-color: @colorGray12;
                color: @colorBaseInverted;
                border-color: @colorGray12;
        // </div>
        //
        // Styleguide 2.1.1.
-       &.mw-ui-quiet {
+       &.mw-ui-quiet,
+       &.mw-ui-quiet.mw-ui-progressive,
+       &.mw-ui-quiet.mw-ui-destructive {
                background-color: transparent;
                // Quiet buttons all start gray, and reveal
                // progressive/destructive color on hover and active.
                        box-shadow: none;
                }
 
-               &:disabled {
+               &:disabled,
+               &:disabled:hover,
+               &:disabled:active {
                        background-color: transparent;
                        color: @colorDisabledText;
                        border-color: transparent;
index 90a5ed1..cd3ddfa 100644 (file)
@@ -118,6 +118,8 @@ class LinksUpdateTest extends MediaWikiLangTestCase {
 
        /**
         * @covers ParserOutput::addExternalLink
+        * @covers LinksUpdate::getAddedExternalLinks
+        * @covers LinksUpdate::getRemovedExternalLinks
         */
        public function testUpdate_externallinks() {
                /** @var ParserOutput $po */
@@ -125,7 +127,7 @@ class LinksUpdateTest extends MediaWikiLangTestCase {
 
                $po->addExternalLink( "http://testing.com/wiki/Foo" );
 
-               $this->assertLinksUpdate(
+               $update = $this->assertLinksUpdate(
                        $t,
                        $po,
                        'externallinks',
@@ -135,6 +137,31 @@ class LinksUpdateTest extends MediaWikiLangTestCase {
                                [ 'http://testing.com/wiki/Foo', 'http://com.testing./wiki/Foo' ],
                        ]
                );
+
+               $this->assertArrayEquals( [
+                       "http://testing.com/wiki/Foo"
+               ], $update->getAddedExternalLinks() );
+
+               $po = new ParserOutput();
+               $po->setTitleText( $t->getPrefixedText() );
+               $po->addExternalLink( 'http://testing.com/wiki/Bar' );
+               $update = $this->assertLinksUpdate(
+                       $t,
+                       $po,
+                       'externallinks',
+                       'el_to, el_index',
+                       'el_from = ' . self::$testingPageId,
+                       [
+                               [ 'http://testing.com/wiki/Bar', 'http://com.testing./wiki/Bar' ],
+                       ]
+               );
+
+               $this->assertArrayEquals( [
+                       "http://testing.com/wiki/Bar"
+               ], $update->getAddedExternalLinks() );
+               $this->assertArrayEquals( [
+                       "http://testing.com/wiki/Foo"
+               ], $update->getRemovedExternalLinks() );
        }
 
        /**