Ori.livneh has uploaded a new change for review.
https://gerrit.wikimedia.org/r/178243
Change subject: Added ApiStashEdit module for pre-emptive edit parsing
......................................................................
Added ApiStashEdit module for pre-emptive edit parsing
* This lets edits be prepared while users enter edit summaries.
* The edit form will now make use of this API, controlled by
$wgAjaxEditStash.
Change-Id: I4f4057bc0d1d4a66a8f7cfb7cdc26d443a8eb0c4
(cherry picked from commit 3a6c9d36c9ebf7dea98ebc3f17a44569f2f220de)
---
M RELEASE-NOTES-1.25
M autoload.php
M includes/DefaultSettings.php
M includes/EditPage.php
M includes/api/ApiMain.php
A includes/api/ApiStashEdit.php
M includes/page/WikiPage.php
M resources/Resources.php
A resources/src/mediawiki.action/mediawiki.action.edit.stash.js
9 files changed, 405 insertions(+), 25 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/43/178243/1
diff --git a/RELEASE-NOTES-1.25 b/RELEASE-NOTES-1.25
index 5d423a1..cf6101f 100644
--- a/RELEASE-NOTES-1.25
+++ b/RELEASE-NOTES-1.25
@@ -27,6 +27,8 @@
longer be used. If extracts and page images are desired, the TextExtracts and
PageImages extensions are required.
* $wgOpenSearchTemplate is deprecated in favor of $wgOpenSearchTemplates.
+* Edits are now prepared via AJAX as users type edit summaries. This behavior
+ can be disabled via $wgAjaxEditStash.
=== New features in 1.25 ===
* (T64861) Updated plural rules to CLDR 26. Includes incompatible changes
diff --git a/autoload.php b/autoload.php
index a1b4bb9..9468374 100644
--- a/autoload.php
+++ b/autoload.php
@@ -120,6 +120,7 @@
'ApiRollback' => __DIR__ . '/includes/api/ApiRollback.php',
'ApiRsd' => __DIR__ . '/includes/api/ApiRsd.php',
'ApiSetNotificationTimestamp' => __DIR__ .
'/includes/api/ApiSetNotificationTimestamp.php',
+ 'ApiStashEdit' => __DIR__ . '/includes/api/ApiStashEdit.php',
'ApiTokens' => __DIR__ . '/includes/api/ApiTokens.php',
'ApiUnblock' => __DIR__ . '/includes/api/ApiUnblock.php',
'ApiUndelete' => __DIR__ . '/includes/api/ApiUndelete.php',
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index af9cda5..f43b4e8 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -5024,6 +5024,13 @@
'ip' => null,
'subnet' => null,
),
+ 'stashedit' => array( // stashing edits into cache before save
+ 'anon' => null,
+ 'user' => null,
+ 'newbie' => null,
+ 'ip' => null,
+ 'subnet' => null,
+ )
);
/**
@@ -7030,6 +7037,12 @@
$wgAjaxLicensePreview = true;
/**
+ * Have clients send edits to be prepared when filling in edit summaries.
+ * This gives the server a head start on the expensive parsing operation.
+ */
+$wgAjaxEditStash = true;
+
+/**
* Settings for incoming cross-site AJAX requests:
* Newer browsers support cross-site AJAX when the target resource allows
requests
* from the origin domain by the Access-Control-Allow-Origin header.
diff --git a/includes/EditPage.php b/includes/EditPage.php
index e51999d..4a013ef 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -321,6 +321,9 @@
/** @var int */
public $oldid = 0;
+ /** @var int */
+ public $parentRevId = 0;
+
/** @var string */
public $editintro = '';
@@ -881,6 +884,7 @@
}
$this->oldid = $request->getInt( 'oldid' );
+ $this->parentRevId = $request->getInt( 'parentRevId' );
$this->bot = $request->getBool( 'bot', true );
$this->nosummary = $request->getBool( 'nosummary' );
@@ -2071,7 +2075,7 @@
}
function setHeaders() {
- global $wgOut, $wgUser;
+ global $wgOut, $wgUser, $wgAjaxEditStash;
$wgOut->addModules( 'mediawiki.action.edit' );
$wgOut->addModuleStyles( 'mediawiki.action.edit.styles' );
@@ -2082,6 +2086,10 @@
if ( $wgUser->getOption( 'useeditwarning', false ) ) {
$wgOut->addModules( 'mediawiki.action.edit.editWarning'
);
+ }
+
+ if ( $wgAjaxEditStash ) {
+ $wgOut->addModules( 'mediawiki.action.edit.stash' );
}
$wgOut->setRobotPolicy( 'noindex,nofollow' );
@@ -2448,6 +2456,8 @@
$wgOut->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
$wgOut->addHTML( Html::hidden( 'oldid', $this->oldid ) );
+ $wgOut->addHTML( Html::hidden( 'parentRevId',
+ $this->parentRevId ?:
$this->mArticle->getRevIdFetched() ) );
$wgOut->addHTML( Html::hidden( 'format', $this->contentFormat )
);
$wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) );
@@ -2856,7 +2866,7 @@
global $wgOut;
$section = htmlspecialchars( $this->section );
$wgOut->addHTML( <<<HTML
-<input type='hidden' value="{$section}" name="wpSection" />
+<input type='hidden' value="{$section}" name="wpSection"/>
<input type='hidden' value="{$this->starttime}" name="wpStarttime" />
<input type='hidden' value="{$this->edittime}" name="wpEdittime" />
<input type='hidden' value="{$this->scrolltop}" name="wpScrolltop"
id="wpScrolltop" />
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 7600066..3d04f95 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -54,6 +54,7 @@
'query' => 'ApiQuery',
'expandtemplates' => 'ApiExpandTemplates',
'parse' => 'ApiParse',
+ 'stashedit' => 'ApiStashEdit',
'opensearch' => 'ApiOpenSearch',
'feedcontributions' => 'ApiFeedContributions',
'feedrecentchanges' => 'ApiFeedRecentChanges',
diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php
new file mode 100644
index 0000000..d8c7077
--- /dev/null
+++ b/includes/api/ApiStashEdit.php
@@ -0,0 +1,293 @@
+<?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
+ * @author Aaron Schulz
+ */
+
+/**
+ * Prepare and edit in shared cache so that it can be reused on edit
+ *
+ * This endpoint can be called via AJAX as the user focuses on the edit
+ * summary box. By the time of submission, the parse may have already
+ * finished, and can be immediately used on page save. Certain parser
+ * functions like {{REVISIONID}} or {{CURRENTTIME}} may cause the cache
+ * to not be used on edit. Template and files used are check for changes
+ * since the output was generated. The cache TTL is also kept low for sanity.
+ *
+ * @ingroup API
+ * @since 1.25
+ */
+class ApiStashEdit extends ApiBase {
+ public function execute() {
+ global $wgMemc;
+
+ $user = $this->getUser();
+ $params = $this->extractRequestParams();
+
+ $page = $this->getTitleOrPageId( $params );
+ $title = $page->getTitle();
+
+ if ( !ContentHandler::getForModelID( $params['contentmodel'] )
+ ->isSupportedFormat( $params['contentformat'] )
+ ) {
+ $this->dieUsage( "Unsupported content model/format",
'badmodelformat' );
+ }
+
+ $text = trim( $params['text'] ); // needed so the key SHA1's
match
+ $textContent = ContentHandler::makeContent(
+ $text, $title, $params['contentmodel'],
$params['contentformat'] );
+
+ $page = WikiPage::factory( $title );
+ if ( $page->exists() ) {
+ // Page exists: get the merged content with the
proposed change
+ $baseRev = Revision::newFromPageId( $page->getId(),
$params['baserevid'] );
+ if ( !$baseRev ) {
+ $this->dieUsage( "No revision ID
{$params['baserevid']}", 'missingrev' );
+ }
+ $currentRev = $page->getRevision();
+ if ( !$currentRev ) {
+ $this->dieUsage( "No current revision of page
ID {$page->getId()}", 'missingrev' );
+ }
+ // Merge in the new version of the section to get the
proposed version
+ $editContent = $page->replaceSectionAtRev(
+ $params['section'],
+ $textContent,
+ $params['sectiontitle'],
+ $baseRev->getId()
+ );
+ if ( !$editContent ) {
+ $this->dieUsage( "Could not merge updated
section.", 'replacefailed' );
+ }
+ if ( $currentRev->getId() == $baseRev->getId() ) {
+ // Base revision was still the latest; nothing
to merge
+ $content = $editContent;
+ } else {
+ // Merge the edit into the current version
+ $baseContent = $baseRev->getContent();
+ $currentContent = $currentRev->getContent();
+ if ( !$baseContent || !$currentContent ) {
+ $this->dieUsage( "Missing content for
page ID {$page->getId()}", 'missingrev' );
+ }
+ $handler = ContentHandler::getForModelID(
$baseContent->getModel() );
+ $content = $handler->merge3( $baseContent,
$editContent, $currentContent );
+ }
+ } else {
+ // New pages: use the user-provided content model
+ $content = $textContent;
+ }
+
+ if ( !$content ) { // merge3() failed
+ $this->getResult()->addValue( null,
+ $this->getModuleName(), array( 'status' =>
'editconflict' ) );
+ return;
+ }
+
+ // The user will abort the AJAX request by pressing "save", so
ignore that
+ ignore_user_abort( true );
+
+ // Get a key based on the source text, format, and user
preferences
+ $key = self::getStashKey( $title, $content, $user );
+ // De-duplicate requests on the same key
+ if ( $user->pingLimiter( 'stashedit' ) ) {
+ $editInfo = false;
+ $status = 'ratelimited';
+ } elseif ( $wgMemc->lock( $key, 0, 30 ) ) {
+ $contentFormat = $content->getDefaultFormat();
+ $editInfo = $page->prepareContentForEdit( $content,
null, $user, $contentFormat );
+ $wgMemc->unlock( $key );
+ $status = 'error'; // default
+ } else {
+ $editInfo = false;
+ $status = 'busy';
+ }
+
+ if ( $editInfo && $editInfo->output ) {
+ $parserOutput = $editInfo->output;
+ // If an item is renewed, mind the cache TTL determined
by config and parser functions
+ $since = time() - wfTimestamp( TS_UNIX,
$parserOutput->getTimestamp() );
+ $ttl = min( $parserOutput->getCacheExpiry() - $since, 5
* 60 );
+ if ( $ttl > 0 && !$parserOutput->getFlag(
'vary-revision' ) ) {
+ // Only store what is actually needed
+ $stashInfo = (object)array(
+ 'pstContent' => $editInfo->pstContent,
+ 'output' => $editInfo->output,
+ 'timestamp' => $editInfo->timestamp
+ );
+ $ok = $wgMemc->set( $key, $stashInfo, $ttl );
+ if ( $ok ) {
+ $status = 'stashed';
+ wfDebugLog( 'PreparedEdit', "Cached
parser output for key '$key'." );
+ } else {
+ $status = 'error';
+ wfDebugLog( 'PreparedEdit', "Failed to
cache parser output for key '$key'." );
+ }
+ } else {
+ $status = 'uncacheable';
+ wfDebugLog( 'PreparedEdit', "Uncacheable parser
output for key '$key'." );
+ }
+ }
+
+ $this->getResult()->addValue( null, $this->getModuleName(),
array( 'status' => $status ) );
+ }
+
+ /**
+ * Get the temporary prepared edit stash key for a user
+ *
+ * @param Title $title
+ * @param Content $content
+ * @param User $user User to get parser options from
+ * @return string
+ */
+ protected static function getStashKey(
+ Title $title, Content $content, User $user
+ ) {
+ return wfMemcKey( 'prepared-edit',
+ md5( $title->getPrefixedDBkey() ), // handle rename
races
+ $content->getModel(),
+ $content->getDefaultFormat(),
+ sha1( $content->serialize( $content->getDefaultFormat()
) ),
+ $user->getId() ?: md5( $user->getName() ), // account
for user parser options
+ $user->getId() ? $user->getTouched() : '-' // handle
preference change races
+ );
+ }
+
+ /**
+ * Check that a prepared edit is in cache and still up-to-date
+ *
+ * This method blocks if the prepared edit is already being rendered,
+ * waiting until rendering finishes before doing final validity checks.
+ *
+ * The cache is rejected if template or file changes are detected.
+ * Note that foreign template or file transclusions are not checked.
+ *
+ * The result is a map (pstContent,output,timestamp) with fields
+ * extracted directly from WikiPage::prepareContentForEdit().
+ *
+ * @param Title $title
+ * @param Content $content
+ * @param User $user User to get parser options from
+ * @return stdClass|bool Returns false on cache miss
+ */
+ public static function checkCache( Title $title, Content $content, User
$user ) {
+ global $wgMemc;
+
+ $key = self::getStashKey( $title, $content, $user );
+ $editInfo = $wgMemc->get( $key );
+ if ( !is_object( $editInfo ) ) {
+ $start = microtime( true );
+ // We ignore user aborts and keep parsing. Block on any
prior parsing
+ // so as to use it's results and make use of the time
spent parsing.
+ if ( $wgMemc->lock( $key, 30, 30 ) ) {
+ $editInfo = $wgMemc->get( $key );
+ $wgMemc->unlock( $key );
+ $sec = microtime( true ) - $start;
+ wfDebugLog( 'PreparedEdit', "Waited $sec
seconds on '$key'." );
+ }
+ }
+
+ if ( !is_object( $editInfo ) || !$editInfo->output ) {
+ return false;
+ }
+
+ $time = wfTimestamp( TS_UNIX, $editInfo->output->getTimestamp()
);
+ if ( ( time() - $time ) <= 3 ) {
+ wfDebugLog( 'PreparedEdit', "Timestamp-based cache hit
for key '$key'." );
+ return $editInfo; // assume nothing changed
+ }
+
+ $dbr = wfGetDB( DB_SLAVE );
+ // Check that no templates used in the output changed...
+ $cWhr = array(); // conditions to find changes/creations
+ $dWhr = array(); // conditions to find deletions
+ foreach ( $editInfo->output->getTemplateIds() as $ns => $stuff
) {
+ foreach ( $stuff as $dbkey => $revId ) {
+ $cWhr[] = array( 'page_namespace' => $ns,
'page_title' => $dbkey,
+ 'page_latest != ' . intval( $revId ) );
+ $dWhr[] = array( 'page_namespace' => $ns,
'page_title' => $dbkey );
+ }
+ }
+ $change = $dbr->selectField( 'page', '1', $dbr->makeList(
$cWhr, LIST_OR ), __METHOD__ );
+ $n = $dbr->selectField( 'page', 'COUNT(*)', $dbr->makeList(
$dWhr, LIST_OR ), __METHOD__ );
+ if ( $change || $n != count( $dWhr ) ) {
+ wfDebugLog( 'PreparedEdit', "Stale cache for key
'$key'; template changed." );
+ return false;
+ }
+
+ // Check that no files used in the output changed...
+ $cWhr = array(); // conditions to find changes/creations
+ $dWhr = array(); // conditions to find deletions
+ foreach ( $editInfo->output->getFileSearchOptions() as $name =>
$options ) {
+ $cWhr[] = array( 'img_name' => $dbkey,
+ 'img_sha1 != ' . $dbr->addQuotes( strval(
$options['sha1'] ) ) );
+ $dWhr[] = array( 'img_name' => $dbkey );
+ }
+ $change = $dbr->selectField( 'image', '1', $dbr->makeList(
$cWhr, LIST_OR ), __METHOD__ );
+ $n = $dbr->selectField( 'image', 'COUNT(*)', $dbr->makeList(
$dWhr, LIST_OR ), __METHOD__ );
+ if ( $change || $n != count( $dWhr ) ) {
+ wfDebugLog( 'PreparedEdit', "Stale cache for key
'$key'; file changed." );
+ return false;
+ }
+
+ wfDebugLog( 'PreparedEdit', "Cache hit for key '$key'." );
+
+ return $editInfo;
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'title' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'section' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'sectiontitle' => array(
+ ApiBase::PARAM_TYPE => 'string'
+ ),
+ 'text' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'contentmodel' => array(
+ ApiBase::PARAM_TYPE =>
ContentHandler::getContentModels(),
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'contentformat' => array(
+ ApiBase::PARAM_TYPE =>
ContentHandler::getAllContentFormats(),
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'baserevid' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ApiBase::PARAM_REQUIRED => true
+ )
+ );
+ }
+
+ function needsToken() {
+ return 'csrf';
+ }
+
+ function mustBePosted() {
+ return true;
+ }
+
+ function isInternal() {
+ return true;
+ }
+}
diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php
index b99093c..8b26c23 100644
--- a/includes/page/WikiPage.php
+++ b/includes/page/WikiPage.php
@@ -1696,7 +1696,7 @@
*
* @param bool|int $baseRevId The revision ID this edit was based off,
if any
* @param User $user The user doing the edit
- * @param string $serialisation_format Format for storing the content
in the
+ * @param string $serialFormat Format for storing the content in the
* database.
*
* @throws MWException
@@ -1717,7 +1717,7 @@
* @since 1.21
*/
public function doEditContent( Content $content, $summary, $flags = 0,
$baseRevId = false,
- User $user = null, $serialisation_format = null
+ User $user = null, $serialFormat = null
) {
global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol,
$wgUseNPPatrol;
@@ -1783,7 +1783,7 @@
$summary = $handler->getAutosummary( $old_content,
$content, $flags );
}
- $editInfo = $this->prepareContentForEdit( $content, null,
$user, $serialisation_format );
+ $editInfo = $this->prepareContentForEdit( $content, null,
$user, $serialFormat, true );
$serialized = $editInfo->pst;
/**
@@ -1825,7 +1825,7 @@
'user_text' => $user->getName(),
'timestamp' => $now,
'content_model' => $content->getModel(),
- 'content_format' => $serialisation_format,
+ 'content_format' => $serialFormat,
) ); // XXX: pass content object?!
$changed = !$content->equals( $old_content );
@@ -1951,7 +1951,7 @@
'user_text' => $user->getName(),
'timestamp' => $now,
'content_model' => $content->getModel(),
- 'content_format' =>
$serialisation_format,
+ 'content_format' => $serialFormat,
) );
$revisionId = $revision->insertOn( $dbw );
@@ -2068,49 +2068,71 @@
* @param Content $content
* @param int|null $revid
* @param User|null $user
- * @param string|null $serialization_format
+ * @param string|null $serialFormat
+ * @param bool $useCache Check shared prepared edit cache
*
- * @return bool|object
+ * @return object
*
* @since 1.21
*/
- public function prepareContentForEdit( Content $content, $revid = null,
User $user = null,
- $serialization_format = null
+ public function prepareContentForEdit(
+ Content $content, $revid = null, User $user = null,
$serialFormat = null, $useCache = false
) {
global $wgContLang, $wgUser;
+
$user = is_null( $user ) ? $wgUser : $user;
//XXX: check $user->getId() here???
- // Use a sane default for $serialization_format, see bug 57026
- if ( $serialization_format === null ) {
- $serialization_format =
$content->getContentHandler()->getDefaultFormat();
+ // Use a sane default for $serialFormat, see bug 57026
+ if ( $serialFormat === null ) {
+ $serialFormat =
$content->getContentHandler()->getDefaultFormat();
}
if ( $this->mPreparedEdit
&& $this->mPreparedEdit->newContent
&& $this->mPreparedEdit->newContent->equals( $content )
&& $this->mPreparedEdit->revid == $revid
- && $this->mPreparedEdit->format == $serialization_format
+ && $this->mPreparedEdit->format == $serialFormat
// XXX: also check $user here?
) {
// Already prepared
return $this->mPreparedEdit;
}
+ // The edit may have already been prepared via
api.php?action=stashedit
+ $cachedEdit = $useCache
+ ? ApiStashEdit::checkCache( $this->getTitle(),
$content, $user )
+ : false;
+
$popts = ParserOptions::newFromUserAndLang( $user, $wgContLang
);
wfRunHooks( 'ArticlePrepareTextForEdit', array( $this, $popts )
);
$edit = (object)array();
+ if ( $cachedEdit ) {
+ $edit->timestamp = $cachedEdit->timestamp;
+ } else {
+ $edit->timestamp = wfTimestampNow();
+ }
+ // @note: $cachedEdit is not used if the rev ID was referenced
in the text
$edit->revid = $revid;
- $edit->timestamp = wfTimestampNow();
- $edit->pstContent = $content ? $content->preSaveTransform(
$this->mTitle, $user, $popts ) : null;
+ if ( $cachedEdit ) {
+ $edit->pstContent = $cachedEdit->pstContent;
+ } else {
+ $edit->pstContent = $content
+ ? $content->preSaveTransform( $this->mTitle,
$user, $popts )
+ : null;
+ }
- $edit->format = $serialization_format;
+ $edit->format = $serialFormat;
$edit->popts = $this->makeParserOptions( 'canonical' );
- $edit->output = $edit->pstContent
- ? $edit->pstContent->getParserOutput( $this->mTitle,
$revid, $edit->popts )
- : null;
+ if ( $cachedEdit ) {
+ $edit->output = $cachedEdit->output;
+ } else {
+ $edit->output = $edit->pstContent
+ ? $edit->pstContent->getParserOutput(
$this->mTitle, $revid, $edit->popts )
+ : null;
+ }
$edit->newContent = $content;
$edit->oldContent = $this->getContent( Revision::RAW );
@@ -2118,7 +2140,7 @@
// NOTE: B/C for hooks! don't use these fields!
$edit->newText = $edit->newContent ?
ContentHandler::getContentText( $edit->newContent ) : '';
$edit->oldText = $edit->oldContent ?
ContentHandler::getContentText( $edit->oldContent ) : '';
- $edit->pst = $edit->pstContent ? $edit->pstContent->serialize(
$serialization_format ) : '';
+ $edit->pst = $edit->pstContent ? $edit->pstContent->serialize(
$serialFormat ) : '';
$this->mPreparedEdit = $edit;
return $edit;
@@ -2288,14 +2310,14 @@
* @param User $user The relevant user
* @param string $comment Comment submitted
* @param bool $minor Whereas it's a minor modification
- * @param string $serialisation_format Format for storing the content
in the database
+ * @param string $serialFormat Format for storing the content in the
database
*/
public function doQuickEditContent( Content $content, User $user,
$comment = '', $minor = false,
- $serialisation_format = null
+ $serialFormat = null
) {
wfProfileIn( __METHOD__ );
- $serialized = $content->serialize( $serialisation_format );
+ $serialized = $content->serialize( $serialFormat );
$dbw = wfGetDB( DB_MASTER );
$revision = new Revision( array(
diff --git a/resources/Resources.php b/resources/Resources.php
index 41e515a..90e922a 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -1051,6 +1051,13 @@
'mediawiki.action.history.diff',
),
),
+ 'mediawiki.action.edit.stash' => array(
+ 'scripts' =>
'resources/src/mediawiki.action/mediawiki.action.edit.stash.js',
+ 'dependencies' => array(
+ 'jquery.getAttrs',
+ 'mediawiki.api',
+ ),
+ ),
'mediawiki.action.history' => array(
'scripts' =>
'resources/src/mediawiki.action/mediawiki.action.history.js',
'styles' =>
'resources/src/mediawiki.action/mediawiki.action.history.css',
diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.stash.js
b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js
new file mode 100644
index 0000000..895cb03
--- /dev/null
+++ b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js
@@ -0,0 +1,31 @@
+/*!
+ * Scripts for pre-emptive edit preparing on action=edit
+ */
+( function ( mw, $ ) {
+ $( function () {
+ var api = new mw.Api(), pending = null, $form = $( '#editform'
);
+
+ function stashEdit( token ) {
+ var data = $form.serializeObject();
+
+ pending = api.post( {
+ action: 'stashedit',
+ token: token,
+ title: mw.config.get( 'wgPageName' ),
+ section: data.wpSection,
+ sectiontitle: data.wpSection === 'new' ?
data.wpSummary : '',
+ text: data.wpTextbox1,
+ contentmodel: data.model,
+ contentformat: data.format,
+ baserevid: data.parentRevId
+ } );
+ }
+
+ $form.on( 'change', function () {
+ if ( pending ) {
+ pending.abort();
+ }
+ api.getToken( 'edit' ).then( stashEdit );
+ } );
+ } );
+}( mediaWiki, jQuery ) );
--
To view, visit https://gerrit.wikimedia.org/r/178243
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4f4057bc0d1d4a66a8f7cfb7cdc26d443a8eb0c4
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: wmf/1.25wmf11
Gerrit-Owner: Ori.livneh <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits