mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-24 00:13:36 +00:00
Support '&preload=...' etc. in new topic tool when '&dtpreload=1' is set
To avoid affecting existing preload forms, the new topic tool is only used when the 'dtpreload' query parameter is also set. Bug: T269310 Change-Id: I4ee024cc4760542790319f302f42b1b2389ac897
This commit is contained in:
parent
0814fd89ee
commit
5af3e90fec
|
@ -483,19 +483,18 @@ class HookUtils {
|
|||
public static function shouldOpenNewTopicTool( IContextSource $context ): bool {
|
||||
$req = $context->getRequest();
|
||||
$out = $context->getOutput();
|
||||
$hasPreload = $req->getCheck( 'editintro' ) || $req->getCheck( 'preload' ) ||
|
||||
$req->getCheck( 'preloadparams' ) || $req->getCheck( 'preloadtitle' ) ||
|
||||
// Switching or previewing from an external tool (T316333)
|
||||
$req->getCheck( 'wpTextbox1' );
|
||||
|
||||
return (
|
||||
// ?title=...&action=edit§ion=new
|
||||
// ?title=...&veaction=editsource§ion=new
|
||||
( $req->getRawVal( 'action' ) === 'edit' || $req->getRawVal( 'veaction' ) === 'editsource' ) &&
|
||||
$req->getRawVal( 'section' ) === 'new' &&
|
||||
// Adding a new topic with preloaded text is not supported yet (T269310)
|
||||
!(
|
||||
$req->getCheck( 'editintro' ) || $req->getCheck( 'preload' ) ||
|
||||
$req->getCheck( 'preloadparams' ) || $req->getCheck( 'preloadtitle' ) ||
|
||||
// Switching or previewing from an external tool (T316333)
|
||||
$req->getCheck( 'wpTextbox1' )
|
||||
) &&
|
||||
// Handle new topic with preloaded text only when requested (T269310)
|
||||
( $req->getCheck( 'dtpreload' ) || !$hasPreload ) &&
|
||||
// User has new topic tool enabled (and not using &dtenable=0)
|
||||
static::isFeatureEnabledForOutput( $out, static::NEWTOPICTOOL )
|
||||
);
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
* @param {number} oldId Revision ID of page at time of editing
|
||||
* @param {Object.<string,string>} notices Edit notices for the page where the reply is being saved.
|
||||
* Keys are message names; values are HTML to display.
|
||||
* @param {string} preloadContent Preload content, may be wikitext or HTML depending on `preloadContentMode`
|
||||
* @param {string} preloadContentMode 'source' or 'visual'
|
||||
*/
|
||||
function CommentDetails( pageName, oldId, notices ) {
|
||||
function CommentDetails( pageName, oldId, notices, preloadContent, preloadContentMode ) {
|
||||
this.pageName = pageName;
|
||||
this.oldId = oldId;
|
||||
this.notices = notices;
|
||||
this.preloadContent = preloadContent;
|
||||
this.preloadContentMode = preloadContentMode;
|
||||
}
|
||||
|
||||
OO.initClass( CommentDetails );
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
var
|
||||
utils = require( './utils.js' ),
|
||||
logger = require( './logger.js' ),
|
||||
controller = require( './controller.js' ),
|
||||
CommentController = require( './CommentController.js' ),
|
||||
|
@ -9,9 +8,10 @@ var
|
|||
* Handles setup, save and teardown of new topic widget
|
||||
*
|
||||
* @param {jQuery} $pageContainer Page container
|
||||
* @param {HeadingItem} threadItem
|
||||
* @param {ThreadItemSet} threadItemSet
|
||||
*/
|
||||
function NewTopicController( $pageContainer, threadItemSet ) {
|
||||
function NewTopicController( $pageContainer, threadItem, threadItemSet ) {
|
||||
this.container = new OO.ui.PanelLayout( {
|
||||
classes: [ 'ext-discussiontools-ui-newTopic' ],
|
||||
expanded: false,
|
||||
|
@ -36,14 +36,10 @@ function NewTopicController( $pageContainer, threadItemSet ) {
|
|||
this.container.$element.append( this.$notices, this.sectionTitleField.$element );
|
||||
|
||||
// HeadingItem representing the heading being added, so that we can pretend we're replying to it
|
||||
var threadItem = new HeadingItem( {
|
||||
startContainer: this.sectionTitleField.$element[ 0 ],
|
||||
startOffset: 0,
|
||||
endContainer: this.sectionTitleField.$element[ 0 ],
|
||||
endOffset: this.sectionTitleField.$element[ 0 ].childNodes.length
|
||||
}, 2 );
|
||||
threadItem.id = utils.NEW_TOPIC_COMMENT_ID;
|
||||
threadItem.isNewTopic = true;
|
||||
threadItem.range.startContainer = this.sectionTitleField.$element[ 0 ];
|
||||
threadItem.range.startOffset = 0;
|
||||
threadItem.range.endContainer = this.sectionTitleField.$element[ 0 ];
|
||||
threadItem.range.endOffset = this.sectionTitleField.$element[ 0 ].childNodes.length;
|
||||
|
||||
NewTopicController.super.call( this, $pageContainer, threadItem, threadItemSet );
|
||||
}
|
||||
|
@ -90,6 +86,10 @@ NewTopicController.prototype.setup = function ( mode ) {
|
|||
|
||||
NewTopicController.super.prototype.setup.call( this, mode );
|
||||
|
||||
if ( this.threadItem.preloadtitle ) {
|
||||
this.sectionTitle.setValue( this.threadItem.preloadtitle );
|
||||
}
|
||||
|
||||
// The section title field is added to the page immediately, we can scroll to the bottom and focus
|
||||
// it while the content field is still loading.
|
||||
rootScrollable.scrollTop = rootScrollable.scrollHeight;
|
||||
|
@ -115,7 +115,16 @@ NewTopicController.prototype.setup = function ( mode ) {
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
NewTopicController.prototype.setupReplyWidget = function ( replyWidget ) {
|
||||
NewTopicController.prototype.setupReplyWidget = function ( replyWidget, data ) {
|
||||
if ( replyWidget.commentDetails.preloadContent && ( !data || data.value === undefined ) ) {
|
||||
if ( replyWidget.commentDetails.preloadContentMode !== replyWidget.getMode() ) {
|
||||
// This should never happen
|
||||
throw new Error( 'Preload content was loaded for wrong mode' );
|
||||
}
|
||||
data = $.extend( {}, data, {
|
||||
value: replyWidget.commentDetails.preloadContent
|
||||
} );
|
||||
}
|
||||
NewTopicController.super.prototype.setupReplyWidget.apply( this, arguments );
|
||||
|
||||
this.$notices.empty();
|
||||
|
@ -144,6 +153,7 @@ NewTopicController.prototype.setupReplyWidget = function ( replyWidget ) {
|
|||
this.replyWidget.editSummaryInput.setValue( generatedSummary );
|
||||
}
|
||||
}
|
||||
this.replyWidget.storage.set( this.replyWidget.storagePrefix + '/title', this.sectionTitle.getValue() );
|
||||
|
||||
if ( this.replyWidget.modeTabSelect ) {
|
||||
// Start with the mode-select widget not-tabbable so focus will go from the title to the body
|
||||
|
@ -270,6 +280,11 @@ NewTopicController.prototype.teardown = function ( abandoned ) {
|
|||
url.searchParams.delete( 'action' );
|
||||
url.searchParams.delete( 'veaction' );
|
||||
url.searchParams.delete( 'section' );
|
||||
url.searchParams.delete( 'dtpreload' );
|
||||
url.searchParams.delete( 'editintro' );
|
||||
url.searchParams.delete( 'preload' );
|
||||
url.searchParams.delete( 'preloadparams[]' );
|
||||
url.searchParams.delete( 'preloadtitle' );
|
||||
history.replaceState( null, '', url );
|
||||
mw.config.set( 'wgDiscussionToolsStartNewTopicTool', false );
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ OO.mixinClass( ReplyLinksController, OO.EventEmitter );
|
|||
* @event link-click
|
||||
* @param {string} id
|
||||
* @param {jQuery} $linkSet
|
||||
* @param {jQuery} $link
|
||||
* @param {Object} [data]
|
||||
*/
|
||||
|
||||
/* Methods */
|
||||
|
@ -104,11 +106,32 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
|
|||
if ( !href ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = this.parseNewTopicLink( href );
|
||||
if ( !data ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !this.isActivationEvent( e ) ) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
this.emit( 'link-click', utils.NEW_TOPIC_COMMENT_ID, $( e.currentTarget ), data );
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given URL is a new topic link, and if so, return parsed parameters.
|
||||
*
|
||||
* @param {string} href
|
||||
* @return {Object|null} `null` if not a new topic link, parameters otherwise
|
||||
*/
|
||||
ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
|
||||
var url = new URL( href );
|
||||
|
||||
var title = mw.Title.newFromText( utils.getTitleFromUrl( href ) || '' );
|
||||
if ( !title ) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Recognize links to add a new topic:
|
||||
|
@ -121,7 +144,7 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
|
|||
var param = title.getMainText().slice( parserData.specialNewSectionName.length + 1 );
|
||||
title = mw.Title.newFromText( param );
|
||||
if ( !title ) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
} else if (
|
||||
|
@ -135,28 +158,34 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
|
|||
|
||||
} else {
|
||||
// Not a link to add a new topic
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( title.getPrefixedDb() !== mw.config.get( 'wgRelevantPageName' ) ) {
|
||||
// Link to add a section on another page, not supported yet (T282205)
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
url.searchParams.get( 'editintro' ) || url.searchParams.get( 'preload' ) ||
|
||||
url.searchParams.getAll( 'preloadparams[]' ).length || url.searchParams.get( 'preloadtitle' )
|
||||
) {
|
||||
// Adding a new topic with preloaded text is not supported yet (T269310)
|
||||
return;
|
||||
var data = {};
|
||||
if ( url.searchParams.get( 'editintro' ) ) {
|
||||
data.editintro = url.searchParams.get( 'editintro' );
|
||||
}
|
||||
if ( url.searchParams.get( 'preload' ) ) {
|
||||
data.preload = url.searchParams.get( 'preload' );
|
||||
}
|
||||
if ( url.searchParams.getAll( 'preloadparams[]' ).length ) {
|
||||
data.preloadparams = url.searchParams.getAll( 'preloadparams[]' );
|
||||
}
|
||||
if ( url.searchParams.get( 'preloadtitle' ) ) {
|
||||
data.preloadtitle = url.searchParams.get( 'preloadtitle' );
|
||||
}
|
||||
|
||||
if ( !this.isActivationEvent( e ) ) {
|
||||
return;
|
||||
// Handle new topic with preloaded text only when requested (T269310)
|
||||
if ( !url.searchParams.get( 'dtpreload' ) && !$.isEmptyObject( data ) ) {
|
||||
return null;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
this.emit( 'link-click', utils.NEW_TOPIC_COMMENT_ID, $( e.currentTarget ) );
|
||||
return data;
|
||||
};
|
||||
|
||||
ReplyLinksController.prototype.isActivationEvent = function ( e ) {
|
||||
|
|
|
@ -10,6 +10,7 @@ var
|
|||
Parser = require( './Parser.js' ),
|
||||
ThreadItemSet = require( './ThreadItemSet.js' ),
|
||||
CommentDetails = require( './CommentDetails.js' ),
|
||||
HeadingItem = require( './HeadingItem.js' ),
|
||||
ReplyLinksController = require( './ReplyLinksController.js' ),
|
||||
logger = require( './logger.js' ),
|
||||
utils = require( './utils.js' ),
|
||||
|
@ -43,13 +44,15 @@ function getApi() {
|
|||
*
|
||||
* @param {string} pageName Page title
|
||||
* @param {number} oldId Revision ID
|
||||
* @param {Object} [apiParams] Additional parameters for the API
|
||||
* @return {jQuery.Promise}
|
||||
*/
|
||||
function getPageData( pageName, oldId ) {
|
||||
function getPageData( pageName, oldId, apiParams ) {
|
||||
var api = getApi();
|
||||
apiParams = apiParams || {};
|
||||
|
||||
pageDataCache[ pageName ] = pageDataCache[ pageName ] || {};
|
||||
if ( pageDataCache[ pageName ][ oldId ] ) {
|
||||
if ( pageDataCache[ pageName ][ oldId ] && $.isEmptyObject( apiParams ) ) {
|
||||
return pageDataCache[ pageName ][ oldId ];
|
||||
}
|
||||
|
||||
|
@ -77,15 +80,15 @@ function getPageData( pageName, oldId ) {
|
|||
transcludedFromPromise = $.Deferred().resolve( {} ).promise();
|
||||
}
|
||||
|
||||
var veMetadataPromise = api.get( {
|
||||
var veMetadataPromise = api.get( $.extend( {
|
||||
action: 'visualeditor',
|
||||
paction: 'metadata',
|
||||
page: pageName
|
||||
} ).then( function ( response ) {
|
||||
}, apiParams ) ).then( function ( response ) {
|
||||
return OO.getProp( response, 'visualeditor' ) || [];
|
||||
} );
|
||||
|
||||
pageDataCache[ pageName ][ oldId ] = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
|
||||
var promise = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
|
||||
.then( function ( linterrors, transcludedfrom, metadata ) {
|
||||
return {
|
||||
linterrors: linterrors,
|
||||
|
@ -98,7 +101,12 @@ function getPageData( pageName, oldId ) {
|
|||
// Let caller handle the error
|
||||
return $.Deferred().rejectWith( this, arguments );
|
||||
} );
|
||||
return pageDataCache[ pageName ][ oldId ];
|
||||
|
||||
if ( $.isEmptyObject( apiParams ) ) {
|
||||
pageDataCache[ pageName ][ oldId ] = promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,8 +120,19 @@ function getPageData( pageName, oldId ) {
|
|||
*/
|
||||
function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||
var isNewTopic = threadItem.id === utils.NEW_TOPIC_COMMENT_ID;
|
||||
var defaultMode = mw.user.options.get( 'discussiontools-editmode' ) || mw.config.get( 'wgDiscussionToolsFallbackEditMode' );
|
||||
var apiParams = null;
|
||||
if ( isNewTopic ) {
|
||||
apiParams = {
|
||||
section: 'new',
|
||||
editintro: threadItem.editintro,
|
||||
preload: threadItem.preload,
|
||||
preloadparams: threadItem.preloadparams,
|
||||
paction: defaultMode === 'source' ? 'wikitext' : 'parse'
|
||||
};
|
||||
}
|
||||
|
||||
return getPageData( pageName, oldId )
|
||||
return getPageData( pageName, oldId, apiParams )
|
||||
.then( function ( response ) {
|
||||
var metadata = response.metadata,
|
||||
lintErrors = response.linterrors,
|
||||
|
@ -192,7 +211,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
|||
} ] } ).promise();
|
||||
}
|
||||
|
||||
return new CommentDetails( pageName, oldId, metadata.notices );
|
||||
return new CommentDetails( pageName, oldId, metadata.notices, metadata.content, defaultMode );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -286,26 +305,26 @@ function init( $container, state ) {
|
|||
/**
|
||||
* Setup comment controllers for each comment, and the new topic controller
|
||||
*
|
||||
* @param {string} commentId Comment ID, or NEW_TOPIC_COMMENT_ID constant
|
||||
* @param {ThreadItem} comment
|
||||
* @param {jQuery} $link Add section link for new topic controller
|
||||
* @param {string} [mode] Optionally force a mode, 'visual' or 'source'
|
||||
* @param {boolean} [hideErrors] Suppress errors, e.g. when restoring auto-save
|
||||
* @param {boolean} [suppressNotifications] Don't notify the user if recovering auto-save
|
||||
*/
|
||||
function setupController( commentId, $link, mode, hideErrors, suppressNotifications ) {
|
||||
function setupController( comment, $link, mode, hideErrors, suppressNotifications ) {
|
||||
var commentController, $addSectionLink;
|
||||
|
||||
if ( commentId === utils.NEW_TOPIC_COMMENT_ID ) {
|
||||
if ( comment.id === utils.NEW_TOPIC_COMMENT_ID ) {
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$addSectionLink = $( '#ca-addsection' ).find( 'a' );
|
||||
// When opening new topic tool using any link, always activate the link in page tabs too
|
||||
$link = $link.add( $addSectionLink );
|
||||
commentController = new NewTopicController( $pageContainer, pageThreads );
|
||||
commentController = new NewTopicController( $pageContainer, comment, pageThreads );
|
||||
} else {
|
||||
commentController = new CommentController( $pageContainer, pageThreads.findCommentById( commentId ), pageThreads );
|
||||
commentController = new CommentController( $pageContainer, comment, pageThreads );
|
||||
}
|
||||
|
||||
activeCommentId = commentId;
|
||||
activeCommentId = comment.id;
|
||||
activeController = commentController;
|
||||
linksController.setActiveLink( $link );
|
||||
|
||||
|
@ -345,11 +364,19 @@ function init( $container, state ) {
|
|||
}
|
||||
}
|
||||
|
||||
function newTopicComment( data ) {
|
||||
var comment = new HeadingItem( {}, 2 );
|
||||
comment.id = utils.NEW_TOPIC_COMMENT_ID;
|
||||
comment.isNewTopic = true;
|
||||
$.extend( comment, data );
|
||||
return comment;
|
||||
}
|
||||
|
||||
// Hook up each link to open a reply widget
|
||||
//
|
||||
// TODO: Allow users to use multiple reply widgets simultaneously.
|
||||
// Currently submitting a reply from one widget would also destroy the other ones.
|
||||
linksController.on( 'link-click', function ( commentId, $link ) {
|
||||
linksController.on( 'link-click', function ( commentId, $link, data ) {
|
||||
// If the reply widget is already open, activate it.
|
||||
// Reply links are also made unclickable using 'pointer-events' in CSS, but that doesn't happen
|
||||
// for new section links, because we don't have a good way of visually disabling them.
|
||||
|
@ -372,7 +399,13 @@ function init( $container, state ) {
|
|||
if ( activeController ) {
|
||||
return;
|
||||
}
|
||||
setupController( commentId, $link );
|
||||
var comment;
|
||||
if ( commentId !== utils.NEW_TOPIC_COMMENT_ID ) {
|
||||
comment = parser.findCommentById( comment.id );
|
||||
} else {
|
||||
comment = newTopicComment( data );
|
||||
}
|
||||
setupController( comment, $link );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
@ -389,7 +422,7 @@ function init( $container, state ) {
|
|||
if ( storage.get( 'reply/' + comment.id + '/saveable' ) ) {
|
||||
mode = storage.get( 'reply/' + comment.id + '/mode' );
|
||||
$link = $( commentNodes[ i ] );
|
||||
setupController( comment.id, $link, mode, true, !state.firstLoad );
|
||||
setupController( comment, $link, mode, true, !state.firstLoad );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -398,9 +431,10 @@ function init( $container, state ) {
|
|||
storage.get( 'reply/' + utils.NEW_TOPIC_COMMENT_ID + '/title' )
|
||||
) {
|
||||
mode = storage.get( 'reply/' + utils.NEW_TOPIC_COMMENT_ID + '/mode' );
|
||||
setupController( utils.NEW_TOPIC_COMMENT_ID, $( [] ), mode, true, !state.firstLoad );
|
||||
setupController( newTopicComment(), $( [] ), mode, true, !state.firstLoad );
|
||||
} else if ( mw.config.get( 'wgDiscussionToolsStartNewTopicTool' ) ) {
|
||||
setupController( utils.NEW_TOPIC_COMMENT_ID, $( [] ) );
|
||||
var data = linksController.parseNewTopicLink( location.href );
|
||||
setupController( newTopicComment( data ), $( [] ) );
|
||||
}
|
||||
}() );
|
||||
|
||||
|
|
Loading…
Reference in a new issue