*.sh eol=lf
*.icc binary
*.webp binary
-*.mp3 binary
\ No newline at end of file
+*.mp3 binary
+*~ export-ignore
+#*# export-ignore
+.* export-ignore
+package.jso export-ignore
+README.mediawik export-ignore
+Gemfile* export-ignore
+vendor/pear/net_smtp/README.rst export-ignore
+
# https://docs.travis-ci.com/user/languages/php#HHVM-versions
- env: dbtype=mysql dbuser=root
php: hhvm-3.18
+ - env: dbtype=mysql dbuser=root
+ php: hhvm-3.21
+ - env: dbtype=mysql dbuser=root
+ php: hhvm-3.24
- env: dbtype=mysql dbuser=root
php: 7
- env: dbtype=mysql dbuser=root
the ParserOutput::getText() post-cache transformations.
* Added a hook, UploadForm:getInitialPageText, to allow extensions to alter the
initial page text for file uploads.
+* (T181651) The info page for File pages now displays the file's base-36 SHA1
+ hash value in the table of basic information.
=== External library changes in 1.31 ===
'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',
$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 {
$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 '';
}
'implementation' => 'PHP',
'version' => PHP_VERSION,
'vendor' => 'the PHP Group',
- 'upstreamSupported' => '5.5.0',
+ 'upstreamSupported' => '5.6.0',
'minSupported' => '5.5.9',
'upgradeURL' => 'https://secure.php.net/downloads.php',
);
$longHtml = <<<HTML
Please consider <a href="{$phpInfo['upgradeURL']}">upgrading your copy of
{$phpInfo['implementation']}</a>.
- {$phpInfo['implementation']} versions less than {$phpInfo['upstreamSupported']} are no
+ {$phpInfo['implementation']} versions less than {$phpInfo['upstreamSupported']} are no
longer supported by {$phpInfo['vendor']} and will not receive
security or bugfix updates.
</p>
<p>
If for some reason you are unable to upgrade your {$phpInfo['implementation']} version,
- you will need to <a href="https://www.mediawiki.org/wiki/Download">download</a> an
+ you will need to <a href="https://www.mediawiki.org/wiki/Download">download</a> an
older version of MediaWiki from our website.
See our<a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
for details of which versions are compatible with prior versions of {$phpInfo['implementation']}.
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
$this->setPageOutCallback( [ $this, 'finishImportPage' ] );
$this->importTitleFactory = new NaiveImportTitleFactory();
+ $this->externalUserNames = new ExternalUserNames( 'imported', false );
}
/**
* @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 );
}
/**
}
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 );
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'] );
$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
*/
--- /dev/null
+<?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;
+ }
+
+}
<?php
/** Ingush (ГӀалгӀай)
- *
- * To improve a translation please visit https://translatewiki.net
- *
- * @ingroup Language
- * @file
- *
- */
+*
+* To improve a translation please visit https://translatewiki.net
+*
+* @ingroup Language
+* @file
+*
+*/
$fallback = 'ru';
+
+$namespaceNames = [
+ NS_MEDIA => 'Медиа',
+ NS_SPECIAL => 'Гӏулакха',
+ NS_MAIN => '',
+ NS_TALK => 'Ювцар',
+ NS_USER => 'Доакъашхо',
+ NS_USER_TALK => 'Доакъашхочун_дувцар',
+ NS_PROJECT_TALK => '$1_ювцар',
+ NS_FILE => 'Файл',
+ NS_FILE_TALK => 'Файл_ювцар',
+ NS_MEDIAWIKI => 'MediaWiki',
+ NS_MEDIAWIKI_TALK => 'MediaWiki_ювцар',
+ NS_TEMPLATE => 'Ло',
+ NS_TEMPLATE_TALK => 'Ло_бувцар',
+ NS_HELP => 'Новкъостал',
+ NS_HELP_TALK => 'Новкъостал_дувцар',
+ NS_CATEGORY => 'ОагӀат',
+ NS_CATEGORY_TALK => 'ОагӀат_ювцар',
+];
} );
}
uri.query = q;
+
+ // Decode uri.fragment, otherwise it gets double-encoded when serializing
+ if ( uri.fragment !== undefined ) {
+ uri.fragment = Uri.decode( uri.fragment );
+ }
},
/**
--- /dev/null
+<?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 )
+ );
+ }
+
+}
uri = uriBase.clone();
uri.fragment = 'frag';
assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
+ uri.fragment = 'café';
+ assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#caf%C3%A9', 'fragment is url-encoded' );
uri = uriBase.clone();
uri.host = 'fr.wiki.local';
QUnit.test( 'Advanced URL', function ( assert ) {
var uri, queryString, relativePath;
- uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
+ uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#caf%C3%A9' );
assert.deepEqual(
{
port: '81',
path: '/dir/dir.2/index.htm',
query: { q1: '0', test1: null, test2: 'value (escaped)' },
- fragment: 'top'
+ fragment: 'café'
},
'basic object properties'
);
relativePath = uri.getRelativePath();
assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
- assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragment in relative path' );
+ assert.ok( relativePath.indexOf( mw.Uri.encode( uri.fragment ) ) >= 0, 'escaped fragment in relative path' );
} );
QUnit.test( 'Parse a uri with an @ symbol in the path and query', function ( assert ) {