Implement extension registration from an extension.json file
[mediawiki.git] / resources / src / mediawiki / mediawiki.feedback.js
blob867134c3ef59a926f5a84c39a39f4be54af646d2
1 /*!
2  * mediawiki.feedback
3  *
4  * @author Ryan Kaldari, 2010
5  * @author Neil Kandalgaonkar, 2010-11
6  * @since 1.19
7  */
8 ( function ( mw, $ ) {
9         /**
10          * This is a way of getting simple feedback from users. It's useful
11          * for testing new features -- users can give you feedback without
12          * the difficulty of opening a whole new talk page. For this reason,
13          * it also tends to collect a wider range of both positive and negative
14          * comments. However you do need to tend to the feedback page. It will
15          * get long relatively quickly, and you often get multiple messages
16          * reporting the same issue.
17          *
18          * It takes the form of thing on your page which, when clicked, opens a small
19          * dialog box. Submitting that dialog box appends its contents to a
20          * wiki page that you specify, as a new section.
21          *
22          * This feature works with classic MediaWiki pages
23          * and is not compatible with LiquidThreads or Flow.
24          *
25          * Minimal usage example:
26          *
27          *     var feedback = new mw.Feedback();
28          *     $( '#myButton' ).click( function () { feedback.launch(); } );
29          *
30          * You can also launch the feedback form with a prefilled subject and body.
31          * See the docs for the #launch() method.
32          *
33          * @class
34          * @constructor
35          * @param {Object} [options]
36          * @param {mw.Api} [options.api] if omitted, will just create a standard API
37          * @param {mw.Title} [options.title="Feedback"] The title of the page where you collect
38          * feedback.
39          * @param {string} [options.dialogTitleMessageKey="feedback-submit"] Message key for the
40          * title of the dialog box
41          * @param {string} [options.bugsLink="//bugzilla.wikimedia.org/enter_bug.cgi"] URL where
42          * bugs can be posted
43          * @param {mw.Uri|string} [options.bugsListLink="//bugzilla.wikimedia.org/query.cgi"]
44          * URL where bugs can be listed
45          */
46         mw.Feedback = function ( options ) {
47                 if ( options === undefined ) {
48                         options = {};
49                 }
51                 if ( options.api === undefined ) {
52                         options.api = new mw.Api();
53                 }
55                 if ( options.title === undefined ) {
56                         options.title = new mw.Title( 'Feedback' );
57                 }
59                 if ( options.dialogTitleMessageKey === undefined ) {
60                         options.dialogTitleMessageKey = 'feedback-submit';
61                 }
63                 if ( options.bugsLink === undefined ) {
64                         options.bugsLink = '//bugzilla.wikimedia.org/enter_bug.cgi';
65                 }
67                 if ( options.bugsListLink === undefined ) {
68                         options.bugsListLink = '//bugzilla.wikimedia.org/query.cgi';
69                 }
71                 $.extend( this, options );
72                 this.setup();
73         };
75         mw.Feedback.prototype = {
76                 /**
77                  * Sets up interface
78                  */
79                 setup: function () {
80                         var $feedbackPageLink,
81                                 $bugNoteLink,
82                                 $bugsListLink,
83                                 fb = this;
85                         $feedbackPageLink = $( '<a>' )
86                                 .attr( {
87                                         href: fb.title.getUrl(),
88                                         target: '_blank'
89                                 } )
90                                 .css( {
91                                         whiteSpace: 'nowrap'
92                                 } );
94                         $bugNoteLink = $( '<a>' ).attr( { href: '#' } ).click( function () {
95                                 fb.displayBugs();
96                         } );
98                         $bugsListLink = $( '<a>' ).attr( {
99                                 href: fb.bugsListLink,
100                                 target: '_blank'
101                         } );
103                         // TODO: Use a stylesheet instead of these inline styles in the template
104                         this.$dialog = mw.template.get( 'mediawiki.feedback', 'dialog.html' ).render();
105                         this.$dialog.find( '.feedback-mode small p' ).msg(
106                                 'feedback-bugornote',
107                                 $bugNoteLink,
108                                 fb.title.getNameText(),
109                                 $feedbackPageLink.clone()
110                         );
111                         this.$dialog.find( '.feedback-form .subject span' ).msg( 'feedback-subject' );
112                         this.$dialog.find( '.feedback-form .message span' ).msg( 'feedback-message' );
113                         this.$dialog.find( '.feedback-bugs p' ).msg( 'feedback-bugcheck', $bugsListLink );
114                         this.$dialog.find( '.feedback-submitting span' ).msg( 'feedback-adding' );
115                         this.$dialog.find( '.feedback-thanks' ).msg( 'feedback-thanks', fb.title.getNameText(),
116                                 $feedbackPageLink.clone() );
118                         this.$dialog.dialog( {
119                                 width: 500,
120                                 autoOpen: false,
121                                 title: mw.message( this.dialogTitleMessageKey ).escaped(),
122                                 modal: true,
123                                 buttons: fb.buttons
124                         } );
126                         this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get( 0 );
127                         this.messageInput = this.$dialog.find( 'textarea.feedback-message' ).get( 0 );
128                 },
130                 /**
131                  * Displays a section of the dialog.
132                  *
133                  * @param {"form"|"bugs"|"submitting"|"thanks"|"error"} s
134                  * The section of the dialog to show.
135                  */
136                 display: function ( s ) {
137                         // Hide the buttons
138                         this.$dialog.dialog( { buttons: {} } );
139                         // Hide everything
140                         this.$dialog.find( '.feedback-mode' ).hide();
141                         // Show the desired div
142                         this.$dialog.find( '.feedback-' + s ).show();
143                 },
145                 /**
146                  * Display the submitting section.
147                  */
148                 displaySubmitting: function () {
149                         this.display( 'submitting' );
150                 },
152                 /**
153                  * Display the bugs section.
154                  */
155                 displayBugs: function () {
156                         var fb = this,
157                                 bugsButtons = {};
159                         this.display( 'bugs' );
160                         bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () {
161                                 window.open( fb.bugsLink, '_blank' );
162                         };
163                         bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
164                                 fb.cancel();
165                         };
166                         this.$dialog.dialog( {
167                                 buttons: bugsButtons
168                         } );
169                 },
171                 /**
172                  * Display the thanks section.
173                  */
174                 displayThanks: function () {
175                         var fb = this,
176                                 closeButton = {};
178                         this.display( 'thanks' );
179                         closeButton[ mw.msg( 'feedback-close' ) ] = function () {
180                                 fb.$dialog.dialog( 'close' );
181                         };
182                         this.$dialog.dialog( {
183                                 buttons: closeButton
184                         } );
185                 },
187                 /**
188                  * Display the feedback form
189                  * @param {Object} [contents] Prefilled contents for the feedback form.
190                  * @param {string} [contents.subject] The subject of the feedback
191                  * @param {string} [contents.message] The content of the feedback
192                  */
193                 displayForm: function ( contents ) {
194                         var fb = this,
195                                 formButtons = {};
197                         this.subjectInput.value = ( contents && contents.subject ) ? contents.subject : '';
198                         this.messageInput.value = ( contents && contents.message ) ? contents.message : '';
200                         this.display( 'form' );
202                         // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
203                         formButtons[ mw.msg( 'feedback-submit' ) ] = function () {
204                                 fb.submit();
205                         };
206                         formButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
207                                 fb.cancel();
208                         };
209                         this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back
210                 },
212                 /**
213                  * Display an error on the form.
214                  *
215                  * @param {string} message Should be a valid message key.
216                  */
217                 displayError: function ( message ) {
218                         var fb = this,
219                                 closeButton = {};
221                         this.display( 'error' );
222                         this.$dialog.find( '.feedback-error-msg' ).msg( message );
223                         closeButton[ mw.msg( 'feedback-close' ) ] = function () {
224                                 fb.$dialog.dialog( 'close' );
225                         };
226                         this.$dialog.dialog( { buttons: closeButton } );
227                 },
229                 /**
230                  * Close the feedback form.
231                  */
232                 cancel: function () {
233                         this.$dialog.dialog( 'close' );
234                 },
236                 /**
237                  * Submit the feedback form.
238                  */
239                 submit: function () {
240                         var subject, message,
241                                 fb = this;
243                         // Get the values to submit.
244                         subject = $.trim( this.subjectInput.value );
246                         // We used to include "mw.html.escape( navigator.userAgent )" but there are legal issues
247                         // with posting this without their explicit consent
248                         message = $.trim( this.messageInput.value );
249                         if ( message.indexOf( '~~~' ) === -1 ) {
250                                 message += ' ~~~~';
251                         }
253                         this.displaySubmitting();
255                         // Post the message, resolving redirects
256                         this.api.newSection(
257                                 this.title,
258                                 subject,
259                                 message,
260                                 { redirect: true }
261                         )
262                         .done( function ( result ) {
263                                 if ( result.edit.result === 'Success' ) {
264                                         fb.displayThanks();
265                                 } else {
266                                         // unknown API result
267                                         fb.displayError( 'feedback-error1' );
268                                 }
269                         } )
270                         .fail( function ( code, result ) {
271                                 if ( code === 'http' ) {
272                                         // ajax request failed
273                                         fb.displayError( 'feedback-error3' );
274                                         mw.log.warn( 'Feedback report failed with HTTP error: ' +  result.textStatus );
275                                 } else {
276                                         fb.displayError( 'feedback-error2' );
277                                         mw.log.warn( 'Feedback report failed with API error: ' +  code );
278                                 }
279                         } );
280                 },
282                 /**
283                  * Modify the display form, and then open it, focusing interface on the subject.
284                  * @param {Object} [contents] Prefilled contents for the feedback form.
285                  * @param {string} [contents.subject] The subject of the feedback
286                  * @param {string} [contents.message] The content of the feedback
287                  */
288                 launch: function ( contents ) {
289                         this.displayForm( contents );
290                         this.$dialog.dialog( 'open' );
291                         this.subjectInput.focus();
292                 }
293         };
294 }( mediaWiki, jQuery ) );