"disallowKeywordsOnNewLine": null,
"disallowQuotedKeysInObjects": null,
+ "disallowImplicitTypeConversion": null,
+ "requireLineBreakAfterVariableAssignment": null,
+ "requireSpaceAfterLineComment": null,
+ "requireSpacesInsideParentheses": null,
"requireSpacesInsideArrayBrackets": null,
"validateIndentation": null
}
'MessageBlobStore' => __DIR__ . '/includes/MessageBlobStore.php',
'MessageCache' => __DIR__ . '/includes/cache/MessageCache.php',
'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
+ 'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
'MigrateUserGroup' => __DIR__ . '/maintenance/migrateUserGroup.php',
'MimeMagic' => __DIR__ . '/includes/MimeMagic.php',
'MinifyScript' => __DIR__ . '/maintenance/minify.php',
'StatCounter' => __DIR__ . '/includes/StatCounter.php',
'StatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
'Status' => __DIR__ . '/includes/Status.php',
+ 'StatusValue' => __DIR__ . '/includes/libs/StatusValue.php',
'StorageTypeStats' => __DIR__ . '/maintenance/storage/storageTypeStats.php',
'StoreFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
'StreamFile' => __DIR__ . '/includes/StreamFile.php',
},
"require-dev": {
"justinrainbow/json-schema": "~1.3",
- "phpunit/phpunit": "*"
+ "phpunit/phpunit": "~4.5"
},
"suggest": {
"ext-fileinfo": "*",
"Unlicense"
]
},
+ "ResourceFileModulePaths": {
+ "type": "object",
+ "description": "Default paths to use for all ResourceLoader file modules",
+ "additionalProperties": false,
+ "properties": {
+ "localBasePath": {
+ "type": "string",
+ "description": "Base path to prepend to all local paths, relative to current directory"
+ },
+ "remoteExtPath": {
+ "type": "string",
+ "description": "Base path to prepend to all remote paths, relative to $wgExtensionAssetsPath"
+ },
+ "remoteSkinPath": {
+ "type": "string",
+ "description": "Base path to prepend to all remote paths, relative to $wgStylePath"
+ }
+ }
+ },
"ResourceLoaderModules": {
"type": "object",
"description": "ResourceLoader modules to register",
header( 'Cache-Control: no-cache' );
header( 'Content-Type: text/html; charset=utf-8' );
echo <<<ENDS
+<!DOCTYPE html>
<html>
+<head>
+<meta charset="UTF-8" />
+<title>$msgHdr</title>
+</head>
<body>
<h1>$msgHdr</h1>
<p>$detailMsg</p>
*
* @since 1.17
*/
-class Message {
+class Message implements MessageSpecifier {
/**
* In which language to get this message. True, which is the default,
* Returns the message key.
*
* If a list of multiple possible keys was supplied to the constructor, this method may
- * return any of these keys. After the message ahs been fetched, this method will return
+ * return any of these keys. After the message has been fetched, this method will return
* the key that was actually used to fetch the message.
*
* @since 1.21
* so that a lack of error-handling will be explicit.
*/
class Status {
- /** @var bool */
- public $ok = true;
+ /** @var StatusValue */
+ protected $sv;
/** @var mixed */
public $value;
-
- /** Counters for batch operations */
- /** @var int */
+ /** @var array Map of (key => bool) to indicate success of each part of batch operations */
+ public $success = array();
+ /** @var int Counter for batch operations */
public $successCount = 0;
-
- /** @var int */
+ /** @var int Counter for batch operations */
public $failCount = 0;
- /** Array to indicate which items of the batch operations were successful */
- /** @var array */
- public $success = array();
-
- /** @var array */
- public $errors = array();
-
/** @var callable */
public $cleanCallback = false;
+ /**
+ * @param StatusValue $sv [optional]
+ */
+ public function __construct( StatusValue $sv = null ) {
+ $this->sv = ( $sv === null ) ? new StatusValue() : $sv;
+ // B/C field aliases
+ $this->value =& $this->sv->value;
+ $this->successCount =& $this->sv->successCount;
+ $this->failCount =& $this->sv->failCount;
+ $this->success =& $this->sv->success;
+ }
+
+ /**
+ * Succinct helper method to wrap a StatusValue
+ *
+ * This is is useful when formatting StatusValue objects:
+ * <code>
+ * $this->getOutput()->addHtml( Status::wrap( $sv )->getHTML() );
+ * </code>
+ *
+ * @param StatusValue|Status $sv
+ * @return Status
+ */
+ public static function wrap( $sv ) {
+ return $sv instanceof Status ? $sv : new self( $sv );
+ }
+
/**
* Factory function for fatal errors
*
* @param string|Message $message Message name or object
* @return Status
*/
- static function newFatal( $message /*, parameters...*/ ) {
- $params = func_get_args();
- $result = new self;
- call_user_func_array( array( &$result, 'error' ), $params );
- $result->ok = false;
- return $result;
+ public static function newFatal( $message /*, parameters...*/ ) {
+ return new self( call_user_func_array(
+ array( 'StatusValue', 'newFatal' ), func_get_args()
+ ) );
}
/**
* @param mixed $value
* @return Status
*/
- static function newGood( $value = null ) {
- $result = new self;
- $result->value = $value;
- return $result;
+ public static function newGood( $value = null ) {
+ $sv = new StatusValue();
+ $sv->value = $value;
+
+ return new self( $sv );
}
/**
* @param mixed $value
*/
public function setResult( $ok, $value = null ) {
- $this->ok = $ok;
- $this->value = $value;
+ $this->sv->setResult( $ok, $value );
}
/**
* @return bool
*/
public function isGood() {
- return $this->ok && !$this->errors;
+ return $this->sv->isGood();
}
/**
* @return bool
*/
public function isOK() {
- return $this->ok;
+ return $this->sv->isOK();
}
/**
* @param string|Message $message Message name or object
*/
public function warning( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'warning',
- 'message' => $message,
- 'params' => $params );
+ call_user_func_array( array( $this->sv, 'warning' ), func_get_args() );
}
/**
* @param string|Message $message Message name or object
*/
public function error( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'error',
- 'message' => $message,
- 'params' => $params );
+ call_user_func_array( array( $this->sv, 'error' ), func_get_args() );
}
/**
* @param string|Message $message Message name or object
*/
public function fatal( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'error',
- 'message' => $message,
- 'params' => $params );
- $this->ok = false;
- }
-
- /**
- * Don't save the callback when serializing, because Closures can't be
- * serialized and we're going to clear it in __wakeup anyway.
- */
- public function __sleep() {
- $keys = array_keys( get_object_vars( $this ) );
- return array_diff( $keys, array( 'cleanCallback' ) );
- }
-
- /**
- * Sanitize the callback parameter on wakeup, to avoid arbitrary execution.
- */
- public function __wakeup() {
- $this->cleanCallback = false;
+ call_user_func_array( array( $this->sv, 'fatal' ), func_get_args() );
}
/**
* @param array $params
* @return array
*/
- protected function cleanParams( $params ) {
+ protected function cleanParams( array $params ) {
if ( !$this->cleanCallback ) {
return $params;
}
* @return string
*/
public function getWikiText( $shortContext = false, $longContext = false ) {
- if ( count( $this->errors ) == 0 ) {
- if ( $this->ok ) {
- $this->fatal( 'internalerror_info',
+ $rawErrors = $this->sv->getErrors();
+ if ( count( $rawErrors ) == 0 ) {
+ if ( $this->sv->isOK() ) {
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . " called for a good result, this is incorrect\n" );
} else {
- $this->fatal( 'internalerror_info',
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . ": Invalid result object: no error text but not OK\n" );
}
+ $rawErrors = $this->sv->getErrors(); // just added a fatal
}
- if ( count( $this->errors ) == 1 ) {
- $s = $this->getErrorMessage( $this->errors[0] )->plain();
+ if ( count( $rawErrors ) == 1 ) {
+ $s = $this->getErrorMessage( $rawErrors[0] )->plain();
if ( $shortContext ) {
$s = wfMessage( $shortContext, $s )->plain();
} elseif ( $longContext ) {
$s = wfMessage( $longContext, "* $s\n" )->plain();
}
} else {
- $errors = $this->getErrorMessageArray( $this->errors );
+ $errors = $this->getErrorMessageArray( $rawErrors );
foreach ( $errors as &$error ) {
$error = $error->plain();
}
* @return Message
*/
public function getMessage( $shortContext = false, $longContext = false ) {
- if ( count( $this->errors ) == 0 ) {
- if ( $this->ok ) {
- $this->fatal( 'internalerror_info',
+ $rawErrors = $this->sv->getErrors();
+ if ( count( $rawErrors ) == 0 ) {
+ if ( $this->sv->isOK() ) {
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . " called for a good result, this is incorrect\n" );
} else {
- $this->fatal( 'internalerror_info',
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . ": Invalid result object: no error text but not OK\n" );
}
+ $rawErrors = $this->sv->getErrors(); // just added a fatal
}
- if ( count( $this->errors ) == 1 ) {
- $s = $this->getErrorMessage( $this->errors[0] );
+ if ( count( $rawErrors ) == 1 ) {
+ $s = $this->getErrorMessage( $rawErrors[0] );
if ( $shortContext ) {
$s = wfMessage( $shortContext, $s );
} elseif ( $longContext ) {
$s = wfMessage( $longContext, $wrapper );
}
} else {
- $msgs = $this->getErrorMessageArray( $this->errors );
+ $msgs = $this->getErrorMessageArray( $rawErrors );
$msgCount = count( $msgs );
if ( $shortContext ) {
* @param bool $overwriteValue Whether to override the "value" member
*/
public function merge( $other, $overwriteValue = false ) {
- $this->errors = array_merge( $this->errors, $other->errors );
- $this->ok = $this->ok && $other->ok;
- if ( $overwriteValue ) {
- $this->value = $other->value;
- }
- $this->successCount += $other->successCount;
- $this->failCount += $other->failCount;
+ $this->sv->merge( $other->sv, $overwriteValue );
}
/**
*
* @return array A list in which each entry is an array with a message key as its first element.
* The remaining array elements are the message parameters.
+ * @deprecated 1.25
*/
public function getErrorsArray() {
- return $this->getStatusArray( "error" );
+ return $this->getStatusArray( 'error' );
}
/**
*
* @return array A list in which each entry is an array with a message key as its first element.
* The remaining array elements are the message parameters.
+ * @deprecated 1.25
*/
public function getWarningsArray() {
- return $this->getStatusArray( "warning" );
+ return $this->getStatusArray( 'warning' );
}
/**
* Returns a list of status messages of the given type (or all if false)
+ *
+ * @note: this handles RawMessage poorly
+ *
* @param string $type
* @return array
*/
protected function getStatusArray( $type = false ) {
$result = array();
- foreach ( $this->errors as $error ) {
+
+ foreach ( $this->sv->getErrors() as $error ) {
if ( $type === false || $error['type'] === $type ) {
- if ( $error['message'] instanceof Message ) {
+ if ( $error['message'] instanceof MessageSpecifier ) {
$result[] = array_merge(
array( $error['message']->getKey() ),
$error['message']->getParams()
* @return array
*/
public function getErrorsByType( $type ) {
- $result = array();
- foreach ( $this->errors as $error ) {
- if ( $error['type'] === $type ) {
- $result[] = $error;
- }
- }
- return $result;
+ return $this->sv->getErrorsByType( $type );
}
/**
* @return bool
*/
public function hasMessage( $message ) {
- if ( $message instanceof Message ) {
- $message = $message->getKey();
- }
- foreach ( $this->errors as $error ) {
- if ( $error['message'] instanceof Message
- && $error['message']->getKey() === $message
- ) {
- return true;
- } elseif ( $error['message'] === $message ) {
- return true;
- }
- }
- return false;
+ return $this->sv->hasMessage( $message );
}
/**
* @return bool Return true if the replacement was done, false otherwise.
*/
public function replaceMessage( $source, $dest ) {
- $replaced = false;
- foreach ( $this->errors as $index => $error ) {
- if ( $error['message'] === $source ) {
- $this->errors[$index]['message'] = $dest;
- $replaced = true;
- }
- }
- return $replaced;
+ return $this->sv->replaceMessage( $source, $dest );
}
/**
* @return mixed
*/
public function getValue() {
- return $this->value;
+ return $this->sv->getValue();
}
/**
- * @return string
+ * Backwards compatibility logic
+ *
+ * @param string $name
*/
- public function __toString() {
- $status = $this->isOK() ? "OK" : "Error";
- if ( count( $this->errors ) ) {
- $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way";
- } else {
- $errorcount = "no errors detected";
+ function __get( $name ) {
+ if ( $name === 'ok' ) {
+ return $this->sv->getOK();
+ } elseif ( $name === 'errors' ) {
+ return $this->sv->getErrors();
}
- if ( isset( $this->value ) ) {
- $valstr = gettype( $this->value ) . " value set";
- if ( is_object( $this->value ) ) {
- $valstr .= "\"" . get_class( $this->value ) . "\" instance";
- }
+ throw new Exception( "Cannot get '$name' property." );
+ }
+
+ /**
+ * Backwards compatibility logic
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ function __set( $name, $value ) {
+ if ( $name === 'ok' ) {
+ $this->sv->setOK( $value );
+ } elseif ( !property_exists( $this, $name ) ) {
+ // Caller is using undeclared ad-hoc properties
+ $this->$name = $value;
} else {
- $valstr = "no value set";
+ throw new Exception( "Cannot set '$name' property." );
}
- $out = sprintf( "<%s, %s, %s>",
- $status,
- $errorcount,
- $valstr
- );
- if ( count( $this->errors ) > 0 ) {
- $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
- $i = 1;
- $out .= "\n";
- $out .= $hdr;
- foreach ( $this->getStatusArray() as $stat ) {
- $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
- $i,
- $stat[0],
- implode( " ", array_slice( $stat, 1 ) )
- );
- $i += 1;
- }
- $out .= $hdr;
- };
- return $out;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return $this->sv->__toString();
+ }
+
+ /**
+ * Don't save the callback when serializing, because Closures can't be
+ * serialized and we're going to clear it in __wakeup anyway.
+ */
+ function __sleep() {
+ $keys = array_keys( get_object_vars( $this ) );
+ return array_diff( $keys, array( 'cleanCallback' ) );
+ }
+
+ /**
+ * Sanitize the callback parameter on wakeup, to avoid arbitrary execution.
+ */
+ function __wakeup() {
+ $this->cleanCallback = false;
}
}
* Create a new fatal error
*
* @param string $message
- * @return FileRepoStatus
+ * @return Status
*/
public function newFatal( $message /*, parameters...*/ ) {
- $params = func_get_args();
- array_unshift( $params, $this );
+ $status = call_user_func_array( array( 'Status', 'newFatal' ), func_get_args() );
+ $status->cleanCallback = $this->getErrorCleanupFunction();
- return call_user_func_array( array( 'FileRepoStatus', 'newFatal' ), $params );
+ return $status;
}
/**
* Create a new good result
*
* @param null|string $value
- * @return FileRepoStatus
+ * @return Status
*/
public function newGood( $value = null ) {
- return FileRepoStatus::newGood( $this, $value );
+ $status = Status::newGood( $this, $value );
+ $status->cleanCallback = $this->getErrorCleanupFunction();
+
+ return $status;
}
/**
/**
* Generic operation result class for FileRepo-related operations
* @ingroup FileRepo
+ * @deprecated 1.25
*/
-class FileRepoStatus extends Status {
- /**
- * Factory function for fatal errors
- *
- * @param FileRepo $repo
- * @return FileRepoStatus
- */
- static function newFatal( $repo /*, parameters...*/ ) {
- $params = array_slice( func_get_args(), 1 );
- $result = new self( $repo );
- call_user_func_array( array( &$result, 'error' ), $params );
- $result->ok = false;
-
- return $result;
- }
-
- /**
- * @param FileRepo|bool $repo Default: false
- * @param mixed $value
- * @return FileRepoStatus
- */
- static function newGood( $repo = false, $value = null ) {
- $result = new self( $repo );
- $result->value = $value;
-
- return $result;
- }
-
- /**
- * @param bool|FileRepo $repo
- */
- function __construct( $repo = false ) {
- if ( $repo ) {
- $this->cleanCallback = $repo->getErrorCleanupFunction();
- }
- }
-}
+class FileRepoStatus extends Status {}
/**
* Class to handle job queues stored in Redis
*
- * This is faster, less resource intensive, queue that JobQueueDB.
+ * This is a faster and less resource-intensive job queue than JobQueueDB.
* All data for a queue using this class is placed into one redis server.
*
* There are eight main redis keys used to track jobs:
*
* This class requires Redis 2.6 as it makes use Lua scripts for fast atomic operations.
* Additionally, it should be noted that redis has different persistence modes, such
- * as rdb snapshots, journaling, and no persistent. Appropriate configuration should be
+ * as rdb snapshots, journaling, and no persistence. Appropriate configuration should be
* made on the servers based on what queues are using it and what tolerance they have.
*
* @ingroup JobQueue
--- /dev/null
+<?php
+/**
+ * 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
+ */
+
+interface MessageSpecifier {
+ /**
+ * Returns the message key
+ *
+ * If a list of multiple possible keys was supplied to the constructor, this method may
+ * return any of these keys. After the message has been fetched, this method will return
+ * the key that was actually used to fetch the message.
+ *
+ * @return string
+ */
+ public function getKey();
+
+ /**
+ * Returns the message parameters
+ *
+ * @return array
+ */
+ public function getParams();
+}
--- /dev/null
+<?php
+/**
+ * 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
+ */
+
+/**
+ * Generic operation result class
+ * Has warning/error list, boolean status and arbitrary value
+ *
+ * "Good" means the operation was completed with no warnings or errors.
+ *
+ * "OK" means the operation was partially or wholly completed.
+ *
+ * An operation which is not OK should have errors so that the user can be
+ * informed as to what went wrong. Calling the fatal() function sets an error
+ * message and simultaneously switches off the OK flag.
+ *
+ * The recommended pattern for Status objects is to return a StatusValue
+ * unconditionally, i.e. both on success and on failure -- so that the
+ * developer of the calling code is reminded that the function can fail, and
+ * so that a lack of error-handling will be explicit.
+ *
+ * The use of Message objects should be avoided when serializability is needed.
+ *
+ * @since 1.25
+ */
+class StatusValue {
+ /** @var bool */
+ protected $ok = true;
+ /** @var array */
+ protected $errors = array();
+
+ /** @var mixed */
+ public $value;
+ /** @var array Map of (key => bool) to indicate success of each part of batch operations */
+ public $success = array();
+ /** @var int Counter for batch operations */
+ public $successCount = 0;
+ /** @var int Counter for batch operations */
+ public $failCount = 0;
+
+ /**
+ * Factory function for fatal errors
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ * @return Status
+ */
+ public static function newFatal( $message /*, parameters...*/ ) {
+ $params = func_get_args();
+ $result = new static();
+ call_user_func_array( array( &$result, 'fatal' ), $params );
+ return $result;
+ }
+
+ /**
+ * Factory function for good results
+ *
+ * @param mixed $value
+ * @return Status
+ */
+ public static function newGood( $value = null ) {
+ $result = new static();
+ $result->value = $value;
+ return $result;
+ }
+
+ /**
+ * Returns whether the operation completed and didn't have any error or
+ * warnings
+ *
+ * @return bool
+ */
+ public function isGood() {
+ return $this->ok && !$this->errors;
+ }
+
+ /**
+ * Returns whether the operation completed
+ *
+ * @return bool
+ */
+ public function isOK() {
+ return $this->ok;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getValue() {
+ return $this->value;
+ }
+
+ /**
+ * Get the list of errors
+ *
+ * Each error is a (message:string or MessageSpecifier,params:array) map
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Change operation status
+ *
+ * @param bool $ok
+ */
+ public function setOK( $ok ) {
+ $this->ok = $ok;
+ }
+
+ /**
+ * Change operation resuklt
+ *
+ * @param bool $ok Whether the operation completed
+ * @param mixed $value
+ */
+ public function setResult( $ok, $value = null ) {
+ $this->ok = $ok;
+ $this->value = $value;
+ }
+
+ /**
+ * Add a new warning
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function warning( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'warning',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ }
+
+ /**
+ * Add an error, do not set fatal flag
+ * This can be used for non-fatal errors
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function error( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'error',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ }
+
+ /**
+ * Add an error and set OK to false, indicating that the operation
+ * as a whole was fatal
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function fatal( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'error',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ $this->ok = false;
+ }
+
+ /**
+ * Merge another status object into this one
+ *
+ * @param Status $other Other Status object
+ * @param bool $overwriteValue Whether to override the "value" member
+ */
+ public function merge( $other, $overwriteValue = false ) {
+ $this->errors = array_merge( $this->errors, $other->errors );
+ $this->ok = $this->ok && $other->ok;
+ if ( $overwriteValue ) {
+ $this->value = $other->value;
+ }
+ $this->successCount += $other->successCount;
+ $this->failCount += $other->failCount;
+ }
+
+ /**
+ * Returns a list of status messages of the given type
+ *
+ * Each entry is a map of (message:string or MessageSpecifier,params:array))
+ *
+ * @param string $type
+ * @return array
+ */
+ public function getErrorsByType( $type ) {
+ $result = array();
+ foreach ( $this->errors as $error ) {
+ if ( $error['type'] === $type ) {
+ $result[] = $error;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns true if the specified message is present as a warning or error
+ *
+ * @param string|MessageSpecifier $message Message key or object to search for
+ *
+ * @return bool
+ */
+ public function hasMessage( $message ) {
+ if ( $message instanceof MessageSpecifier ) {
+ $message = $message->getKey();
+ }
+ foreach ( $this->errors as $error ) {
+ if ( $error['message'] instanceof MessageSpecifier
+ && $error['message']->getKey() === $message
+ ) {
+ return true;
+ } elseif ( $error['message'] === $message ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * If the specified source message exists, replace it with the specified
+ * destination message, but keep the same parameters as in the original error.
+ *
+ * Note, due to the lack of tools for comparing IStatusMessage objects, this
+ * function will not work when using such an object as the search parameter.
+ *
+ * @param IStatusMessage|string $source Message key or object to search for
+ * @param IStatusMessage|string $dest Replacement message key or object
+ * @return bool Return true if the replacement was done, false otherwise.
+ */
+ public function replaceMessage( $source, $dest ) {
+ $replaced = false;
+
+ foreach ( $this->errors as $index => $error ) {
+ if ( $error['message'] === $source ) {
+ $this->errors[$index]['message'] = $dest;
+ $replaced = true;
+ }
+ }
+
+ return $replaced;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ $status = $this->isOK() ? "OK" : "Error";
+ if ( count( $this->errors ) ) {
+ $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way";
+ } else {
+ $errorcount = "no errors detected";
+ }
+ if ( isset( $this->value ) ) {
+ $valstr = gettype( $this->value ) . " value set";
+ if ( is_object( $this->value ) ) {
+ $valstr .= "\"" . get_class( $this->value ) . "\" instance";
+ }
+ } else {
+ $valstr = "no value set";
+ }
+ $out = sprintf( "<%s, %s, %s>",
+ $status,
+ $errorcount,
+ $valstr
+ );
+ if ( count( $this->errors ) > 0 ) {
+ $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
+ $i = 1;
+ $out .= "\n";
+ $out .= $hdr;
+ foreach ( $this->errors as $error ) {
+ if ( $error['message'] instanceof MessageSpecifier ) {
+ $key = $error['message']->getKey();
+ $params = $error['message']->getParams();
+ } elseif ( $error['params'] ) {
+ $key = $error['message'];
+ $params = $error['params'];
+ } else {
+ $key = $error['message'];
+ $params = array();
+ }
+
+ $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
+ $i,
+ $key,
+ implode( " ", $params )
+ );
+ $i += 1;
+ }
+ $out .= $hdr;
+ }
+
+ return $out;
+ }
+}
}
protected function extractResourceLoaderModules( $dir, array $info ) {
+ $defaultPaths = isset( $info['ResourceFileModulePaths'] )
+ ? $info['ResourceFileModulePaths']
+ : false;
+ if ( isset( $defaultPaths['localBasePath'] ) ) {
+ $defaultPaths['localBasePath'] = "$dir/{$defaultPaths['localBasePath']}";
+ }
+
if ( isset( $info['ResourceModules'] ) ) {
foreach ( $info['ResourceModules'] as $name => $data ) {
if ( isset( $data['localBasePath'] ) ) {
$data['localBasePath'] = "$dir/{$data['localBasePath']}";
}
+ if ( $defaultPaths && !isset( $data['class'] ) ) {
+ $data += $defaultPaths;
+ }
$this->globals['wgResourceModules'][$name] = $data;
}
}
}
if ( $isatty && function_exists( 'readline' ) ) {
- return readline( $prompt );
+ $resp = readline( $prompt );
+ if ( $resp === null ) {
+ // Workaround for https://github.com/facebook/hhvm/issues/4776
+ return false;
+ } else {
+ return $resp;
+ }
} else {
if ( $isatty ) {
$st = self::readlineEmulation( $prompt );
}
protected function handleResourceModules( $realName, $value ) {
+ $defaults = array();
+ $remote = $this->hasOption( 'skin' ) ? 'remoteSkinPath' : 'remoteExtPath';
foreach ( $value as $name => $data ) {
if ( isset( $data['localBasePath'] ) ) {
$data['localBasePath'] = $this->stripPath( $data['localBasePath'], $this->dir );
+ if ( !$defaults ) {
+ $defaults['localBasePath'] = $data['localBasePath'];
+ unset( $data['localBasePath'] );
+ if ( isset( $data[$remote] ) ) {
+ $defaults[$remote] = $data[$remote];
+ unset( $data[$remote] );
+ }
+ } else {
+ if ( $data['localBasePath'] === $defaults['localBasePath'] ) {
+ unset( $data['localBasePath'] );
+ }
+ if ( isset( $data[$remote] ) && isset( $defaults[$remote] )
+ && $data[$remote] === $defaults[$remote]
+ ) {
+ unset( $data[$remote] );
+ }
+ }
}
+
+
$this->json[$realName][$name] = $data;
}
+ if ( $defaults ) {
+ $this->json['ResourceFileModulePaths'] = $defaults;
+ }
}
}
"test": "grunt test"
},
"devDependencies": {
- "grunt": "0.4.2",
+ "grunt": "0.4.5",
"grunt-banana-checker": "0.2.0",
- "grunt-contrib-jshint": "0.10.0",
+ "grunt-contrib-jshint": "0.11.0",
"grunt-contrib-watch": "0.6.1",
- "grunt-jscs": "0.8.1",
+ "grunt-jscs": "1.5.0",
"grunt-jsonlint": "1.0.4",
- "grunt-karma": "0.9.0",
+ "grunt-karma": "0.10.1",
"karma": "0.12.31",
"karma-chrome-launcher": "0.1.7",
- "karma-firefox-launcher": "0.1.3",
+ "karma-firefox-launcher": "0.1.4",
"karma-qunit": "0.1.4",
- "qunitjs": "1.15.0"
+ "qunitjs": "1.17.1"
}
}
<!DOCTYPE html>
<html>
<head>
- <meta charset="UTF-8">
+ <meta charset="UTF-8" />
<title>Profiling data</title>
<style>
/* noc.wikimedia.org/base.css */
?>
</tbody>
</table>
-<hr>
+<hr />
<p>Total time: <code><?php printf( '%5.02f', profile_point::$totaltime ); ?></code></p>
<p>Total memory: <code><?php printf( '%5.02f', profile_point::$totalmemory / 1024 ); ?></code></p>
/* MediaWiki */
'mediawiki' => array(
- 'scripts' => 'resources/src/mediawiki/mediawiki.js',
+ 'scripts' => array(
+ 'resources/src/mediawiki/mediawiki.js',
+ 'resources/src/mediawiki/mediawiki.startUp.js',
+ ),
'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js',
'raw' => true,
'targets' => array( 'desktop', 'mobile' ),
return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
},
escapeRE: function ( str ) {
- return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+ return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
},
isDomElement: function ( el ) {
return !!el && !!el.nodeType;
},
isEmpty: function ( v ) {
var key;
- if ( v === '' || v === 0 || v === '0' || v === null
- || v === false || v === undefined )
- {
+ if (
+ v === '' || v === 0 || v === '0' || v === null || v === false || v === undefined
+ ) {
return true;
}
// the for-loop could potentially contain prototypes
* @version 2.1.0
* @license MIT
*/
-(function ($) {
+( function ($) {
var isInputSupported = 'placeholder' in document.createElement('input'),
isTextareaSupported = 'placeholder' in document.createElement('textarea'),
$this
.filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
- .filter(function () {
+ .filter( function () {
return !$(this).data('placeholder-enabled');
})
.bind({
propHooks.value = hooks;
}
- $(function () {
+ $( function () {
// Look for forms
$(document).delegate('form', 'submit.placeholder', function () {
// Clear the placeholder values so they don't get submitted
var $inputs = $('.placeholder', this).each(clearPlaceholder);
- setTimeout(function () {
+ setTimeout( function () {
$inputs.each(setPlaceholder);
}, 10);
});
// Clear placeholder values upon page reload
$(window).bind('beforeunload.placeholder', function () {
- $('.placeholder').each(function () {
+ $('.placeholder').each( function () {
this.value = '';
});
});
endPos = this.selectionEnd;
scrollTop = this.scrollTop;
checkSelectedText();
- if ( options.selectionStart !== undefined
- && endPos - startPos !== options.selectionEnd - options.selectionStart )
- {
+ if (
+ options.selectionStart !== undefined &&
+ endPos - startPos !== options.selectionEnd - options.selectionStart
+ ) {
// This means there is a difference in the selection range returned by browser and what we passed.
// This happens for Chrome in the case of composite characters. Ref bug #30130
// Set the startPos to the correct position.
mw.cookie.set( cookieKey, null );
}
-} ( mediaWiki, jQuery ) );
+}( mediaWiki, jQuery ) );
$( '#mw-pl-options-2' ).prop( 'checked', true );
} );
} );
-} ( jQuery ) );
+}( jQuery ) );
}
};
-} ( mediaWiki, jQuery ) );
+}( mediaWiki, jQuery ) );
// Attach to window and globally alias
window.mw = window.mediaWiki = mw;
-
- // Auto-register from pre-loaded startup scripts
- if ( $.isFunction( window.startUp ) ) {
- window.startUp();
- window.startUp = undefined;
- }
-
}( jQuery ) );
--- /dev/null
+/**
+ * Auto-register from pre-loaded startup scripts
+ * @ignore (this line will make JSDuck happy)
+ */
+( function ( $ ) {
+ 'use strict';
+
+ if ( $.isFunction( window.startUp ) ) {
+ window.startUp();
+ window.startUp = undefined;
+ }
+}( jQuery ) );
public function testIsGood( $ok, $errors, $expected ) {
$status = new Status();
$status->ok = $ok;
- $status->errors = $errors;
+ foreach ( $errors as $error ) {
+ $status->warning( $error );
+ }
$this->assertEquals( $expected, $status->isGood() );
}
}
}
+ /**
+ * @covers ExtensionProcessor::extractResourceLoaderModules
+ * @dataProvider provideExtractResourceLoaderModules
+ */
+ public function testExtractResourceLoaderModules( $input, $expected ) {
+ $processor = new ExtensionProcessor();
+ $processor->extractInfo( $this->dir, $input + self::$default );
+ $out = $processor->getExtractedInfo();
+ foreach ( $expected as $key => $value ) {
+ $this->assertEquals( $value, $out['globals'][$key] );
+ }
+ }
+
+ public static function provideExtractResourceLoaderModules() {
+ $dir = __DIR__ . '/FooBar/';
+ return array(
+ // Generic module with localBasePath/remoteExtPath specified
+ array(
+ // Input
+ array(
+ 'ResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foobar.js',
+ 'localBasePath' => '',
+ 'remoteExtPath' => 'FooBar',
+ ),
+ ),
+ ),
+ // Expected
+ array(
+ 'wgResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foobar.js',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ ),
+ ),
+ ),
+ ),
+ // ResourceFileModulePaths specified:
+ array(
+ // Input
+ array(
+ 'ResourceFileModulePaths' => array(
+ 'localBasePath' => '',
+ 'remoteExtPath' => 'FooBar',
+ ),
+ 'ResourceModules' => array(
+ // No paths
+ 'test.foo' => array(
+ 'styles' => 'foo.js',
+ ),
+ // Different paths set
+ 'test.bar' => array(
+ 'styles' => 'bar.js',
+ 'localBasePath' => 'subdir',
+ 'remoteExtPath' => 'FooBar/subdir',
+ ),
+ // Custom class with no paths set
+ 'test.class' => array(
+ 'class' => 'FooBarModule',
+ 'extra' => 'argument',
+ ),
+ // Custom class with a localBasePath
+ 'test.class.with.path' => array(
+ 'class' => 'FooBarPathModule',
+ 'extra' => 'argument',
+ 'localBasePath' => '',
+ )
+ ),
+ ),
+ // Expected
+ array(
+ 'wgResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foo.js',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ ),
+ 'test.bar' => array(
+ 'styles' => 'bar.js',
+ 'localBasePath' => $dir . 'subdir',
+ 'remoteExtPath' => 'FooBar/subdir',
+ ),
+ 'test.class' => array(
+ 'class' => 'FooBarModule',
+ 'extra' => 'argument',
+ ),
+ 'test.class.with.path' => array(
+ 'class' => 'FooBarPathModule',
+ 'extra' => 'argument',
+ 'localBasePath' => $dir,
+ )
+ ),
+ ),
+ ),
+ );
+ }
+
public static function provideSetToGlobal() {
return array(
array(
} );
QUnit.test( 'updateTooltipAccessKeys - current browser', 2, function ( assert ) {
- var title = $( makeInput ( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
+ var title = $( makeInput( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
//The new title should be something like "Title [alt-a]", but the exact label will depend on the browser.
//The "a" could be capitalized, and the prefix could be anything, e.g. a simple "^" for ctrl-
//(no browser is known using such a short prefix, though) or "Alt+Umschalt+" in German Firefox.
-(function ($) {
+( function ($) {
QUnit.module('jquery.placeholder', QUnit.newMwEnvironment());
QUnit.test('caches results of feature tests', 2, function (assert) {
- assert.strictEqual(typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
- assert.strictEqual(typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
+ assert.strictEqual( typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
+ assert.strictEqual( typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
});
if ($.fn.placeholder.input && $.fn.placeholder.textarea) {
only: 'scripts'
},
dataType: 'script'
- } ).done(function () {
+ } ).done( function () {
mwLanguageCache[langCode].fire( mw.language );
} ).fail( function () {
mwLanguageCache[langCode].fire( false );
// Check for thumbnail generation errors...
$msg = wfMessage( 'thumbnail_error' );
+ $errorCode = 500;
if ( !$thumb ) {
$errorMsg = $errorMsg ?: $msg->rawParams( 'File::transform() returned false' )->escaped();
} elseif ( $thumb->isError() ) {
} elseif ( $thumb->fileIsSource() ) {
$errorMsg = $msg->
rawParams( 'Image was not scaled, is the requested width bigger than the source?' )->escaped();
+ $errorCode = 400;
}
if ( $errorMsg !== false ) {
- wfThumbError( 500, $errorMsg );
+ wfThumbError( $errorCode, $errorMsg );
} else {
// Stream the file if there were no errors
$thumb->streamFile( $headers );
header( 'Cache-Control: no-cache' );
header( 'Content-Type: text/html; charset=utf-8' );
- if ( $status == 404 ) {
+ if ( $status == 400 ) {
+ header( 'HTTP/1.1 400 Bad request' );
+ } elseif ( $status == 404 ) {
header( 'HTTP/1.1 404 Not found' );
} elseif ( $status == 403 ) {
header( 'HTTP/1.1 403 Forbidden' );