source 'https://rubygems.org'
-gem 'mediawiki_selenium', '~> 1.7'
+gem 'mediawiki_selenium', '~> 1.7', '>= 1.7.1'
gem 'rake', '~> 11.1', '>= 11.1.1'
gem 'rubocop', '~> 0.32.1', require: false
domain_name (~> 0.5)
i18n (0.7.0)
json (1.8.3)
- mediawiki_api (0.5.0)
+ mediawiki_api (0.6.0)
faraday (~> 0.9, >= 0.9.0)
faraday-cookie_jar (~> 0.0, >= 0.0.6)
- mediawiki_selenium (1.7.0)
+ mediawiki_selenium (1.7.1)
cucumber (~> 1.3, >= 1.3.20)
headless (~> 2.0, >= 2.1.0)
json (~> 1.8, >= 1.8.1)
- mediawiki_api (~> 0.5, >= 0.5.0)
+ mediawiki_api (~> 0.6, >= 0.6.0)
page-object (~> 1.0)
rest-client (~> 1.6, >= 1.6.7)
rspec-core (~> 2.14, >= 2.14.4)
rspec-expectations (~> 2.14, >= 2.14.4)
syntax (~> 1.2, >= 1.2.0)
thor (~> 0.19, >= 0.19.1)
- mime-types (2.99.1)
- multi_json (1.11.3)
+ mime-types (2.99.2)
+ multi_json (1.12.1)
multi_test (0.1.2)
multipart-post (2.0.0)
netrc (0.11.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.5)
rubyzip (1.2.0)
- selenium-webdriver (2.53.0)
+ selenium-webdriver (2.53.1)
childprocess (~> 0.5)
rubyzip (~> 1.0)
websocket (~> 1.0)
- syntax (1.2.0)
+ syntax (1.2.1)
thor (0.19.1)
unf (0.1.4)
unf_ext
ruby
DEPENDENCIES
- mediawiki_selenium (~> 1.7)
+ mediawiki_selenium (~> 1.7, >= 1.7.1)
rake (~> 11.1, >= 11.1.1)
rubocop (~> 0.32.1)
is configurable via $wgSessionPbkdf2Iterations.
* Upload dialog's file upload log comment can now be configured separately for
local and foreign uploads.
+* $wgForeignUploadTargets now defaults to `[ 'local' ]`, where `'local'`
+ signifies local uploads. A value of `[]` (empty array) now means that
+ no upload targets are allowed, effectively disabling the upload dialog.
=== New features in 1.28 ===
* User::isBot() method for checking if an account is a bot role account.
&$data: An array with all the components that will be joined in order to create the line
$block: An array of RecentChange objects in that block
$rc: The RecentChange object for this line
+&$classes: An array of classes to change
'EnhancedChangesListModifyBlockLineData': to alter data used to build
a non-grouped recent change line in EnhancedChangesList.
$page: the WikiPage of the candidate edit
$content: the Content object of the candidate edit
$output: the ParserOutput result of the candidate edit
+$summary: the change summary of the candidate edit
+$user: the User considering the edit
'PasswordPoliciesForUser': Alter the effective password policy for a user.
$user: User object whose policy you are modifying
* Array of foreign file repo names (set in $wgForeignFileRepos above) that
* are allowable upload targets. These wikis must have some method of
* authentication (i.e. CentralAuth), and be CORS-enabled for this wiki.
+ * The string 'local' signifies the default local file repository.
*
* Example:
* $wgForeignUploadTargets = array( 'shared' );
*/
-$wgForeignUploadTargets = [];
+$wgForeignUploadTargets = [ 'local' ];
/**
* Configuration for file uploads using the embeddable upload dialog
}
}
+ // Set a hidden field so JS knows what edit form mode we are in
+ if ( $this->isConflict ) {
+ $mode = 'conflict';
+ } elseif ( $this->preview ) {
+ $mode = 'preview';
+ } elseif ( $this->diff ) {
+ $mode = 'diff';
+ } else {
+ $mode = 'text';
+ }
+ $wgOut->addHTML( Html::hidden( 'mode', $mode, [ 'id' => 'mw-edit-mode' ] ) );
+
// Marker for detecting truncated form data. This must be the last
// parameter sent in order to be of use, so do not move me.
$wgOut->addHTML( Html::hidden( 'wpUltimateParam', true ) );
*/
function getPreviewText() {
global $wgOut, $wgUser, $wgRawHtml, $wgLang;
- global $wgAllowUserCss, $wgAllowUserJs, $wgAjaxEditStash;
+ global $wgAllowUserCss, $wgAllowUserJs;
$stats = $wgOut->getContext()->getStats();
$this->mTitle, $pstContent, $wgUser );
$parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
- # Try to stash the edit for the final submission step
- # @todo: different date format preferences cause cache misses
- if ( $wgAjaxEditStash ) {
- ApiStashEdit::stashEditFromPreview(
- $this->getArticle(), $content, $pstContent,
- $parserOutput, $parserOptions, $parserOptions, wfTimestampNow()
- );
- }
-
$parserOutput->setEditSectionTokens( false ); // no section edit links
$previewHTML = $parserOutput->getText();
$this->mParserOutput = $parserOutput;
// @codingStandardsIgnoreEnd
wfMissingVendorError( $entryPoint, $mwVersion );
}
+
+ // List of functions and their associated PHP extension to check for
+ // @codingStandardsIgnoreStart Generic.Arrays.DisallowLongArraySyntax
+ $extensions = array(
+ 'mb_substr' => 'mbstring',
+ 'utf8_encode' => 'xml',
+ 'ctype_digit' => 'ctype',
+ 'json_decode' => 'json',
+ 'iconv' => 'iconv',
+ );
+ // List of extensions we're missing
+ $missingExtensions = array();
+ // @codingStandardsIgnoreEnd
+
+ foreach ( $extensions as $function => $extension ) {
+ if ( !function_exists( $function ) ) {
+ $missingExtensions[] = $extension;
+ }
+ }
+
+ if ( $missingExtensions ) {
+ wfMissingExtensions( $entryPoint, $mwVersion, $missingExtensions );
+ }
}
/**
padding: 2em;
text-align: center;
}
- p, img, h1, h2 {
+ p, img, h1, h2, ul {
text-align: left;
margin: 0.5em 0 1em;
}
wfGenericError( $type, $mwVersion, 'External dependencies', $shortText, $longText, $longHtml );
}
+
+/**
+ * Display an error for a PHP extension not existing.
+ *
+ * @param string $type See wfGenericError
+ * @param string $mwVersion See wfGenericError
+ * @param array $missingExts The extensions we're missing
+ */
+function wfMissingExtensions( $type, $mwVersion, $missingExts ) {
+ $shortText = "Installing some PHP extensions is required.";
+
+ $missingExtText = '';
+ $missingExtHtml = '';
+ $baseUrl = 'https://secure.php.net';
+ foreach ( $missingExts as $ext ) {
+ $missingExtText .= " * $ext <$baseUrl/$ext>\n";
+ $missingExtHtml .= "<li><b>$ext</b> "
+ . "(<a href=\"$baseUrl/$ext\">more information</a>)</li>";
+ }
+
+ $cliText = "Error: Missing one or more required components of PHP.\n"
+ . "You are missing a required extension to PHP that MediaWiki needs.\n"
+ . "Please install:\n" . $missingExtText;
+
+ $longHtml = <<<HTML
+ You are missing a required extension to PHP that MediaWiki
+ requires to run. Please install:
+ <ul>
+ $missingExtHtml
+ </ul>
+HTML;
+
+ wfGenericError( $type, $mwVersion, 'Required components', $shortText,
+ $cliText, $longHtml );
+}
+++ /dev/null
-<?php
-/**
- * Backwards compatibility. The PHP version error function is now
- * included in PHPVersionCheck.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
- *
- * @deprecated 1.25
- * @file
- */
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/PHPVersionCheck.php';
-// @codingStandardsIgnoreEnd
/**
* Set the revision ID
*
+ * This should only be used for proposed revisions that turn out to be null edits
+ *
* @since 1.19
* @param int $id
*/
public function setId( $id ) {
- $this->mId = $id;
+ $this->mId = (int)$id;
+ }
+
+ /**
+ * Set the user ID/name
+ *
+ * This should only be used for proposed revisions that turn out to be null edits
+ *
+ * @since 1.28
+ * @param integer $id User ID
+ * @param string $name User name
+ */
+ public function setUserIdAndName( $id, $name ) {
+ $this->mUser = (int)$id;
+ $this->mUserText = $name;
+ $this->mOrigUserText = $name;
}
/**
die( 1 );
}
+mb_internal_encoding( 'UTF-8' );
+
// Set various default paths sensibly...
$ps_default = Profiler::instance()->scopedProfileIn( $fname . '-defaults' );
if ( $user->pingLimiter( 'stashedit' ) ) {
$status = 'ratelimited';
} elseif ( $dbw->lock( $key, __METHOD__, 1 ) ) {
- $status = self::parseAndStash( $page, $content, $user );
+ $status = self::parseAndStash( $page, $content, $user, $params['summary'] );
$dbw->unlock( $key, __METHOD__ );
} else {
$status = 'busy';
/**
* @param WikiPage $page
- * @param Content $content
+ * @param Content $content Edit content
* @param User $user
+ * @param string $summary Edit summary
* @return integer ApiStashEdit::ERROR_* constant
* @since 1.25
*/
- public static function parseAndStash( WikiPage $page, Content $content, User $user ) {
+ public static function parseAndStash( WikiPage $page, Content $content, User $user, $summary ) {
$cache = ObjectCache::getLocalClusterInstance();
$logger = LoggerFactory::getInstance( 'StashEdit' );
$key = self::getStashKey( $title, $content, $user );
// Let extensions add ParserOutput metadata or warm other caches
- Hooks::run( 'ParserOutputStashForEdit', [ $page, $content, $editInfo->output ] );
+ Hooks::run( 'ParserOutputStashForEdit',
+ [ $page, $content, $editInfo->output, $summary, $user ] );
list( $stashInfo, $ttl, $code ) = self::buildStashValue(
$editInfo->pstContent,
return self::ERROR_PARSE;
}
- /**
- * Attempt to cache PST content and corresponding parser output in passing
- *
- * This method can be called when the output was already generated for other
- * reasons. Parsing should not be done just to call this method, however.
- * $pstOpts must be that of the user doing the edit preview. If $pOpts does
- * not match the options of WikiPage::makeParserOptions( 'canonical' ), this
- * will do nothing. Provided the values are cacheable, they will be stored
- * in memcached so that final edit submission might make use of them.
- *
- * @param Page|Article|WikiPage $page Page title
- * @param Content $content Proposed page content
- * @param Content $pstContent The result of preSaveTransform() on $content
- * @param ParserOutput $pOut The result of getParserOutput() on $pstContent
- * @param ParserOptions $pstOpts Options for $pstContent (MUST be for prospective author)
- * @param ParserOptions $pOpts Options for $pOut
- * @param string $timestamp TS_MW timestamp of parser output generation
- * @return bool Success
- */
- public static function stashEditFromPreview(
- Page $page, Content $content, Content $pstContent, ParserOutput $pOut,
- ParserOptions $pstOpts, ParserOptions $pOpts, $timestamp
- ) {
- $cache = ObjectCache::getLocalClusterInstance();
- $logger = LoggerFactory::getInstance( 'StashEdit' );
-
- // getIsPreview() controls parser function behavior that references things
- // like user/revision that don't exists yet. The user/text should already
- // be set correctly by callers, just double check the preview flag.
- if ( !$pOpts->getIsPreview() ) {
- return false; // sanity
- } elseif ( $pOpts->getIsSectionPreview() ) {
- return false; // short-circuit (need the full content)
- }
-
- // PST parser options are for the user (handles signatures, etc...)
- $user = $pstOpts->getUser();
- // Get a key based on the source text, format, and user preferences
- $title = $page->getTitle();
- $key = self::getStashKey( $title, $content, $user );
-
- // Parser output options must match cannonical options.
- // Treat some options as matching that are different but don't matter.
- $canonicalPOpts = $page->makeParserOptions( 'canonical' );
- $canonicalPOpts->setIsPreview( true ); // force match
- $canonicalPOpts->setTimestamp( $pOpts->getTimestamp() ); // force match
- if ( !$pOpts->matches( $canonicalPOpts ) ) {
- $logger->info( "Uncacheable preview output for key '$key' ('$title') [options]." );
- return false;
- }
-
- // Set the time the output was generated
- $pOut->setCacheTime( wfTimestampNow() );
-
- // Build a value to cache with a proper TTL
- list( $stashInfo, $ttl ) = self::buildStashValue( $pstContent, $pOut, $timestamp, $user );
- if ( !$stashInfo ) {
- $logger->info( "Uncacheable parser output for key '$key' ('$title') [rev/TTL]." );
- return false;
- }
-
- $ok = $cache->set( $key, $stashInfo, $ttl );
- if ( !$ok ) {
- $logger->error( "Failed to cache preview parser output for key '$key' ('$title')." );
- } else {
- $logger->debug( "Cached preview output for key '$key'." );
- }
-
- return $ok;
- }
-
/**
* Check that a prepared edit is in cache and still up-to-date
*
ApiBase::PARAM_TYPE => 'text',
ApiBase::PARAM_REQUIRED => true
],
+ 'summary' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ],
'contentmodel' => [
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
ApiBase::PARAM_REQUIRED => true
"apihelp-stashedit-param-contentmodel": "Content model of the new content.",
"apihelp-stashedit-param-contentformat": "Content serialization format used for the input text.",
"apihelp-stashedit-param-baserevid": "Revision ID of the base revision.",
+ "apihelp-stashedit-param-summary": "Change summary.",
"apihelp-tag-description": "Add or remove change tags from individual revisions or log entries.",
"apihelp-tag-param-rcid": "One or more recent changes IDs from which to add or remove the tag.",
"apihelp-stashedit-param-contentmodel": "{{doc-apihelp-param|stashedit|contentmodel}}",
"apihelp-stashedit-param-contentformat": "{{doc-apihelp-param|stashedit|contentformat}}",
"apihelp-stashedit-param-baserevid": "{{doc-apihelp-param|stashedit|baserevid}}",
+ "apihelp-stashedit-param-summary": "{{doc-apihelp-param|stashedit|summary}}",
"apihelp-tag-description": "{{doc-apihelp-description|tag}}",
"apihelp-tag-param-rcid": "{{doc-apihelp-param|tag|rcid}}",
"apihelp-tag-param-revid": "{{doc-apihelp-param|tag|revid}}",
) {
$lineParams['classes'] = [ 'mw-enhanced-watched' ];
}
+
$separator = ' <span class="mw-changeslist-separator">. .</span> ';
$data['recentChangesFlags'] = [
// give the hook a chance to modify the data
$success = Hooks::run( 'EnhancedChangesListModifyLineData',
- [ $this, &$data, $block, $rcObj ] );
+ [ $this, &$data, $block, $rcObj, &$classes ] );
if ( !$success ) {
// skip entry if hook aborted it
return [];
protected $page;
/** @var integer */
protected $pageId;
+ /** @var string */
+ protected $timestamp;
/**
* @param WikiPage $page Page we are updating
* @param integer|null $pageId ID of the page we are updating [optional]
+ * @param string|null $timestamp TS_MW timestamp of deletion
* @throws MWException
*/
- function __construct( WikiPage $page, $pageId = null ) {
+ function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
parent::__construct( false ); // no implicit transaction
$this->page = $page;
- if ( $page->exists() ) {
+ if ( $pageId ) {
+ $this->pageId = $pageId; // page ID at time of deletion
+ } elseif ( $page->exists() ) {
$this->pageId = $page->getId();
- } elseif ( $pageId ) {
- $this->pageId = $pageId;
} else {
throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" );
}
+
+ $this->timestamp = $timestamp ?: wfTimestampNow();
}
public function doUpdate() {
[
'rc_type != ' . RC_LOG,
'rc_namespace' => $title->getNamespace(),
- 'rc_title' => $title->getDBkey()
+ 'rc_title' => $title->getDBkey(),
+ 'rc_timestamp < ' .
+ $this->mDb->addQuotes( $this->mDb->timestamp( $this->timestamp ) )
],
__METHOD__
);
'wiki' => $this->mDb->getWikiID(),
'job' => new JobSpecification(
'deleteLinks',
- [ 'pageId' => $this->pageId ],
+ [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
[ 'removeDuplicates' => true ],
$this->page->getTitle()
)
protected $envChecks = [
'envCheckDB',
'envCheckBrokenXML',
- 'envCheckMbstring',
- 'envCheckXML',
'envCheckPCRE',
'envCheckMemory',
'envCheckCache',
'envCheckUploadsDirectory',
'envCheckLibicu',
'envCheckSuhosinMaxValueLength',
- 'envCheckCtype',
- 'envCheckIconv',
- 'envCheckJSON',
];
/**
return true;
}
- /**
- * Environment check for mbstring.func_overload.
- * @return bool
- */
- protected function envCheckMbstring() {
- if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
- $this->showError( 'config-mbstring' );
-
- return false;
- }
-
- if ( !function_exists( 'mb_substr' ) ) {
- $this->showError( 'config-mbstring-absent' );
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Environment check for the XML module.
- * @return bool
- */
- protected function envCheckXML() {
- if ( !function_exists( "utf8_encode" ) ) {
- $this->showError( 'config-xml-bad' );
-
- return false;
- }
-
- return true;
- }
-
/**
* Environment check for the PCRE module.
*
}
}
- /**
- * @return bool
- */
- protected function envCheckCtype() {
- if ( !function_exists( 'ctype_digit' ) ) {
- $this->showError( 'config-ctype' );
-
- return false;
- }
-
- return true;
- }
-
- /**
- * @return bool
- */
- protected function envCheckIconv() {
- if ( !function_exists( 'iconv' ) ) {
- $this->showError( 'config-iconv' );
-
- return false;
- }
-
- return true;
- }
-
- /**
- * @return bool
- */
- protected function envCheckJSON() {
- if ( !function_exists( 'json_decode' ) ) {
- $this->showError( 'config-json' );
-
- return false;
- }
-
- return true;
- }
-
/**
* Environment prep for the server hostname.
*/
"config-no-db": "Could not find a suitable database driver! You need to install a database driver for PHP.\nThe following database {{PLURAL:$2|type is|types are}} supported: $1.\n\nIf you compiled PHP yourself, reconfigure it with a database client enabled, for example, using <code>./configure --with-mysqli</code>.\nIf you installed PHP from a Debian or Ubuntu package, then you also need to install, for example, the <code>php5-mysql</code> package.",
"config-outdated-sqlite": "<strong>Warning:</strong> you have SQLite $1, which is lower than minimum required version $2. SQLite will be unavailable.",
"config-no-fts3": "<strong>Warning:</strong> SQLite is compiled without the [//sqlite.org/fts3.html FTS3 module], search features will be unavailable on this backend.",
- "config-mbstring": "<strong>Fatal: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] is active!</strong>\nThis option causes errors and may corrupt data unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
- "config-xml-bad": "PHP's XML module is missing.\nMediaWiki requires functions in this module and will not work in this configuration.\nYou may need to install the php-xml RPM package.",
"config-pcre-old": "<strong>Fatal:</strong> PCRE $1 or later is required.\nYour PHP binary is linked with PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE More information].",
"config-pcre-no-utf8": "<strong>Fatal:</strong> PHP's PCRE module seems to be compiled without PCRE_UTF8 support.\nMediaWiki requires UTF-8 support to function correctly.",
"config-memory-raised": "PHP's <code>memory_limit</code> is $1, raised to $2.",
"config-memory-bad": "<strong>Warning:</strong> PHP's <code>memory_limit</code> is $1.\nThis is probably too low.\nThe installation may fail!",
- "config-ctype": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/ctype.installation.php Ctype extension].",
- "config-iconv": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/iconv.installation.php iconv extension].",
- "config-json": "<strong>Fatal:</strong> PHP was compiled without JSON support.\nYou must install either the PHP JSON extension or the [http://pecl.php.net/package/jsonc PECL jsonc] extension before installing MediaWiki.\n* The PHP extension is included in Red Hat Enterprise Linux (CentOS) 5 and 6, though must be enabled in <code>/etc/php.ini</code> or <code>/etc/php.d/json.ini</code>.\n* Some Linux distributions released after May 2013 omit the PHP extension, instead packaging the PECL extension as <code>php5-json</code> or <code>php-pecl-jsonc</code>.",
- "config-mbstring-absent": "<strong>Fatal:</strong> PHP must be compiled with support for the [http://www.php.net/manual/en/mbstring.setup.php mbstring extension].",
"config-xcache": "[http://xcache.lighttpd.net/ XCache] is installed",
"config-apc": "[http://www.php.net/apc APC] is installed",
"config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] is installed",
"config-no-db": "{{doc-important|Do not translate \"<code>./configure --with-mysqli</code>\" and \"<code>php5-mysql</code>\".}}\nParameters:\n* $1 is comma separated list of database types supported by MediaWiki.\n* $2 is the count of items in $1 - for use in plural.",
"config-outdated-sqlite": "Used as warning. Parameters:\n* $1 - the version of SQLite that has been installed\n* $2 - minimum version",
"config-no-fts3": "A \"[[:wikipedia:Front and back ends|backend]]\" is a system or component that ordinary users don't interact with directly and don't need to know about, and that is responsible for a distinct task or service - for example, a storage back-end is a generic system for storing data which other applications can use. Possible alternatives for back-end are \"system\" or \"service\", or (depending on context and language) even leave it untranslated.",
- "config-mbstring": "{{Related|Config-fatal}}",
- "config-xml-bad": "Status message in the MediaWiki installer environment checks.",
"config-pcre-old": "Parameters:\n* $1 - minimum PCRE version number\n* $2 - the installed version of [[wikipedia:PCRE|PCRE]]\n{{Related|Config-fatal}}",
"config-pcre-no-utf8": "PCRE is a name of a programmers' library for supporting regular expressions. It can probably be translated without change.\n{{Related|Config-fatal}}",
"config-memory-raised": "Parameters:\n* $1 is the configured <code>memory_limit</code>.\n* $2 is the value to which <code>memory_limit</code> was raised.",
"config-memory-bad": "Parameters:\n* $1 is the configured <code>memory_limit</code>.",
- "config-ctype": "Message if support for [http://www.php.net/manual/en/ctype.installation.php Ctype] is missing from PHP.\n{{Related|Config-fatal}}",
- "config-iconv": "Message if support for [http://www.php.net/manual/en/iconv.installation.php iconv] is missing from PHP.\n{{Related|Config-fatal}}",
- "config-json": "Message if support for [[wikipedia:JSON|JSON]] is missing from PHP.\n* \"[[wikipedia:Red Hat Enterprise Linux|Red Hat Enterprise Linux]]\" (RHEL) and \"[[wikipedia:CentOS|CentOS]]\" refer to two almost-identical Linux distributions. \"5 and 6\" refers to version 5 or 6 of either distribution. Because RHEL 7 likely will not include the PHP extension, do not translate as \"5 or newer\".\n* \"The [http://www.php.net/json PHP extension]\" is the JSON extension included with PHP 5.2 and newer.\n* \"The [http://pecl.php.net/package/jsonc PECL extension]\" is based on the PHP extension, though excludes code some distributions have found unacceptable (see [[phab:T49431]]).\n{{Related|Config-fatal}}",
- "config-mbstring-absent": "Message if support for [http://www.php.net/manual/en/mbstring.installation.php mbstring] is missing from PHP.\n{{Related|Config-fatal}}",
"config-xcache": "Message indicates if this program is available",
"config-apc": "Message indicates if this program is available",
"config-wincache": "Message indicates if this program is available",
return false;
}
+ $timestamp = isset( $this->params['timestamp'] ) ? $this->params['timestamp'] : null;
+
$page = WikiPage::factory( $this->title ); // title when deleted
- $update = new LinksDeletionUpdate( $page, $pageId );
+ $update = new LinksDeletionUpdate( $page, $pageId, $timestamp );
DataUpdate::runUpdates( [ $update ] );
return true;
case 'Flash':
$flashDecode = [
- 'fired' => $val & bindec( '00000001' ),
- 'return' => ( $val & bindec( '00000110' ) ) >> 1,
- 'mode' => ( $val & bindec( '00011000' ) ) >> 3,
- 'function' => ( $val & bindec( '00100000' ) ) >> 5,
- 'redeye' => ( $val & bindec( '01000000' ) ) >> 6,
- // 'reserved' => ( $val & bindec( '10000000' ) ) >> 7,
+ 'fired' => $val & 0b00000001,
+ 'return' => ( $val & 0b00000110 ) >> 1,
+ 'mode' => ( $val & 0b00011000 ) >> 3,
+ 'function' => ( $val & 0b00100000 ) >> 5,
+ 'redeye' => ( $val & 0b01000000 ) >> 6,
+ // 'reserved' => ( $val & 0b10000000 ) >> 7,
];
$flashMsgs = [];
# We do not need to handle unknown values since all are used.
$this->mTimestamp = $now;
} else {
// Bug 32948: revision ID must be set to page {{REVISIONID}} and
- // related variables correctly
+ // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
$revision->setId( $this->getLatest() );
+ $revision->setUserIdAndName(
+ $this->getUser( Revision::RAW ),
+ $this->getUserText( Revision::RAW )
+ );
}
if ( $changed ) {
} else {
$edit->timestamp = wfTimestampNow();
}
- // @note: $cachedEdit is not used if the rev ID was referenced in the text
+ // @note: $cachedEdit is safely not used if the rev ID was referenced in the text
$edit->revid = $revid;
if ( $cachedEdit ) {
];
$content = $revision->getContent();
- // Parse the text
- // Be careful not to do pre-save transform twice: $text is usually
- // already pre-save transformed once.
- if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
- wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
- $editInfo = $this->prepareContentForEdit( $content, $revision, $user );
+ // See if the parser output before $revision was inserted is still valid
+ $editInfo = false;
+ if ( !$this->mPreparedEdit ) {
+ wfDebug( __METHOD__ . ": No prepared edit...\n" );
+ } elseif ( $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
+ wfDebug( __METHOD__ . ": Prepared edit has vary-revision...\n" );
+ } elseif ( $this->mPreparedEdit->output->getFlag( 'vary-user' ) && !$options['changed'] ) {
+ wfDebug( __METHOD__ . ": Prepared edit has vary-user and is null...\n" );
} else {
- wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
+ wfDebug( __METHOD__ . ": Using prepared edit...\n" );
$editInfo = $this->mPreparedEdit;
}
+ if ( !$editInfo ) {
+ // Parse the text again if needed. Be careful not to do pre-save transform twice:
+ // $text is usually already pre-save transformed once. Avoid using the edit stash
+ // as any prepared content from there or in doEditContent() was already rejected.
+ $editInfo = $this->prepareContentForEdit( $content, $revision, $user, null, false );
+ }
+
// Save it to the parser cache.
// Make sure the cache time matches page_touched to avoid double parsing.
ParserCache::singleton()->save(
break;
case 'revisionuser':
# Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-revision...\n" );
+ # *after* a revision ID has been assigned for null edits.
+ $this->mOutput->setFlag( 'vary-user' );
+ wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-user...\n" );
$value = $this->getRevisionUser();
break;
case 'revisionsize':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONSIZE}} used, setting vary-revision...\n" );
$value = $this->getRevisionSize();
break;
case 'namespace':
&& $this->mOptions->getAllowSpecialInclusion()
&& $this->ot['html']
) {
+ $specialPage = SpecialPageFactory::getPage( $title->getDBkey() );
// Pass the template arguments as URL parameters.
// "uselang" will have no effect since the Language object
// is forced to the one defined in ParserOptions.
$context = new RequestContext;
$context->setTitle( $title );
$context->setRequest( new FauxRequest( $pageArgs ) );
- $context->setUser( $this->getUser() );
+ if ( $specialPage && $specialPage->maxIncludeCacheTime() === 0 ) {
+ $context->setUser( $this->getUser() );
+ } else {
+ // If this page is cached, then we better not be per user.
+ $context->setUser( User::newFromName( '127.0.0.1', false ) );
+ }
$context->setLanguage( $this->mOptions->getUserLangObj() );
$ret = SpecialPageFactory::capturePath( $title, $context );
if ( $ret ) {
$this->mOutput->addOutputPageMetadata( $context->getOutput() );
$found = true;
$isHTML = true;
- $this->disableCache();
+ if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
+ $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+ }
}
} elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
$found = false; # access denied
# will change the size.
if ( $revObject ) {
$this->mRevisionSize = $revObject->getSize();
- } elseif ( $this->ot['wiki'] || $this->mOptions->getIsPreview() ) {
+ } else {
$this->mRevisionSize = $this->mInputSize;
}
}
'rememberMe' => [
// option for saving the user token to a cookie
'type' => 'check',
+ 'name' => 'wpRemember',
'label-message' => $this->msg( 'userlogin-remembermypassword' )
->numParams( $expirationDays ),
'id' => 'wpRemember',
'loginattempt' => [
// submit button
'type' => 'submit',
- 'name' => 'wpRemember',
'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
'id' => 'wpLoginAttempt',
'weight' => 100,
return $this->mIncludable;
}
+ /**
+ * How long to cache page when it is being included.
+ *
+ * @note If cache time is not 0, then the current user becomes an anon
+ * if you want to do any per-user customizations, than this method
+ * must be overriden to return 0.
+ * @since 1.26
+ * @return int Time in seconds, 0 to disable caching altogether,
+ * false to use the parent page's cache settings
+ */
+ public function maxIncludeCacheTime() {
+ global $wgMiserMode;
+ if ( !$wgMiserMode ) {
+ return 0;
+ } else {
+ return 60*60;
+ }
+ }
+
/**
* Whether the special page is being evaluated via transclusion
* @param bool $x
protected function getGroupName() {
return 'changes';
}
+
+ /**
+ * How long to cache page when it is being included.
+ *
+ * @return int Time in seconds, 0 to disable caching altogether
+ */
+ public function maxIncludeCacheTime() {
+ global $wgMiserMode;
+ if ( !$wgMiserMode ) {
+ return 0;
+ } else {
+ return 60*5;
+ }
+ }
}
public function isIncludable() {
return true;
}
+
+ /**
+ * How long to cache page when it is being included.
+ *
+ * @return int|bool Time in seconds, 0 to disable caching altogether
+ */
+ public function maxIncludeCacheTime() {
+ global $wgMiserMode;
+ if ( !$wgMiserMode ) {
+ return 0;
+ } else {
+ return 60*5;
+ }
+ }
+
}
exit( 1 );
}
-mb_internal_encoding( 'UTF-8' );
-
use CLDRPluralRuleParser\Evaluator;
/**
"upload-copy-upload-invalid-domain": "Copy uploads are not available from this domain.",
"upload-foreign-cant-upload": "This wiki is not configured to upload files to the requested foreign file repository.",
"upload-foreign-cant-load-config": "Loading file upload configuration for the foreign file repository failed.",
+ "upload-dialog-disabled": "File uploads using this dialog are disabled on this wiki.",
"upload-dialog-title": "Upload file",
"upload-dialog-button-cancel": "Cancel",
"upload-dialog-button-done": "Done",
"upload-copy-upload-invalid-domain": "Error message shown if a user is trying to upload (i.e. copy) a file from a website that is not in $wgCopyUploadsDomains (if set).\n\nSee also:\n* {{msg-mw|http-invalid-url}}\n* {{msg-mw|tmp-create-error}}\n* {{msg-mw|tmp-write-error}}",
"upload-foreign-cant-upload": "Error message shown when a user is trying to upload a file to foreign repository that is not configured to receive file uploads from current wiki.",
"upload-foreign-cant-load-config": "Error message shown when a user is trying to upload a file to foreign repository and the foreign wiki is down or otherwise unable to respond to API requests.",
+ "upload-dialog-disabled": "Message shown when the upload dialog functionality is disabled. (This doesn't mean that uploads in general are disabled, only this specific method of uploading.)",
"upload-dialog-title": "Title of the upload dialog box\n{{Identical|Upload file}}",
"upload-dialog-button-cancel": "Button to cancel the dialog\n{{Identical|Cancel}}",
"upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
*/
// Bail on old versions of PHP, or if composer has not been run yet to install
-// dependencies. Using dirname( __FILE__ ) here because __DIR__ is PHP5.3+.
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/../includes/PHPVersionCheck.php';
-// @codingStandardsIgnoreEnd
+// dependencies.
+require_once __DIR__ . '/../includes/PHPVersionCheck.php';
wfEntryPointCheck( 'cli' );
/**
// Comma for second argument
'(?:\s*(,))?' .
// Second argument must start with array to be processed
- '(?:\s*array\s*\(' .
+ '(?:\s*(?:array\s*\(|\[)' .
// Matching inside array - allows one deep of brackets
- '((?:[^\(\)]|\([^\(\)]*\))*)' .
+ '((?:[^\(\)\[\]]|\((?-1)\)|\[(?-1)\])*)' .
// End
- '\))?/',
+ '[\)\]])?/',
$content,
$m,
PREG_SET_ORDER
* @ingroup Maintenance
*/
-// Checking for old versions of PHP is done in Maintenance.php
-// We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
-// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
-require_once dirname( __FILE__ ) . '/Maintenance.php';
-// @codingStandardsIgnoreEnd
+require_once __DIR__ . '/Maintenance.php';
define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' );
define( 'MEDIAWIKI_INSTALL', true );
* @ingroup Maintenance
*/
-$wgUseMasterForMaintenance = true;
require_once __DIR__ . '/Maintenance.php';
/**
"ABORTING (see https://bugs.php.net/bug.php?id=45996).\n",
true );
}
-
- if ( !function_exists( 'mb_strlen' ) ) {
- $this->error(
- "MediaWiki now requires the mbstring PHP extension, your system doesn't have it.\n"
- . "ABORTING.\n",
- true );
- }
}
function execute() {
],
'messages' => [
'uploaddisabledtext',
+ 'upload-dialog-disabled',
'upload-foreign-cant-upload',
]
],
section: data.wpSection,
sectiontitle: '',
text: data.wpTextbox1,
+ summary: data.wpSummary,
contentmodel: data.model,
contentformat: data.format,
baserevid: data.parentRevId
return newText !== data.wpTextbox1;
}
- function onTextChanged() {
+ function onEditorIdle() {
if ( !isChanged() ) {
return;
}
stashEdit();
}
- function onTextKeyPress( e ) {
+ function onTextKeyUp( e ) {
// Ignore keystrokes that don't modify text, like cursor movements.
- // See <http://stackoverflow.com/q/2284844>.
- if ( e.which === 0 ) {
+ // See <http://www.javascripter.net/faq/keycodes.htm> and
+ // <http://www.quirksmode.org/js/keys.html>. We don't have to be
+ // exhaustive, because the cost of misfiring is low.
+ if ( ( e.which >= 33 && e.which <= 40 ) || ( e.which >= 16 && e.which <= 18 ) ) {
return;
}
clearTimeout( timer );
-
- if ( pending ) {
- pending.abort();
- }
-
- timer = setTimeout( onTextChanged, idleTimeout );
+ timer = setTimeout( onEditorIdle, idleTimeout );
}
function onFormLoaded() {
- // Reverts may involve use (undo) links; stash as they review the diff.
- // Since the form has a pre-filled summary, stash the edit immediately.
- if ( mw.util.getParamValue( 'undo' ) !== null ) {
+ if (
+ // Reverts may involve use (undo) links; stash as they review the diff.
+ // Since the form has a pre-filled summary, stash the edit immediately.
+ mw.util.getParamValue( 'undo' ) !== null
+ // Pressing "show changes" and "preview" also signify that the user will
+ // probably save the page soon
+ || $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1
+ ) {
stashEdit();
}
}
return;
}
- $text.on( { change: onTextChanged, keypress: onTextKeyPress } );
- $summary.on( { focus: onTextChanged } );
+ $text.on( { change: onEditorIdle, keyup: onTextKeyUp } );
+ $summary.on( { focus: onEditorIdle } );
onFormLoaded();
} );
}
#mw-search-top-table div.oo-ui-actionFieldLayout {
float: left;
+ width: 100%;
}
fieldset#mw-searchoptions {
margin: 0;
// However, if the target is a remote wiki, we must check the API
// to confirm that the target is one that this site is configured to
// support.
- if ( this.target === 'local' ) {
+ if ( validTargets.length === 0 ) {
+ this.apiPromise = $.Deferred().reject( 'upload-dialog-disabled' );
+ } else if ( this.target === 'local' ) {
// If local uploads were requested, but they are disabled, fail.
if ( !mw.config.get( 'wgEnableUploads' ) ) {
this.apiPromise = $.Deferred().reject( 'uploaddisabledtext' );
*
* This file is where we decide whether to initialise the modern run-time.
*/
-/*jshint unused: false, evil: true */
+/*jshint unused: false */
/*globals mw, RLQ: true, NORLQ: true, $VARS, $CODE, performance */
var mediaWikiLoadStart = ( new Date() ).getTime(),
'fileExists' => true
], $this->db->timestamp( '20010115123500' ), $user );
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+ $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
+ 'size' => 12345,
+ 'width' => 0,
+ 'height' => 0,
+ 'bits' => 0,
+ 'media_type' => MEDIATYPE_AUDIO,
+ 'mime' => 'application/ogg',
+ 'metadata' => serialize( [] ),
+ 'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ], $this->db->timestamp( '20010115123500' ), $user );
+
# A DjVu file
$image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
$image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
copy( "$IP/tests/phpunit/data/parser/LoremIpsum.djvu", "$dir/5/5f/LoremIpsum.djvu" );
wfMkdirParents( $dir . '/0/00', null, __METHOD__ );
copy( "$IP/tests/phpunit/data/parser/320x240.ogv", "$dir/0/00/Video.ogv" );
+ wfMkdirParents( $dir . '/4/41', null, __METHOD__ );
+ copy( "$IP/tests/phpunit/data/media/say-test.ogg", "$dir/4/41/Audio.oga" );
return;
}
"$dir/thumb/0/00/Video.ogv/270px--Video.ogv.jpg",
"$dir/thumb/0/00/Video.ogv/320px-seek=2-Video.ogv.jpg",
"$dir/thumb/0/00/Video.ogv/320px-seek=3.3666666666667-Video.ogv.jpg",
+ "$dir/4/41/Audio.oga",
]
);
"$dir/thumb/5/5f",
"$dir/thumb/5",
"$dir/thumb",
+ "$dir/4/41",
+ "$dir/4",
"$dir/math/f/a/5",
"$dir/math/f/a",
"$dir/math/f",
!! end
!! test
-Strip unsupported table tags
+Strip unsupported table tags, but introduce row wikitext as required
!! options
parsoid=html2wt
!! html/parsoid
<table>
+<caption>Test</caption>
<thead>
<tr>
<th>Month</th>
</table>
!! wikitext
{|
+|+Test
!Month
!Savings
+|-
|January
|$100
|February
|$80
+|-
|Sum
|$180
|}
+!! html/php+tidy
+<table>
+<caption>Test</caption>
+<tr>
+<th>Month</th>
+<th>Savings</th>
+</tr>
+<tr>
+<td>January</td>
+<td>$100</td>
+</tr>
+<tr>
+<td>February</td>
+<td>$80</td>
+</tr>
+<tr>
+<td>Sum</td>
+<td>$180</td>
+</tr>
+</table>
!! end
!! test
</table>
!! end
+!! test
+Serialize wikitext list items as HTML list items when embedded in a HTML list
+!! options
+parsoid=html2wt
+!! html
+<ul data-parsoid='{"stx": "html"}'>
+<li data-parsoid='{}'>a</li>
+<li>b</li>
+</ul>
+!! wikitext
+<ul>
+<li>a</li>
+<li>b</li>
+</ul>
+!! end
+
+# SSS FIXME: Is this actually a good thing given the
+# odd nested list output that is generated by MW?
+# <ul><li>foo<ul>..</ul></li></ul> instead of
+# <ul><li>foo</li><ul>..</ul></ul>
+!! test
+Wikitext lists can be nested inside HTML lists
+!! options
+parsoid=html2wt
+!! html
+<ul data-parsoid='{"stx": "html"}'>
+<li data-parsoid='{"stx": "html"}'>a
+<ul><li>b</li></ul>
+</li>
+</ul>
+
+<ul data-parsoid='{"stx": "html"}'>
+<li>x
+<ul><li>y</li></ul>
+</li>
+</ul>
+!! wikitext
+<ul>
+<li>a
+* b
+</li>
+</ul>
+
+<ul>
+<li>x
+* y
+</li>
+</ul>
+!! end
+
###
### Internal links
###
!! wikitext
{{NUMBEROFFILES}}
!! html
-<p>6
+<p>7
</p>
!! end
], $this->db->timestamp( '20010115123500' ), $user );
}
- # A DjVu file
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+ if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
+ $image->recordUpload2( '', 'An awesome hitsong ', 'Will it play', [
+ 'size' => 12345,
+ 'width' => 0,
+ 'height' => 0,
+ 'bits' => 0,
+ 'media_type' => MEDIATYPE_AUDIO,
+ 'mime' => 'application/ogg',
+ 'metadata' => serialize( [] ),
+ 'sha1' => Wikimedia\base_convert( '', 16, 36, 32 ),
+ 'fileExists' => true
+ ], $this->db->timestamp( '20010115123500' ), $user );
+ }
+
# A DjVu file
$image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
}
function getLength( $file ) {
+ if ( $this->isAudio( $file ) ) {
+ return 0.99875;
+ }
return 4.3666666666667;
}
function getBitRate( $file ) {
+ if ( $this->isAudio( $file ) ) {
+ return 41107;
+ }
return 590013;
}
function getWebType( $file ) {
+ if ( $this->isAudio( $file ) ) {
+ return "audio/ogg; codecs=\"vorbis\"";
+ }
return "video/ogg; codecs=\"theora\"";
}
function getFramerate( $file ) {
+ if ( $this->isAudio( $file ) ) {
+ return 0;
+ }
return 30;
}
}
/*global CompletenessTest, sinon */
-/*jshint evil: true */
( function ( $, mw, QUnit ) {
'use strict';