* (T140807) The wgResourceLoaderLESSImportPaths configuration option was removed
from ResourceLoader. Instead, use `@import` statements in LESS to import
files directly from nearby directories within the same project.
+* (T140804) The wgResourceLoaderLESSVars configuration option, deprecated
+ since 1.30, was removed. Instead, to expose variables from PHP to LESS, use
+ the ResourceLoaderModule::getLessVars() method.
* The protected methods PHPSessionHandler::returnSuccess() and returnFailure(),
only needed for PHP5 compatibility, have been removed. It now uses the boolean
values `true` and `false` respectively.
"type": "object",
"description": "ResourceLoader sources to register"
},
- "ResourceLoaderLESSVars": {
- "type": "object",
- "description": "ResourceLoader LESS variables"
- },
"ConfigRegistry": {
"type": "object",
"description": "Registry of factory functions to create Config objects"
"type": "object",
"description": "ResourceLoader sources to register"
},
- "ResourceLoaderLESSVars": {
- "type": "object",
- "description": "ResourceLoader LESS variables"
- },
"ConfigRegistry": {
"type": "object",
"description": "Registry of factory functions to create Config objects"
*/
$wgResourceLoaderValidateStaticJS = false;
-/**
- * Global LESS variables. An associative array binding variable names to
- * LESS code snippets representing their values.
- *
- * Adding an item here is equivalent to writing `@variable: value;`
- * at the beginning of all your .less files, with all the consequences.
- * In particular, string values must be escaped and quoted.
- *
- * Changes to this configuration do NOT trigger cache invalidation.
- *
- * @par Example:
- * @code
- * $wgResourceLoaderLESSVars = [
- * 'exampleFontSize' => '1em',
- * 'exampleBlue' => '#36c',
- * ];
- * @endcode
- * @since 1.22
- * @deprecated since 1.30 Use ResourceLoaderModule::getLessVars() instead to
- * add variables to individual modules that need them.
- */
-$wgResourceLoaderLESSVars = [
- /**
- * Minimum available screen width at which a device can be considered a tablet
- * The number is currently based on the device width of a Samsung Galaxy S5 mini and is low
- * enough to cover iPad (768px). Number is prone to change with new information.
- * @since 1.27
- * @deprecated 1.31 Use mediawiki.ui/variables instead
- */
- 'deviceWidthTablet' => '720px',
-];
-
/**
* Whether ResourceLoader should attempt to persist modules in localStorage on
* browsers that support the Web Storage API.
* @return bool True on success
*/
private function secureAndSplit() {
- # Initialisation
- $this->mInterwiki = '';
- $this->mFragment = '';
- $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
-
- $dbkey = $this->mDbkeyform;
-
// @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
// the parsing code with Title, while avoiding massive refactoring.
// @todo: get rid of secureAndSplit, refactor parsing code.
// splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
$titleCodec = MediaWikiServices::getInstance()->getTitleParser();
// MalformedTitleException can be thrown here
- $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
+ $parts = $titleCodec->splitTitleString( $this->mDbkeyform, $this->getDefaultNamespace() );
# Fill fields
$this->setFragment( '#' . $parts['fragment'] );
public function appendExtensionTags( $property ) {
global $wgParser;
- $wgParser->firstCallInit();
$tags = array_map(
function ( $item ) {
return "<$item>";
public function appendFunctionHooks( $property ) {
global $wgParser;
- $wgParser->firstCallInit();
$hooks = $wgParser->getFunctionHooks();
ApiResult::setArrayType( $hooks, 'BCarray' );
ApiResult::setIndexedTagName( $hooks, 'h' );
* @return string|bool Database domain ID or false
*/
private function getDomainId( array $server ) {
+ if ( isset( $this->params['wiki'] ) ) {
+ return $this->params['wiki']; // explicit domain
+ }
+
if ( isset( $server['dbname'] ) ) {
// T200471: for b/c, treat any "dbname" field as forcing which database to use.
// MediaWiki/LoadBalancer previously did not enforce any concept of a local DB
return $domain->getId();
}
- return $this->params['wiki'] ?? false; // local domain unless explictly given
+ return false; // local LB domain
}
/**
const TYPE_LITERAL = 117; // all literals, identifiers and unrecognised tokens
const ACTION_GOTO = 201;
+ const ACTION_PUSH = 202;
+ const ACTION_POP = 203;
// Sanity limit to avoid excessive memory usage
const STACK_LIMIT = 1000;
// $model : This is the main table for our state machine. For every state/token pair
// the desired action is defined.
+ //
+ // The state pushed onto the stack by ACTION_PUSH will be returned to by ACTION_POP.
+ //
+ // A given state/token pair MAY NOT specify both ACTION_POP and ACTION_GOTO.
+ // In the event of such mistake, ACTION_POP is used instead of ACTION_GOTO.
$model = [
// Statement - This is the initial state.
self::STATEMENT => [
self::TYPE_ADD_OP => [
self::ACTION_GOTO => self::EXPRESSION,
],
+ self::TYPE_BRACE_OPEN => [
+ // Use of '{' in statement context, creates a Block.
+ self::ACTION_PUSH => self::STATEMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ // Ends a Block
+ self::ACTION_POP => true,
+ ],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_RETURN => [
],
self::CONDITION => [
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::STATEMENT,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
],
self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
],
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_ASSIGNMENT,
self::ACTION_GOTO => self::STATEMENT,
],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION => [
self::TYPE_SEMICOLON => [
self::ACTION_GOTO => self::STATEMENT,
],
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_FUNC => [
self::ACTION_GOTO => self::STATEMENT,
],
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_FUNC => [
self::ACTION_GOTO => self::EXPRESSION,
],
self::TYPE_HOOK => [
+ self::ACTION_PUSH => self::EXPRESSION,
self::ACTION_GOTO => self::EXPRESSION_TERNARY,
],
self::TYPE_COLON => [
self::ACTION_GOTO => self::STATEMENT,
],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION_FUNC => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
self::ACTION_GOTO => self::STATEMENT,
],
],
self::EXPRESSION_TERNARY => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_FUNC => [
self::ACTION_GOTO => self::EXPRESSION_TERNARY,
],
self::TYPE_HOOK => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY,
self::ACTION_GOTO => self::EXPRESSION_TERNARY,
],
self::TYPE_COMMA => [
self::ACTION_GOTO => self::EXPRESSION_TERNARY,
],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
+ self::TYPE_COLON => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION_TERNARY_FUNC => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
self::ACTION_GOTO => self::STATEMENT,
],
],
self::PAREN_EXPRESSION => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
+ self::TYPE_PAREN_CLOSE => [
+ self::ACTION_POP => true,
+ ],
self::TYPE_FUNC => [
self::ACTION_GOTO => self::PAREN_EXPRESSION_FUNC,
],
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
+ self::TYPE_PAREN_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::PAREN_EXPRESSION_FUNC => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
self::ACTION_GOTO => self::STATEMENT,
],
],
self::PROPERTY_EXPRESSION => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
self::TYPE_FUNC => [
self::TYPE_COMMA => [
self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
],
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
self::ACTION_GOTO => self::PAREN_EXPRESSION,
],
],
self::PROPERTY_EXPRESSION_FUNC => [
self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
self::ACTION_GOTO => self::STATEMENT,
],
- ]
- ];
-
- // $push : This table contains the rules for when to push a state onto the stack.
- // The pushed state is the state to return to when the corresponding
- // closing token is found
- $push = [
- self::STATEMENT => [
- self::TYPE_BRACE_OPEN => self::STATEMENT,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
- ],
- self::CONDITION => [
- self::TYPE_PAREN_OPEN => self::STATEMENT
- ],
- self::PROPERTY_ASSIGNMENT => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT
- ],
- self::EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
- ],
- self::EXPRESSION_NO_NL => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
- ],
- self::EXPRESSION_OP => [
- self::TYPE_HOOK => self::EXPRESSION,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
- ],
- self::EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP
- ],
- self::EXPRESSION_TERNARY => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
- ],
- self::EXPRESSION_TERNARY_OP => [
- self::TYPE_HOOK => self::EXPRESSION_TERNARY,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
- ],
- self::EXPRESSION_TERNARY_FUNC => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP
- ],
- self::PAREN_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
],
- self::PAREN_EXPRESSION_OP => [
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
- ],
- self::PAREN_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP
- ],
- self::PROPERTY_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
- ],
- self::PROPERTY_EXPRESSION_OP => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
- ],
- self::PROPERTY_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP
- ]
- ];
-
- // $pop : Rules for when to pop a state from the stack
- $pop = [
- self::STATEMENT => [ self::TYPE_BRACE_CLOSE => true ],
- self::PROPERTY_ASSIGNMENT => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_NO_NL => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_OP => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_TERNARY_OP => [ self::TYPE_COLON => true ],
- self::PAREN_EXPRESSION => [ self::TYPE_PAREN_CLOSE => true ],
- self::PAREN_EXPRESSION_OP => [ self::TYPE_PAREN_CLOSE => true ],
- self::PROPERTY_EXPRESSION => [ self::TYPE_BRACE_CLOSE => true ],
- self::PROPERTY_EXPRESSION_OP => [ self::TYPE_BRACE_CLOSE => true ]
];
// $semicolon : Rules for when a semicolon insertion is appropriate
$semicolon = [
self::EXPRESSION_NO_NL => [
- self::TYPE_UN_OP => true,
- self::TYPE_INCR_OP => true,
- self::TYPE_ADD_OP => true,
+ self::TYPE_UN_OP => true,
+ self::TYPE_INCR_OP => true,
+ self::TYPE_ADD_OP => true,
self::TYPE_BRACE_OPEN => true,
self::TYPE_PAREN_OPEN => true,
- self::TYPE_RETURN => true,
- self::TYPE_IF => true,
- self::TYPE_DO => true,
- self::TYPE_FUNC => true,
- self::TYPE_LITERAL => true
+ self::TYPE_RETURN => true,
+ self::TYPE_IF => true,
+ self::TYPE_DO => true,
+ self::TYPE_FUNC => true,
+ self::TYPE_LITERAL => true
],
self::EXPRESSION_OP => [
- self::TYPE_UN_OP => true,
- self::TYPE_INCR_OP => true,
+ self::TYPE_UN_OP => true,
+ self::TYPE_INCR_OP => true,
self::TYPE_BRACE_OPEN => true,
- self::TYPE_RETURN => true,
- self::TYPE_IF => true,
- self::TYPE_DO => true,
- self::TYPE_FUNC => true,
- self::TYPE_LITERAL => true
+ self::TYPE_RETURN => true,
+ self::TYPE_IF => true,
+ self::TYPE_DO => true,
+ self::TYPE_FUNC => true,
+ self::TYPE_LITERAL => true
]
];
$newlineFound = false;
// Now that we have output our token, transition into the new state.
- if ( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) {
- $stack[] = $push[$state][$type];
+ if ( isset( $model[$state][$type][self::ACTION_PUSH] ) &&
+ count( $stack ) < self::STACK_LIMIT
+ ) {
+ $stack[] = $model[$state][$type][self::ACTION_PUSH];
}
- if ( $stack && isset( $pop[$state][$type] ) ) {
+ if ( $stack && isset( $model[$state][$type][self::ACTION_POP] ) ) {
$state = array_pop( $stack );
} elseif ( isset( $model[$state][$type][self::ACTION_GOTO] ) ) {
$state = $model[$state][$type][self::ACTION_GOTO];
$conn = $this->openForeignConnection( $i, $domain, $flags );
} else {
// Connection is to the local domain
- $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
- if ( isset( $this->conns[$connKey][$i][0] ) ) {
- $conn = $this->conns[$connKey][$i][0];
- } else {
- if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
- throw new InvalidArgumentException( "No server with index '$i'." );
- }
- // Open a new connection
- $server = $this->servers[$i];
- $server['serverIndex'] = $i;
- $server['autoCommitOnly'] = $autoCommit;
- if ( $this->localDomain->getDatabase() !== null ) {
- // Use the local domain table prefix if the local domain is specified
- $server['tablePrefix'] = $this->localDomain->getTablePrefix();
- }
- $conn = $this->reallyOpenConnection( $server, $this->localDomain );
- $host = $this->getServerName( $i );
- if ( $conn->isOpen() ) {
- $this->connLogger->debug(
- __METHOD__ . ": connected to database $i at '$host'." );
- $this->conns[$connKey][$i][0] = $conn;
- } else {
- $this->connLogger->warning(
- __METHOD__ . ": failed to connect to database $i at '$host'." );
- $this->errorConnection = $conn;
- $conn = false;
- }
- }
+ $conn = $this->openLocalConnection( $i, $flags );
}
if ( $conn instanceof IDatabase && !$conn->isOpen() ) {
return $conn;
}
+ /**
+ * Open a connection to a local DB, or return one if it is already open.
+ *
+ * On error, returns false, and the connection which caused the
+ * error will be available via $this->errorConnection.
+ *
+ * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
+ *
+ * @param int $i Server index
+ * @param int $flags Class CONN_* constant bitfield
+ * @return Database
+ */
+ private function openLocalConnection( $i, $flags = 0 ) {
+ $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
+
+ $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
+ if ( isset( $this->conns[$connKey][$i][0] ) ) {
+ $conn = $this->conns[$connKey][$i][0];
+ } else {
+ if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
+ throw new InvalidArgumentException( "No server with index '$i'." );
+ }
+ // Open a new connection
+ $server = $this->servers[$i];
+ $server['serverIndex'] = $i;
+ $server['autoCommitOnly'] = $autoCommit;
+ if ( $this->localDomain->getDatabase() !== null ) {
+ // Use the local domain table prefix if the local domain is specified
+ $server['tablePrefix'] = $this->localDomain->getTablePrefix();
+ }
+ $conn = $this->reallyOpenConnection( $server, $this->localDomain );
+ $host = $this->getServerName( $i );
+ if ( $conn->isOpen() ) {
+ $this->connLogger->debug(
+ __METHOD__ . ": connected to database $i at '$host'." );
+ $this->conns[$connKey][$i][0] = $conn;
+ } else {
+ $this->connLogger->warning(
+ __METHOD__ . ": failed to connect to database $i at '$host'." );
+ $this->errorConnection = $conn;
+ $conn = false;
+ }
+ }
+
+ return $conn;
+ }
+
/**
* Open a connection to a foreign DB, or return one if it is already open.
*
* @return array
*/
public function getFunctionHooks() {
+ $this->firstCallInit();
return array_keys( $this->mFunctionHooks );
}
* @return array
*/
public function getTags() {
+ $this->firstCallInit();
return array_merge(
array_keys( $this->mTransparentTagHooks ),
array_keys( $this->mTagHooks ),
'RecentChangesFlags',
'RemoveCredentialsBlacklist',
'RemoveGroups',
- 'ResourceLoaderLESSVars',
'ResourceLoaderSources',
'RevokePermissions',
'SessionProviders',
/** @var bool */
protected static $debugMode = null;
- /** @var array */
- private $lessVars = null;
-
/**
* Module name/ResourceLoaderModule object pairs
* @var array
* Get global LESS variables.
*
* @since 1.27
+ * @deprecated since 1.32 Use ResourceLoderModule::getLessVars() instead.
* @return array Map of variable names to string CSS values.
*/
public function getLessVars() {
- if ( $this->lessVars === null ) {
- $this->lessVars = $this->config->get( 'ResourceLoaderLESSVars' );
- }
- return $this->lessVars;
+ return [];
}
}
$cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
}
- $vars = array_merge(
- $context->getResourceLoader()->getLessVars(),
- $this->getLessVars( $context )
- );
+ $vars = $this->getLessVars( $context );
// Construct a cache key from the LESS file name, and a hash digest
// of the LESS variables used for compilation.
ksort( $vars );
'u' => true,
];
+ /** @var Serializer */
+ private $serializer;
+
+ /** @var bool */
+ private $trace;
+
/**
* @param Serializer $serializer
+ * @param bool $trace
*/
- public function __construct( Serializer $serializer ) {
+ public function __construct( Serializer $serializer, $trace = false ) {
$this->serializer = $serializer;
+ $this->trace = $trace;
}
public function startDocument( $fragmentNamespace, $fragmentName ) {
}
private function trace( $msg ) {
- // echo "[RCM] $msg\n";
+ if ( $this->trace ) {
+ wfDebug( "[RCM] $msg" );
+ }
}
/**
) {
list( $parent, $newRef ) = $this->getParentForInsert( $preposition, $refElement );
$parentData = $parent->snData;
- $parentNs = $parent->namespace;
- $parentName = $parent->name;
$elementName = $element->htmlName;
$inline = isset( self::$onlyInlineElements[$elementName] );
$under = $preposition === TreeBuilder::UNDER;
+ $elementToEnd = null;
if ( $under && $parentData->isPWrapper && !$inline ) {
// [B/b] The element is non-inline and the parent is a p-wrapper,
$root = $serializer->getRootNode();
$nodes = [];
$removableNodes = [];
- $haveContent = false;
while ( $node !== $cloneEnd ) {
$nextParent = $serializer->getParentNode( $node );
if ( $nextParent === $root ) {
namespace MediaWiki\Tidy;
use RemexHtml\Serializer\Serializer;
+use RemexHtml\Serializer\SerializerWithTracer;
use RemexHtml\Tokenizer\Tokenizer;
use RemexHtml\TreeBuilder\Dispatcher;
use RemexHtml\TreeBuilder\TreeBuilder;
use RemexHtml\TreeBuilder\TreeMutationTracer;
class RemexDriver extends TidyDriverBase {
- private $trace;
+ private $treeMutationTrace;
+ private $serializerTrace;
+ private $mungerTrace;
private $pwrap;
public function __construct( array $config ) {
$config += [
'treeMutationTrace' => false,
+ 'serializerTrace' => false,
+ 'mungerTrace' => false,
'pwrap' => true
];
- $this->trace = $config['treeMutationTrace'];
+ $this->treeMutationTrace = $config['treeMutationTrace'];
+ $this->serializerTrace = $config['serializerTrace'];
+ $this->mungerTrace = $config['mungerTrace'];
$this->pwrap = $config['pwrap'];
parent::__construct( $config );
}
public function tidy( $text ) {
+ $traceCallback = function ( $msg ) {
+ wfDebug( "RemexHtml: $msg" );
+ };
+
$formatter = new RemexCompatFormatter;
- $serializer = new Serializer( $formatter );
+ if ( $this->serializerTrace ) {
+ $serializer = new SerializerWithTracer( $formatter, null, $traceCallback );
+ } else {
+ $serializer = new Serializer( $formatter );
+ }
if ( $this->pwrap ) {
- $munger = new RemexCompatMunger( $serializer );
+ $munger = new RemexCompatMunger( $serializer, $this->mungerTrace );
} else {
$munger = $serializer;
}
- if ( $this->trace ) {
- $tracer = new TreeMutationTracer( $munger, function ( $msg ) {
- wfDebug( "RemexHtml: $msg" );
- } );
+ if ( $this->treeMutationTrace ) {
+ $tracer = new TreeMutationTracer( $munger, $traceCallback );
} else {
$tracer = $munger;
}
'ignoreNulls' => true,
'skipPreprocess' => true,
] );
+
$tokenizer->execute( [
'fragmentNamespace' => \RemexHtml\HTMLData::NS_HTML,
'fragmentName' => 'body'
* @throws MalformedTitleException
* @return TitleValue
*/
- public function parseTitle( $text, $defaultNamespace ) {
+ public function parseTitle( $text, $defaultNamespace = NS_MAIN ) {
// NOTE: this is an ugly cludge that allows this class to share the
// code for parsing with the old Title class. The parser code should
// be refactored to avoid this.
* @throws MalformedTitleException If the text is not a valid representation of a page title.
* @return TitleValue
*/
- public function parseTitle( $text, $defaultNamespace );
+ public function parseTitle( $text, $defaultNamespace = NS_MAIN );
}
*/
use MediaWiki\Linker\LinkTarget;
use Wikimedia\Assert\Assert;
+use Wikimedia\Assert\ParameterTypeException;
/**
* Represents a page (or page fragment) title within MediaWiki.
* @throws InvalidArgumentException
*/
public function __construct( $namespace, $dbkey, $fragment = '', $interwiki = '' ) {
- Assert::parameterType( 'integer', $namespace, '$namespace' );
- Assert::parameterType( 'string', $dbkey, '$dbkey' );
- Assert::parameterType( 'string', $fragment, '$fragment' );
- Assert::parameterType( 'string', $interwiki, '$interwiki' );
+ if ( !is_int( $namespace ) ) {
+ throw new ParameterTypeException( '$namespace', 'int' );
+ }
+ if ( !is_string( $dbkey ) ) {
+ throw new ParameterTypeException( '$dbkey', 'string' );
+ }
+ if ( !is_string( $fragment ) ) {
+ throw new ParameterTypeException( '$fragment', 'string' );
+ }
+ if ( !is_string( $interwiki ) ) {
+ throw new ParameterTypeException( '$interwiki', 'string' );
+ }
// Sanity check, no full validation or normalization applied here!
Assert::parameter( !preg_match( '/^_|[ \r\n\t]|_$/', $dbkey ), '$dbkey',
false,
true
);
+ $this->addOption( 'populate-only', 'Do not update change_tag_def table' );
}
public function execute() {
$this->countDown( 5 );
if ( $wgChangeTagsSchemaMigrationStage < MIGRATION_NEW ) {
- $this->updateCountTag();
+ if ( !$this->hasOption( 'populate-only' ) ) {
+ $this->updateCountTag();
+ }
$this->backpopulateChangeTagId();
} else {
$this->updateCountTagId();
'Reuse content table rows when the address and model are the same. '
. 'This will increase the script\'s time and memory usage, perhaps significantly.',
false, false );
+ $this->addOption( 'start-revision', 'The rev_id to start at', false, true );
+ $this->addOption( 'start-archive', 'The ar_rev_id to start at', false, true );
$this->setBatchSize( 500 );
}
'slots' => [ 'LEFT JOIN', 'rev_id=slot_revision_id' ],
'page' => [ 'LEFT JOIN', 'rev_page=page_id' ],
];
+ $startOption = 'start-revision';
} else {
$idField = 'ar_rev_id';
$tables = [ 'archive', 'slots' ];
$joins = [
'slots' => [ 'LEFT JOIN', 'ar_rev_id=slot_revision_id' ],
];
+ $startOption = 'start-archive';
}
$minmax = $this->dbw->selectRow(
'',
__METHOD__
);
+ if ( $this->hasOption( $startOption ) ) {
+ $minmax->min = (int)$this->getOption( $startOption );
+ }
if ( !$minmax || !is_numeric( $minmax->min ) || !is_numeric( $minmax->max ) ) {
// No rows?
$minmax = (object)[ 'min' => 1, 'max' => 0 ];
&.oo-ui-widget-enabled {
.mw-widgets-datetime-dateTimeInputWidget-handle {
- .oo-ui-transition( border-color @transition-medium );
+ .oo-ui-transition( border-color @transition-ease-medium );
&:hover {
border-color: @border-color-input--hover;
@opacity-base--disabled: 0.51;
@transition-base: @transition-duration-base;
-@transition-medium: @transition-duration-medium;
+@transition-ease-medium: @transition-duration-medium;
// Transitions > Durations
@transition-duration-base: 100ms;
@transition-duration-medium: 250ms;
*/
public function testIsValidMerge( $source, $dest, $timestamp, $error ) {
$this->setMwGlobals( 'wgContentHandlerUseDB', false );
+ if ( $timestamp === true ) {
+ // Although this timestamp is after the latest timestamp of both pages,
+ // MergeHistory should select the latest source timestamp up to this which should
+ // still work for the merge.
+ $timestamp = time() + ( 24 * 3600 );
+ }
$mh = new MergeHistory(
Title::newFromText( $source ),
Title::newFromText( $dest ),
return [
// for MergeHistory::isValidMerge
[ 'Test', 'Test2', false, true ],
- // Although this timestamp is after the latest timestamp of both pages,
- // MergeHistory should select the latest source timestamp up to this which should
- // still work for the merge.
- [ 'Test', 'Test2', strtotime( 'tomorrow' ), true ],
+ // Timestamp of `true` is a placeholder for "in the future""
+ [ 'Test', 'Test2', true, true ],
[ 'Test', 'Test', false, 'mergehistory-fail-self-merge' ],
[ 'Nonexistant', 'Test2', false, 'mergehistory-fail-invalid-source' ],
[ 'Test', 'Nonexistant', false, 'mergehistory-fail-invalid-dest' ],
public function testExtensionTags() {
global $wgParser;
- $wgParser->firstCallInit();
$expected = array_map(
function ( $tag ) {
return "<$tag>";
public function testFunctionHooks() {
global $wgParser;
- $wgParser->firstCallInit();
$this->assertSame( $wgParser->getFunctionHooks(), $this->doQuery( 'functionhooks' ) );
}
);
}
+ /**
+ * @covers ResourceLoaderFileModule::compileLessFile
+ */
+ public function testLessFileCompilation() {
+ $context = $this->getResourceLoaderContext();
+ $basePath = __DIR__ . '/../../data/less/module';
+ $module = new ResourceLoaderFileTestModule( [
+ 'localBasePath' => $basePath,
+ 'styles' => [ 'styles.less' ],
+ ], [
+ 'lessVars' => [ 'foo' => '2px', 'Foo' => '#eeeeee' ]
+ ] );
+ $module->setName( 'test.less' );
+ $styles = $module->getStyles( $context );
+ $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
+ }
+
/**
* @covers ResourceLoaderFileModule::getDefinitionSummary
* @covers ResourceLoaderFileModule::getFileHashes
parent::setUp();
$this->setMwGlobals( [
- 'wgResourceLoaderLESSVars' => [
- 'foo' => '2px',
- 'Foo' => '#eeeeee',
- 'bar' => 5,
- ],
- // Clear ResourceLoaderGetConfigVars hooks (called by StartupModule)
- // to avoid notices during testMakeModuleResponse for missing
- // wgResourceLoaderLESSVars keys in extension hooks.
- 'wgHooks' => [],
'wgShowExceptionDetails' => true,
] );
}
);
}
- /**
- * @covers ResourceLoaderFileModule::compileLessFile
- */
- public function testLessFileCompilation() {
- $context = $this->getResourceLoaderContext();
- $basePath = __DIR__ . '/../../data/less/module';
- $module = new ResourceLoaderFileModule( [
- 'localBasePath' => $basePath,
- 'styles' => [ 'styles.less' ],
- ] );
- $module->setName( 'test.less' );
- $styles = $module->getStyles( $context );
- $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
- }
-
/**
* @covers ResourceLoader::getLessCompiler
*/
public function testLessImportDirs() {
$rl = new EmptyResourceLoader();
- $lc = $rl->getLessCompiler( $rl->getLessVars() );
+ $lc = $rl->getLessCompiler( [ 'foo' => '2px', 'Foo' => '#eeeeee' ] );
$basePath = dirname( dirname( __DIR__ ) ) . '/data/less';
$lc->SetImportDirs( [
"$basePath/common" => '',