Refactoring mw.Feedback to work with OOUI elements
authorMoriel Schottlender <moriel@gmail.com>
Tue, 24 Feb 2015 22:59:33 +0000 (14:59 -0800)
committerMoriel Schottlender <moriel@gmail.com>
Fri, 27 Feb 2015 00:54:52 +0000 (16:54 -0800)
Transform mw.Feedback into an ooui dialog.
* Added a configurable checkbox for useragent and terms
  of servvice options. The checkbox can be configured
  to be visible or not, and to be mandatory or optional.
  (useragent addition based largely on the unmerged
  fix I37b1a271af115)
* Transformed the 'feedback-bugornote' message to a
  configurable intro message with a link to the feedback
  page.
* Added a 'thank you' or 'error' dialog at the end of the
  operation indicating whether the feedback was sent or
  if there was some error.

Bug: T89878
Bug: T43291
Bug: T35365
Bug: T54588
Bug: T65290
Change-Id: Id1967a83a502f689c40f1af71398c3be99e30640

languages/i18n/en.json
languages/i18n/qqq.json
maintenance/jsduck/categories.json
resources/Resources.php
resources/src/mediawiki/mediawiki.feedback.css
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/templates/dialog.html [deleted file]

index 31c766f..7ff3b25 100644 (file)
        "newuserlog-autocreate-entry": "Account created automatically",
        "rightslogentry": "changed group membership for $1 from $2 to $3",
        "rightslogentry-autopromote": "was automatically promoted from $2 to $3",
+       "feedback-adding": "Adding feedback to page...",
+       "feedback-back": "Back",
+       "feedback-bugcheck": "Great! Just check that it is not already one of the [$1 known bugs].",
+       "feedback-bugnew": "I checked. Report a new bug",
        "feedback-bugornote": "If you are ready to describe a technical problem in detail please [$1 report a bug].\nOtherwise, you can use the easy form below. Your comment will be added to the page \"[$3 $2]\", along with your username.",
-       "feedback-subject": "Subject:",
-       "feedback-message": "Message:",
        "feedback-cancel": "Cancel",
-       "feedback-submit": "Submit Feedback",
-       "feedback-adding": "Adding feedback to page...",
+       "feedback-close": "Done",
+       "feedback-external-bug-report-button": "File a technical task",
+       "feedback-dialog-title": "Submit feedback",
+       "feedback-dialog-intro": "You can use the easy form below to submit your feedback. Your comment will be added to the page \"$1\", along with your username.",
+       "feedback-error-title": "Error",
        "feedback-error1": "Error: Unrecognized result from API",
        "feedback-error2": "Error: Edit failed",
        "feedback-error3": "Error: No response from API",
+       "feedback-message": "Message:",
+       "feedback-subject": "Subject:",
+       "feedback-submit": "Submit",
+       "feedback-terms": "I understand that my user agent information includes information about my exact browser and operating system version and will be shared publicly alongside my feedback.",
+       "feedback-termsofuse": "I agree to provide feedback in accordance with the Terms of Use.",
        "feedback-thanks": "Thanks! Your feedback has been posted to the page \"[$2 $1]\".",
-       "feedback-close": "Done",
-       "feedback-bugcheck": "Great! Just check that it is not already one of the [$1 known bugs].",
-       "feedback-bugnew": "I checked. Report a new bug",
+       "feedback-thanks-title": "Thank you!",
+       "feedback-useragent": "User agent:",
        "searchsuggest-search": "Search",
        "searchsuggest-containing": "containing...",
        "api-error-badaccess-groups": "You are not permitted to upload files to this wiki.",
index 6253fc9..3fab943 100644 (file)
        "newuserlog-autocreate-entry": "This message is used in the [[:mw:Extension:Newuserlog|new user log]] to mark an account that was created by MediaWiki as part of a [[:mw:Extension:CentralAuth|CentralAuth]] global account.",
        "rightslogentry": "This message is displayed in the [[Special:Log/rights|User Rights Log]] when a bureaucrat changes the user groups for a user.\n\nParameters:\n* $1 - the username\n* $2 - list of user groups or {{msg-mw|Rightsnone}}\n* $3 - list of user groups or {{msg-mw|Rightsnone}}\n\nThe name of the bureaucrat who did this task appears before this message.\n\nSimilar to {{msg-mw|Gur-rightslog-entry}}",
        "rightslogentry-autopromote": "This message is displayed in the [[Special:Log/rights|User Rights Log]] when a user is automatically promoted to a user group.\n\nParameters:\n* $1 - (Unused)\n* $2 - a comma separated list of old user groups or {{msg-mw|Rightsnone}}\n* $3 - a comma separated list of new user groups",
+       "feedback-adding": "Progress notice",
+       "feedback-back": "Button to go back to the previous action in the feedback dialog.",
+       "feedback-bugcheck": "Message that appears before the user submits a bug, reminding them to check for known bugs.\n\nParameters:\n* $1 - bug list page URL",
+       "feedback-bugnew": "Button label - asserts that the user has checked for existing bugs. When clicked will launch an external form to add a new bug in a new tab or window",
        "feedback-bugornote": "When feedback dialog box is opened, this introductory message in small print explains the options to report a bug or add simple feedback.\n\nWe expect that people in a hurry will not read this.\n\nParameters:\n* $1 - Bug note URL\n* $2 - \"Feedback\"\n* $3 - Feedback page URL",
-       "feedback-subject": "Label for a text input\n{{Identical|Subject}}",
-       "feedback-message": "Label for a textarea; signature referrs to a Wikitext signature.\n{{Identical|Message}}",
+       "feedback-external-bug-report-button": "A button for submitting an external technical bug report.",
        "feedback-cancel": "Button label\n{{Identical|Cancel}}",
-       "feedback-submit": "Button label\n{{Identical|Submit}}",
-       "feedback-adding": "Progress notice",
+       "feedback-close": "Button label\n{{Identical|Done}}",
+       "feedback-dialog-title": "Title of the feedback dialog",
        "feedback-error1": "Error message, appears when an unknown error occurs submitting feedback",
        "feedback-error2": "Error message, appears when we could not add feedback",
        "feedback-error3": "Error message, appears when we lose our connection to the wiki",
+       "feedback-dialog-intro": "An introduction at the top of the feedback dialog. $1 - Feedback page link",
+       "feedback-message": "Label for a textarea; signature refers to a Wikitext signature.\n{{Identical|Message}}",
+       "feedback-subject": "Label for a text input\n{{Identical|Subject}}",
+       "feedback-submit": "Button label\n{{Identical|Submit}}",
+       "feedback-terms": "Label for a checkbox asking for permissions to submit browser information.",
+       "feedback-termsofuse": "Label with an agreement about the terms of use.",
        "feedback-thanks": "Thanks message, appears if feedback was successful. Parameters:\n* $1 - \"Feedback\"\n* $2 - Feedback page URL",
-       "feedback-close": "Button label\n{{Identical|Done}}",
-       "feedback-bugcheck": "Message that appears before the user submits a bug, reminding them to check for known bugs.\n\nParameters:\n* $1 - bug list page URL",
-       "feedback-bugnew": "Button label - asserts that the user has checked for existing bugs. When clicked will launch a bugzilla form to add a new bug in a new tab or window",
+       "feedback-thanks-title": "The title of the message dialog at the end of the submission process that shows error in submitting the feedback.",
+       "feedback-thanks-title": "The title of the thank you dialog at the end of the submission process.",
+       "feedback-useragent": "A label denoting the user agent in the feedback that is posted to the feedback page.",
        "searchsuggest-search": "Greyed out default text in the simple search box in the Vector skin. (It disappears and lets the user enter the requested search terms when the search box receives focus.)\n\n{{Identical|Search}}",
        "searchsuggest-containing": "Label used in the special item of the search suggestions list which gives the user an option to perform a full text search for the term.",
        "api-error-badaccess-groups": "API error message that can be used for client side localisation of API errors.",
index 145905f..c0d0499 100644 (file)
@@ -55,7 +55,8 @@
                        {
                                "name": "Interfaces",
                                "classes": [
-                                       "mw.Feedback"
+                                       "mw.Feedback",
+                                       "mw.Feedback.Dialog"
                                ]
                        },
                        {
index 965c783..a9f383c 100644 (file)
@@ -856,31 +856,36 @@ return array(
                'position' => 'bottom',
        ),
        'mediawiki.feedback' => array(
-               'templates' => array(
-                       'dialog.html' => 'resources/src/mediawiki/templates/dialog.html',
-               ),
                'scripts' => 'resources/src/mediawiki/mediawiki.feedback.js',
                'styles' => 'resources/src/mediawiki/mediawiki.feedback.css',
                'dependencies' => array(
                        'mediawiki.api.edit',
                        'mediawiki.Title',
-                       'mediawiki.jqueryMsg',
-                       'jquery.ui.dialog',
+                       'oojs-ui',
                ),
                'messages' => array(
+                       'feedback-adding',
+                       'feedback-back',
+                       'feedback-bugcheck',
+                       'feedback-dialog-intro',
+                       'feedback-external-bug-report-button',
+                       'feedback-bugnew',
                        'feedback-bugornote',
-                       'feedback-subject',
-                       'feedback-message',
                        'feedback-cancel',
-                       'feedback-submit',
-                       'feedback-adding',
+                       'feedback-close',
+                       'feedback-dialog-title',
+                       'feedback-error-title',
                        'feedback-error1',
                        'feedback-error2',
                        'feedback-error3',
+                       'feedback-message',
+                       'feedback-subject',
+                       'feedback-submit',
+                       'feedback-terms',
+                       'feedback-termsofuse',
                        'feedback-thanks',
-                       'feedback-close',
-                       'feedback-bugcheck',
-                       'feedback-bugnew',
+                       'feedback-thanks-title',
+                       'feedback-useragent'
                ),
        ),
        'mediawiki.hidpi' => array(
index 6bd47bb..f2859db 100644 (file)
@@ -7,3 +7,16 @@
        width: 18px;
        height: 18px;
 }
+
+.mw-feedbackDialog-welcome-message,
+.mw-feedbackDialog-feedback-terms {
+       line-height: 1.2em;
+}
+
+.mw-feedbackDialog-feedback-form {
+       margin-top: 1em;
+}
+
+.mw-feedbackDialog-feedback-termsofuse {
+       margin-left: 2.5em;
+}
index 867134c..da17222 100644 (file)
@@ -3,8 +3,11 @@
  *
  * @author Ryan Kaldari, 2010
  * @author Neil Kandalgaonkar, 2010-11
+ * @author Moriel Schottlender, 2015
  * @since 1.19
  */
+/*jshint es3:false */
+/*global OO*/
 ( function ( mw, $ ) {
        /**
         * This is a way of getting simple feedback from users. It's useful
         *
         * @class
         * @constructor
-        * @param {Object} [options]
-        * @param {mw.Api} [options.api] if omitted, will just create a standard API
-        * @param {mw.Title} [options.title="Feedback"] The title of the page where you collect
-        * feedback.
-        * @param {string} [options.dialogTitleMessageKey="feedback-submit"] Message key for the
-        * title of the dialog box
-        * @param {string} [options.bugsLink="//bugzilla.wikimedia.org/enter_bug.cgi"] URL where
-        * bugs can be posted
-        * @param {mw.Uri|string} [options.bugsListLink="//bugzilla.wikimedia.org/query.cgi"]
-        * URL where bugs can be listed
+        * @param {Object} [config] Configuration object
+        * @cfg {mw.Api} [api] if omitted, will just create a standard API
+        * @cfg {mw.Title} [title="Feedback"] The title of the page where you collect
+        *  feedback.
+        * @cfg {string} [dialogTitleMessageKey="feedback-dialog-title"] Message key for the
+        *  title of the dialog box
+        * @cfg {mw.Uri|string} [bugsLink="//phabricator.wikimedia.org/maniphest/task/create/"] URL where
+        *  bugs can be posted
+        * @cfg {mw.Uri|string} [bugsListLink="//phabricator.wikimedia.org/maniphest/query/advanced"] URL
+        *  where bugs can be listed
+        * @cfg {boolean} [showUseragentCheckbox=false] Show a Useragent agreement checkbox as part of the form.
+        * @cfg {boolean} [useragentCheckboxMandatory=false] Make the Useragent checkbox mandatory.
+        * @cfg {string|jQuery} [useragentCheckboxMessage] Supply a custom message for the useragent checkbox.
+        *  defaults to a combination of 'feedback-terms' and 'feedback-termsofuse' which includes a link to the
+        *  wiki's Term of Use page.
         */
-       mw.Feedback = function ( options ) {
-               if ( options === undefined ) {
-                       options = {};
-               }
+       mw.Feedback = function MwFeedback( config ) {
+               config = config || {};
 
-               if ( options.api === undefined ) {
-                       options.api = new mw.Api();
-               }
+               this.api = config.api || new mw.Api();
+               this.dialogTitleMessageKey = config.dialogTitleMessageKey || 'feedback-dialog-title';
 
-               if ( options.title === undefined ) {
-                       options.title = new mw.Title( 'Feedback' );
-               }
+               // Feedback page title
+               this.feedbackPageTitle = config.title || new mw.Title( 'Feedback' );
 
-               if ( options.dialogTitleMessageKey === undefined ) {
-                       options.dialogTitleMessageKey = 'feedback-submit';
-               }
+               // Links
+               this.bugsTaskSubmissionLink = config.bugsLink || '//phabricator.wikimedia.org/maniphest/task/create/';
+               this.bugsTaskListLink = config.bugsListLink || '//phabricator.wikimedia.org/maniphest/query/advanced';
 
-               if ( options.bugsLink === undefined ) {
-                       options.bugsLink = '//bugzilla.wikimedia.org/enter_bug.cgi';
-               }
+               // Terms of use
+               this.useragentCheckboxShow = !!config.showUseragentCheckbox;
+               this.useragentCheckboxMandatory = !!config.useragentCheckboxMandatory;
+               this.useragentCheckboxMessage = config.useragentCheckboxMessage ||
+                       $( '<p>' )
+                               .append( mw.msg( 'feedback-terms' ) )
+                               .add( $( '<p>' ).append( mw.message( 'feedback-termsofuse' ).parse() ) );
+
+               // Message dialog
+               this.thankYouDialog = new OO.ui.MessageDialog();
+       };
+
+       /* Initialize */
+       OO.initClass( mw.Feedback );
+
+       /* Static Properties */
+       mw.Feedback.static.windowManager = null;
+       mw.Feedback.static.dialog = null;
+
+       /* Methods */
 
-               if ( options.bugsListLink === undefined ) {
-                       options.bugsListLink = '//bugzilla.wikimedia.org/query.cgi';
+       /**
+        * Respond to dialog submit event. If the information was
+        * submitted, either successfully or with an error, open
+        * a MessageDialog to thank the user.
+        * @param {string} [status] A status of the end of operation
+        *  of the main feedback dialog. Empty if the dialog was
+        *  dismissed with no action or the user followed the button
+        *  to the external task reporting site.
+        */
+       mw.Feedback.prototype.onDialogSubmit = function ( status ) {
+               var dialogConfig = {};
+               switch ( status ) {
+                       case 'submitted':
+                               dialogConfig = {
+                                       title: mw.msg( 'feedback-thanks-title' ),
+                                       message: $( '<span>' ).append(
+                                               mw.message(
+                                                       'feedback-thanks',
+                                                       this.feedbackPageTitle.getNameText(),
+                                                       $( '<a>' )
+                                                               .attr( {
+                                                                       target: '_blank',
+                                                                       href: this.feedbackPageTitle.getUrl()
+                                                               } )
+                                               ).parse()
+                                       ),
+                                       actions: [
+                                               {
+                                                       action: 'accept',
+                                                       label: mw.msg( 'feedback-close' ),
+                                                       flags: 'primary'
+                                               }
+                                       ]
+                               };
+                               break;
+                       case 'error1':
+                       case 'error2':
+                       case 'error3':
+                               dialogConfig = {
+                                       title: mw.msg( 'feedback-error-title' ),
+                                       message: mw.msg( 'feedback-' + status ),
+                                       actions: [
+                                               {
+                                                       action: 'accept',
+                                                       label: mw.msg( 'feedback-close' ),
+                                                       flags: 'primary'
+                                               }
+                                       ]
+                               };
+                               break;
                }
 
-               $.extend( this, options );
-               this.setup();
+               // Show the message dialog
+               if ( !$.isEmptyObject( dialogConfig ) ) {
+                       this.constructor.static.windowManager.openWindow(
+                               this.thankYouDialog,
+                               dialogConfig
+                       );
+               }
        };
 
-       mw.Feedback.prototype = {
-               /**
-                * Sets up interface
-                */
-               setup: function () {
-                       var $feedbackPageLink,
-                               $bugNoteLink,
-                               $bugsListLink,
-                               fb = this;
-
-                       $feedbackPageLink = $( '<a>' )
-                               .attr( {
-                                       href: fb.title.getUrl(),
-                                       target: '_blank'
-                               } )
-                               .css( {
-                                       whiteSpace: 'nowrap'
-                               } );
+       /**
+        * Modify the display form, and then open it, focusing interface on the subject.
+        *
+        * @param {Object} [contents] Prefilled contents for the feedback form.
+        * @param {string} [contents.subject] The subject of the feedback
+        * @param {string} [contents.message] The content of the feedback
+        */
+       mw.Feedback.prototype.launch = function ( contents ) {
+               // Dialog
+               if ( !this.constructor.static.dialog ) {
+                       this.constructor.static.dialog = new mw.Feedback.Dialog();
+                       this.constructor.static.dialog.connect( this, { submit: 'onDialogSubmit' } );
+               }
+               if ( !this.constructor.static.windowManager ) {
+                       this.constructor.static.windowManager = new OO.ui.WindowManager();
+                       this.constructor.static.windowManager.addWindows( [
+                               this.constructor.static.dialog,
+                               this.thankYouDialog
+                       ] );
+                       $( 'body' )
+                               .append( this.constructor.static.windowManager.$element );
+               }
+               // Open the dialog
+               this.constructor.static.windowManager.openWindow(
+                       this.constructor.static.dialog,
+                       {
+                               title: mw.msg( this.dialogTitleMessageKey ),
+                               settings: {
+                                       api: this.api,
+                                       title: this.feedbackPageTitle,
+                                       dialogTitleMessageKey: this.dialogTitleMessageKey,
+                                       bugsTaskSubmissionLink: this.bugsTaskSubmissionLink,
+                                       bugsTaskListLink: this.bugsTaskListLink,
+                                       useragentCheckbox: {
+                                               show: this.useragentCheckboxShow,
+                                               mandatory: this.useragentCheckboxMandatory,
+                                               message: this.useragentCheckboxMessage
+                                       }
+                               },
+                               contents: contents
+                       }
+               );
+       };
 
-                       $bugNoteLink = $( '<a>' ).attr( { href: '#' } ).click( function () {
-                               fb.displayBugs();
-                       } );
-
-                       $bugsListLink = $( '<a>' ).attr( {
-                               href: fb.bugsListLink,
-                               target: '_blank'
-                       } );
-
-                       // TODO: Use a stylesheet instead of these inline styles in the template
-                       this.$dialog = mw.template.get( 'mediawiki.feedback', 'dialog.html' ).render();
-                       this.$dialog.find( '.feedback-mode small p' ).msg(
-                               'feedback-bugornote',
-                               $bugNoteLink,
-                               fb.title.getNameText(),
-                               $feedbackPageLink.clone()
-                       );
-                       this.$dialog.find( '.feedback-form .subject span' ).msg( 'feedback-subject' );
-                       this.$dialog.find( '.feedback-form .message span' ).msg( 'feedback-message' );
-                       this.$dialog.find( '.feedback-bugs p' ).msg( 'feedback-bugcheck', $bugsListLink );
-                       this.$dialog.find( '.feedback-submitting span' ).msg( 'feedback-adding' );
-                       this.$dialog.find( '.feedback-thanks' ).msg( 'feedback-thanks', fb.title.getNameText(),
-                               $feedbackPageLink.clone() );
-
-                       this.$dialog.dialog( {
-                               width: 500,
-                               autoOpen: false,
-                               title: mw.message( this.dialogTitleMessageKey ).escaped(),
-                               modal: true,
-                               buttons: fb.buttons
-                       } );
-
-                       this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get( 0 );
-                       this.messageInput = this.$dialog.find( 'textarea.feedback-message' ).get( 0 );
-               },
+       /**
+        * mw.Feedback Dialog
+        *
+        * @class
+        * @extends OO.ui.ProcessDialog
+        *
+        * @constructor
+        * @param {Object} config Configuration object
+        */
+       mw.Feedback.Dialog = function mwFeedbackDialog( config ) {
+               // Parent constructor
+               mw.Feedback.Dialog.super.call( this, config );
+
+               this.status = '';
+               this.feedbackPageTitle = null;
+               // Initialize
+               this.$element.addClass( 'mwFeedback-Dialog' );
+       };
 
-               /**
-                * Displays a section of the dialog.
-                *
-                * @param {"form"|"bugs"|"submitting"|"thanks"|"error"} s
-                * The section of the dialog to show.
-                */
-               display: function ( s ) {
-                       // Hide the buttons
-                       this.$dialog.dialog( { buttons: {} } );
-                       // Hide everything
-                       this.$dialog.find( '.feedback-mode' ).hide();
-                       // Show the desired div
-                       this.$dialog.find( '.feedback-' + s ).show();
+       OO.inheritClass( mw.Feedback.Dialog, OO.ui.ProcessDialog );
+
+       /* Static properties */
+       mw.Feedback.Dialog.static.name = 'mwFeedbackDialog';
+       mw.Feedback.Dialog.static.title = mw.msg( 'feedback-dialog-title' );
+       mw.Feedback.Dialog.static.size = 'medium';
+       mw.Feedback.Dialog.static.actions = [
+               {
+                       action: 'submit',
+                       label: mw.msg( 'feedback-submit' ),
+                       flags: [ 'primary', 'constructive' ]
                },
-
-               /**
-                * Display the submitting section.
-                */
-               displaySubmitting: function () {
-                       this.display( 'submitting' );
+               {
+                       action: 'external',
+                       label: mw.msg( 'feedback-external-bug-report-button' ),
+                       flags: 'constructive'
                },
+               {
+                       action: 'cancel',
+                       label: mw.msg( 'feedback-cancel' ),
+                       flags: 'safe'
+               }
+       ];
 
-               /**
-                * Display the bugs section.
-                */
-               displayBugs: function () {
-                       var fb = this,
-                               bugsButtons = {};
-
-                       this.display( 'bugs' );
-                       bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () {
-                               window.open( fb.bugsLink, '_blank' );
-                       };
-                       bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
-                               fb.cancel();
-                       };
-                       this.$dialog.dialog( {
-                               buttons: bugsButtons
-                       } );
-               },
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.initialize = function () {
+               var feedbackSubjectFieldLayout, feedbackMessageFieldLayout,
+                       feedbackFieldsetLayout;
+
+               // Parent method
+               mw.Feedback.Dialog.super.prototype.initialize.call( this );
+
+               this.feedbackPanel = new OO.ui.PanelLayout( {
+                       scrollable: false,
+                       expanded: false,
+                       padded: true
+               } );
+
+               this.$spinner = $( '<div>' )
+                       .addClass( 'feedback-spinner' );
+
+               // Feedback form
+               this.feedbackMessageLabel = new OO.ui.LabelWidget( {
+                       classes: [ 'mw-feedbackDialog-welcome-message' ]
+               } );
+               this.feedbackSubjectInput = new OO.ui.TextInputWidget( {
+                       multiline: false
+               } );
+               this.feedbackMessageInput = new OO.ui.TextInputWidget( {
+                       multiline: true
+               } );
+               feedbackSubjectFieldLayout = new OO.ui.FieldLayout( this.feedbackSubjectInput, {
+                       label: mw.msg( 'feedback-subject' )
+               } );
+               feedbackMessageFieldLayout = new OO.ui.FieldLayout( this.feedbackMessageInput, {
+                       label: mw.msg( 'feedback-message' )
+               } );
+               feedbackFieldsetLayout = new OO.ui.FieldsetLayout( {
+                       items: [ feedbackSubjectFieldLayout, feedbackMessageFieldLayout ],
+                       classes: [ 'mw-feedbackDialog-feedback-form' ]
+               } );
+
+               // Useragent terms of use
+               this.useragentCheckbox = new OO.ui.CheckboxInputWidget();
+               this.useragentFieldLayout = new OO.ui.FieldLayout( this.useragentCheckbox, {
+                       classes: [ 'mw-feedbackDialog-feedback-terms' ],
+                       align: 'inline'
+               } );
+
+               this.feedbackPanel.$element.append(
+                       this.feedbackMessageLabel.$element,
+                       feedbackFieldsetLayout.$element,
+                       this.useragentFieldLayout.$element
+               );
+
+               // Events
+               this.feedbackSubjectInput.connect( this, { change: 'validateFeedbackForm' } );
+               this.feedbackMessageInput.connect( this, { change: 'validateFeedbackForm' } );
+               this.useragentCheckbox.connect( this, { change: 'validateFeedbackForm' } );
+
+               this.$body.append( this.feedbackPanel.$element );
+       };
 
-               /**
-                * Display the thanks section.
-                */
-               displayThanks: function () {
-                       var fb = this,
-                               closeButton = {};
-
-                       this.display( 'thanks' );
-                       closeButton[ mw.msg( 'feedback-close' ) ] = function () {
-                               fb.$dialog.dialog( 'close' );
-                       };
-                       this.$dialog.dialog( {
-                               buttons: closeButton
-                       } );
-               },
+       /**
+        * Validate the feedback form
+        */
+       mw.Feedback.Dialog.prototype.validateFeedbackForm = function () {
+               var isValid = (
+                               (
+                                       !this.useragentMandatory ||
+                                       this.useragentCheckbox.isSelected()
+                               ) &&
+                               (
+                                       !!this.feedbackMessageInput.getValue() ||
+                                       !!this.feedbackSubjectInput.getValue()
+                               )
+                       );
 
-               /**
-                * Display the feedback form
-                * @param {Object} [contents] Prefilled contents for the feedback form.
-                * @param {string} [contents.subject] The subject of the feedback
-                * @param {string} [contents.message] The content of the feedback
-                */
-               displayForm: function ( contents ) {
-                       var fb = this,
-                               formButtons = {};
-
-                       this.subjectInput.value = ( contents && contents.subject ) ? contents.subject : '';
-                       this.messageInput.value = ( contents && contents.message ) ? contents.message : '';
-
-                       this.display( 'form' );
-
-                       // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
-                       formButtons[ mw.msg( 'feedback-submit' ) ] = function () {
-                               fb.submit();
-                       };
-                       formButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
-                               fb.cancel();
-                       };
-                       this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back
-               },
+               this.actions.setAbilities( { submit:  isValid } );
+       };
 
-               /**
-                * Display an error on the form.
-                *
-                * @param {string} message Should be a valid message key.
-                */
-               displayError: function ( message ) {
-                       var fb = this,
-                               closeButton = {};
-
-                       this.display( 'error' );
-                       this.$dialog.find( '.feedback-error-msg' ).msg( message );
-                       closeButton[ mw.msg( 'feedback-close' ) ] = function () {
-                               fb.$dialog.dialog( 'close' );
-                       };
-                       this.$dialog.dialog( { buttons: closeButton } );
-               },
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.getBodyHeight = function () {
+               return this.feedbackPanel.$element.outerHeight( true );
+       };
 
-               /**
-                * Close the feedback form.
-                */
-               cancel: function () {
-                       this.$dialog.dialog( 'close' );
-               },
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.getSetupProcess = function ( data ) {
+               return mw.Feedback.Dialog.super.prototype.getSetupProcess.call( this, data )
+                       .next( function () {
+                               var plainMsg, parsedMsg,
+                                       settings = data.settings;
+                               data.contents = data.contents || {};
+
+                               // Prefill subject/message
+                               this.feedbackSubjectInput.setValue( data.contents.subject );
+                               this.feedbackMessageInput.setValue( data.contents.message );
+
+                               this.status = '';
+                               this.api = settings.api;
+                               this.setBugReportLink( settings.bugsTaskSubmissionLink );
+                               this.feedbackPageTitle = settings.title;
+                               this.feedbackPageName = settings.title.getNameText();
+                               this.feedbackPageUrl = settings.title.getUrl();
+
+                               // Useragent checkbox
+                               if ( settings.useragentCheckbox.show ) {
+                                       this.useragentFieldLayout.setLabel( settings.useragentCheckbox.message );
+                               }
+                               this.useragentMandatory = settings.useragentCheckbox.mandatory;
+                               this.useragentFieldLayout.toggle( settings.useragentCheckbox.show );
+
+                               // HACK: Setting a link in the messages doesn't work. There is already a report
+                               // about this, and the bug report offers a somewhat hacky work around that
+                               // includes setting a separate message to be parsed.
+                               // We want to make sure the user can configure both the title of the page and
+                               // a separate url, so this must be allowed to parse correctly.
+                               // See https://phabricator.wikimedia.org/T49395#490610
+                               mw.messages.set( {
+                                       'feedback-dialog-temporary-message':
+                                               '<a href="' + this.feedbackPageUrl + '" target="_blank">' + this.feedbackPageName + '</a>'
+                               } );
+                               plainMsg = mw.message( 'feedback-dialog-temporary-message' ).plain();
+                               mw.messages.set( { 'feedback-dialog-temporary-message-parsed': plainMsg } );
+                               parsedMsg = mw.message( 'feedback-dialog-temporary-message-parsed' );
+                               this.feedbackMessageLabel.setLabel(
+                                       // Double-parse
+                                       $( '<span>' )
+                                               .append( mw.message( 'feedback-dialog-intro', parsedMsg ).parse() )
+                               );
+
+                               this.validateFeedbackForm();
+                       }, this );
+       };
 
-               /**
-                * Submit the feedback form.
-                */
-               submit: function () {
-                       var subject, message,
-                               fb = this;
-
-                       // Get the values to submit.
-                       subject = $.trim( this.subjectInput.value );
-
-                       // We used to include "mw.html.escape( navigator.userAgent )" but there are legal issues
-                       // with posting this without their explicit consent
-                       message = $.trim( this.messageInput.value );
-                       if ( message.indexOf( '~~~' ) === -1 ) {
-                               message += ' ~~~~';
-                       }
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.getReadyProcess = function ( data ) {
+               return mw.Feedback.Dialog.super.prototype.getReadyProcess.call( this, data )
+                       .next( function () {
+                               this.feedbackSubjectInput.focus();
+                       }, this );
+       };
 
-                       this.displaySubmitting();
-
-                       // Post the message, resolving redirects
-                       this.api.newSection(
-                               this.title,
-                               subject,
-                               message,
-                               { redirect: true }
-                       )
-                       .done( function ( result ) {
-                               if ( result.edit.result === 'Success' ) {
-                                       fb.displayThanks();
-                               } else {
-                                       // unknown API result
-                                       fb.displayError( 'feedback-error1' );
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.getActionProcess = function ( action ) {
+               if ( action === 'cancel' ) {
+                       return new OO.ui.Process( function () {
+                               this.close( { action: action } );
+                       }, this );
+               } else if ( action === 'external' ) {
+                       return new OO.ui.Process( function () {
+                               // Open in a new window
+                               window.open( this.getBugReportLink(), '_blank' );
+                               // Close the dialog
+                               this.close();
+                       }, this );
+               } else if ( action === 'submit' ) {
+                       return new OO.ui.Process( function () {
+                               var fb = this,
+                                       userAgentMessage = ':' +
+                                               '<small>' +
+                                               mw.msg( 'feedback-useragent' ) +
+                                               ' ' +
+                                               mw.html.escape( navigator.userAgent ) +
+                                               '</small>\n\n',
+                                       subject = this.feedbackSubjectInput.getValue(),
+                                       message = this.feedbackMessageInput.getValue();
+
+                               // Add user agent if checkbox is selected
+                               if ( this.useragentCheckbox.isSelected() ) {
+                                       message = userAgentMessage + message;
                                }
-                       } )
-                       .fail( function ( code, result ) {
-                               if ( code === 'http' ) {
-                                       // ajax request failed
-                                       fb.displayError( 'feedback-error3' );
-                                       mw.log.warn( 'Feedback report failed with HTTP error: ' +  result.textStatus );
-                               } else {
-                                       fb.displayError( 'feedback-error2' );
-                                       mw.log.warn( 'Feedback report failed with API error: ' +  code );
+
+                               // Add signature if needed
+                               if ( message.indexOf( '~~~' ) === -1 ) {
+                                       message += '\n\n~~~~';
                                }
-                       } );
-               },
 
-               /**
-                * Modify the display form, and then open it, focusing interface on the subject.
-                * @param {Object} [contents] Prefilled contents for the feedback form.
-                * @param {string} [contents.subject] The subject of the feedback
-                * @param {string} [contents.message] The content of the feedback
-                */
-               launch: function ( contents ) {
-                       this.displayForm( contents );
-                       this.$dialog.dialog( 'open' );
-                       this.subjectInput.focus();
+                               // Post the message, resolving redirects
+                               this.pushPending();
+                               this.api.newSection(
+                                       this.feedbackPageTitle,
+                                       subject,
+                                       message,
+                                       { redirect: true }
+                               )
+                               .done( function ( result ) {
+                                       if ( result.edit.result === 'Success' ) {
+                                               fb.status = 'submitted';
+                                       } else {
+                                               fb.status = 'error1';
+                                       }
+                                       fb.popPending();
+                                       fb.close();
+                               } )
+                               .fail( function ( code, result ) {
+                                       if ( code === 'http' ) {
+                                               fb.status = 'error3';
+                                               // ajax request failed
+                                               mw.log.warn( 'Feedback report failed with HTTP error: ' +  result.textStatus );
+                                       } else {
+                                               fb.status = 'error2';
+                                               mw.log.warn( 'Feedback report failed with API error: ' +  code );
+                                       }
+                                       fb.popPending();
+                                       fb.close();
+                               } );
+                       }, this );
                }
+               // Fallback to parent handler
+               return mw.Feedback.Dialog.super.prototype.getActionProcess.call( this, action );
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.Feedback.Dialog.prototype.getTeardownProcess = function ( data ) {
+               return mw.Feedback.Dialog.super.prototype.getTeardownProcess.call( this, data )
+                       .first( function () {
+                               this.emit( 'submit', this.status, this.feedbackPageName, this.feedbackPageUrl );
+                               // Cleanup
+                               this.status = '';
+                               this.feedbackPageTitle = null;
+                               this.feedbackSubjectInput.setValue( '' );
+                               this.feedbackMessageInput.setValue( '' );
+                               this.useragentCheckbox.setSelected( false );
+                       }, this );
        };
+
+       /**
+        * Set the bug report link
+        * @param {string} link Link to the external bug report form
+        */
+       mw.Feedback.Dialog.prototype.setBugReportLink = function ( link ) {
+               this.bugReportLink = link;
+       };
+
+       /**
+        * Get the bug report link
+        * @returns {string} Link to the external bug report form
+        */
+       mw.Feedback.Dialog.prototype.getBugReportLink = function () {
+               return this.bugReportLink;
+       };
+
 }( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/templates/dialog.html b/resources/src/mediawiki/templates/dialog.html
deleted file mode 100644 (file)
index e116f3e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<div style="position: relative; display: block;" class="ui-dialog-content ui-widget-content">
-       <div class="feedback-mode feedback-form">
-               <small><p></p></small>
-               <div class="subject" style="margin-top: 1em;">
-                       <span></span><br>
-                       <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;">
-               </div>
-               <div class="message" style="margin-top: 0.4em;">
-                       <span></span><br>
-                       <textarea name="message" class="feedback-message" rows="5" cols="60"></textarea>
-               </div>
-       </div>
-       <div class="feedback-mode feedback-bugs">
-               <p></p>
-       </div>
-       <div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;">
-               <span></span><br>
-               <span class="feedback-spinner"></span>
-       </div>
-       <div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div>
-       <div class="feedback-mode feedback-error" style="position: relative;">
-               <div class="feedback-error-msg" style=" color:#990000; margin-top:0.4em;"></div>
-       </div>
-</div>