'UncategorizedTemplatesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedtemplates.php',
'Undelete' => __DIR__ . '/maintenance/undelete.php',
'UnifiedDiffFormatter' => __DIR__ . '/includes/diff/UnifiedDiffFormatter.php',
+ 'UnknownContent' => __DIR__ . '/includes/content/UnknownContent.php',
+ 'UnknownContentHandler' => __DIR__ . '/includes/content/UnknownContentHandler.php',
'UnlistedSpecialPage' => __DIR__ . '/includes/specialpage/UnlistedSpecialPage.php',
'UnprotectAction' => __DIR__ . '/includes/actions/UnprotectAction.php',
'UnregisteredLocalFile' => __DIR__ . '/includes/filerepo/file/UnregisteredLocalFile.php',
+ 'UnsupportedSlotDiffRenderer' => __DIR__ . '/includes/diff/UnsupportedSlotDiffRenderer.php',
'UnusedCategoriesPage' => __DIR__ . '/includes/specials/SpecialUnusedcategories.php',
'UnusedimagesPage' => __DIR__ . '/includes/specials/SpecialUnusedimages.php',
'UnusedtemplatesPage' => __DIR__ . '/includes/specials/SpecialUnusedtemplates.php',
# checking, etc.
if ( $this->formtype == 'initial' || $this->firsttime ) {
if ( $this->initialiseForm() === false ) {
- $out = $this->context->getOutput();
- if ( $out->getRedirect() === '' ) { // mcrundo hack redirects, don't override it
- $this->noSuchSectionPage();
- }
return;
}
$content = $this->getContentObject( false ); # TODO: track content object?!
if ( $content === false ) {
+ $out = $this->context->getOutput();
+ if ( $out->getRedirect() === '' ) { // mcrundo hack redirects, don't override it
+ $this->noSuchSectionPage();
+ }
+ return false;
+ }
+
+ if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
+ $modelMsg = $this->getContext()->msg( 'content-model-' . $content->getModel() );
+ $modelName = $modelMsg->exists() ? $modelMsg->text() : $content->getModel();
+
+ $out = $this->context->getOutput();
+ $out->showErrorPage(
+ 'modeleditnotsupported-title',
+ 'modeleditnotsupported-text',
+ $modelName
+ );
return false;
}
+
$this->textbox1 = $this->toEditText( $content );
$user = $this->context->getUser();
global $wgUser;
if ( !$this->mParserOptions ) {
- if ( !$wgUser->isSafeToLoad() ) {
- // $wgUser isn't unstubbable yet, so don't try to get a
+ if ( !$wgUser || !$wgUser->isSafeToLoad() ) {
+ // $wgUser isn't available yet, so don't try to get a
// ParserOptions for it. And don't cache this ParserOptions
// either.
$po = ParserOptions::newFromAnon();
--- /dev/null
+<?php
+/**
+ * Content object implementation for representing unknown content.
+ *
+ * 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
+ *
+ * @since 1.34
+ *
+ * @file
+ * @ingroup Content
+ *
+ * @author Daniel Kinzler
+ */
+
+/**
+ * Content object implementation representing unknown content.
+ *
+ * This can be used to handle content for which no ContentHandler exists on the system,
+ * perhaps because the extension that provided it has been removed.
+ *
+ * UnknownContent instances are immutable.
+ *
+ * @ingroup Content
+ */
+class UnknownContent extends AbstractContent {
+
+ /** @var string */
+ private $data;
+
+ /**
+ * @param string $data
+ * @param string $model_id The model ID to handle
+ */
+ public function __construct( $data, $model_id ) {
+ parent::__construct( $model_id );
+
+ $this->data = $data;
+ }
+
+ /**
+ * @return Content $this
+ */
+ public function copy() {
+ // UnknownContent is immutable, so no need to copy.
+ return $this;
+ }
+
+ /**
+ * Returns an empty string.
+ *
+ * @param int $maxlength
+ *
+ * @return string
+ */
+ public function getTextForSummary( $maxlength = 250 ) {
+ return '';
+ }
+
+ /**
+ * Returns the data size in bytes.
+ *
+ * @return int
+ */
+ public function getSize() {
+ return strlen( $this->data );
+ }
+
+ /**
+ * Returns false.
+ *
+ * @param bool|null $hasLinks If it is known whether this content contains links,
+ * provide this information here, to avoid redundant parsing to find out.
+ *
+ * @return bool
+ */
+ public function isCountable( $hasLinks = null ) {
+ return false;
+ }
+
+ /**
+ * @return string data of unknown format and meaning
+ */
+ public function getNativeData() {
+ return $this->getData();
+ }
+
+ /**
+ * @return string data of unknown format and meaning
+ */
+ public function getData() {
+ return $this->data;
+ }
+
+ /**
+ * Returns an empty string.
+ *
+ * @return string The raw text.
+ */
+ public function getTextForSearchIndex() {
+ return '';
+ }
+
+ /**
+ * Returns false.
+ */
+ public function getWikitextForTransclusion() {
+ return false;
+ }
+
+ /**
+ * Fills the ParserOutput with an error message.
+ */
+ protected function fillParserOutput( Title $title, $revId,
+ ParserOptions $options, $generateHtml, ParserOutput &$output
+ ) {
+ $msg = wfMessage( 'unsupported-content-model', [ $this->getModel() ] );
+ $html = Html::rawElement( 'div', [ 'class' => 'error' ], $msg->inContentLanguage()->parse() );
+ $output->setText( $html );
+ }
+
+ /**
+ * Returns false.
+ */
+ public function convert( $toModel, $lossy = '' ) {
+ return false;
+ }
+
+ protected function equalsInternal( Content $that ) {
+ if ( !$that instanceof UnknownContent ) {
+ return false;
+ }
+
+ return $this->getData() == $that->getData();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Base content handler class for flat text contents.
+ *
+ * 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
+ *
+ * @since 1.34
+ *
+ * @file
+ * @ingroup Content
+ */
+
+/**
+ * Content handler implementation for unknown content.
+ *
+ * This can be used to handle content for which no ContentHandler exists on the system,
+ * perhaps because the extension that provided it has been removed.
+ *
+ * @ingroup Content
+ */
+class UnknownContentHandler extends ContentHandler {
+
+ /**
+ * Constructs an UnknownContentHandler. Since UnknownContentHandler can be registered
+ * for multiple model IDs on a system, multiple instances of UnknownContentHandler may
+ * coexist.
+ *
+ * To preserve the serialization format of the original content model, it must be supplied
+ * to the constructor via the $formats parameter. If not given, the default format is
+ * reported as 'application/octet-stream'.
+ *
+ * @param string $modelId
+ * @param string[]|null $formats
+ */
+ public function __construct( $modelId, $formats = null ) {
+ parent::__construct(
+ $modelId,
+ $formats ?? [
+ 'application/octet-stream',
+ 'application/unknown',
+ 'application/x-binary',
+ 'text/unknown',
+ 'unknown/unknown',
+ ]
+ );
+ }
+
+ /**
+ * Returns the content's data as-is.
+ *
+ * @param Content $content
+ * @param string|null $format The serialization format to check
+ *
+ * @return mixed
+ */
+ public function serializeContent( Content $content, $format = null ) {
+ /** @var UnknownContent $content */
+ return $content->getData();
+ }
+
+ /**
+ * Constructs an UnknownContent instance wrapping the given data.
+ *
+ * @since 1.21
+ *
+ * @param string $blob serialized content in an unknown format
+ * @param string|null $format ignored
+ *
+ * @return Content The UnknownContent object wrapping $data
+ */
+ public function unserializeContent( $blob, $format = null ) {
+ return new UnknownContent( $blob, $this->getModelID() );
+ }
+
+ /**
+ * Creates an empty UnknownContent object.
+ *
+ * @since 1.21
+ *
+ * @return Content A new UnknownContent object with empty text.
+ */
+ public function makeEmptyContent() {
+ return $this->unserializeContent( '' );
+ }
+
+ /**
+ * @return false
+ */
+ public function supportsDirectEditing() {
+ return false;
+ }
+
+ /**
+ * @param IContextSource $context
+ *
+ * @return SlotDiffRenderer
+ */
+ protected function getSlotDiffRendererInternal( IContextSource $context ) {
+ return new UnsupportedSlotDiffRenderer( $context );
+ }
+}
* @param array $lbConf Config for LBFactory::__construct()
* @param ServiceOptions $options
* @param ConfiguredReadOnlyMode $readOnlyMode
- * @param BagOStuff $srvCace
+ * @param BagOStuff $srvCache
* @param BagOStuff $mainStash
* @param WANObjectCache $wanCache
* @return array
array $lbConf,
ServiceOptions $options,
ConfiguredReadOnlyMode $readOnlyMode,
- BagOStuff $srvCace,
+ BagOStuff $srvCache,
BagOStuff $mainStash,
WANObjectCache $wanCache
) {
$options->get( 'DBprefix' )
);
- $lbConf = self::injectObjectCaches( $lbConf, $srvCace, $mainStash, $wanCache );
+ $lbConf = self::injectObjectCaches( $lbConf, $srvCache, $mainStash, $wanCache );
return $lbConf;
}
private static function injectObjectCaches(
array $lbConf, BagOStuff $sCache, BagOStuff $mStash, WANObjectCache $wCache
) {
+ // Fallback if APC style caching is not an option
+ if ( $sCache instanceof EmptyBagOStuff ) {
+ $sCache = new HashBagOStuff( [ 'maxKeys' => 100 ] );
+ }
+
// Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) {
$lbConf['srvCache'] = $sCache;
}
}
- if ( !$this->mOldRev->isDeleted( RevisionRecord::DELETED_TEXT ) &&
- !$this->mNewRev->isDeleted( RevisionRecord::DELETED_TEXT )
+ if ( $this->userCanEdit( $this->mOldRev ) &&
+ $this->userCanEdit( $this->mNewRev )
) {
$undoLink = Html::element( 'a', [
'href' => $this->mNewPage->getLocalURL( [
return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse();
}
+ /**
+ * @param Revision $rev
+ * @return bool whether the user can see and edit the revision.
+ */
+ private function userCanEdit( Revision $rev ) {
+ $user = $this->getUser();
+
+ if ( !$rev->getContentHandler()->supportsDirectEditing() ) {
+ return false;
+ }
+
+ if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Get a header for a specified revision.
*
$header = Linker::linkKnown( $title, $header, [],
[ 'oldid' => $rev->getId() ] );
- if ( $rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
+ if ( $this->userCanEdit( $rev ) ) {
$editQuery = [ 'action' => 'edit' ];
if ( !$rev->isCurrent() ) {
$editQuery['oldid'] = $rev->getId();
* must have the same content model that was used to obtain this diff renderer.
* @param Content|null $oldContent
* @param Content|null $newContent
- * @return string
+ * @return string HTML, one or more <tr> tags.
*/
abstract public function getDiff( Content $oldContent = null, Content $newContent = null );
* Diff the text representations of two content objects (or just two pieces of text in general).
* @param string $oldText
* @param string $newText
- * @return string
+ * @return string HTML, one or more <tr> tags.
*/
public function getTextDiff( $oldText, $newText ) {
Assert::parameterType( 'string', $oldText, '$oldText' );
--- /dev/null
+<?php
+/**
+ * Renders a slot diff by doing a text diff on the native representation.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Produces a warning message about not being able to render a slot diff.
+ *
+ * @since 1.34
+ *
+ * @ingroup DifferenceEngine
+ */
+class UnsupportedSlotDiffRenderer extends SlotDiffRenderer {
+
+ /**
+ * @var MessageLocalizer
+ */
+ private $localizer;
+
+ /**
+ * UnsupportedSlotDiffRenderer constructor.
+ *
+ * @param MessageLocalizer $localizer
+ */
+ public function __construct( MessageLocalizer $localizer ) {
+ $this->localizer = $localizer;
+ }
+
+ /** @inheritDoc */
+ public function getDiff( Content $oldContent = null, Content $newContent = null ) {
+ $this->normalizeContents( $oldContent, $newContent );
+
+ $oldModel = $oldContent->getModel();
+ $newModel = $newContent->getModel();
+
+ if ( $oldModel !== $newModel ) {
+ $msg = $this->localizer->msg( 'unsupported-content-diff2', $oldModel, $newModel );
+ } else {
+ $msg = $this->localizer->msg( 'unsupported-content-diff', $oldModel );
+ }
+
+ return Html::rawElement(
+ 'tr',
+ [],
+ Html::rawElement(
+ 'td',
+ [ 'colspan' => 4, 'class' => 'error' ],
+ $msg->parse()
+ )
+ );
+ }
+
+}
} elseif ( !$hadError ) {
return false; // file does not exist
} else {
- return null; // failure
+ return self::UNKNOWN; // failure
}
}
$exists = is_dir( $dir );
$hadError = $this->untrapWarnings();
- return $hadError ? null : $exists;
+ return $hadError ? self::UNKNOWN : $exists;
}
/**
} elseif ( !is_readable( $dir ) ) {
$this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
+ return self::UNKNOWN; // bad permissions?
}
return new FSFileBackendDirList( $dir, $params );
} elseif ( !is_readable( $dir ) ) {
$this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
+ return self::UNKNOWN; // bad permissions?
}
return new FSFileBackendFileList( $dir, $params );
const ATTR_METADATA = 2; // files can be stored with metadata key/values
const ATTR_UNICODE_PATHS = 4; // files can have Unicode paths (not just ASCII)
+ /** @var null Idiom for "could not determine due to I/O errors" */
+ const UNKNOWN = null;
+
/**
* Create a new backend instance from configuration.
* This should only be called from within FileBackendGroup.
}
/**
- * Get the unique backend name.
+ * Get the unique backend name
+ *
* We may have multiple different backends of the same type.
* For example, we can have two Swift backends using different proxies.
*
/**
* Alias to getDomainId()
+ *
* @return string
* @since 1.20
* @deprecated Since 1.34 Use getDomainId()
abstract public function getFileHttpUrl( array $params );
/**
- * Check if a directory exists at a given storage path.
- * Backends using key/value stores will check if the path is a
- * virtual directory, meaning there are files under the given directory.
+ * Check if a directory exists at a given storage path
+ *
+ * For backends using key/value stores, a directory is said to exist whenever
+ * there exist any files with paths using the given directory path as a prefix
+ * followed by a forward slash. For example, if there is a file called
+ * "mwstore://backend/container/dir/path.svg" then directories are said to exist
+ * at "mwstore://backend/container" and "mwstore://backend/container/dir". These
+ * can be thought of as "virtual" directories.
+ *
+ * Backends that directly use a filesystem layer might enumerate empty directories.
+ * The clean() method should always be used when files are deleted or moved if this
+ * is a concern. This is a trade-off to avoid write amplication/contention on file
+ * changes or read amplification when calling this method.
*
* Storage backends with eventual consistency might return stale data.
*
+ * @see FileBackend::clean()
+ *
* @param array $params Parameters include:
* - dir : storage directory
- * @return bool|null Returns null on failure
+ * @return bool|null Whether a directory exists or null on failure
* @since 1.20
*/
abstract public function directoryExists( array $params );
/**
- * Get an iterator to list *all* directories under a storage directory.
+ * Get an iterator to list *all* directories under a storage directory
+ *
* If the directory is of the form "mwstore://backend/container",
* then all directories in the container will be listed.
* If the directory is of form "mwstore://backend/container/dir",
*
* Failures during iteration can result in FileBackendError exceptions (since 1.22).
*
+ * @see FileBackend::directoryExists()
+ *
* @param array $params Parameters include:
* - dir : storage directory
* - topOnly : only return direct child dirs of the directory
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null Directory list enumerator null on failure
* @since 1.20
*/
abstract public function getDirectoryList( array $params );
*
* Failures during iteration can result in FileBackendError exceptions (since 1.22).
*
+ * @see FileBackend::directoryExists()
+ *
* @param array $params Parameters include:
* - dir : storage directory
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null Directory list enumerator or null on failure
* @since 1.20
*/
final public function getTopDirectoryList( array $params ) {
}
/**
- * Get an iterator to list *all* stored files under a storage directory.
- * If the directory is of the form "mwstore://backend/container",
- * then all files in the container will be listed.
- * If the directory is of form "mwstore://backend/container/dir",
- * then all files under that directory will be listed.
- * Results will be storage paths relative to the given directory.
+ * Get an iterator to list *all* stored files under a storage directory
+ *
+ * If the directory is of the form "mwstore://backend/container", then all
+ * files in the container will be listed. If the directory is of form
+ * "mwstore://backend/container/dir", then all files under that directory will
+ * be listed. Results will be storage paths relative to the given directory.
*
* Storage backends with eventual consistency might return stale data.
*
* - dir : storage directory
* - topOnly : only return direct child files of the directory (since 1.20)
* - adviseStat : set to true if stat requests will be made on the files (since 1.22)
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null File list enumerator or null on failure
*/
abstract public function getFileList( array $params );
* @param array $params Parameters include:
* - dir : storage directory
* - adviseStat : set to true if stat requests will be made on the files (since 1.22)
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null File list enumerator or null on failure
* @since 1.20
*/
final public function getTopFileList( array $params ) {
* @param array $params Parameters include:
* - srcs : list of source storage paths
* - latest : use the latest available data
- * @return bool All requests proceeded without I/O errors (since 1.24)
+ * @return bool Whether all requests proceeded without I/O errors (since 1.24)
* @since 1.23
*/
abstract public function preloadFileStat( array $params );
$ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
$stat = $this->getFileStat( $params );
- return ( $stat === null ) ? null : (bool)$stat; // null => failure
+ return ( $stat === self::UNKNOWN ) ? self::UNKNOWN : (bool)$stat;
}
final public function getFileTimestamp( array $params ) {
// cache entries from mass object listings that do not include the SHA-1. In that
// case, loading the persistent stat cache will likely yield the SHA-1.
if (
- $stat === null ||
+ $stat === self::UNKNOWN ||
( $requireSHA1 && is_array( $stat ) && !isset( $stat['sha1'] ) )
) {
$this->primeFileCache( [ $path ] ); // check persistent cache
$res = true;
break; // found one!
} elseif ( $exists === null ) { // error?
- $res = null; // if we don't find anything, it is indeterminate
+ $res = self::UNKNOWN; // if we don't find anything, it is indeterminate
}
}
final public function getDirectoryList( array $params ) {
list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
if ( $dir === null ) { // invalid storage path
- return null;
+ return self::UNKNOWN;
}
if ( $shard !== null ) {
// File listing is confined to a single container/shard
final public function getFileList( array $params ) {
list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
if ( $dir === null ) { // invalid storage path
- return null;
+ return self::UNKNOWN;
}
if ( $shard !== null ) {
// File listing is confined to a single container/shard
protected function doGetFileStat( array $params ) {
$src = $this->resolveHashKey( $params['src'] );
if ( $src === null ) {
- return null;
+ return false; // invalid path
}
if ( isset( $this->files[$src] ) ) {
$stat = $this->getContainerStat( $fullCont );
if ( is_array( $stat ) ) {
return $status; // already there
- } elseif ( $stat === null ) {
+ } elseif ( $stat === self::UNKNOWN ) {
$status->fatal( 'backend-fail-internal', $this->name );
$this->logger->error( __METHOD__ . ': cannot get container stat' );
return ( count( $status->value ) ) > 0;
}
- return null; // error
+ return self::UNKNOWN; // error
}
/**
if ( !$this->containerStatCache->hasField( $container, 'stat' ) ) {
$auth = $this->getAuthentication();
if ( !$auth ) {
- return null;
+ return self::UNKNOWN;
}
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [
$this->onError( null, __METHOD__,
[ 'cont' => $container ], $rerr, $rcode, $rdesc );
- return null;
+ return self::UNKNOWN;
}
}
$stats[$path] = false;
continue; // invalid storage path
} elseif ( !$auth ) {
- $stats[$path] = null;
+ $stats[$path] = self::UNKNOWN;
continue;
}
$stats[$path] = false;
continue; // ok, nothing to do
} elseif ( !is_array( $cstat ) ) {
- $stats[$path] = null;
+ $stats[$path] = self::UNKNOWN;
continue;
}
} elseif ( $rcode === 404 ) {
$stat = false;
} else {
- $stat = null;
+ $stat = self::UNKNOWN;
$this->onError( null, __METHOD__, $params, $rerr, $rcode, $rdesc );
}
$stats[$path] = $stat;
*/
public static function detectLocalServerCache() {
if ( function_exists( 'apcu_fetch' ) ) {
- return 'apcu';
+ // Make sure the APCu methods actually store anything
+ if ( PHP_SAPI !== 'cli' || ini_get( 'apc.enable_cli' ) ) {
+ return 'apcu';
+ }
} elseif ( function_exists( 'apc_fetch' ) ) {
- return 'apc';
+ // Make sure the APC methods actually store anything
+ if ( PHP_SAPI !== 'cli' || ini_get( 'apc.enable_cli' ) ) {
+ return 'apc';
+ }
} elseif ( function_exists( 'wincache_ucache_get' ) ) {
return 'wincache';
}
+
return CACHE_NONE;
}
}
* @since 1.26
*/
public function logDataPageOutputOnly() {
+ if ( !$this->allowOutput ) {
+ return;
+ }
+
$outputs = [];
foreach ( $this->getOutputs() as $output ) {
if ( $output->logsToOutput() ) {
* @param bool $t
*/
public function setTemplated( $t ) {
- // wfDeprecated( __METHOD__, '1.34' );
+ wfDeprecated( __METHOD__, '1.34' );
$this->allowOutput = ( $t === true );
}
* @return bool
*/
public function getTemplated() {
- // wfDeprecated( __METHOD__, '1.34' );
+ wfDeprecated( __METHOD__, '1.34' );
return $this->getAllowOutput();
}
}
/**
- * Does log() just send the data to the request/script output?
+ * May the log() try to write to standard output?
* @return bool
* @since 1.33
*/
}
/**
- * Log MediaWiki-style profiling data
+ * Log MediaWiki-style profiling data.
+ *
+ * For classes that enable logsToOutput(), this must not
+ * be called unless Profiler::setAllowOutput is enabled.
*
* @param array $stats Result of Profiler::getFunctionStats()
*/
*/
class ProfilerOutputText extends ProfilerOutput {
/** @var float Min real time display threshold */
- protected $thresholdMs;
+ private $thresholdMs;
+
+ /** @var bool Whether to use visible text or a comment (for HTML responses) */
+ private $visible;
function __construct( Profiler $collector, array $params ) {
parent::__construct( $collector, $params );
$this->thresholdMs = $params['thresholdMs'] ?? 1.0;
+ $this->visible = $params['visible'] ?? false;
}
public function logsToOutput() {
}
public function log( array $stats ) {
- if ( $this->collector->getTemplated() ) {
- $out = '';
+ $out = '';
- // Filter out really tiny entries
- $min = $this->thresholdMs;
- $stats = array_filter( $stats, function ( $a ) use ( $min ) {
- return $a['real'] > $min;
- } );
- // Sort descending by time elapsed
- usort( $stats, function ( $a, $b ) {
- return $b['real'] <=> $a['real'];
- } );
+ // Filter out really tiny entries
+ $min = $this->thresholdMs;
+ $stats = array_filter( $stats, function ( $a ) use ( $min ) {
+ return $a['real'] > $min;
+ } );
+ // Sort descending by time elapsed
+ usort( $stats, function ( $a, $b ) {
+ return $b['real'] <=> $a['real'];
+ } );
- array_walk( $stats,
- function ( $item ) use ( &$out ) {
- $out .= sprintf( "%6.2f%% %3.3f %6d - %s\n",
- $item['%real'], $item['real'], $item['calls'], $item['name'] );
- }
- );
+ array_walk( $stats,
+ function ( $item ) use ( &$out ) {
+ $out .= sprintf( "%6.2f%% %3.3f %6d - %s\n",
+ $item['%real'], $item['real'], $item['calls'], $item['name'] );
+ }
+ );
- $contentType = $this->collector->getContentType();
- if ( wfIsCLI() ) {
+ $contentType = $this->collector->getContentType();
+ if ( wfIsCLI() ) {
+ print "<!--\n{$out}\n-->\n";
+ } elseif ( $contentType === 'text/html' ) {
+ if ( $this->visible ) {
+ print "<pre>{$out}</pre>";
+ } else {
print "<!--\n{$out}\n-->\n";
- } elseif ( $contentType === 'text/html' ) {
- $visible = $this->params['visible'] ?? false;
- if ( $visible ) {
- print "<pre>{$out}</pre>";
- } else {
- print "<!--\n{$out}\n-->\n";
- }
- } elseif ( $contentType === 'text/javascript' || $contentType === 'text/css' ) {
- print "\n/*\n{$out}*/\n";
}
+ } elseif ( $contentType === 'text/javascript' || $contentType === 'text/css' ) {
+ print "\n/*\n{$out}*/\n";
}
}
}
* Initialize various variables and generate the template
*/
function outputPage() {
- Profiler::instance()->setTemplated( true );
+ Profiler::instance()->setAllowOutput();
$out = $this->getOutput();
$this->initPage( $out );
"nocreate-loggedin": "You do not have permission to create new pages.",
"sectioneditnotsupported-title": "Section editing not supported",
"sectioneditnotsupported-text": "Section editing is not supported in this page.",
+ "modeleditnotsupported-title": "Editing not supported",
+ "modeleditnotsupported-text": "Editing is not supported for content model $1.",
"permissionserrors": "Permission error",
"permissionserrorstext": "You do not have permission to do that, for the following {{PLURAL:$1|reason|reasons}}:",
"permissionserrorstext-withaction": "You do not have permission to $2, for the following {{PLURAL:$1|reason|reasons}}:",
"content-model-json": "JSON",
"content-json-empty-object": "Empty object",
"content-json-empty-array": "Empty array",
+ "unsupported-content-model": "<strong>Warning:</strong> Content model $1 is not supported on this wiki.",
+ "unsupported-content-diff": "Diffs are not supported for content model $1.",
+ "unsupported-content-diff2": "Diffs between the content models $1 and $2 are not supported on this wiki.",
"deprecated-self-close-category": "Pages using invalid self-closed HTML tags",
"deprecated-self-close-category-desc": "The page contains invalid self-closed HTML tags, such as <code><b/></code> or <code><span/></code>. The behavior of these will change soon to be consistent with the HTML5 specification, so their use in wikitext is deprecated.",
"duplicate-args-warning": "<strong>Warning:</strong> [[:$1]] is calling [[:$2]] with more than one value for the \"$3\" parameter. Only the last value provided will be used.",
"nocreate-loggedin": "Used as error message.\n\nSee also:\n* {{msg-mw|Nocreatetext}}",
"sectioneditnotsupported-title": "Page title of special page, which presumably appears when someone tries to edit a section, and section editing is disabled. Explanation of section editing on [[meta:Help:Section_editing#Section_editing|meta]].",
"sectioneditnotsupported-text": "I think this is the text of an error message, which presumably appears when someone tries to edit a section, and section editing is disabled. Explanation of section editing on [[meta:Help:Section_editing#Section_editing|meta]].",
+ "modeleditnotsupported-title": "Page title used on the edit page when editing is not supported for the page's content model.",
+ "modeleditnotsupported-text": "Error message show on the edit page when editing is not supported for the page's content model..\n\nParameters:\n* $1 - the name of the content model.",
"permissionserrors": "Used as title of error message.\n\nSee also:\n* {{msg-mw|loginreqtitle}}\n{{Identical|Permission error}}",
"permissionserrorstext": "This message is \"without action\" version of {{msg-mw|Permissionserrorstext-withaction}}.\n\nParameters:\n* $1 - the number of reasons that were found why ''the action'' cannot be performed",
"permissionserrorstext-withaction": "This message is \"with action\" version of {{msg-mw|Permissionserrorstext}}.\n\nParameters:\n* $1 - the number of reasons that were found why the action cannot be performed\n* $2 - one of the action-* messages (for example {{msg-mw|action-edit}}) or other such messages tagged with {{tl|doc-action}} in their documentation\n\nPlease report at [[Support]] if you are unable to properly translate this message. Also see [[phab:T16246]] (now closed) for background.",
"content-model-json": "{{optional}}\nName for the JSON content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}\n{{identical|JSON}}",
"content-json-empty-object": "Used to represent an object with no properties on a JSON content model page.",
"content-json-empty-array": "Used to represent an array with no values on a JSON content model page.",
+ "unsupported-content-model": "Warning shown when trying to display content with an unknown model.\n\nParameters:\n* $1 - the technical name of the content model.",
+ "unsupported-content-diff": "Warning shown when trying to display a diff between content with a model that does not support diffing (perhaps because it's an unknown model).\n\nParameters:\n* $1 - the technical name of the model of the content",
+ "unsupported-content-diff2": "Warning shown when trying to display a diff between content that uses models that do not support diffing with each other.\n\nParameters:\n* $1 - the technical name of the model of the old content\n* $2 - the technical name of the model of the new content.",
"deprecated-self-close-category": "This message is used as a category name for a [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages are placed automatically if they contain invalid self-closed HTML tags, such as <code><b/></code> or <code><span/></code>. The behavior of these will change soon to be consistent with the HTML5 specification, so their use in wikitext is deprecated.",
"deprecated-self-close-category-desc": "Invalid self-closed HTML tag category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|deprecated-self-close-category}}",
"duplicate-args-warning": "If a page calls a template and specifies the same argument more than once, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>, this warning is displayed when previewing.\n\nParameters:\n* $1 - The calling page\n* $2 - The called template\n* $3 - The name of the duplicated argument",
// Respond to ResourceLoader request
$resourceLoader->respond( $context );
-Profiler::instance()->setTemplated( true );
+Profiler::instance()->setAllowOutput();
$mediawiki = new MediaWiki();
$mediawiki->doPostOutputShutdown( 'fast' );
+ $wgProfiler
+ [ 'threshold' => $wgProfileLimit ]
);
- $profiler->setTemplated( true );
+ $profiler->setAllowOutput();
Profiler::replaceStubInstance( $profiler );
}
DELETE {
?category ?x ?y
} WHERE {
+ ?category ?x ?y
VALUES ?category {
%s
}
} INSERT {
%s
} WHERE {
+ ?category ?x ?y
VALUES ?category {
%s
}
}
},
"eslint-utils": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
- "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
- "dev": true
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
+ "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.0.0"
+ }
},
"eslint-visitor-keys": {
"version": "1.0.0",
$this->tmpFiles = array_merge( $this->tmpFiles, (array)$files );
}
- // @todo Make const when we no longer support HHVM (T192166)
- private static $namespaceAffectingSettings = [
- 'wgAllowImageMoving',
- 'wgCanonicalNamespaceNames',
- 'wgCapitalLinkOverrides',
- 'wgCapitalLinks',
- 'wgContentNamespaces',
- 'wgExtensionMessagesFiles',
- 'wgExtensionNamespaces',
- 'wgExtraNamespaces',
- 'wgExtraSignatureNamespaces',
- 'wgNamespaceContentModels',
- 'wgNamespaceProtection',
- 'wgNamespacesWithSubpages',
- 'wgNonincludableNamespaces',
- 'wgRestrictionLevels',
- ];
-
protected function tearDown() {
global $wgRequest, $wgSQLMode;
foreach ( $this->iniSettings as $name => $value ) {
ini_set( $name, $value );
}
- if (
- array_intersect( self::$namespaceAffectingSettings, array_keys( $this->mwGlobals ) ) ||
- array_intersect( self::$namespaceAffectingSettings, $this->mwGlobalsToUnset )
- ) {
- $this->resetNamespaces();
- }
$this->mwGlobals = [];
$this->mwGlobalsToUnset = [];
$this->restoreLoggers();
);
if ( $name === 'ContentLanguage' ) {
- $this->doSetMwGlobals( [ 'wgContLang' => $this->localServices->getContentLanguage() ] );
+ $this->setMwGlobals( [ 'wgContLang' => $this->localServices->getContentLanguage() ] );
}
}
* The key is added to the array of globals that will be reset afterwards
* in the tearDown().
*
- * It may be necessary to call resetServices() to allow any changed configuration variables
- * to take effect on services that get initialized based on these variables.
- *
* @par Example
* @code
* protected function setUp() {
* @param mixed|null $value Value to set the global to (ignored
* if an array is given as first argument).
*
- * @note To allow changes to global variables to take effect on global service instances,
- * call resetServices().
+ * @note This will call resetServices().
*
* @since 1.21
*/
$pairs = [ $pairs => $value ];
}
- if ( isset( $pairs['wgContLang'] ) ) {
- throw new MWException(
- 'No setting $wgContLang, use setContentLang() or setService( \'ContentLanguage\' )'
- );
- }
-
- $this->doSetMwGlobals( $pairs, $value );
- }
-
- /**
- * An internal method that allows setService() to set globals that tests are not supposed to
- * touch.
- */
- private function doSetMwGlobals( $pairs, $value = null ) {
$this->doStashMwGlobals( array_keys( $pairs ) );
foreach ( $pairs as $key => $value ) {
$GLOBALS[$key] = $value;
}
- if ( array_intersect( self::$namespaceAffectingSettings, array_keys( $pairs ) ) ) {
- $this->resetNamespaces();
- }
+ $this->resetServices();
}
/**
ini_set( $name, $value );
}
- /**
- * Must be called whenever namespaces are changed, e.g., $wgExtraNamespaces is altered.
- * Otherwise old namespace data will lurk and cause bugs.
- */
- private function resetNamespaces() {
- if ( !$this->localServices ) {
- throw new Exception( __METHOD__ . ' must be called after MediaWikiTestCase::run()' );
- }
-
- if ( $this->localServices !== MediaWikiServices::getInstance() ) {
- throw new Exception( __METHOD__ . ' will not work because the global MediaWikiServices '
- . 'instance has been replaced by test code.' );
- }
-
- Language::clearCaches();
- }
-
/**
* Check if we can back up a value by performing a shallow copy.
* Values which fail this test are copied recursively.
* Useful for setting some entries in a configuration array, instead of
* setting the entire array.
*
- * It may be necessary to call resetServices() to allow any changed configuration variables
- * to take effect on services that get initialized based on these variables.
- *
* @param string $name The name of the global, as in wgFooBar
* @param array $values The array containing the entries to set in that global
*
* @throws MWException If the designated global is not an array.
*
- * @note To allow changes to global variables to take effect on global service instances,
- * call resetServices().
+ * @note This will call resetServices().
*
* @since 1.21
*/
}
self::resetGlobalParser();
+ Language::clearCaches();
}
/**
*/
public function setContentLang( $lang ) {
if ( $lang instanceof Language ) {
- $this->setMwGlobals( 'wgLanguageCode', $lang->getCode() );
// Set to the exact object requested
$this->setService( 'ContentLanguage', $lang );
+ $this->setMwGlobals( 'wgLanguageCode', $lang->getCode() );
} else {
$this->setMwGlobals( 'wgLanguageCode', $lang );
- // Let the service handler make up the object. Avoid calling setService(), because if
- // we do, overrideMwServices() will complain if it's called later on.
- $services = MediaWikiServices::getInstance();
- $services->resetServiceForTesting( 'ContentLanguage' );
- $this->doSetMwGlobals( [ 'wgContLang' => $services->getContentLanguage() ] );
}
}
* or three values to set a single permission, like
* $this->setGroupPermissions( '*', 'read', false );
*
+ * @note This will call resetServices().
+ *
* @since 1.31
* @param array|string $newPerms Either an array of permissions to change,
* in which case the next two parameters are ignored; or a single string
}
$this->setMwGlobals( 'wgGroupPermissions', $newPermissions );
-
- // Reset services so they pick up the new permissions.
- // Resetting just PermissionManager is not sufficient, since other services may
- // have the old instance of PermissionManager injected.
- $this->resetServices();
}
/**
/**
* Create a temporary hook handler which will be reset by tearDown.
* This replaces other handlers for the same hook.
+ *
+ * @note This will call resetServices().
+ *
* @param string $hookName Hook name
* @param mixed $handler Value suitable for a hook handler
* @since 1.28
global $wgHooks;
$wgHooks[$hookName] = [ $handler ];
}
+
+ protected function getMockMessage( $text, ...$params ) {
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+
+ $msg = $this->getMockBuilder( Message::class )
+ ->disableOriginalConstructor()
+ ->setMethods( [] )
+ ->getMock();
+
+ $msg->method( 'toString' )->willReturn( $text );
+ $msg->method( '__toString' )->willReturn( $text );
+ $msg->method( 'text' )->willReturn( $text );
+ $msg->method( 'parse' )->willReturn( $text );
+ $msg->method( 'plain' )->willReturn( $text );
+ $msg->method( 'parseAsBlock' )->willReturn( $text );
+ $msg->method( 'escaped' )->willReturn( $text );
+
+ $msg->method( 'title' )->willReturn( $msg );
+ $msg->method( 'inLanguage' )->willReturn( $msg );
+ $msg->method( 'inContentLanguage' )->willReturn( $msg );
+ $msg->method( 'useDatabase' )->willReturn( $msg );
+ $msg->method( 'setContext' )->willReturn( $msg );
+
+ $msg->method( 'exists' )->willReturn( true );
+ $msg->method( 'content' )->willReturn( new MessageContent( $msg ) );
+
+ return $msg;
+ }
}
DELETE {
?category ?x ?y
} WHERE {
+ ?category ?x ?y
VALUES ?category {
<http://acme.test/wiki/Category:Test> <http://acme.test/wiki/Category:Test_2>
}
DELETE {
?category ?x ?y
} WHERE {
+ ?category ?x ?y
VALUES ?category {
<http://acme.test/wiki/Category:Changed_category>
}
DELETE {
?category ?x ?y
} WHERE {
+ ?category ?x ?y
VALUES ?category {
<http://acme.test/wiki/Category:Test> <http://acme.test/wiki/Category:MovedTo> <http://acme.test/wiki/Category:Test_2> <http://acme.test/wiki/Category:Test_3> <http://acme.test/wiki/Category:Test_4>
}
// for User::getActorId()
'wgActorTableSchemaMigrationStage' => $stage
] );
- $this->overrideMwServices();
$user = $this->getMutableTestUser()->getUser();
$userIdentity = $this->getMock( UserIdentity::class );
// Note, there are some obscure globals which
// could affect the results which aren't included above.
- $this->overrideMwServices();
$context = RequestContext::getMain();
$resp = $context->getRequest()->response();
$conf = $context->getConfig();
fwrite( $f, 'Message' );
fclose( $f );
- // Reset the service to avoid cached results
- $this->overrideMwServices();
-
$this->assertTrue( wfReadOnly() );
$this->assertTrue( wfReadOnly() ); # Check cached
}
$this->setMwGlobals( [
'wgReadOnly' => 'reason'
] );
- $this->overrideMwServices();
$this->assertSame( 'reason', wfReadOnlyReason() );
}
] );
$this->setLogger( 'wfDebug', new LegacyLogger( 'wfDebug' ) );
+ unlink( $debugLogFile );
wfDebug( "This is a normal string" );
$this->assertEquals( "This is a normal string\n", file_get_contents( $debugLogFile ) );
- unlink( $debugLogFile );
+ unlink( $debugLogFile );
wfDebug( "This is nöt an ASCII string" );
$this->assertEquals( "This is nöt an ASCII string\n", file_get_contents( $debugLogFile ) );
- unlink( $debugLogFile );
+ unlink( $debugLogFile );
wfDebug( "\00305This has böth UTF and control chars\003" );
$this->assertEquals(
" 05This has böth UTF and control chars \n",
file_get_contents( $debugLogFile )
);
- unlink( $debugLogFile );
+ unlink( $debugLogFile );
wfDebugMem();
$this->assertGreaterThan(
1000,
preg_replace( '/\D/', '', file_get_contents( $debugLogFile ) )
);
- unlink( $debugLogFile );
+ unlink( $debugLogFile );
wfDebugMem( true );
$this->assertGreaterThan(
1000000,
preg_replace( '/\D/', '', file_get_contents( $debugLogFile ) )
);
+
unlink( $debugLogFile );
}
// Don't try to fetch the files from Commons or anything, please
$this->setMwGlobals( 'wgForeignFileRepos', [] );
- // We need to reset services immediately so that editPage() doesn't use the old RepoGroup
- // and hit the network
- $this->resetServices();
// XXX How do we get file redirects to work?
$this->editPage( 'File:Redirect to bad.jpg', '#REDIRECT [[Bad.jpg]]' );
*/
public function testRawHtmlInMsg() {
$this->setMwGlobals( 'wgRawHtml', true );
- // We have to reset the core hook registration.
- // to register the html hook
- $this->overrideMwServices();
$msg = new RawMessage( '<html><script>alert("xss")</script></html>' );
$txt = '<span class="error"><html> tags cannot be' .
foreach ( $extraOptions as $key => $val ) {
$this->setMwGlobals( "wg$key", $val );
}
- $this->overrideMwServices();
$this->setService( 'RepoGroup', $this->getMockRepoGroup() );
// This returns true instead of an array if there are no errors
$this->hideDeprecated( 'Title::isValidMoveOperation' );
$this->user = $this->userUser;
}
-
- $this->resetServices();
}
public function tearDown() {
} else {
$this->user = $this->altUser;
}
- $this->resetServices();
}
/**
global $wgGroupPermissions;
$old = $wgGroupPermissions;
- $wgGroupPermissions = [];
- $this->resetServices();
+ $this->setMwGlobals( 'wgGroupPermissions', [] );
$this->assertEquals( $check[$action][1],
MediaWikiServices::getInstance()->getPermissionManager()
$this->assertEquals( $check[$action][1],
MediaWikiServices::getInstance()->getPermissionManager()
->getPermissionErrors( $action, $this->user, $this->title, 'secure' ) );
- $wgGroupPermissions = $old;
- $this->resetServices();
+ $this->setMwGlobals( 'wgGroupPermissions', $old );
$this->overrideUserPermissions( $this->user, $action );
$this->assertEquals( $check[$action][2],
}
protected function runGroupPermissions( $perm, $action, $result, $result2 = null ) {
- global $wgGroupPermissions;
-
if ( $result2 === null ) {
$result2 = $result;
}
- $wgGroupPermissions['autoconfirmed']['move'] = false;
- $wgGroupPermissions['user']['move'] = false;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', false );
+ $this->setGroupPermissions( 'user', 'move', false );
$this->overrideUserPermissions( $this->user, $perm );
$res = MediaWikiServices::getInstance()->getPermissionManager()
->getPermissionErrors( $action, $this->user, $this->title );
$this->assertEquals( $result, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = true;
- $wgGroupPermissions['user']['move'] = false;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', true );
+ $this->setGroupPermissions( 'user', 'move', false );
$this->overrideUserPermissions( $this->user, $perm );
$res = MediaWikiServices::getInstance()->getPermissionManager()
->getPermissionErrors( $action, $this->user, $this->title );
$this->assertEquals( $result2, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = true;
- $wgGroupPermissions['user']['move'] = true;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', true );
+ $this->setGroupPermissions( 'user', 'move', true );
$this->overrideUserPermissions( $this->user, $perm );
$res = MediaWikiServices::getInstance()->getPermissionManager()
->getPermissionErrors( $action, $this->user, $this->title );
$this->assertEquals( $result2, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = false;
- $wgGroupPermissions['user']['move'] = true;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', false );
+ $this->setGroupPermissions( 'user', 'move', true );
$this->overrideUserPermissions( $this->user, $perm );
$res = MediaWikiServices::getInstance()->getPermissionManager()
->getPermissionErrors( $action, $this->user, $this->title );
->getPermissionErrors( 'edit', $this->user, $this->title ) );
$this->setMwGlobals( 'wgEmailConfirmToEdit', false );
- $this->resetServices();
$this->overrideUserPermissions( $this->user, [
'createpage',
'edit',
$rights = array_diff( $rights, [ 'writetest' ] );
} );
- $this->resetServices();
$rights = MediaWikiServices::getInstance()
->getPermissionManager()
->getUserPermissions( $user );
'wgRestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
'wgAutopromote' => []
] );
- $this->resetServices();
$user = is_null( $userGroups ) ? null : $this->getTestUser( $userGroups )->getUser();
$this->assertSame( $expected, MediaWikiServices::getInstance()
->getPermissionManager()
$this->originalHandlers = TestingAccessWrapper::newFromClass( Hooks::class )->handlers;
TestingAccessWrapper::newFromClass( Hooks::class )->handlers = [];
-
- $this->overrideMwServices();
}
public function tearDown() {
$user = User::newFromName( 'Test user' );
// Don't allow the rights to everybody so that user rights kick in.
$this->mergeMwGlobalArrayValue( 'wgGroupPermissions', [ '*' => $userRights ] );
- $this->resetServices();
$this->overrideUserPermissions(
$user,
array_keys( array_filter( $userRights ), function ( $value ) {
*/
public function testRevisionSelectFields( $migrationStageSettings, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$this->hideDeprecated( 'Revision::selectFields' );
$this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectFields() );
*/
public function testRevisionSelectArchiveFields( $migrationStageSettings, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$this->hideDeprecated( 'Revision::selectArchiveFields' );
$this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectArchiveFields() );
public function testRevisionUserJoinCond() {
$this->hideDeprecated( 'Revision::userJoinCond' );
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_OLD );
- $this->overrideMwServices();
$this->assertEquals(
[ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
Revision::userJoinCond()
*/
public function testRevisionGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$queryInfo = Revision::getArchiveQueryInfo();
$this->assertQueryInfoEquals( $expected, $queryInfo );
*/
public function testRevisionGetQueryInfo( $migrationStageSettings, $options, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$queryInfo = Revision::getQueryInfo( $options );
$this->assertQueryInfoEquals( $expected, $queryInfo );
*/
public function testRevisionStoreGetQueryInfo( $migrationStageSettings, $options, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
$expected
) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
*/
public function testRevisionStoreGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
$this->setMwGlobals( $migrationStageSettings );
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
-
- $this->overrideMwServices();
}
protected function addCoreDBData() {
$title = $this->getTestPageTitle();
$rev = $this->getRevisionRecordFromDetailsArray( $revDetails );
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
$return = $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) );
'user' => true,
];
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
// Insert the first revision
* @covers \MediaWiki\Revision\RevisionStore::findSlotContentId
*/
public function testNewNullRevision( Title $title, $revDetails, $comment, $minor = false ) {
- $this->overrideMwServices();
-
$user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser();
$page = WikiPage::factory( $title );
*/
public function testNewRevisionFromRow_anonEdit_legacyEncoding() {
$this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
- $this->overrideMwServices();
$page = $this->getTestPage();
$text = __METHOD__ . 'a-ä';
/** @var Revision $rev */
*/
public function testNewRevisionFromArchiveRow_legacyEncoding() {
$this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
- $this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
$title = Title::newFromText( __METHOD__ );
$text = __METHOD__ . '-bä';
public function testGetKnownCurrentRevision_userNameChange() {
global $wgActorTableSchemaMigrationStage;
- $this->overrideMwServices();
$cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
$this->setService( 'MainWANObjectCache', $cache );
* @covers \MediaWiki\Revision\RevisionStore::getKnownCurrentRevision
*/
public function testGetKnownCurrentRevision_revDelete() {
- $this->overrideMwServices();
$cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
$this->setService( 'MainWANObjectCache', $cache );
'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
- $this->overrideMwServices();
-
if ( !$this->testPage ) {
/**
* We have to create a new page for each subclass as the page creation may result
* @covers \MediaWiki\Revision\RevisionStore::newMutableRevisionFromArray
*/
public function testConstructFromRowWithBadPageId() {
- $this->overrideMwServices();
Wikimedia\suppressWarnings();
$rev = new Revision( (object)[
'rev_page' => 77777777,
*/
public function testLoadFromTitle() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$title = $this->getMockTitle();
$conditions = [
$this->user = $this->userUser;
}
- $this->resetServices();
}
protected function setTitle( $ns, $title = "Main_Page" ) {
global $wgGroupPermissions;
$old = $wgGroupPermissions;
- $wgGroupPermissions = [];
-
- $this->resetServices();
+ $this->setMwGlobals( 'wgGroupPermissions', [] );
$this->assertEquals( $check[$action][1],
$this->title->getUserPermissionsErrors( $action, $this->user, true ) );
$this->assertEquals( $check[$action][1],
$this->title->getUserPermissionsErrors( $action, $this->user, 'secure' ) );
- $wgGroupPermissions = $old;
- $this->resetServices();
+ $this->setMwGlobals( 'wgGroupPermissions', $old );
$this->overrideUserPermissions( $this->user, $action );
$this->assertEquals( $check[$action][2],
}
protected function runGroupPermissions( $action, $result, $result2 = null ) {
- global $wgGroupPermissions;
-
if ( $result2 === null ) {
$result2 = $result;
}
$userPermsOverrides = MediaWikiServices::getInstance()->getPermissionManager()
->getUserPermissions( $this->user );
- $wgGroupPermissions['autoconfirmed']['move'] = false;
- $wgGroupPermissions['user']['move'] = false;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', false );
+ $this->setGroupPermissions( 'user', 'move', false );
$this->overrideUserPermissions( $this->user, $userPermsOverrides );
$res = $this->title->getUserPermissionsErrors( $action, $this->user );
$this->assertEquals( $result, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = true;
- $wgGroupPermissions['user']['move'] = false;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', true );
+ $this->setGroupPermissions( 'user', 'move', false );
$this->overrideUserPermissions( $this->user, $userPermsOverrides );
$res = $this->title->getUserPermissionsErrors( $action, $this->user );
$this->assertEquals( $result2, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = true;
- $wgGroupPermissions['user']['move'] = true;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', true );
+ $this->setGroupPermissions( 'user', 'move', true );
$this->overrideUserPermissions( $this->user, $userPermsOverrides );
$res = $this->title->getUserPermissionsErrors( $action, $this->user );
$this->assertEquals( $result2, $res );
- $wgGroupPermissions['autoconfirmed']['move'] = false;
- $wgGroupPermissions['user']['move'] = true;
- $this->resetServices();
+ $this->setGroupPermissions( 'autoconfirmed', 'move', false );
+ $this->setGroupPermissions( 'user', 'move', true );
$this->overrideUserPermissions( $this->user, $userPermsOverrides );
$res = $this->title->getUserPermissionsErrors( $action, $this->user );
$this->assertEquals( $result2, $res );
'wgEmailAuthentication' => true,
'wgBlockDisablesLogin' => false,
] );
- $this->resetServices();
$this->overrideUserPermissions(
$this->user,
$this->title->getUserPermissionsErrors( 'edit', $this->user ) );
$this->setMwGlobals( 'wgEmailConfirmToEdit', false );
- $this->resetServices();
$this->overrideUserPermissions(
$this->user,
[ 'createpage', 'edit', 'move', 'rollback', 'patrol', 'upload', 'purge' ]
],
],
] );
- $this->resetServices();
$now = time();
$this->user->mBlockedby = $this->user->getName();
]
]
] );
-
- // Reset services since we modified $wgLocalInterwikis
- $this->overrideMwServices();
}
/**
$this->setMwGlobals( 'wgRevokePermissions',
[ 'user' => [ 'applychangetags' => true ] ] );
- $this->resetServices();
-
$this->doBlock( [ 'tags' => 'custom tag' ] );
}
$this->mergeMwGlobalArrayValue( 'wgGroupPermissions',
[ 'sysop' => $newPermissions ] );
- $this->resetServices();
$res = $this->doBlock( [ 'hidename' => '' ] );
$dbw = wfGetDB( DB_MASTER );
$this->setMwGlobals( 'wgRevokePermissions',
[ 'sysop' => [ 'blockemail' => true ] ] );
- $this->resetServices();
-
$this->doBlock( [ 'noemail' => '' ] );
}
ChangeTags::defineTag( 'custom tag' );
$this->setMwGlobals( 'wgRevokePermissions',
[ 'user' => [ 'applychangetags' => true ] ] );
- $this->resetServices();
$this->editPage( $name, 'Some text' );
$this->tablesUsed,
[ 'change_tag', 'change_tag_def', 'logging' ]
);
- $this->resetServices();
}
public function testEdit() {
ChangeTags::defineTag( 'custom tag' );
$this->setMwGlobals( 'wgRevokePermissions',
[ 'user' => [ 'applychangetags' => true ] ] );
- // Supply services with updated globals
- $this->resetServices();
try {
$this->doApiRequestWithToken( [
$this->setMwGlobals( 'wgRevokePermissions',
[ 'user' => [ 'upload' => true ] ] );
- // Supply services with updated globals
- $this->resetServices();
$this->doApiRequestWithToken( [
'action' => 'edit',
'The content you supplied exceeds the article size limit of 1 kilobyte.' );
$this->setMwGlobals( 'wgMaxArticleSize', 1 );
- // Supply services with updated globals
- $this->resetServices();
$text = str_repeat( '!', 1025 );
'The action you have requested is limited to users in the group: ' );
$this->setMwGlobals( 'wgRevokePermissions', [ '*' => [ 'edit' => true ] ] );
- // Supply services with updated globals
- $this->resetServices();
$this->doApiRequestWithToken( [
'action' => 'edit',
$this->setMwGlobals( 'wgRevokePermissions',
[ 'user' => [ 'editcontentmodel' => true ] ] );
- // Supply services with updated globals
- $this->resetServices();
$this->doApiRequestWithToken( [
'action' => 'edit',
public function testSetCacheModeUnrecognized() {
$api = new ApiMain();
$api->setCacheMode( 'unrecognized' );
- $this->resetServices();
$this->assertSame(
'private',
TestingAccessWrapper::newFromObject( $api )->mCacheMode,
$name = ucfirst( __FUNCTION__ );
$this->mergeMwGlobalArrayValue( 'wgNamespacesWithSubpages', [ NS_MAIN => true ] );
- $this->resetServices();
$pages = [ $name, "$name/1", "$name/2", "Talk:$name", "Talk:$name/1", "Talk:$name/3" ];
$ids = [];
$this->mUserMock->method( 'getOptions' )
->willReturn( [] );
+ // DefaultPreferencesFactory calls a ton of user methods, but we still want to list all of
+ // them in case bugs are caused by unexpected things returning null that shouldn't.
+ $this->mUserMock->expects( $this->never() )->method( $this->anythingBut(
+ 'getEffectiveGroups', 'getOptionKinds', 'getInstanceForUpdate', 'getOptions', 'getId',
+ 'isAnon', 'getRequest', 'isLoggedIn', 'getName', 'getGroupMemberships', 'getEditCount',
+ 'getRegistration', 'isAllowed', 'getRealName', 'getOption', 'getStubThreshold',
+ 'getBoolOption', 'getEmail', 'getDatePreference', 'useRCPatrol', 'useNPPatrol',
+ 'setOption', 'saveSettings', 'resetOptions', 'isRegistered'
+ ) );
+
// Create a new context
$this->mContext = new DerivativeContext( new RequestContext() );
$this->mContext->getContext()->setTitle( Title::newFromText( 'Test' ) );
private function executeQuery( $request ) {
$this->mContext->setRequest( new FauxRequest( $request, true, $this->mSession ) );
+ $this->mUserMock->method( 'getRequest' )->willReturn( $this->mContext->getRequest() );
+
$this->mTested->execute();
return $this->mTested->getResult()->getResultData( null, [ 'Strip' => 'all' ] );
$this->setMwGlobals( 'wgExtraInterlanguageLinkPrefixes', [ 'madeuplanguage' ] );
$this->tablesUsed[] = 'interwiki';
- $this->resetServices();
}
/**
$this->setExpectedApiException( 'apierror-siteinfo-includealldenied' );
}
- $mockLB = $this->getMockBuilder( LoadBalancer::class )
- ->disableOriginalConstructor()
- ->setMethods( [ 'getMaxLag', 'getLagTimes', 'getServerName', '__destruct' ] )
- ->getMock();
+ $mockLB = $this->createMock( LoadBalancer::class );
$mockLB->method( 'getMaxLag' )->willReturn( [ null, 7, 1 ] );
$mockLB->method( 'getLagTimes' )->willReturn( [ 5, 7 ] );
$mockLB->method( 'getServerName' )->will( $this->returnValueMap( [
[ 0, 'apple' ], [ 1, 'carrot' ]
] ) );
+ $mockLB->method( 'getLocalDomainID' )->willReturn( 'testdomain' );
+ $mockLB->expects( $this->never() )->method( $this->anythingBut(
+ 'getMaxLag', 'getLagTimes', 'getServerName', 'getLocalDomainID', '__destruct'
+ ) );
$this->setService( 'DBLoadBalancer', $mockLB );
$this->setMwGlobals( 'wgShowHostnames', $showHostnames );
if ( $remove ) {
$this->mergeMwGlobalArrayValue( 'wgRemoveGroups', [ 'bureaucrat' => $remove ] );
}
-
- $this->resetServices();
}
/**
ChangeTags::defineTag( 'custom tag' );
$this->setGroupPermissions( 'user', 'applychangetags', false );
- $this->resetServices();
$this->doFailedRightsChange(
'You do not have permission to apply change tags along with your changes.',
global $wgActorTableSchemaMigrationStage;
$reset = new \Wikimedia\ScopedCallback( function ( $v ) {
- global $wgActorTableSchemaMigrationStage;
- $wgActorTableSchemaMigrationStage = $v;
- $this->overrideMwServices();
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $v );
}, [ $wgActorTableSchemaMigrationStage ] );
// Needs to WRITE_BOTH so READ_OLD tests below work. READ mode here doesn't really matter.
- $wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW;
- $this->overrideMwServices();
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage',
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW );
$users = [
User::newFromName( '192.168.2.2', false ),
$this->markTestSkippedIfDbType( 'sqlite' );
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
- $this->overrideMwServices();
if ( isset( $params['ucuserids'] ) ) {
$params['ucuserids'] = implode( '|', array_map( 'User::idFromName', $params['ucuserids'] ) );
*/
public function testInterwikiUser( $stage ) {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
- $this->overrideMwServices();
$params = [
'action' => 'query',
],
'wgProxyWhitelist' => [],
] );
- $this->resetServices();
$status = $this->manager->checkAccountCreatePermissions( new \User );
$this->assertFalse( $status->isOK() );
$this->assertTrue( $status->hasMessage( 'sorbs_create_account_reason' ) );
$this->setMwGlobals( 'wgProxyWhitelist', [ '127.0.0.1' ] );
- $this->resetServices();
$status = $this->manager->checkAccountCreatePermissions( new \User );
$this->assertTrue( $status->isGood() );
}
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'noname', $ret->message->getKey() );
+ $this->hook( 'LocalUserCreated', $this->never() );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
- $this->hook( 'LocalUserCreated', $this->never() );
$userReq->username = self::usernameForCreation();
$ret = $this->manager->beginAccountCreation( $creator, [ $userReq ], 'http://localhost/' );
$this->unhook( 'LocalUserCreated' );
$session, $this->request->getSession()->getSecret( 'AuthManager::accountCreationState' )
);
+ $this->hook( 'LocalUserCreated', $this->never() );
$this->request->getSession()->setSecret( 'AuthManager::accountCreationState',
[ 'username' => $creator->getName() ] + $session );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
- $this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->continueAccountCreation( [] );
$this->unhook( 'LocalUserCreated' );
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->mergeMwGlobalArrayValue( 'wgObjectCaches',
[ __METHOD__ => [ 'class' => 'HashBagOStuff' ] ] );
$this->setMwGlobals( [ 'wgMainCacheType' => __METHOD__ ] );
- // Supply services with updated globals
- $this->resetServices();
// Set up lots of mocks...
$mocks = [];
// Wiki is read-only
$session->clear();
+ $this->hook( 'LocalUserCreated', $this->never() );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$user = \User::newFromName( $username );
- $this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_SESSION, true );
$this->unhook( 'LocalUserCreated' );
$this->assertEquals( \Status::newFatal( wfMessage( 'readonlytext', 'Because' ) ), $ret );
private function getBlockManagerConstructorArgs( $overrideConfig ) {
$blockManagerConfig = array_merge( $this->blockManagerConfig, $overrideConfig );
$this->setMwGlobals( $blockManagerConfig );
- $this->overrideMwServices();
return [
new LoggedServiceOptions(
self::$serviceOptionsAccessLog,
// let's choose e.g. German (de)
$this->setUserLang( 'de' );
$this->setContentLang( 'de' );
- $this->resetServices();
}
function addDBDataOnce() {
]
]
] );
- $this->overrideMwServices();
$messageCache = MessageCache::singleton();
$messageCache->enable();
--- /dev/null
+<?php
+
+use MediaWiki\Revision\SlotRecord;
+use MediaWiki\Revision\SlotRenderingProvider;
+
+/**
+ * @group ContentHandler
+ */
+class UnknownContentHandlerTest extends MediaWikiLangTestCase {
+ /**
+ * @covers UnknownContentHandler::supportsDirectEditing
+ */
+ public function testSupportsDirectEditing() {
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $this->assertFalse( $handler->supportsDirectEditing(), 'direct editing supported' );
+ }
+
+ /**
+ * @covers UnknownContentHandler::serializeContent
+ */
+ public function testSerializeContent() {
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $content = new UnknownContent( 'hello world', 'horkyporky' );
+
+ $this->assertEquals( 'hello world', $handler->serializeContent( $content ) );
+ $this->assertEquals(
+ 'hello world',
+ $handler->serializeContent( $content, 'application/horkyporky' )
+ );
+ }
+
+ /**
+ * @covers UnknownContentHandler::unserializeContent
+ */
+ public function testUnserializeContent() {
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $content = $handler->unserializeContent( 'hello world' );
+ $this->assertEquals( 'hello world', $content->getData() );
+
+ $content = $handler->unserializeContent( 'hello world', 'application/horkyporky' );
+ $this->assertEquals( 'hello world', $content->getData() );
+ }
+
+ /**
+ * @covers UnknownContentHandler::makeEmptyContent
+ */
+ public function testMakeEmptyContent() {
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $content = $handler->makeEmptyContent();
+
+ $this->assertTrue( $content->isEmpty() );
+ $this->assertEquals( '', $content->getData() );
+ }
+
+ public static function dataIsSupportedFormat() {
+ return [
+ [ null, true ],
+ [ 'application/octet-stream', true ],
+ [ 'unknown/unknown', true ],
+ [ 'text/plain', false ],
+ [ 99887766, false ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataIsSupportedFormat
+ * @covers UnknownContentHandler::isSupportedFormat
+ */
+ public function testIsSupportedFormat( $format, $supported ) {
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $this->assertEquals( $supported, $handler->isSupportedFormat( $format ) );
+ }
+
+ /**
+ * @covers ContentHandler::getSecondaryDataUpdates
+ */
+ public function testGetSecondaryDataUpdates() {
+ $title = Title::newFromText( 'Somefile.jpg', NS_FILE );
+ $content = new UnknownContent( '', 'horkyporky' );
+
+ /** @var SlotRenderingProvider $srp */
+ $srp = $this->getMock( SlotRenderingProvider::class );
+
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $updates = $handler->getSecondaryDataUpdates( $title, $content, SlotRecord::MAIN, $srp );
+
+ $this->assertEquals( [], $updates );
+ }
+
+ /**
+ * @covers ContentHandler::getDeletionUpdates
+ */
+ public function testGetDeletionUpdates() {
+ $title = Title::newFromText( 'Somefile.jpg', NS_FILE );
+
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $updates = $handler->getDeletionUpdates( $title, SlotRecord::MAIN );
+
+ $this->assertEquals( [], $updates );
+ }
+
+ /**
+ * @covers ContentHandler::getDeletionUpdates
+ */
+ public function testGetSlotDiffRenderer() {
+ $context = new RequestContext();
+ $context->setRequest( new FauxRequest() );
+
+ $handler = new UnknownContentHandler( 'horkyporky' );
+ $slotDiffRenderer = $handler->getSlotDiffRenderer( $context );
+
+ $oldContent = $handler->unserializeContent( 'Foo' );
+ $newContent = $handler->unserializeContent( 'Foo bar' );
+
+ $diff = $slotDiffRenderer->getDiff( $oldContent, $newContent );
+ $this->assertNotEmpty( $diff );
+ }
+
+}
--- /dev/null
+<?php
+
+/**
+ * @group ContentHandler
+ */
+class UnknownContentTest extends MediaWikiLangTestCase {
+
+ /**
+ * @param string $data
+ * @return UnknownContent
+ */
+ public function newContent( $data, $type = 'xyzzy' ) {
+ return new UnknownContent( $data, $type );
+ }
+
+ /**
+ * @covers UnknownContent::getParserOutput
+ */
+ public function testGetParserOutput() {
+ $this->setUserLang( 'en' );
+ $this->setContentLang( 'qqx' );
+
+ $title = Title::newFromText( 'Test' );
+ $content = $this->newContent( 'Horkyporky' );
+
+ $po = $content->getParserOutput( $title );
+ $html = $po->getText();
+ $html = preg_replace( '#<!--.*?-->#sm', '', $html ); // strip comments
+
+ $this->assertNotContains( 'Horkyporky', $html );
+ $this->assertNotContains( '(unsupported-content-model)', $html );
+ }
+
+ /**
+ * @covers UnknownContent::preSaveTransform
+ */
+ public function testPreSaveTransform() {
+ $title = Title::newFromText( 'Test' );
+ $user = $this->getTestUser()->getUser();
+ $content = $this->newContent( 'Horkyporky ~~~' );
+
+ $options = new ParserOptions();
+
+ $this->assertSame( $content, $content->preSaveTransform( $title, $user, $options ) );
+ }
+
+ /**
+ * @covers UnknownContent::preloadTransform
+ */
+ public function testPreloadTransform() {
+ $title = Title::newFromText( 'Test' );
+ $content = $this->newContent( 'Horkyporky ~~~' );
+
+ $options = new ParserOptions();
+
+ $this->assertSame( $content, $content->preloadTransform( $title, $options ) );
+ }
+
+ /**
+ * @covers UnknownContent::getRedirectTarget
+ */
+ public function testGetRedirectTarget() {
+ $content = $this->newContent( '#REDIRECT [[Horkyporky]]' );
+ $this->assertNull( $content->getRedirectTarget() );
+ }
+
+ /**
+ * @covers UnknownContent::isRedirect
+ */
+ public function testIsRedirect() {
+ $content = $this->newContent( '#REDIRECT [[Horkyporky]]' );
+ $this->assertFalse( $content->isRedirect() );
+ }
+
+ /**
+ * @covers UnknownContent::isCountable
+ */
+ public function testIsCountable() {
+ $content = $this->newContent( '[[Horkyporky]]' );
+ $this->assertFalse( $content->isCountable( true ) );
+ }
+
+ /**
+ * @covers UnknownContent::getTextForSummary
+ */
+ public function testGetTextForSummary() {
+ $content = $this->newContent( 'Horkyporky' );
+ $this->assertSame( '', $content->getTextForSummary() );
+ }
+
+ /**
+ * @covers UnknownContent::getTextForSearchIndex
+ */
+ public function testGetTextForSearchIndex() {
+ $content = $this->newContent( 'Horkyporky' );
+ $this->assertSame( '', $content->getTextForSearchIndex() );
+ }
+
+ /**
+ * @covers UnknownContent::copy
+ */
+ public function testCopy() {
+ $content = $this->newContent( 'hello world.' );
+ $copy = $content->copy();
+
+ $this->assertSame( $content, $copy );
+ }
+
+ /**
+ * @covers UnknownContent::getSize
+ */
+ public function testGetSize() {
+ $content = $this->newContent( 'hello world.' );
+
+ $this->assertEquals( 12, $content->getSize() );
+ }
+
+ /**
+ * @covers UnknownContent::getData
+ */
+ public function testGetData() {
+ $content = $this->newContent( 'hello world.' );
+
+ $this->assertEquals( 'hello world.', $content->getData() );
+ }
+
+ /**
+ * @covers UnknownContent::getNativeData
+ */
+ public function testGetNativeData() {
+ $content = $this->newContent( 'hello world.' );
+
+ $this->assertEquals( 'hello world.', $content->getNativeData() );
+ }
+
+ /**
+ * @covers UnknownContent::getWikitextForTransclusion
+ */
+ public function testGetWikitextForTransclusion() {
+ $content = $this->newContent( 'hello world.' );
+
+ $this->assertEquals( '', $content->getWikitextForTransclusion() );
+ }
+
+ /**
+ * @covers UnknownContent::getModel
+ */
+ public function testGetModel() {
+ $content = $this->newContent( "hello world.", 'horkyporky' );
+
+ $this->assertEquals( 'horkyporky', $content->getModel() );
+ }
+
+ /**
+ * @covers UnknownContent::getContentHandler
+ */
+ public function testGetContentHandler() {
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers',
+ [ 'horkyporky' => 'UnknownContentHandler' ]
+ );
+
+ $content = $this->newContent( "hello world.", 'horkyporky' );
+
+ $this->assertInstanceOf( UnknownContentHandler::class, $content->getContentHandler() );
+ $this->assertEquals( 'horkyporky', $content->getContentHandler()->getModelID() );
+ }
+
+ public static function dataIsEmpty() {
+ return [
+ [ '', true ],
+ [ ' ', false ],
+ [ '0', false ],
+ [ 'hallo welt.', false ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataIsEmpty
+ * @covers UnknownContent::isEmpty
+ */
+ public function testIsEmpty( $text, $empty ) {
+ $content = $this->newContent( $text );
+
+ $this->assertEquals( $empty, $content->isEmpty() );
+ }
+
+ public function provideEquals() {
+ return [
+ [ new UnknownContent( "hallo", 'horky' ), null, false ],
+ [ new UnknownContent( "hallo", 'horky' ), new UnknownContent( "hallo", 'horky' ), true ],
+ [ new UnknownContent( "hallo", 'horky' ), new UnknownContent( "hallo", 'xyzzy' ), false ],
+ [ new UnknownContent( "hallo", 'horky' ), new JavaScriptContent( "hallo" ), false ],
+ [ new UnknownContent( "hallo", 'horky' ), new WikitextContent( "hallo" ), false ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideEquals
+ * @covers UnknownContent::equals
+ */
+ public function testEquals( Content $a, Content $b = null, $equal = false ) {
+ $this->assertEquals( $equal, $a->equals( $b ) );
+ }
+
+ public static function provideConvert() {
+ return [
+ [ // #0
+ 'Hallo Welt',
+ CONTENT_MODEL_WIKITEXT,
+ 'lossless',
+ 'Hallo Welt'
+ ],
+ [ // #1
+ 'Hallo Welt',
+ CONTENT_MODEL_WIKITEXT,
+ 'lossless',
+ 'Hallo Welt'
+ ],
+ [ // #1
+ 'Hallo Welt',
+ CONTENT_MODEL_CSS,
+ 'lossless',
+ 'Hallo Welt'
+ ],
+ [ // #1
+ 'Hallo Welt',
+ CONTENT_MODEL_JAVASCRIPT,
+ 'lossless',
+ 'Hallo Welt'
+ ],
+ ];
+ }
+
+ /**
+ * @covers UnknownContent::convert
+ */
+ public function testConvert() {
+ $content = $this->newContent( 'More horkyporky?' );
+
+ $this->assertFalse( $content->convert( CONTENT_MODEL_TEXT ) );
+ }
+
+ /**
+ * @covers UnknownContent::__construct
+ * @covers UnknownContentHandler::serializeContent
+ */
+ public function testSerialize() {
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers',
+ [ 'horkyporky' => 'UnknownContentHandler' ]
+ );
+
+ $content = $this->newContent( 'Hörkypörky', 'horkyporky' );
+
+ $this->assertSame( 'Hörkypörky', $content->serialize() );
+ }
+
+}
* @dataProvider provideGenerateContentDiffBody
*/
public function testGenerateContentDiffBody(
- Content $oldContent, Content $newContent, $expectedDiff
+ array $oldContentArgs, array $newContentArgs, $expectedDiff
) {
+ $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
+ 'testing-nontext' => DummyNonTextContentHandler::class,
+ ] );
+ $oldContent = ContentHandler::makeContent( ...$oldContentArgs );
+ $newContent = ContentHandler::makeContent( ...$newContentArgs );
+
// Set $wgExternalDiffEngine to something bogus to try to force use of
// the PHP engine rather than wikidiff2.
$this->setMwGlobals( [
$this->assertSame( $expectedDiff, $this->getPlainDiff( $diff ) );
}
- public function provideGenerateContentDiffBody() {
- $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
- 'testing-nontext' => DummyNonTextContentHandler::class,
- ] );
- $content1 = ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT );
- $content2 = ContentHandler::makeContent( 'yyy', null, CONTENT_MODEL_TEXT );
+ public static function provideGenerateContentDiffBody() {
+ $content1 = [ 'xxx', null, CONTENT_MODEL_TEXT ];
+ $content2 = [ 'yyy', null, CONTENT_MODEL_TEXT ];
return [
'self-diff' => [ $content1, $content1, '' ],
/**
* @dataProvider provideGetDiff
- * @param Content|null $oldContent
- * @param Content|null $newContent
+ * @param array|null $oldContentArgs To pass to makeContent() (if not null)
+ * @param array|null $newContentArgs
* @param string|Exception $expectedResult
* @throws Exception
*/
public function testGetDiff(
- Content $oldContent = null, Content $newContent = null, $expectedResult
+ array $oldContentArgs = null, array $newContentArgs = null, $expectedResult
) {
+ $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
+ 'testing' => DummyContentHandlerForTesting::class,
+ 'testing-nontext' => DummyNonTextContentHandler::class,
+ ] );
+
+ $oldContent = $oldContentArgs ? self::makeContent( ...$oldContentArgs ) : null;
+ $newContent = $newContentArgs ? self::makeContent( ...$newContentArgs ) : null;
+
if ( $expectedResult instanceof Exception ) {
$this->setExpectedException( get_class( $expectedResult ), $expectedResult->getMessage() );
}
$this->assertSame( $expectedResult, $plainDiff );
}
- public function provideGetDiff() {
- $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
- 'testing' => DummyContentHandlerForTesting::class,
- 'testing-nontext' => DummyNonTextContentHandler::class,
- ] );
-
+ public static function provideGetDiff() {
return [
'same text' => [
- $this->makeContent( "aaa\nbbb\nccc" ),
- $this->makeContent( "aaa\nbbb\nccc" ),
+ [ "aaa\nbbb\nccc" ],
+ [ "aaa\nbbb\nccc" ],
"",
],
'different text' => [
- $this->makeContent( "aaa\nbbb\nccc" ),
- $this->makeContent( "aaa\nxxx\nccc" ),
+ [ "aaa\nbbb\nccc" ],
+ [ "aaa\nxxx\nccc" ],
" aaa aaa\n-bbb+xxx\n ccc ccc",
],
'no right content' => [
- $this->makeContent( "aaa\nbbb\nccc" ),
+ [ "aaa\nbbb\nccc" ],
null,
"-aaa+ \n-bbb \n-ccc ",
],
'no left content' => [
null,
- $this->makeContent( "aaa\nbbb\nccc" ),
+ [ "aaa\nbbb\nccc" ],
"- +aaa\n +bbb\n +ccc",
],
'no content' => [
new InvalidArgumentException( '$oldContent and $newContent cannot both be null' ),
],
'non-text left content' => [
- $this->makeContent( '', 'testing-nontext' ),
- $this->makeContent( "aaa\nbbb\nccc" ),
+ [ '', 'testing-nontext' ],
+ [ "aaa\nbbb\nccc" ],
new ParameterTypeException( '$oldContent', 'TextContent|null' ),
],
'non-text right content' => [
- $this->makeContent( "aaa\nbbb\nccc" ),
- $this->makeContent( '', 'testing-nontext' ),
+ [ "aaa\nbbb\nccc" ],
+ [ '', 'testing-nontext' ],
new ParameterTypeException( '$newContent', 'TextContent|null' ),
],
];
* @param string $model
* @return null|TextContent
*/
- private function makeContent( $str, $model = CONTENT_MODEL_TEXT ) {
+ private static function makeContent( $str, $model = CONTENT_MODEL_TEXT ) {
return ContentHandler::makeContent( $str, null, $model );
}
--- /dev/null
+<?php
+
+/**
+ * @covers UnsupportedSlotDiffRenderer
+ */
+class UnsupportedSlotDiffRendererTest extends MediaWikiTestCase {
+
+ public function provideDiff() {
+ $oldContent = new TextContent( 'Kittens' );
+ $newContent = new TextContent( 'Goats' );
+ $badContent = new UnknownContent( 'Dragons', 'xyzzy' );
+
+ yield [ '(unsupported-content-diff)', $oldContent, null ];
+ yield [ '(unsupported-content-diff)', null, $newContent ];
+ yield [ '(unsupported-content-diff)', $oldContent, $newContent ];
+ yield [ '(unsupported-content-diff2)', $badContent, $newContent ];
+ yield [ '(unsupported-content-diff2)', $oldContent, $badContent ];
+ yield [ '(unsupported-content-diff)', null, $badContent ];
+ yield [ '(unsupported-content-diff)', $badContent, null ];
+ }
+
+ /**
+ * @dataProvider provideDiff
+ */
+ public function testDiff( $expected, $oldContent, $newContent ) {
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers',
+ [ 'xyzzy' => 'UnknownContentHandler' ]
+ );
+
+ $localizer = $this->getMock( MessageLocalizer::class );
+
+ $localizer->method( 'msg' )
+ ->willReturnCallback( function ( $key, ...$params ) {
+ return new RawMessage( "($key)", $params );
+ } );
+
+ $sdr = new UnsupportedSlotDiffRenderer( $localizer );
+ $this->assertContains( $expected, $sdr->getDiff( $oldContent, $newContent ) );
+ }
+
+}
$services = MediaWikiServices::getInstance();
foreach ( $serviceMembers as $key => $name ) {
- $this->$key = $services->getService( $name );
+ if ( $key === 'srvCache' ) {
+ $this->$key = ObjectCache::getLocalServerInstance( 'hash' );
+ } else {
+ $this->$key = $services->getService( $name );
+ }
}
return FileBackendGroup::singleton();
function testHasForeignRepoNegative() {
$this->setMwGlobals( 'wgForeignFileRepos', [] );
- $this->overrideMwServices();
FileBackendGroup::destroySingleton();
$this->assertFalse( RepoGroup::singleton()->hasForeignRepos() );
}
function testForEachForeignRepoNone() {
$this->setMwGlobals( 'wgForeignFileRepos', [] );
- $this->overrideMwServices();
FileBackendGroup::destroySingleton();
$fakeCallback = $this->createMock( RepoGroupTestHelper::class );
$fakeCallback->expects( $this->never() )->method( 'callback' );
'apiThumbCacheExpiry' => 86400,
'directory' => $wgUploadDirectory
] ] );
- $this->overrideMwServices();
FileBackendGroup::destroySingleton();
}
}
}
private function setWgInterwikiCache( $interwikiCache ) {
- $this->overrideMwServices();
MediaWikiServices::getInstance()->resetServiceForTesting( 'InterwikiLookup' );
$this->setMwGlobals( 'wgInterwikiCache', $interwikiCache );
}
/** @covers ObjectCache::newAnything */
public function testNewAnythingNoAccelNoDb() {
- $this->overrideMwServices(); // Ensures restore on tear down
- MediaWiki\MediaWikiServices::disableStorageBackend();
-
$this->setMwGlobals( [
'wgMainCacheType' => CACHE_ACCEL
] );
CACHE_ACCEL => [ 'class' => EmptyBagOStuff::class ]
] );
+ MediaWiki\MediaWikiServices::disableStorageBackend();
+
$this->assertInstanceOf(
EmptyBagOStuff::class,
ObjectCache::newAnything( [] ),
/** @covers ObjectCache::newAnything */
public function testNewAnythingNothingNoDb() {
- $this->overrideMwServices();
MediaWiki\MediaWikiServices::disableStorageBackend();
$this->assertInstanceOf(
$this->tablesUsed += $this->getMcrTablesToReset();
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->setMwGlobals( 'wgContentHandlerUseDB', $this->getContentHandlerUseDB() );
- $this->setMwGlobals(
- 'wgMultiContentRevisionSchemaMigrationStage',
- $this->getMcrMigrationStage()
- );
- $this->overrideMwServices();
+ $this->setMwGlobals( [
+ 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
+ 'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
+ 'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
+ ] );
// First create our dummy page
$page = Title::newFromText( 'PageArchiveTest_thePage' );
$this->getMcrMigrationStage()
);
$this->pagesToDelete = [];
-
- $this->overrideMwServices();
}
protected function tearDown() {
* @coversNothing
*/
public function testServiceWiring() {
- $this->overrideMwServices();
-
$ranHook = 0;
$this->setMwGlobals( 'wgHooks', [
'ResourceLoaderRegisterModules' => [
$this->originalHandlers = TestingAccessWrapper::newFromClass( Hooks::class )->handlers;
TestingAccessWrapper::newFromClass( Hooks::class )->handlers = [];
-
- $this->overrideMwServices();
}
public function tearDown() {
public function testRcHidemyselfFilter() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
public function testRcHidebyothersFilter() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
public function testFilterUserExpLevelAllExperienceLevels() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$this->assertConditions(
[
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$this->assertConditions(
[
public function testFilterUserExpLevelRegistrered() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$this->assertConditions(
[
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$this->assertConditions(
[
public function testFilterUserExpLevelUnregistrered() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$this->assertConditions(
[
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$this->assertConditions(
[
public function testFilterUserExpLevelRegistreredOrLearner() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$this->assertConditions(
[
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$this->assertConditions(
[
public function testFilterUserExpLevelUnregistreredOrExperienced() {
$this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
- $this->overrideMwServices();
$conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
- $this->overrideMwServices();
$conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
$count++;
}
] ] );
- $this->overrideMwServices();
$spf = MediaWikiServices::getInstance()->getSpecialPageFactory();
$spf->getNames();
$spf->getNames();
*/
public function testGetPage( $spec, $shouldReuseInstance ) {
$this->mergeMwGlobalArrayValue( 'wgSpecialPages', [ 'testdummy' => $spec ] );
- $this->overrideMwServices();
$page = SpecialPageFactory::getPage( 'testdummy' );
$this->assertInstanceOf( SpecialPage::class, $page );
*/
public function testGetNames() {
$this->mergeMwGlobalArrayValue( 'wgSpecialPages', [ 'testdummy' => SpecialAllPages::class ] );
- $this->overrideMwServices();
$names = SpecialPageFactory::getNames();
$this->assertInternalType( 'array', $names );
*/
public function testResolveAlias() {
$this->setContentLang( 'de' );
- $this->overrideMwServices();
list( $name, $param ) = SpecialPageFactory::resolveAlias( 'Spezialseiten/Foo' );
$this->assertEquals( 'Specialpages', $name );
*/
public function testGetLocalNameFor() {
$this->setContentLang( 'de' );
- $this->overrideMwServices();
$name = SpecialPageFactory::getLocalNameFor( 'Specialpages', 'Foo' );
$this->assertEquals( 'Spezialseiten/Foo', $name );
*/
public function testGetTitleForAlias() {
$this->setContentLang( 'de' );
- $this->overrideMwServices();
$title = SpecialPageFactory::getTitleForAlias( 'Specialpages/Foo' );
$this->assertEquals( 'Spezialseiten/Foo', $title->getText() );
$this->setMwGlobals( 'wgSpecialPages',
array_combine( array_keys( $aliasesList ), array_keys( $aliasesList ) )
);
- $this->overrideMwServices();
$this->setContentLang( $lang );
// Catch the warnings we expect to be raised
}
],
] );
- $this->overrideMwServices();
SpecialPageFactory::getLocalNameFor( 'Specialpages' );
$this->assertTrue( $called, 'Recursive call succeeded' );
}
'wgRestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
'wgAutopromote' => []
] );
- $this->resetServices();
$obj = $this->newObj();
$user = is_null( $groups ) ? null : $this->getTestUser( $groups )->getUser();
$this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
$this->userTester->addGroup( 'unittesters' );
$this->expiryTime = wfTimestamp( TS_MW, time() + 100500 );
$this->userTester->addGroup( 'testwriters', $this->expiryTime );
-
- $this->resetServices();
}
/**
'wgRevokePermissions' => [],
'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
- $this->overrideMwServices();
$this->setUpPermissionGlobals();
RequestContext::getMain()->setRequest( $request );
TestingAccessWrapper::newFromObject( $user )->mRequest = $request;
$request->getSession()->setUser( $user );
- $this->overrideMwServices();
}
/**
$rights = array_diff( $rights, [ 'writetest' ] );
} );
- $this->resetServices();
$rights = $user->getRights();
$this->assertContains( 'test', $rights );
$this->assertContains( 'runtest', $rights );
$this->setMwGlobals( [
'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
] );
- $this->overrideMwServices();
$domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
$this->hideDeprecated( 'User::selectFields' );
// the actual test: change config, reset services.
$this->setMwGlobals( 'wgLanguageCode', 'qqx' );
- $this->resetServices();
// the overridden service instance should still be there
$this->assertSame( $myReadOnlyMode, $services->getService( 'ReadOnlyMode' ) );