rather than consume everything until the end of the page.
* New maintenance script resetUserEmail.php allows sysadmins to reset user emails in case
a user forgot password/account was stolen.
+* wfCheckEntropy() was removed (deprecated in 1.27).
== Compatibility ==
'LinksDeletionUpdate' => __DIR__ . '/includes/deferred/LinksDeletionUpdate.php',
'LinksUpdate' => __DIR__ . '/includes/deferred/LinksUpdate.php',
'ListDuplicatedFilesPage' => __DIR__ . '/includes/specials/SpecialListDuplicatedFiles.php',
+ 'ListToggle' => __DIR__ . '/includes/ListToggle.php',
'ListVariants' => __DIR__ . '/maintenance/language/listVariants.php',
'ListredirectsPage' => __DIR__ . '/includes/specials/SpecialListredirects.php',
'LoadBalancer' => __DIR__ . '/includes/db/loadbalancer/LoadBalancer.php',
return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
}
-/**
- * Check if there is sufficient entropy in php's built-in session generation
- *
- * @deprecated since 1.27, PHP's session generation isn't used with
- * MediaWiki\\Session\\SessionManager
- * @return bool True = there is sufficient entropy
- */
-function wfCheckEntropy() {
- wfDeprecated( __FUNCTION__, '1.27' );
- return (
- ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
- || ini_get( 'session.entropy_file' )
- )
- && intval( ini_get( 'session.entropy_length' ) ) >= 32;
-}
-
/**
* @deprecated since 1.27, PHP's session generation isn't used with
* MediaWiki\\Session\\SessionManager
--- /dev/null
+<?php
+/**
+ * Class for generating clickable toggle links for a list of checkboxes.
+ *
+ * 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
+ */
+
+/**
+ * Class for generating clickable toggle links for a list of checkboxes.
+ *
+ * This is only supported on clients that have JavaScript enabled; it is hidden
+ * for clients that have it disabled.
+ *
+ * @since 1.27
+ */
+class ListToggle {
+ /** @var OutputPage */
+ private $output;
+
+ public function __construct( OutputPage $output ) {
+ $this->output = $output;
+
+ $output->addModules( 'mediawiki.checkboxtoggle' );
+ $output->addModuleStyles( 'mediawiki.checkboxtoggle.styles' );
+ }
+
+ private function checkboxLink( $checkboxType ) {
+ return Html::element(
+ 'a', [ 'href' => '#', 'class' => 'mw-checkbox-' . $checkboxType ],
+ $this->output->msg( 'checkbox-' . $checkboxType )->text()
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function getHTML() {
+ // Select: All, None, Invert
+ $links = [];
+ $links[] = $this->checkboxLink( 'all' );
+ $links[] = $this->checkboxLink( 'none' );
+ $links[] = $this->checkboxLink( 'invert' );
+
+ return Html::rawElement( 'div',
+ [
+ 'class' => 'mw-checkbox-toggle-controls'
+ ],
+ $this->output->msg( 'checkbox-select' )
+ ->rawParams( $this->output->getLanguage()->commaList( $links ) )->escaped()
+ );
+ }
+}
$this->buttons .= Xml::tags( 'div', [ 'class' =>
'mw-history-revisionactions' ], $actionButtons );
}
+
+ if ( $user->isAllowed( 'deleterevision' ) || $this->showTagEditUI ) {
+ $this->buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML();
+ }
+
$this->buttons .= '</div>';
$s .= $this->buttons;
*/
protected function rootValueTable( $val ) {
if ( is_object( $val ) ) {
- return self::objectTable( $val );
+ return $this->objectTable( $val );
}
if ( is_array( $val ) ) {
// Wrap arrays in another array so that they're visually boxed in a container.
// Otherwise they are visually indistinguishable from a single value.
- return self::arrayTable( [ $val ] );
+ return $this->arrayTable( [ $val ] );
}
return Html::rawElement( 'table', [ 'class' => 'mw-json mw-json-single-value' ],
Html::rawElement( 'tbody', [],
Html::rawElement( 'tr', [],
- Html::element( 'td', [], self::primitiveValue( $val ) )
+ Html::element( 'td', [], $this->primitiveValue( $val ) )
)
)
);
*/
protected function objectRow( $key, $val ) {
$th = Html::element( 'th', [], $key );
- $td = self::valueCell( $val );
+ $td = $this->valueCell( $val );
return Html::rawElement( 'tr', [], $th . $td );
}
* @return string HTML.
*/
protected function arrayRow( $val ) {
- $td = self::valueCell( $val );
+ $td = $this->valueCell( $val );
return Html::rawElement( 'tr', [], $td );
}
*/
protected function valueCell( $val ) {
if ( is_object( $val ) ) {
- return Html::rawElement( 'td', [], self::objectTable( $val ) );
+ return Html::rawElement( 'td', [], $this->objectTable( $val ) );
}
if ( is_array( $val ) ) {
- return Html::rawElement( 'td', [], self::arrayTable( $val ) );
+ return Html::rawElement( 'td', [], $this->arrayTable( $val ) );
}
- return Html::element( 'td', [ 'class' => 'value' ], self::primitiveValue( $val ) );
+ return Html::element( 'td', [ 'class' => 'value' ], $this->primitiveValue( $val ) );
}
/**
protected function doGet( $key, $flags = 0 ) {
$ret = parent::doGet( $key, $flags );
- if ( $ret === false ) {
+ if ( $ret === false && !$this->hasKey( $key ) ) {
$ret = $this->backend->doGet( $key, $flags );
- if ( $ret !== false ) {
- $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
- }
+ $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
}
return $ret;
}
return true;
}
+ /**
+ * Does this bag have a non-null value for the given key?
+ *
+ * @param string $key
+ * @return bool
+ * @since 1.27
+ */
+ protected function hasKey( $key ) {
+ return isset( $this->bag[$key] );
+ }
+
protected function doGet( $key, $flags = 0 ) {
- if ( !isset( $this->bag[$key] ) ) {
+ if ( !$this->hasKey( $key ) ) {
return false;
}
function doBatchLookups() {
# Do a link batch query
$this->mResult->seek( 0 );
- $revIds = [];
+ $parentRevIds = [];
+ $this->mParentLens = [];
$batch = new LinkBatch();
# Give some pointers to make (last) links
foreach ( $this->mResult as $row ) {
if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
- $revIds[] = $row->rev_parent_id;
+ $parentRevIds[] = $row->rev_parent_id;
}
if ( isset( $row->rev_id ) ) {
+ $this->mParentLens[$row->rev_id] = $row->rev_len;
if ( $this->contribs === 'newbie' ) { // multiple users
$batch->add( NS_USER, $row->user_name );
$batch->add( NS_USER_TALK, $row->user_name );
$batch->add( $row->page_namespace, $row->page_title );
}
}
- $this->mParentLens = Revision::getParentLengths( $this->mDbSecondary, $revIds );
+ # Fetch rev_len for revisions not already scanned above
+ $this->mParentLens += Revision::getParentLengths(
+ $this->mDbSecondary,
+ array_diff( $parentRevIds, array_keys( $this->mParentLens ) )
+ );
$batch->execute();
$this->mResult->seek( 0 );
}
) . "\n";
}
- // Select: All, None, Invert
- $links = [];
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-all' ],
- $this->msg( 'checkbox-all' )->text()
- );
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-none' ],
- $this->msg( 'checkbox-none' )->text()
- );
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-invert' ],
- $this->msg( 'checkbox-invert' )->text()
- );
-
- $buttons .= Html::rawElement( 'p',
- [
- 'class' => "mw-checkbox-toggle-controls"
- ],
- $this->msg( 'checkbox-select' )
- ->rawParams( $this->getLanguage()->commaList( $links ) )->escaped()
- );
-
- $this->getOutput()->addModules( 'mediawiki.checkboxtoggle' );
- $this->getOutput()->addModuleStyles( 'mediawiki.checkboxtoggle.styles' );
+ $buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML();
$s .= $buttons . $formcontents . $buttons;
$s .= Html::closeElement( 'form' );
$wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory,
$wgExtraNamespaces, $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
$wgExtraInterlanguageLinkPrefixes, $wgLocalInterwikis,
- $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
+ $parserMemc, $wgThumbnailScriptPath, $wgScriptPath, $wgResourceBasePath,
$wgArticlePath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
$wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
+ $wgScriptPath = '';
$wgScript = '/index.php';
- $wgScriptPath = '/';
- $wgArticlePath = '/wiki/$1';
$wgStylePath = '/skins';
+ $wgResourceBasePath = '';
$wgExtensionAssetsPath = '/extensions';
+ $wgArticlePath = '/wiki/$1';
$wgThumbnailScriptPath = false;
$wgLockManagers = [ [
'name' => 'fsLockManager',
!! wikitext
{{SCRIPTPATH}}
!! html
-<p>/
-</p>
+
!! end
!! test
class LegacyLoggerTest extends MediaWikiTestCase {
/**
- * @covers LegacyLogger::interpolate
+ * @covers MediaWiki\Logger\LegacyLogger::interpolate
* @dataProvider provideInterpolate
*/
public function testInterpolate( $message, $context, $expect ) {
}
/**
- * @covers LegacyLogger::shouldEmit
+ * @covers MediaWiki\Logger\LegacyLogger::shouldEmit
* @dataProvider provideShouldEmit
*/
public function testShouldEmit( $level, $config, $expected ) {
class MonologSpiTest extends MediaWikiTestCase {
/**
- * @covers MonologSpi::mergeConfig
+ * @covers MediaWiki\Logger\MonologSpi::mergeConfig
*/
public function testMergeConfig() {
$base = [
}
/**
- * @covers LineFormatter::normalizeException
+ * @covers MediaWiki\Logger\Monolog\LineFormatter::normalizeException
*/
public function testNormalizeExceptionNoTrace() {
$fixture = new LineFormatter();
}
/**
- * @covers LineFormatter::normalizeException
+ * @covers MediaWiki\Logger\Monolog\LineFormatter::normalizeException
*/
public function testNormalizeExceptionTrace() {
$fixture = new LineFormatter();
}
/**
- * @dataProvider provider_testSanitzeHdrs
- * @covers SwiftFileBackend::sanitzeHdrs
+ * @dataProvider provider_testSanitizeHdrs
+ * @covers SwiftFileBackend::sanitizeHdrs
* @covers SwiftFileBackend::getCustomHeaders
*/
- public function testSanitzeHdrs( $raw, $sanitized ) {
+ public function testSanitizeHdrs( $raw, $sanitized ) {
$hdrs = $this->backend->sanitizeHdrs( [ 'headers' => $raw ] );
$this->assertEquals( $hdrs, $sanitized, 'sanitizeHdrs() has expected result' );
}
- public static function provider_testSanitzeHdrs() {
+ public static function provider_testSanitizeHdrs() {
return [
[
[
/**
* @covers ProcessCacheLRU::get
* @covers ProcessCacheLRU::set
- * @covers ProcessCacheLRU::het
+ * @covers ProcessCacheLRU::has
*/
public function testAddAndGetAKey() {
$oneCache = new ProcessCacheLRUTestable( 1 );
/**
* @covers ProcessCacheLRU::get
* @covers ProcessCacheLRU::set
- * @covers ProcessCacheLRU::het
+ * @covers ProcessCacheLRU::has
*/
public function testRecentlyAccessedKeyStickIn() {
$cache = new ProcessCacheLRUTestable( 2 );
$cache->delete( 'foo', CachedBagOStuff::WRITE_CACHE_ONLY );
$this->assertEquals( 'old', $cache->get( 'foo' ) ); // Reloaded from backend
}
+
+ public function testCacheBackendMisses() {
+ $backend = new HashBagOStuff;
+ $cache = new CachedBagOStuff( $backend );
+
+ // First hit primes the cache with miss from the backend
+ $this->assertEquals( false, $cache->get( 'foo' ) );
+
+ // Change the value in the backend
+ $backend->set( 'foo', true );
+
+ // Second hit returns the cached miss
+ $this->assertEquals( false, $cache->get( 'foo' ) );
+
+ // But a fresh value is read from the backend
+ $backend->set( 'bar', true );
+ $this->assertEquals( true, $cache->get( 'bar' ) );
+ }
}
/**
* @dataProvider provideSwappingICCProfile
- * @covers BitmapHandler::swapICCProfile
+ * @covers ExifBitmapHandler::swapICCProfile
*/
public function testSwappingICCProfile(
$sourceFilename, $controlFilename, $newProfileFilename, $oldProfileName
/**
* Test for multi-section, hostile XML
- * @covers checkParseSafety
+ * @covers XMPReader::checkParseSafety
*/
public function testCheckParseSafety() {
$this->assertEquals( 2, $this->article->mLatest, "Article __set magic" );
}
- /**
- * @depends testImplementsSetMagic
- * @covers Article::__call
- */
- public function testImplementsCallMagic() {
- $this->article->mLatest = 33;
- $this->article->mDataLoaded = true;
- $this->assertEquals( 33, $this->article->getLatest(), "Article __call magic" );
- }
-
/**
* @covers Article::__get
* @covers Article::__set
$tmpGlobals['wgSitename'] = 'MediaWiki';
$tmpGlobals['wgServer'] = 'http://example.org';
$tmpGlobals['wgServerName'] = 'example.org';
+ $tmpGlobals['wgScriptPath'] = '';
$tmpGlobals['wgScript'] = '/index.php';
- $tmpGlobals['wgScriptPath'] = '/';
+ $tmpGlobals['wgResourceBasePath'] = '';
+ $tmpGlobals['wgStylePath'] = '/skins';
+ $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
$tmpGlobals['wgArticlePath'] = '/wiki/$1';
$tmpGlobals['wgActionPaths'] = [];
$tmpGlobals['wgVariantArticlePath'] = false;
- $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
- $tmpGlobals['wgStylePath'] = '/skins';
$tmpGlobals['wgEnableUploads'] = true;
$tmpGlobals['wgUploadNavigationUrl'] = false;
$tmpGlobals['wgThumbnailScriptPath'] = false;
/**
* @group large
*/
-class BcryptPasswordTestCase extends PasswordTestCase {
+class BcryptPasswordTest extends PasswordTestCase {
protected function getTypeConfigs() {
return [ 'bcrypt' => [
'class' => 'BcryptPassword',
$this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
}
- public static function provideExtracttExtensionMessagesFiles() {
+ public static function provideExtractExtensionMessagesFiles() {
$dir = __DIR__ . '/FooBar/';
return [
[
}
/**
- * @covers ExtensionProcessor::extracttExtensionMessagesFiles
- * @dataProvider provideExtracttExtensionMessagesFiles
+ * @covers ExtensionProcessor::extractExtensionMessagesFiles
+ * @dataProvider provideExtractExtensionMessagesFiles
*/
- public function testExtracttExtensionMessagesFiles( $input, $expected ) {
+ public function testExtractExtensionMessagesFiles( $input, $expected ) {
$processor = new ExtensionProcessor();
$processor->extractInfo( $this->dir, $input + self::$default, 1 );
$out = $processor->getExtractedInfo();
}
/**
- * @covers FileContentsHasher::getFileContentHash
+ * @covers FileContentsHasher::getFileContentsHash
* @covers FileContentsHasher::getFileContentsHashInternal
* @dataProvider provideSingleFile
*/
}
/**
- * @covers FileContentsHasher::getFileContentHash
+ * @covers FileContentsHasher::getFileContentsHash
* @covers FileContentsHasher::getFileContentsHashInternal
* @dataProvider provideMultipleFiles
*/