* Added ApiBase::lacksSameOriginSecurity() to allow modules to easily check if
the current request was sent with the 'callback' parameter (or any future
method that breaks the same-origin policy).
+* Profiling methods in ApiBase are deprecated and no longer need to be called.
* The following methods have been deprecated and may be removed in a future
release:
* ApiBase::getDescription
* ApiBase::makeHelpMsg
* ApiBase::makeHelpArrayToString
* ApiBase::makeHelpMsgParameters
+ * ApiBase::getModuleProfileName
+ * ApiBase::profileIn
+ * ApiBase::profileOut
+ * ApiBase::safeProfileOut
+ * ApiBase::getProfileTime
+ * ApiBase::profileDBIn
+ * ApiBase::profileDBOut
+ * ApiBase::getProfileDBTime
* ApiFormatBase::setUnescapeAmps
* ApiFormatBase::getWantsHelp
* ApiFormatBase::setHelp
*/
public function getUniqueId() {
if ( $this->uniqueId ) {
- return $this->xmlEncode( $this->uniqueId );
+ return $this->xmlEncode( wfExpandUrl( $this->uniqueId, PROTO_CURRENT ) );
}
}
* Module parameters: Derived classes can define getAllowedParams() to specify
* which parameters to expect, how to parse and validate them.
*
- * Profiling: various methods to allow keeping tabs on various tasks and their
- * time costs
- *
* Self-documentation: code to allow the API to document its own state
*
* @ingroup API
*/
protected function getDB() {
if ( !isset( $this->mSlaveDB ) ) {
- $this->profileDBIn();
$this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
- $this->profileDBOut();
}
return $this->mSlaveDB;
* @throws UsageException
*/
public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
- Profiler::instance()->close();
throw new UsageException(
$description,
$this->encodeParamName( $errorCode ),
throw new MWException( "Internal error in $method: $message" );
}
+ /**
+ * Write logging information for API features to a debug log, for usage
+ * analysis.
+ * @param string $feature Feature being used.
+ */
+ protected function logFeatureUsage( $feature ) {
+ $request = $this->getRequest();
+ $s = '"' . addslashes( $feature ) . '"' .
+ ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
+ ' "' . $request->getIP() . '"' .
+ ' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' .
+ ' "' . addslashes( $this->getMain()->getUserAgent() ) . '"';
+ wfDebugLog( 'api-feature-usage', $s, 'private' );
+ }
+
/**@}*/
/************************************************************************//**
/**@}*/
- /************************************************************************//**
- * @name Profiling
- * @{
- */
-
- /**
- * Profiling: total module execution time
- */
- private $mTimeIn = 0, $mModuleTime = 0;
- /** @var ScopedCallback */
- private $profile;
- /** @var ScopedCallback */
- private $dbProfile;
-
- /**
- * Get the name of the module as shown in the profiler log
- *
- * @param DatabaseBase|bool $db
- *
- * @return string
- */
- public function getModuleProfileName( $db = false ) {
- if ( $db ) {
- return 'API:' . $this->mModuleName . '-DB';
- }
-
- return 'API:' . $this->mModuleName;
- }
-
- /**
- * Start module profiling
- */
- public function profileIn() {
- if ( $this->mTimeIn !== 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called twice without calling profileOut()' );
- }
- $this->mTimeIn = microtime( true );
- $this->profile = Profiler::instance()->scopedProfileIn( $this->getModuleProfileName() );
- }
-
- /**
- * End module profiling
- */
- public function profileOut() {
- if ( $this->mTimeIn === 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called without calling profileIn() first' );
- }
- if ( $this->mDBTimeIn !== 0 ) {
- ApiBase::dieDebug(
- __METHOD__,
- 'Must be called after database profiling is done with profileDBOut()'
- );
- }
-
- $this->mModuleTime += microtime( true ) - $this->mTimeIn;
- $this->mTimeIn = 0;
- Profiler::instance()->scopedProfileOut( $this->profile );
- }
-
- /**
- * When modules crash, sometimes it is needed to do a profileOut() regardless
- * of the profiling state the module was in. This method does such cleanup.
- */
- public function safeProfileOut() {
- if ( $this->mTimeIn !== 0 ) {
- if ( $this->mDBTimeIn !== 0 ) {
- $this->profileDBOut();
- }
- $this->profileOut();
- }
- }
-
- /**
- * Total time the module was executed
- * @return float
- */
- public function getProfileTime() {
- if ( $this->mTimeIn !== 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called without calling profileOut() first' );
- }
-
- return $this->mModuleTime;
- }
-
- /**
- * Profiling: database execution time
- */
- private $mDBTimeIn = 0, $mDBTime = 0;
-
- /**
- * Start module profiling
- */
- public function profileDBIn() {
- if ( $this->mTimeIn === 0 ) {
- ApiBase::dieDebug(
- __METHOD__,
- 'Must be called while profiling the entire module with profileIn()'
- );
- }
- if ( $this->mDBTimeIn !== 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called twice without calling profileDBOut()' );
- }
- $this->mDBTimeIn = microtime( true );
-
- $this->dbProfile = Profiler::instance()->scopedProfileIn( $this->getModuleProfileName( true ) );
- }
-
- /**
- * End database profiling
- */
- public function profileDBOut() {
- if ( $this->mTimeIn === 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Must be called while profiling ' .
- 'the entire module with profileIn()' );
- }
- if ( $this->mDBTimeIn === 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called without calling profileDBIn() first' );
- }
-
- $time = microtime( true ) - $this->mDBTimeIn;
- $this->mDBTimeIn = 0;
-
- $this->mDBTime += $time;
- $this->getMain()->mDBTime += $time;
- Profiler::instance()->scopedProfileOut( $this->dbProfile );
- }
-
- /**
- * Total time the module used the database
- * @return float
- */
- public function getProfileDBTime() {
- if ( $this->mDBTimeIn !== 0 ) {
- ApiBase::dieDebug( __METHOD__, 'Called without calling profileDBOut() first' );
- }
-
- return $this->mDBTime;
- }
-
- /**
- * Write logging information for API features to a debug log, for usage
- * analysis.
- * @param string $feature Feature being used.
- */
- protected function logFeatureUsage( $feature ) {
- $request = $this->getRequest();
- $s = '"' . addslashes( $feature ) . '"' .
- ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
- ' "' . $request->getIP() . '"' .
- ' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' .
- ' "' . addslashes( $this->getMain()->getUserAgent() ) . '"';
- wfDebugLog( 'api-feature-usage', $s, 'private' );
- }
-
- /**@}*/
-
/************************************************************************//**
* @name Deprecated
* @{
return false;
}
+ /**
+ * @deprecated since 1.25, always returns empty string
+ * @param DatabaseBase|bool $db
+ * @return string
+ */
+ public function getModuleProfileName( $db = false ) {
+ wfDeprecated( __METHOD__, '1.25' );
+ return '';
+ }
+
+ /**
+ * @deprecated since 1.25
+ */
+ public function profileIn() {
+ // No wfDeprecated() yet because extensions call this and might need to
+ // keep doing so for BC.
+ }
+
+ /**
+ * @deprecated since 1.25
+ */
+ public function profileOut() {
+ // No wfDeprecated() yet because extensions call this and might need to
+ // keep doing so for BC.
+ }
+
+ /**
+ * @deprecated since 1.25
+ */
+ public function safeProfileOut() {
+ wfDeprecated( __METHOD__, '1.25' );
+ }
+
+ /**
+ * @deprecated since 1.25, always returns 0
+ * @return float
+ */
+ public function getProfileTime() {
+ wfDeprecated( __METHOD__, '1.25' );
+ return 0;
+ }
+
+ /**
+ * @deprecated since 1.25
+ */
+ public function profileDBIn() {
+ wfDeprecated( __METHOD__, '1.25' );
+ }
+
+ /**
+ * @deprecated since 1.25
+ */
+ public function profileDBOut() {
+ wfDeprecated( __METHOD__, '1.25' );
+ }
+
+ /**
+ * @deprecated since 1.25, always returns 0
+ * @return float
+ */
+ public function getProfileDBTime() {
+ wfDeprecated( __METHOD__, '1.25' );
+ return 0;
+ }
+
/**@}*/
}
* Execute api request. Any errors will be handled if the API was called by the remote client.
*/
public function execute() {
- $this->profileIn();
if ( $this->mInternalMode ) {
$this->executeAction();
} else {
$this->executeActionWithErrorHandling();
}
-
- $this->profileOut();
}
/**
// Reset and print just the error message
ob_clean();
- // If the error occurred during printing, do a printer->profileOut()
- $this->mPrinter->safeProfileOut();
$this->printResult( true );
}
// Printer may not be able to handle errors. This is particularly
// likely if the module returns something for getCustomPrinter().
if ( !$this->mPrinter->canPrintErrors() ) {
- $this->mPrinter->safeProfileOut();
$this->mPrinter = $this->createPrinterByName( self::API_DEFAULT_FORMAT );
}
$this->checkAsserts( $params );
// Execute
- $module->profileIn();
$module->execute();
Hooks::run( 'APIAfterExecute', array( &$module ) );
- $module->profileOut();
$this->reportUnusedParams();
$this->getResult()->cleanUpUTF8();
$printer = $this->mPrinter;
- $printer->profileIn();
$printer->initPrinter( false );
-
$printer->execute();
$printer->closePrinter();
- $printer->profileOut();
}
/**
$this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
$this->mDefaultNamespace = $defaultNamespace;
- $this->profileIn();
$this->mParams = $this->extractRequestParams();
$this->mResolveRedirects = $this->mParams['redirects'];
$this->mConvertTitles = $this->mParams['converttitles'];
- $this->profileOut();
}
/**
* relevant parameters as used
*/
private function executeInternal( $isDryRun ) {
- $this->profileIn();
-
$generatorName = $this->mAllowGenerator ? $this->mParams['generator'] : null;
if ( isset( $generatorName ) ) {
$dbSource = $this->mDbSource;
- $isQuery = $dbSource instanceof ApiQuery;
- if ( !$isQuery ) {
+ if ( !$dbSource instanceof ApiQuery ) {
// If the parent container of this pageset is not ApiQuery, we must create it to run generator
$dbSource = $this->getMain()->getModuleManager()->getModule( 'query' );
- // Enable profiling for query module because it will be used for db sql profiling
- $dbSource->profileIn();
}
$generator = $dbSource->getModuleManager()->getModule( $generatorName, null, true );
if ( $generator === null ) {
$tmpPageSet->executeInternal( $isDryRun );
// populate this pageset with the generator output
- $this->profileOut();
- $generator->profileIn();
-
if ( !$isDryRun ) {
$generator->executeGenerator( $this );
Hooks::run( 'APIQueryGeneratorAfterExecute', array( &$generator, &$this ) );
$main->getVal( $generator->encodeParamName( $paramName ) );
}
}
- $generator->profileOut();
- $this->profileIn();
if ( !$isDryRun ) {
$this->resolvePendingRedirects();
}
-
- if ( !$isQuery ) {
- // If this pageset is not part of the query, we called profileIn() above
- $dbSource->profileOut();
- }
} else {
// Only one of the titles/pageids/revids is allowed at the same time
$dataSource = null;
}
}
}
- $this->profileOut();
}
/**
* @param array $titles Array of Title objects
*/
public function populateFromTitles( $titles ) {
- $this->profileIn();
$this->initFromTitles( $titles );
- $this->profileOut();
}
/**
* @param array $pageIDs Array of page IDs
*/
public function populateFromPageIDs( $pageIDs ) {
- $this->profileIn();
$this->initFromPageIds( $pageIDs );
- $this->profileOut();
}
/**
* @param ResultWrapper $queryResult Query result object
*/
public function populateFromQueryResult( $db, $queryResult ) {
- $this->profileIn();
$this->initFromQueryResult( $queryResult );
- $this->profileOut();
}
/**
* @param array $revIDs Array of revision IDs
*/
public function populateFromRevisionIDs( $revIDs ) {
- $this->profileIn();
$this->initFromRevIDs( $revIDs );
- $this->profileOut();
}
/**
$set = $linkBatch->constructSet( 'page', $db );
// Get pageIDs data from the `page` table
- $this->profileDBIn();
$res = $db->select( 'page', $this->getPageTableFields(), $set,
__METHOD__ );
- $this->profileDBOut();
// Hack: get the ns:titles stored in array(ns => array(titles)) format
$this->initFromQueryResult( $res, $linkBatch->data, true ); // process Titles
$db = $this->getDB();
// Get pageIDs data from the `page` table
- $this->profileDBIn();
$res = $db->select( 'page', $this->getPageTableFields(), $set,
__METHOD__ );
- $this->profileDBOut();
}
$this->initFromQueryResult( $res, $remaining, false ); // process PageIDs
$where = array( 'rev_id' => $revids, 'rev_page = page_id' );
// Get pageIDs data from the `page` table
- $this->profileDBIn();
$res = $db->select( $tables, $fields, $where, __METHOD__ );
foreach ( $res as $row ) {
$revid = intval( $row->rev_id );
$pageids[$pageid] = '';
unset( $remaining[$revid] );
}
- $this->profileDBOut();
}
$this->mMissingRevIDs = array_keys( $remaining );
$fields = array( 'ar_rev_id', 'ar_namespace', 'ar_title' );
$where = array( 'ar_rev_id' => $this->mMissingRevIDs );
- $this->profileDBIn();
$res = $db->select( $tables, $fields, $where, __METHOD__ );
$titles = array();
foreach ( $res as $row ) {
$titles[$revid] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
unset( $remaining[$revid] );
}
- $this->profileDBOut();
$this->initFromTitles( $titles );
}
// Get pageIDs data from the `page` table
- $this->profileDBIn();
$res = $db->select( 'page', $pageFlds, $set, __METHOD__ );
- $this->profileDBOut();
// Hack: get the ns:titles stored in array(ns => array(titles)) format
$this->initFromQueryResult( $res, $linkBatch->data, true );
$lb = new LinkBatch();
$db = $this->getDB();
- $this->profileDBIn();
$res = $db->select(
'redirect',
array(
), array( 'rd_from' => array_keys( $this->mPendingRedirectIDs ) ),
__METHOD__
);
- $this->profileDBOut();
foreach ( $res as $row ) {
$rdfrom = intval( $row->rd_from );
$from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
*/
public function getNamedDB( $name, $db, $groups ) {
if ( !array_key_exists( $name, $this->mNamedDB ) ) {
- $this->profileDBIn();
$this->mNamedDB[$name] = wfGetDB( $db, $groups );
- $this->profileDBOut();
}
return $this->mNamedDB[$name];
$params = $module->extractRequestParams();
$cacheMode = $this->mergeCacheMode(
$cacheMode, $module->getCacheMode( $params ) );
- $module->profileIn();
$module->execute();
Hooks::run( 'APIQueryAfterExecute', array( &$module ) );
- $module->profileOut();
}
// Set the cache mode
isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array()
);
- // getDB has its own profileDBIn/Out calls
- $db = $this->getDB();
-
- $this->profileDBIn();
- $res = $db->select( $tables, $fields, $where, $method, $options, $join_conds );
- $this->profileDBOut();
+ $res = $this->getDB()->select( $tables, $fields, $where, $method, $options, $join_conds );
return $res;
}
protected function checkRowCount() {
wfDeprecated( __METHOD__, '1.24' );
$db = $this->getDB();
- $this->profileDBIn();
$rowcount = $db->estimateRowCount(
$this->tables,
$this->fields,
__METHOD__,
$this->options
);
- $this->profileDBOut();
if ( $rowcount > $this->getConfig()->get( 'APIMaxDBRows' ) ) {
return false;
public $supportedEngines = array( 'InnoDB', 'MyISAM' );
- public $minimumVersion = '5.0.2';
+ public $minimumVersion = '5.0.3';
public $webUserPrivs = array(
'DELETE',
array( 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ),
array( 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ),
array( 'doUserNewTalkUseridUnsigned' ),
+ // note this patch covers other _comment and _description fields too
+ array( 'modifyField', 'recentchanges', 'rc_comment', 'patch-editsummary-length.sql' ),
);
}
array( 'dropTable', 'hitcounter' ),
array( 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ),
array( 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ),
+ array( 'modifyField', 'filearchive', 'fa_deleted_reason', 'patch-editsummary-length.sql' ),
);
}
--- /dev/null
+ALTER TABLE /*_*/revision MODIFY rev_comment varbinary(767) NOT NULL;
+ALTER TABLE /*_*/archive MODIFY ar_comment varbinary(767) NOT NULL;
+ALTER TABLE /*_*/image MODIFY img_description varbinary(767) NOT NULL;
+ALTER TABLE /*_*/oldimage MODIFY oi_description varbinary(767) NOT NULL;
+ALTER TABLE /*_*/filearchive MODIFY fa_description varbinary(767);
+ALTER TABLE /*_*/filearchive MODIFY fa_deleted_reason varbinary(767) default '';
+ALTER TABLE /*_*/recentchanges MODIFY rc_comment varbinary(767) NOT NULL default '';
+ALTER TABLE /*_*/logging MODIFY log_comment varbinary(767) NOT NULL default '';
+ALTER TABLE /*_*/ipblocks MODIFY ipb_reason varbinary(767) NOT NULL;
+ALTER TABLE /*_*/protected_titles MODIFY pt_reason varbinary(767);
+
<head>
<meta charset="utf-8">
<title>MediaWiki Code Example</title>
+ <script>
+ /**
+ * Basic log console for the example iframe in documentation pages.
+ */
+ var log = ( function () {
+ var pre;
+ return function () {
+ var str, i, len, line;
+ if ( !pre ) {
+ pre = document.createElement( 'pre' );
+ pre.className = 'mw-jsduck-log';
+ ( document.body || document.documentElement ).appendChild( pre );
+ }
+ str = [];
+ for ( i = 0, len = arguments.length; i < len; i++ ) {
+ str.push( String( arguments[ i ] ) );
+ }
+ line = document.createElement( 'div' );
+ line.className = 'mw-jsduck-log-line';
+ line.appendChild(
+ document.createTextNode( str.join( ' , ' ) + '\n' )
+ );
+ pre.appendChild( line );
+ };
+ }() );
+
+ window.onerror = function ( error, filePath, linerNr ) {
+ log( error + '\n' + filePath + ':' + linerNr );
+ };
+ </script>
<script src="modules/src/startup.js"></script>
<script>
function startUp() {
</script>
<script src="modules/lib/jquery/jquery.js"></script>
<script src="modules/src/mediawiki/mediawiki.js"></script>
+ <script src="modules/src/mediawiki/mediawiki.startUp.js"></script>
<style>
.mw-jsduck-log {
position: relative;
</head>
<body>
<script>
- /**
- * Basic log console for the example iframe in documentation pages.
- */
- ( function () {
- var pre;
- mw.log = function () {
- var str, i, len, line;
- if ( !pre ) {
- pre = document.createElement( 'pre' );
- pre.className = 'mw-jsduck-log';
- document.body.appendChild( pre );
- }
- str = [];
- for ( i = 0, len = arguments.length; i < len; i++ ) {
- str.push( String( arguments[ i ] ) );
- }
- line = document.createElement( 'div' );
- line.className = 'mw-jsduck-log-line';
- line.appendChild(
- document.createTextNode( str.join( ' , ' ) + '\n' )
- );
- pre.appendChild( line );
- };
- }() );
+ if ( window.mw ) {
+ mw.log = log;
+ }
+
+ window.onerror = function ( error, filePath, linerNr ) {
+ log( filePath + ':' + linerNr );
+ };
/**
* Method called by jsduck to execute the example code.
eval( code );
callback && callback( true );
} catch ( e ) {
- mw.log( 'Uncaught ' + e );
+ log( 'Uncaught ' + e );
callback && callback( false, e );
throw e;
}
--- /dev/null
+CREATE TABLE /*_*/filearchive_tmp (
+ -- Unique row id
+ fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+
+ -- Original base filename; key to image.img_name, page.page_title, etc
+ fa_name varchar(255) binary NOT NULL default '',
+
+ -- Filename of archived file, if an old revision
+ fa_archive_name varchar(255) binary default '',
+
+ -- Which storage bin (directory tree or object store) the file data
+ -- is stored in. Should be 'deleted' for files that have been deleted;
+ -- any other bin is not yet in use.
+ fa_storage_group varbinary(16),
+
+ -- SHA-1 of the file contents plus extension, used as a key for storage.
+ -- eg 8f8a562add37052a1848ff7771a2c515db94baa9.jpg
+ --
+ -- If NULL, the file was missing at deletion time or has been purged
+ -- from the archival storage.
+ fa_storage_key varbinary(64) default '',
+
+ -- Deletion information, if this file is deleted.
+ fa_deleted_user int,
+ fa_deleted_timestamp binary(14) default '',
+ fa_deleted_reason varbinary(767) default '',
+ -- Duped fields from image
+ fa_size int unsigned default 0,
+ fa_width int default 0,
+ fa_height int default 0,
+ fa_metadata mediumblob,
+ fa_bits int default 0,
+ fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
+ fa_minor_mime varbinary(100) default "unknown",
+ fa_description varbinary(767),
+ fa_user int unsigned default 0,
+ fa_user_text varchar(255) binary,
+ fa_timestamp binary(14) default '',
+
+ -- Visibility of deleted revisions, bitfield
+ fa_deleted tinyint unsigned NOT NULL default 0,
+
+ -- sha1 hash of file content
+ fa_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+
+INSERT INTO /*_*/filearchive_tmp
+ SELECT fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key, fa_deleted_user, fa_deleted_timestamp,
+ fa_deleted_reason, fa_size, fa_width, fa_height, fa_metadata, fa_bits, fa_media_type, fa_major_mime,
+ fa_minor_mime, fa_description, fa_user, fa_user_text, fa_timestamp, fa_deleted, fa_sha1
+ FROM /*_*/filearchive;
+
+DROP TABLE /*_*/filearchive;
+
+ALTER TABLE /*_*/filearchive_tmp RENAME TO /*_*/filearchive;
+
+
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
+
-- Text comment summarizing the change.
-- This text is shown in the history and other changes lists,
-- rendered in a subset of wiki markup by Linker::formatComment()
- rev_comment tinyblob NOT NULL,
+ rev_comment varbinary(767) NOT NULL,
-- Key to user.user_id of the user who made this edit.
-- Stores 0 for anonymous edits and for some mass imports.
ar_text mediumblob NOT NULL,
-- Basic revision stuff...
- ar_comment tinyblob NOT NULL,
+ ar_comment varbinary(767) NOT NULL,
ar_user int unsigned NOT NULL default 0,
ar_user_text varchar(255) binary NOT NULL,
ar_timestamp binary(14) NOT NULL default '',
ipb_by_text varchar(255) binary NOT NULL default '',
-- Text comment made by blocker.
- ipb_reason tinyblob NOT NULL,
+ ipb_reason varbinary(767) NOT NULL,
-- Creation (or refresh) date in standard YMDHMS form.
-- IP blocks expire automatically.
-- Description field as entered by the uploader.
-- This is displayed in image upload history and logs.
- img_description tinyblob NOT NULL,
+ img_description varbinary(767) NOT NULL,
-- user_id and user_name of uploader.
img_user int unsigned NOT NULL default 0,
oi_width int NOT NULL default 0,
oi_height int NOT NULL default 0,
oi_bits int NOT NULL default 0,
- oi_description tinyblob NOT NULL,
+ oi_description varbinary(767) NOT NULL,
oi_user int unsigned NOT NULL default 0,
oi_user_text varchar(255) binary NOT NULL,
oi_timestamp binary(14) NOT NULL default '',
-- Deletion information, if this file is deleted.
fa_deleted_user int,
fa_deleted_timestamp binary(14) default '',
- fa_deleted_reason text,
+ fa_deleted_reason varbinary(767) default '',
-- Duped fields from image
fa_size int unsigned default 0,
fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
fa_minor_mime varbinary(100) default "unknown",
- fa_description tinyblob,
+ fa_description varbinary(767),
fa_user int unsigned default 0,
fa_user_text varchar(255) binary,
fa_timestamp binary(14) default '',
rc_title varchar(255) binary NOT NULL default '',
-- as in revision...
- rc_comment varchar(255) binary NOT NULL default '',
+ rc_comment varbinary(767) NOT NULL default '',
rc_minor tinyint unsigned NOT NULL default 0,
-- Edits by user accounts with the 'bot' rights key are
log_page int unsigned NULL,
-- Freeform text. Interpreted as edit history comments.
- log_comment varchar(255) NOT NULL default '',
+ log_comment varbinary(767) NOT NULL default '',
-- miscellaneous parameters:
-- LF separated list (old system) or serialized PHP array (new system)
pt_namespace int NOT NULL,
pt_title varchar(255) binary NOT NULL,
pt_user int unsigned NOT NULL,
- pt_reason tinyblob,
+ pt_reason varbinary(767),
pt_timestamp binary(14) NOT NULL,
pt_expiry varbinary(14) NOT NULL default '',
pt_create_perm varbinary(60) NOT NULL
/* MediaWiki */
'mediawiki' => array(
+ // Keep maintenance/jsduck/eg-iframe.html in sync
'scripts' => array(
'resources/src/mediawiki/mediawiki.js',
'resources/src/mediawiki/mediawiki.startUp.js',
trackCallbacks = $.Callbacks( 'memory' ),
trackQueue = [];
- /**
- * Log a message to window.console, if possible.
- *
- * Useful to force logging of some errors that are otherwise hard to detect (i.e., this logs
- * also in production mode). Gets console references in each invocation instead of caching the
- * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE).
- *
- * @private
- * @method log_
- * @param {string} msg Text for the log entry.
- * @param {Error} [e]
- */
- function log( msg, e ) {
- var console = window.console;
- if ( console && console.log ) {
- console.log( msg );
- // If we have an exception object, log it to the error channel to trigger a
- // proper stacktraces in browsers that support it. No fallback as we have no browsers
- // that don't support error(), but do support log().
- if ( e && console.error ) {
- console.error( String( e ), e );
- }
- }
- }
-
/**
* Create an object that can be read from or written to from methods that allow
* interaction both with single and multiple properties at once.
* error is not module-related or the module cannot be easily identified due to
* batched handling.
* @param {string} source Source of the error. Possible values:
+ *
* - style: stylesheet error (only affects old IE where a special style loading method
* is used)
* - load-callback: exception thrown by user callback
* - module-execute: exception thrown by module code
+ * - store-eval: could not evaluate module code cached in localStorage
+ * - store-localstorage-init: localStorage or JSON parse error in mw.loader.store.init
+ * - store-localstorage-json: JSON conversion error in mw.loader.store.set
+ * - store-localstorage-update: localStorage or JSON conversion error in mw.loader.store.update
+ */
+
+ /**
+ * Fired via mw.track on resource loading error conditions.
+ *
+ * @event resourceloader_assert
+ * @param {string} source Source of the error. Possible values:
+ *
+ * - bug-T59567: failed to cache script due to an Opera function -> string conversion
+ * bug; see <https://phabricator.wikimedia.org/T59567> for details
*/
/**
try {
styleEl.styleSheet.cssText += cssText;
} catch ( e ) {
- log( 'Stylesheet error', e );
mw.track( 'resourceloader.exception', { exception: e, source: 'stylesheet' } );
}
} else {
} catch ( e ) {
// A user-defined callback raised an exception.
// Swallow it to protect our state machine!
- log( 'Exception thrown by user callback', e );
- mw.track( 'resourceloader.exception',
- { exception: e, module: module, source: 'load-callback' } );
+ mw.track( 'resourceloader.exception', { exception: e, module: module, source: 'load-callback' } );
}
}
}
} catch ( e ) {
// This needs to NOT use mw.log because these errors are common in production mode
// and not in debug mode, such as when a symbol that should be global isn't exported
- log( 'Exception thrown by ' + module, e );
registry[module].state = 'error';
mw.track( 'resourceloader.exception', { exception: e, module: module, source: 'module-execute' } );
handlePending( module );
// repopulate these modules to the cache.
// This means that at most one module will be useless (the one that had
// the error) instead of all of them.
- log( 'Error while evaluating data from mw.loader.store', err );
+ mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
origBatch = $.grep( origBatch, function ( module ) {
return registry[module].state === 'loading';
} );
return;
}
} catch ( e ) {
- log( 'Storage error', e );
+ mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-init' } );
}
if ( raw === undefined ) {
JSON.stringify( descriptor.messages ),
JSON.stringify( descriptor.templates )
];
- // Attempted workaround for a possible Opera bug (bug 57567).
+ // Attempted workaround for a possible Opera bug (bug T59567).
// This regex should never match under sane conditions.
if ( /^\s*\(/.test( args[1] ) ) {
args[1] = 'function' + args[1];
- log( 'Detected malformed function stringification (bug 57567)' );
+ mw.track( 'resourceloader.assert', { source: 'bug-T59567' } );
}
} catch ( e ) {
- log( 'Storage error', e );
+ mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-json' } );
return;
}
data = JSON.stringify( mw.loader.store );
localStorage.setItem( key, data );
} catch ( e ) {
- log( 'Storage error', e );
+ mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-update' } );
}
}
// @deprecated since 1.23 Use $ or jQuery instead
mw.log.deprecate( window, '$j', $, 'Use $ or jQuery instead.' );
+ /**
+ * Log a message to window.console, if possible.
+ *
+ * Useful to force logging of some errors that are otherwise hard to detect (i.e., this logs
+ * also in production mode). Gets console references in each invocation instead of caching the
+ * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE).
+ *
+ * @private
+ * @method log_
+ * @param {string} topic Stream name passed by mw.track
+ * @param {Object} data Data passed by mw.track
+ * @param {Error} [data.exception]
+ * @param {string} data.source Error source
+ * @param {string} [data.module] Name of module which caused the error
+ */
+ function log( topic, data ) {
+ var msg,
+ e = data.exception,
+ source = data.source,
+ module = data.module,
+ console = window.console;
+
+ if ( console && console.log ) {
+ msg = ( e ? 'Exception' : 'Error' ) + ' in ' + source;
+ if ( module ) {
+ msg += ' in module ' + module;
+ }
+ msg += ( e ? ':' : '.' );
+ console.log( msg );
+
+ // If we have an exception object, log it to the error channel to trigger a
+ // proper stacktraces in browsers that support it. No fallback as we have no browsers
+ // that don't support error(), but do support log().
+ if ( e && console.error ) {
+ console.error( String( e ), e );
+ }
+ }
+ }
+
+ // subscribe to error streams
+ mw.trackSubscribe( 'resourceloader.exception', log );
+ mw.trackSubscribe( 'resourceloader.assert', log );
+
// Attach to window and globally alias
window.mw = window.mediaWiki = mw;
}( jQuery ) );