Move methods for handling external usernames to a dedicated class
authorAmir Sarabadani <ladsgroup@gmail.com>
Thu, 11 Jan 2018 12:20:35 +0000 (13:20 +0100)
committerAmir Sarabadani <ladsgroup@gmail.com>
Thu, 18 Jan 2018 20:02:49 +0000 (21:02 +0100)
This makes things centralized to reduce maintenance cost and also
enables me to use this methods in Wikibase to handle RC injection

Bug: T185034
Change-Id: Ic8c602e316144ccb5b05c69a0cc607cd53e38912

autoload.php
includes/Linker.php
includes/import/WikiImporter.php
includes/user/ExternalUserNames.php [new file with mode: 0644]
tests/phpunit/includes/user/ExternalUserNamesTest.php [new file with mode: 0644]

index 5d6104c..da79fb1 100644 (file)
@@ -462,6 +462,7 @@ $wgAutoloadLocalClasses = [
        'ExternalStoreHttp' => __DIR__ . '/includes/externalstore/ExternalStoreHttp.php',
        'ExternalStoreMedium' => __DIR__ . '/includes/externalstore/ExternalStoreMedium.php',
        'ExternalStoreMwstore' => __DIR__ . '/includes/externalstore/ExternalStoreMwstore.php',
+       'ExternalUserNames' => __DIR__ . '/includes/user/ExternalUserNames.php',
        'FSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/FSFile.php',
        'FSFileBackend' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
        'FSFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
index 3b0e72d..4589a5a 100644 (file)
@@ -894,24 +894,12 @@ class Linker {
                $classes = 'mw-userlink';
                $page = null;
                if ( $userId == 0 ) {
-                       $pos = strpos( $userName, '>' );
-                       if ( $pos !== false ) {
-                               $iw = explode( ':', substr( $userName, 0, $pos ) );
-                               $firstIw = array_shift( $iw );
-                               $interwikiLookup = MediaWikiServices::getInstance()->getInterwikiLookup();
-                               if ( $interwikiLookup->isValidInterwiki( $firstIw ) ) {
-                                       $title = MWNamespace::getCanonicalName( NS_USER ) . ':' . substr( $userName, $pos + 1 );
-                                       if ( $iw ) {
-                                               $title = join( ':', $iw ) . ':' . $title;
-                                       }
-                                       $page = Title::makeTitle( NS_MAIN, $title, '', $firstIw );
-                               }
+                       $page = ExternalUserNames::getUserLinkTitle( $userName );
+
+                       if ( ExternalUserNames::isExternal( $userName ) ) {
                                $classes .= ' mw-extuserlink';
-                       } else {
-                               $page = SpecialPage::getTitleFor( 'Contributions', $userName );
-                               if ( $altUserName === false ) {
-                                       $altUserName = IP::prettifyIP( $userName );
-                               }
+                       } elseif ( $altUserName === false ) {
+                               $altUserName = IP::prettifyIP( $userName );
                        }
                        $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
                } else {
@@ -948,7 +936,7 @@ class Linker {
                $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
                $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
 
-               if ( $userId == 0 && strpos( $userText, '>' ) !== false ) {
+               if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
                        // No tools for an external user
                        return '';
                }
index ed5ec1a..5978550 100644 (file)
@@ -47,9 +47,8 @@ class WikiImporter {
        private $countableCache = [];
        /** @var bool */
        private $disableStatisticsUpdate = false;
-       private $usernamePrefix = 'imported';
-       private $assignKnownUsers = false;
-       private $triedCreations = [];
+       /** @var ExternalUserNames */
+       private $externalUserNames;
 
        /**
         * Creates an ImportXMLReader drawing from the source provided
@@ -94,6 +93,7 @@ class WikiImporter {
                $this->setPageOutCallback( [ $this, 'finishImportPage' ] );
 
                $this->importTitleFactory = new NaiveImportTitleFactory();
+               $this->externalUserNames = new ExternalUserNames( 'imported', false );
        }
 
        /**
@@ -322,8 +322,7 @@ class WikiImporter {
         * @param bool $assignKnownUsers Whether to apply the prefix to usernames that exist locally
         */
        public function setUsernamePrefix( $usernamePrefix, $assignKnownUsers ) {
-               $this->usernamePrefix = rtrim( (string)$usernamePrefix, ':>' );
-               $this->assignKnownUsers = (bool)$assignKnownUsers;
+               $this->externalUserNames = new ExternalUserNames( $usernamePrefix, $assignKnownUsers );
        }
 
        /**
@@ -732,9 +731,11 @@ class WikiImporter {
                }
 
                if ( !isset( $logInfo['contributor']['username'] ) ) {
-                       $revision->setUsername( $this->usernamePrefix . '>Unknown user' );
+                       $revision->setUsername( $this->externalUserNames->addPrefix( 'Unknown user' ) );
                } else {
-                       $revision->setUsername( $this->prefixUsername( $logInfo['contributor']['username'] ) );
+                       $revision->setUsername(
+                               $this->externalUserNames->applyPrefix( $logInfo['contributor']['username'] )
+                       );
                }
 
                return $this->logItemCallback( $revision );
@@ -928,9 +929,11 @@ class WikiImporter {
                if ( isset( $revisionInfo['contributor']['ip'] ) ) {
                        $revision->setUserIP( $revisionInfo['contributor']['ip'] );
                } elseif ( isset( $revisionInfo['contributor']['username'] ) ) {
-                       $revision->setUsername( $this->prefixUsername( $revisionInfo['contributor']['username'] ) );
+                       $revision->setUsername(
+                               $this->externalUserNames->applyPrefix( $revisionInfo['contributor']['username'] )
+                       );
                } else {
-                       $revision->setUsername( $this->usernamePrefix . '>Unknown user' );
+                       $revision->setUsername( $this->externalUserNames->addPrefix( 'Unknown user' ) );
                }
                if ( isset( $revisionInfo['sha1'] ) ) {
                        $revision->setSha1Base36( $revisionInfo['sha1'] );
@@ -1037,43 +1040,15 @@ class WikiImporter {
                        $revision->setUserIP( $uploadInfo['contributor']['ip'] );
                }
                if ( isset( $uploadInfo['contributor']['username'] ) ) {
-                       $revision->setUsername( $this->prefixUsername( $uploadInfo['contributor']['username'] ) );
+                       $revision->setUsername(
+                               $this->externalUserNames->applyPrefix( $uploadInfo['contributor']['username'] )
+                       );
                }
                $revision->setNoUpdates( $this->mNoUpdates );
 
                return call_user_func( $this->mUploadCallback, $revision );
        }
 
-       /**
-        * Add an interwiki prefix to the username, if appropriate
-        * @since 1.31
-        * @param string $name Name being imported
-        * @return string Name, possibly with the prefix prepended.
-        */
-       protected function prefixUsername( $name ) {
-               if ( !User::isUsableName( $name ) ) {
-                       return $name;
-               }
-
-               if ( $this->assignKnownUsers ) {
-                       if ( User::idFromName( $name ) ) {
-                               return $name;
-                       }
-
-                       // See if any extension wants to create it.
-                       if ( !isset( $this->triedCreations[$name] ) ) {
-                               $this->triedCreations[$name] = true;
-                               if ( !Hooks::run( 'ImportHandleUnknownUser', [ $name ] ) &&
-                                       User::idFromName( $name, User::READ_LATEST )
-                               ) {
-                                       return $name;
-                               }
-                       }
-               }
-
-               return substr( $this->usernamePrefix . '>' . $name, 0, 255 );
-       }
-
        /**
         * @return array
         */
diff --git a/includes/user/ExternalUserNames.php b/includes/user/ExternalUserNames.php
new file mode 100644 (file)
index 0000000..13ac6b2
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Class to parse and build external user names
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Class to parse and build external user names
+ * @since 1.31
+ */
+class ExternalUserNames {
+       private $usernamePrefix = 'imported';
+       private $assignKnownUsers = false;
+       private $triedCreations = [];
+
+       /**
+        * @param string $usernamePrefix Prefix to apply to unknown (and possibly also known) usernames
+        * @param bool $assignKnownUsers Whether to apply the prefix to usernames that exist locally
+        */
+       public function __construct( $usernamePrefix, $assignKnownUsers ) {
+               $this->usernamePrefix = rtrim( (string)$usernamePrefix, ':>' );
+               $this->assignKnownUsers = (bool)$assignKnownUsers;
+       }
+
+       /**
+        * Get a target Title to link a username.
+        *
+        * @param string $userName Username to link
+        *
+        * @return null|Title
+        */
+       public static function getUserLinkTitle( $userName ) {
+               $pos = strpos( $userName, '>' );
+               if ( $pos !== false ) {
+                       $iw = explode( ':', substr( $userName, 0, $pos ) );
+                       $firstIw = array_shift( $iw );
+                       $interwikiLookup = MediaWikiServices::getInstance()->getInterwikiLookup();
+                       if ( $interwikiLookup->isValidInterwiki( $firstIw ) ) {
+                               $title = MWNamespace::getCanonicalName( NS_USER ) . ':' . substr( $userName, $pos + 1 );
+                               if ( $iw ) {
+                                       $title = join( ':', $iw ) . ':' . $title;
+                               }
+                               return Title::makeTitle( NS_MAIN, $title, '', $firstIw );
+                       }
+                       return null;
+               } else {
+                       return SpecialPage::getTitleFor( 'Contributions', $userName );
+               }
+       }
+
+       /**
+        * Add an interwiki prefix to the username, if appropriate
+        *
+        * @param string $name Name being imported
+        * @return string Name, possibly with the prefix prepended.
+        */
+       public function applyPrefix( $name ) {
+               if ( !User::isUsableName( $name ) ) {
+                       return $name;
+               }
+
+               if ( $this->assignKnownUsers ) {
+                       if ( User::idFromName( $name ) ) {
+                               return $name;
+                       }
+
+                       // See if any extension wants to create it.
+                       if ( !isset( $this->triedCreations[$name] ) ) {
+                               $this->triedCreations[$name] = true;
+                               if ( !Hooks::run( 'ImportHandleUnknownUser', [ $name ] ) &&
+                                       User::idFromName( $name, User::READ_LATEST )
+                               ) {
+                                       return $name;
+                               }
+                       }
+               }
+
+               return $this->addPrefix( $name );
+       }
+
+       /**
+        * Add an interwiki prefix to the username regardless of circumstances
+        *
+        * @param string $name Name being imported
+        * @return string Name
+        */
+       public function addPrefix( $name ) {
+               return substr( $this->usernamePrefix . '>' . $name, 0, 255 );
+       }
+
+       /**
+        * Tells whether the username is external or not
+        *
+        * @param string $username Username to check
+        * @return bool true if it's external, false otherwise.
+        */
+       public static function isExternal( $username ) {
+               return strpos( $username, '>' ) !== false;
+       }
+
+}
diff --git a/tests/phpunit/includes/user/ExternalUserNamesTest.php b/tests/phpunit/includes/user/ExternalUserNamesTest.php
new file mode 100644 (file)
index 0000000..cf395f2
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+use MediaWiki\Interwiki\InterwikiLookup;
+
+/**
+ * @covers ExternalUserNames
+ */
+class ExternalUserNamesTest extends MediaWikiTestCase {
+
+       public function provideGetUserLinkTitle() {
+               return [
+                       [ 'valid:>User1', Title::makeTitle( NS_MAIN, ':User:User1', '', 'valid' ) ],
+                       [
+                               'valid:valid:>User1',
+                               Title::makeTitle( NS_MAIN, 'valid::User:User1', '', 'valid' )
+                       ],
+                       [
+                               '127.0.0.1',
+                               Title::makeTitle( NS_SPECIAL, 'Contributions/127.0.0.1', '', '' )
+                       ],
+                       [ 'invalid:>User1', null ]
+               ];
+       }
+
+       /**
+        * @covers ExternalUserNames::getUserLinkTitle
+        * @dataProvider provideGetUserLinkTitle
+        */
+       public function testGetUserLinkTitle( $username, $expected ) {
+               $interwikiLookupMock = $this->getMockBuilder( InterwikiLookup::class )
+                       ->getMock();
+
+               $interwikiValueMap = [
+                       [ 'valid', true ],
+                       [ 'invalid', false ]
+               ];
+               $interwikiLookupMock->expects( $this->any() )
+                       ->method( 'isValidInterwiki' )
+                       ->will( $this->returnValueMap( $interwikiValueMap ) );
+
+               $this->setService( 'InterwikiLookup', $interwikiLookupMock );
+
+               $this->assertEquals(
+                       $expected,
+                       ExternalUserNames::getUserLinkTitle( $username )
+               );
+       }
+
+       public function provideApplyPrefix() {
+               return [
+                       [ 'User1', 'prefix', 'prefix>User1' ],
+                       [ 'User1', 'prefix:>', 'prefix>User1' ],
+                       [ 'User1', 'prefix:', 'prefix>User1' ],
+               ];
+       }
+
+       /**
+        * @covers ExternalUserNames::applyPrefix
+        * @dataProvider provideApplyPrefix
+        */
+       public function testApplyPrefix( $username, $prefix, $expected ) {
+               $externalUserNames = new ExternalUserNames( $prefix, true );
+
+               $this->assertSame(
+                       $expected,
+                       $externalUserNames->applyPrefix( $username )
+               );
+       }
+
+       public function provideAddPrefix() {
+               return [
+                       [ 'User1', 'prefix', 'prefix>User1' ],
+                       [ 'User2', 'prefix2', 'prefix2>User2' ],
+                       [ 'User3', 'prefix3', 'prefix3>User3' ],
+               ];
+       }
+
+       /**
+        * @covers ExternalUserNames::addPrefix
+        * @dataProvider provideAddPrefix
+        */
+       public function testAddPrefix( $username, $prefix, $expected ) {
+               $externalUserNames = new ExternalUserNames( $prefix, true );
+
+               $this->assertSame(
+                       $expected,
+                       $externalUserNames->addPrefix( $username )
+               );
+       }
+
+       public function provideIsExternal() {
+               return [
+                       [ 'User1', false ],
+                       [ '>User1', true ],
+                       [ 'prefix>User1', true ],
+                       [ 'prefix:>User1', true ],
+               ];
+       }
+
+       /**
+        * @covers ExternalUserNames::isExternal
+        * @dataProvider provideIsExternal
+        */
+       public function testIsExternal( $username, $expected ) {
+               $this->assertSame(
+                       $expected,
+                       ExternalUserNames::isExternal( $username )
+               );
+       }
+
+}