PrefixSearch: Avoid notice when no subpage exists
[mediawiki.git] / resources / src / mediawiki / mediawiki.feedback.js
blob88de7d8c5777f1270ab1c968cc753193dcfab6cb
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
104                         this.$dialog =
105                                 $( '<div style="position: relative;"></div>' ).append(
106                                         $( '<div class="feedback-mode feedback-form"></div>' ).append(
107                                                 $( '<small>' ).append(
108                                                         $( '<p>' ).msg(
109                                                                 'feedback-bugornote',
110                                                                 $bugNoteLink,
111                                                                 fb.title.getNameText(),
112                                                                 $feedbackPageLink.clone()
113                                                         )
114                                                 ),
115                                                 $( '<div style="margin-top: 1em;"></div>' ).append(
116                                                         mw.msg( 'feedback-subject' ),
117                                                         $( '<br>' ),
118                                                         $( '<input type="text" class="feedback-subject" name="subject" maxlength="60" style="width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;"/>' )
119                                                 ),
120                                                 $( '<div style="margin-top: 0.4em;"></div>' ).append(
121                                                         mw.msg( 'feedback-message' ),
122                                                         $( '<br>' ),
123                                                         $( '<textarea name="message" class="feedback-message" rows="5" cols="60"></textarea>' )
124                                                 )
125                                         ),
126                                         $( '<div class="feedback-mode feedback-bugs"></div>' ).append(
127                                                 $( '<p>' ).msg( 'feedback-bugcheck', $bugsListLink )
128                                         ),
129                                         $( '<div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;"></div>' ).append(
130                                                 mw.msg( 'feedback-adding' ),
131                                                 $( '<br>' ),
132                                                 $( '<span class="feedback-spinner"></span>' )
133                                         ),
134                                         $( '<div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div>' ).msg(
135                                                 'feedback-thanks', fb.title.getNameText(), $feedbackPageLink.clone()
136                                         ),
137                                         $( '<div class="feedback-mode feedback-error" style="position: relative;"></div>' ).append(
138                                                 $( '<div class="feedback-error-msg style="color: #990000; margin-top: 0.4em;"></div>' )
139                                         )
140                                 );
142                         this.$dialog.dialog( {
143                                 width: 500,
144                                 autoOpen: false,
145                                 title: mw.msg( this.dialogTitleMessageKey ),
146                                 modal: true,
147                                 buttons: fb.buttons
148                         } );
150                         this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get( 0 );
151                         this.messageInput = this.$dialog.find( 'textarea.feedback-message' ).get( 0 );
152                 },
154                 /**
155                  * Displays a section of the dialog.
156                  *
157                  * @param {"form"|"bugs"|"submitting"|"thanks"|"error"} s
158                  * The section of the dialog to show.
159                  */
160                 display: function ( s ) {
161                         // Hide the buttons
162                         this.$dialog.dialog( { buttons: {} } );
163                         // Hide everything
164                         this.$dialog.find( '.feedback-mode' ).hide();
165                         // Show the desired div
166                         this.$dialog.find( '.feedback-' + s ).show();
167                 },
169                 /**
170                  * Display the submitting section.
171                  */
172                 displaySubmitting: function () {
173                         this.display( 'submitting' );
174                 },
176                 /**
177                  * Display the bugs section.
178                  */
179                 displayBugs: function () {
180                         var fb = this,
181                                 bugsButtons = {};
183                         this.display( 'bugs' );
184                         bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () {
185                                 window.open( fb.bugsLink, '_blank' );
186                         };
187                         bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
188                                 fb.cancel();
189                         };
190                         this.$dialog.dialog( {
191                                 buttons: bugsButtons
192                         } );
193                 },
195                 /**
196                  * Display the thanks section.
197                  */
198                 displayThanks: function () {
199                         var fb = this,
200                                 closeButton = {};
202                         this.display( 'thanks' );
203                         closeButton[ mw.msg( 'feedback-close' ) ] = function () {
204                                 fb.$dialog.dialog( 'close' );
205                         };
206                         this.$dialog.dialog( {
207                                 buttons: closeButton
208                         } );
209                 },
211                 /**
212                  * Display the feedback form
213                  * @param {Object} [contents] Prefilled contents for the feedback form.
214                  * @param {string} [contents.subject] The subject of the feedback
215                  * @param {string} [contents.message] The content of the feedback
216                  */
217                 displayForm: function ( contents ) {
218                         var fb = this,
219                                 formButtons = {};
221                         this.subjectInput.value = ( contents && contents.subject ) ? contents.subject : '';
222                         this.messageInput.value = ( contents && contents.message ) ? contents.message : '';
224                         this.display( 'form' );
226                         // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
227                         formButtons[ mw.msg( 'feedback-submit' ) ] = function () {
228                                 fb.submit();
229                         };
230                         formButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
231                                 fb.cancel();
232                         };
233                         this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back
234                 },
236                 /**
237                  * Display an error on the form.
238                  *
239                  * @param {string} message Should be a valid message key.
240                  */
241                 displayError: function ( message ) {
242                         var fb = this,
243                                 closeButton = {};
245                         this.display( 'error' );
246                         this.$dialog.find( '.feedback-error-msg' ).msg( message );
247                         closeButton[ mw.msg( 'feedback-close' ) ] = function () {
248                                 fb.$dialog.dialog( 'close' );
249                         };
250                         this.$dialog.dialog( { buttons: closeButton } );
251                 },
253                 /**
254                  * Close the feedback form.
255                  */
256                 cancel: function () {
257                         this.$dialog.dialog( 'close' );
258                 },
260                 /**
261                  * Submit the feedback form.
262                  */
263                 submit: function () {
264                         var subject, message,
265                                 fb = this;
267                         function ok( result ) {
268                                 if ( result.edit !== undefined ) {
269                                         if ( result.edit.result === 'Success' ) {
270                                                 fb.displayThanks();
271                                         } else {
272                                                 // unknown API result
273                                                 fb.displayError( 'feedback-error1' );
274                                         }
275                                 } else {
276                                         // edit failed
277                                         fb.displayError( 'feedback-error2' );
278                                 }
279                         }
281                         function err() {
282                                 // ajax request failed
283                                 fb.displayError( 'feedback-error3' );
284                         }
286                         // Get the values to submit.
287                         subject = this.subjectInput.value;
289                         // We used to include "mw.html.escape( navigator.userAgent )" but there are legal issues
290                         // with posting this without their explicit consent
291                         message = this.messageInput.value;
292                         if ( message.indexOf( '~~~' ) === -1 ) {
293                                 message += ' ~~~~';
294                         }
296                         this.displaySubmitting();
298                         // Post the message, resolving redirects
299                         this.api.newSection(
300                                 this.title,
301                                 subject,
302                                 message,
303                                 { redirect: true }
304                         ).done( ok ).fail( err );
305                 },
307                 /**
308                  * Modify the display form, and then open it, focusing interface on the subject.
309                  * @param {Object} [contents] Prefilled contents for the feedback form.
310                  * @param {string} [contents.subject] The subject of the feedback
311                  * @param {string} [contents.message] The content of the feedback
312                  */
313                 launch: function ( contents ) {
314                         this.displayForm( contents );
315                         this.$dialog.dialog( 'open' );
316                         this.subjectInput.focus();
317                 }
318         };
319 }( mediaWiki, jQuery ) );