3 * @classdesc Feedback dialog for use within the context mw.Feedback. Typically
4 * constructed using {@link mw.Feedback#launch} instead of directly using the constructor.
5 * @memberof mw.Feedback
6 * @extends OO.ui.ProcessDialog
9 * @description Create an instance of `mw.Feedback.Dialog`.
10 * @param {Object} config Configuration object
12 function FeedbackDialog( config ) {
14 FeedbackDialog.super.call( this, config );
17 this.feedbackPageTitle = null;
19 this.$element.addClass( 'mwFeedback-Dialog' );
22 OO.inheritClass( FeedbackDialog, OO.ui.ProcessDialog );
24 /* Static properties */
25 FeedbackDialog.static.name = 'mwFeedbackDialog';
26 FeedbackDialog.static.title = mw.msg( 'feedback-dialog-title' );
27 FeedbackDialog.static.size = 'medium';
28 FeedbackDialog.static.actions = [
31 label: mw.msg( 'feedback-submit' ),
32 flags: [ 'primary', 'progressive' ]
36 label: mw.msg( 'feedback-external-bug-report-button' ),
41 label: mw.msg( 'feedback-cancel' ),
47 * Initializes the dialog.
52 FeedbackDialog.prototype.initialize = function () {
54 FeedbackDialog.super.prototype.initialize.call( this );
56 this.feedbackPanel = new OO.ui.PanelLayout( {
63 this.feedbackMessageLabel = new OO.ui.LabelWidget( {
64 classes: [ 'mw-feedbackDialog-welcome-message' ]
66 this.feedbackSubjectInput = new OO.ui.TextInputWidget( {
69 this.feedbackMessageInput = new OO.ui.MultilineTextInputWidget( {
72 const feedbackSubjectFieldLayout = new OO.ui.FieldLayout( this.feedbackSubjectInput, {
73 label: mw.msg( 'feedback-subject' )
75 const feedbackMessageFieldLayout = new OO.ui.FieldLayout( this.feedbackMessageInput, {
76 label: mw.msg( 'feedback-message' )
78 const feedbackFieldsetLayout = new OO.ui.FieldsetLayout( {
79 items: [ feedbackSubjectFieldLayout, feedbackMessageFieldLayout ],
80 classes: [ 'mw-feedbackDialog-feedback-form' ]
83 // Useragent terms of use
84 this.useragentCheckbox = new OO.ui.CheckboxInputWidget();
85 this.useragentFieldLayout = new OO.ui.FieldLayout( this.useragentCheckbox, {
86 classes: [ 'mw-feedbackDialog-feedback-terms' ],
90 const $termsOfUseLabelText = $( '<p>' ).append( mw.message( 'feedback-termsofuse' ).parseDom() );
91 $termsOfUseLabelText.find( 'a' ).attr( 'target', '_blank' );
92 const termsOfUseLabel = new OO.ui.LabelWidget( {
93 classes: [ 'mw-feedbackDialog-feedback-termsofuse' ],
94 label: $termsOfUseLabelText
97 this.feedbackPanel.$element.append(
98 this.feedbackMessageLabel.$element,
99 feedbackFieldsetLayout.$element,
100 this.useragentFieldLayout.$element,
101 termsOfUseLabel.$element
105 this.feedbackSubjectInput.connect( this, { change: 'validateFeedbackForm' } );
106 this.feedbackMessageInput.connect( this, { change: 'validateFeedbackForm' } );
107 this.feedbackMessageInput.connect( this, { change: 'updateSize' } );
108 this.useragentCheckbox.connect( this, { change: 'validateFeedbackForm' } );
110 this.$body.append( this.feedbackPanel.$element );
114 * Validate the feedback form.
116 * @method validateFeedbackForm
117 * @memberof mw.Feedback.Dialog
119 FeedbackDialog.prototype.validateFeedbackForm = function () {
122 !this.useragentMandatory ||
123 this.useragentCheckbox.isSelected()
125 this.feedbackSubjectInput.getValue()
128 this.actions.setAbilities( { submit: isValid } );
135 FeedbackDialog.prototype.getBodyHeight = function () {
136 return this.feedbackPanel.$element.outerHeight( true );
143 FeedbackDialog.prototype.getSetupProcess = function ( data ) {
144 return FeedbackDialog.super.prototype.getSetupProcess.call( this, data )
146 // Get the URL of the target page, we want to use that in links in the intro
147 // and in the success dialog
148 if ( data.foreignApi ) {
149 return data.foreignApi.get( {
154 titles: data.settings.title.getPrefixedText()
155 } ).then( ( response ) => {
156 this.feedbackPageUrl = OO.getProp( response, 'query', 'pages', 0, 'canonicalurl' );
159 this.feedbackPageUrl = data.settings.title.getUrl();
163 const settings = data.settings;
164 data.contents = data.contents || {};
166 // Prefill subject/message
167 this.feedbackSubjectInput.setValue( data.contents.subject );
168 this.feedbackMessageInput.setValue( data.contents.message );
171 this.messagePosterPromise = settings.messagePosterPromise;
172 this.setBugReportLink( settings.bugsTaskSubmissionLink );
173 this.feedbackPageTitle = settings.title;
174 this.feedbackPageName = settings.title.getMainText();
176 // Useragent checkbox
177 if ( settings.useragentCheckbox.show ) {
178 this.useragentFieldLayout.setLabel( settings.useragentCheckbox.message );
181 this.useragentMandatory = settings.useragentCheckbox.mandatory;
182 this.useragentFieldLayout.toggle( settings.useragentCheckbox.show );
184 const $link = $( '<a>' )
185 .attr( 'href', this.feedbackPageUrl )
186 .attr( 'target', '_blank' )
187 .text( this.feedbackPageName );
188 this.feedbackMessageLabel.setLabel(
189 mw.message( 'feedback-dialog-intro', $link ).parseDom()
192 this.validateFeedbackForm();
200 FeedbackDialog.prototype.getReadyProcess = function ( data ) {
201 return FeedbackDialog.super.prototype.getReadyProcess.call( this, data )
203 this.feedbackSubjectInput.focus();
211 FeedbackDialog.prototype.getActionProcess = function ( action ) {
212 if ( action === 'cancel' ) {
213 return new OO.ui.Process( () => {
214 this.close( { action: action } );
216 } else if ( action === 'external' ) {
217 return new OO.ui.Process( () => {
218 // Open in a new window
219 window.open( this.getBugReportLink(), '_blank' );
223 } else if ( action === 'submit' ) {
224 return new OO.ui.Process( () => {
225 const userAgentMessage = ':' +
227 mw.msg( 'feedback-useragent' ) +
229 mw.html.escape( navigator.userAgent ) +
231 subject = this.feedbackSubjectInput.getValue();
232 let message = this.feedbackMessageInput.getValue();
234 // Add user agent if checkbox is selected
235 if ( this.useragentCheckbox.isSelected() ) {
236 message = userAgentMessage + message;
240 return this.messagePosterPromise.then( ( poster ) => this.postMessage( poster, subject, message ), () => {
241 this.status = 'error4';
242 mw.log.warn( 'Feedback report failed because MessagePoster could not be fetched' );
245 }, () => this.getErrorMessage() );
248 // Fallback to parent handler
249 return FeedbackDialog.super.prototype.getActionProcess.call( this, action );
253 * Returns an error message for the current status.
257 * @return {OO.ui.Error}
259 FeedbackDialog.prototype.getErrorMessage = function () {
260 if ( this.$statusFromApi ) {
261 return new OO.ui.Error( this.$statusFromApi );
263 // The following messages can be used here:
266 return new OO.ui.Error( mw.msg( 'feedback-' + this.status ) );
274 * @param {mw.messagePoster.MessagePoster} poster Poster implementation used to leave feedback
275 * @param {string} subject Subject of message
276 * @param {string} message Body of message
277 * @return {jQuery.Promise} Promise representing success of message posting action
279 FeedbackDialog.prototype.postMessage = function ( poster, subject, message ) {
284 this.status = 'submitted';
285 }, ( mainCode, secondaryCode, details ) => {
286 if ( mainCode === 'api-fail' ) {
287 if ( secondaryCode === 'http' ) {
288 this.status = 'error3';
289 // ajax request failed
290 mw.log.warn( 'Feedback report failed with HTTP error: ' + details.textStatus );
292 this.status = 'error2';
293 mw.log.warn( 'Feedback report failed with API error: ' + secondaryCode );
295 this.$statusFromApi = ( new mw.Api() ).getErrorMessage( details );
297 this.status = 'error1';
306 FeedbackDialog.prototype.getTeardownProcess = function ( data ) {
307 return FeedbackDialog.super.prototype.getTeardownProcess.call( this, data )
309 this.emit( 'submit', this.status, this.feedbackPageName, this.feedbackPageUrl );
312 this.feedbackPageTitle = null;
313 this.feedbackSubjectInput.setValue( '' );
314 this.feedbackMessageInput.setValue( '' );
315 this.useragentCheckbox.setSelected( false );
320 * Set the bug report link.
322 * @method setBugReportLink
323 * @param {string} link Link to the external bug report form
324 * @memberof mw.Feedback.Dialog
326 FeedbackDialog.prototype.setBugReportLink = function ( link ) {
327 this.bugReportLink = link;
331 * Get the bug report link.
333 * @method getBugReportLink
334 * @return {string} Link to the external bug report form
335 * @memberof mw.Feedback.Dialog
337 FeedbackDialog.prototype.getBugReportLink = function () {
338 return this.bugReportLink;
341 module.exports = FeedbackDialog;