r114709 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r114708‎ | r114709 | r114710 >
Date:17:50, 4 April 2012
Author:catrope
Status:deferred
Tags:
Comment:
1.19wmf1: Update to master state from git
Modified paths:
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/ArticleFeedbackv5.i18n.php (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/ArticleFeedbackv5.php (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiArticleFeedbackv5.php (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiFlagFeedbackArticleFeedbackv5.php (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiViewFeedbackArticleFeedbackv5.php (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.js (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.special.js (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/sql/ArticleFeedbackv5.sql (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/sql/alter.sql (modified) (history)

Diff [purge]

Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/ArticleFeedbackv5.i18n.php
@@ -39,13 +39,14 @@
4040 'articlefeedbackv5-error-email' => 'That e-mail address is not valid.',
4141 'articlefeedbackv5-error-blocked' => 'Blocked users may not submit feedback.',
4242 'articlefeedbackv5-error-validation' => 'Validation error.',
43 - 'articlefeedbackv5-error-abuse' => 'Your comment violates the $1. Please revise it.',
44 - 'articlefeedbackv5-error-abuse-linktext' => 'feedback abuse policy',
45 - 'articlefeedbackv5-error-abuse-link' => '#', // TODO: Build page and set link here
 43+ 'articlefeedbackv5-error-abuse' => 'Your post has been rejected by a software filter that suggests it may have violated Wikipedia\'s $1. Please revise your post and try again.',
 44+ 'articlefeedbackv5-error-abuse-linktext' => 'feedback guidelines',
 45+ 'articlefeedbackv5-error-abuse-link' => '//en.wikipedia.org/wiki/Wikipedia:Feedback_guidelines',
4646 'articlefeedbackv5-error-unknown' => 'Unknown error.',
4747 'articlefeedbackv5-error-submit' => 'Form submission error.',
4848 'articlefeedbackv5-error-nofeedback' => 'Please enter your feedback.',
4949 'articlefeedbackv5-error-flagging' => 'Error flagging feedback.',
 50+ 'articlefeedbackv5-warning-abuse' => 'Your comment has been automatically identified as harmful. If you believe this comment to be constructive, you may click the submit button again to confirm it. A brief description of the abuse rule which your action matched is: $1',
5051
5152 /* Special Page */
5253 'articlefeedbackv5-form-tools-label' => 'Tools',
@@ -427,6 +428,13 @@
428429 'articlefeedbackv5-disable-flyover-help-location' => 'My preferences > Appearance',
429430 'articlefeedbackv5-disable-flyover-prefbutton' => 'Go to my preferences',
430431
 432+ /* Custom AbuseFilter actions */
 433+ 'abusefilter-edit-action-aftv5flagabuse' => '(Article Feedback) Auto-flag as abuse',
 434+ 'articlefeedbackv5-abusefilter-note-aftv5flagabuse' => 'Automatically flagged as abuse. Rule: $1',
 435+ 'abusefilter-edit-action-aftv5hide' => '(Article Feedback) Auto-hide',
 436+ 'articlefeedbackv5-abusefilter-note-aftv5hide' => 'Automatically hidden. Rule: $1',
 437+ 'abusefilter-edit-action-aftv5requestoversight' => '(Article Feedback) Auto-request oversight',
 438+ 'articlefeedbackv5-abusefilter-note-aftv5requestoversight' => 'Oversight automatically requested. Rule: $1',
431439 );
432440
433441 /** Message documentation (Message documentation)
@@ -709,6 +717,13 @@
710718 'articlefeedbackv5-disable-flyover-help-emphasis-text' => 'The emphasis text for {{msg-mw|articlefeedbackv5-disable-flyover-help}} (the name of the tool)',
711719 'articlefeedbackv5-disable-flyover-help-location' => 'A short indication of where to go to change your Article Feedback preferences, inside {{msg-mw|articlefeedbackv5-disable-flyover-help}}',
712720 'articlefeedbackv5-disable-flyover-prefbutton' => 'The text of the big glossy button used to send the user to their preferences in the tooltip that pops up when you click the close button on a feedback trigger link, explaining how to remove the Article Feedback tool',
 721+ 'articlefeedbackv5-abusefilter-note-aftv5flagabuse' => 'The note to add to the activity log when auto-flagging a new feedback post as abuse (<code>$1</code> is the name of the filter)',
 722+ 'abusefilter-edit-action-aftv5flagabuse' => 'The text for the checkbox used by Special:AbuseFilter to indicate that a rule should result in the feedback being auto-flagged as abuse',
 723+ 'abusefilter-edit-action-aftv5hide' => 'The text for the checkbox used by Special:AbuseFilter to indicate that a rule should result in the feedback being auto-hidden',
 724+ 'abusefilter-edit-action-aftv5requestoversight' => 'The text for the checkbox used by Special:AbuseFilter to indicate that a rule should result in oversight being automatically requested for the feedback',
 725+ 'articlefeedbackv5-abusefilter-note-aftv5hide' => 'The note to add to the activity log when auto-hiding a new feedback post (<code>$1</code> is the name of the filter)',
 726+ 'articlefeedbackv5-abusefilter-note-aftv5requestoversight' => 'The note to add to the activity log when automatically requesting oversight for a new feedback post (<code>$1</code> is the name of the filter)',
 727+ 'articlefeedbackv5-warning-abuse' => 'A basic warning message that may be used by Special:AbuseFilter to let the user know their comment has violated policy (<code>$1</code> is the name of the filter)',
713728 );
714729
715730 /** Afrikaans (Afrikaans)
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.special.js
@@ -16,6 +16,8 @@
1717 * set foldlevel=0
1818 * set foldcolumn=0
1919 *
 20+ * TODO: jam sort/filter options into URL anchors, and use them as defaults if present.
 21+ *
2022 * @package ArticleFeedback
2123 * @subpackage Resources
2224 * @author Greg Chiasson <gchiasson@omniti.com>
@@ -26,8 +28,6 @@
2729
2830 // {{{ articleFeedbackv5special definition
2931
30 - // TODO: jam sort/filter options into URL anchors, and use them as defaults if present.
31 -
3232 $.articleFeedbackv5special = {};
3333
3434 // {{{ Properties
@@ -71,14 +71,14 @@
7272 * @var string
7373 */
7474 $.articleFeedbackv5special.activityCookieName = 'activity-';
75 -
 75+
7676 /**
7777 * Currently displayed panel host element id attribute value
7878 *
7979 * @var string
8080 */
8181 $.articleFeedbackv5special.currentPanelHostId = undefined;
82 -
 82+
8383 /**
8484 * Action note flyover panel HTML template
8585 *
@@ -97,7 +97,7 @@
9898 <a class="articlefeedbackv5-flyover-help" id="articlefeedbackv5-noteflyover-help" href="#"></a>\
9999 </div>\
100100 </form>';
101 -
 101+
102102 /**
103103 * Mask HMTL template
104104 */
@@ -108,7 +108,7 @@
109109 <span class="articleFeedbackv5-mask-postid"></span>\
110110 </div>\
111111 </div>';
112 -
 112+
113113 // }}}
114114 // {{{ Init methods
115115
@@ -152,12 +152,12 @@
153153 title: 'title', // attribute/callback containing tooltip text
154154 trigger: 'manual' // how tooltip is triggered - hover | focus | manual
155155 };
156 -
 156+
157157 // i18n, create action-specific tipsy panels from template
158158 var container = $( '<div></div>' );
159159 container.html( $.articleFeedbackv5special.notePanelHtmlTemplate );
160 - for( var action in $.articleFeedbackv5special.actions ) {
161 - if( $.articleFeedbackv5special.actions[action].hasTipsy && (undefined == $.articleFeedbackv5special.actions[action].tipsyHtml) ) {
 160+ for ( var action in $.articleFeedbackv5special.actions ) {
 161+ if ( $.articleFeedbackv5special.actions[action].hasTipsy && (undefined == $.articleFeedbackv5special.actions[action].tipsyHtml) ) {
162162 container.find( '#articlefeedbackv5-noteflyover-caption' ).text( mw.msg( 'articlefeedbackv5-noteflyover-' + action + '-caption' ) );
163163 container.find( '#articlefeedbackv5-noteflyover-label' ).text( mw.msg( 'articlefeedbackv5-noteflyover-' + action + '-label' ) );
164164 container.find( '#articlefeedbackv5-noteflyover-submit' ).text( mw.msg( 'articlefeedbackv5-noteflyover-' + action + '-submit' ) );
@@ -168,7 +168,7 @@
169169 $.articleFeedbackv5special.actions[action].tipsyHtml = container.html();
170170 }
171171 }
172 -
 172+
173173 // Initial load
174174 $.articleFeedbackv5special.loadFeedback( true );
175175 };
@@ -198,7 +198,7 @@
199199 $.articleFeedbackv5special.listControls.continueId = null;
200200
201201 // unless we're flipping the direction on the current sort.
202 - if( id == oldId && $.articleFeedbackv5special.listControls.sortDirection == 'desc' ) {
 202+ if ( id == oldId && $.articleFeedbackv5special.listControls.sortDirection == 'desc' ) {
203203 $.articleFeedbackv5special.listControls.sortDirection = 'asc';
204204 } else {
205205 $.articleFeedbackv5special.listControls.sortDirection = 'desc';
@@ -231,10 +231,10 @@
232232 } );
233233
234234 // Bind actions
235 - for( var action in $.articleFeedbackv5special.actions ) {
 235+ for ( var action in $.articleFeedbackv5special.actions ) {
236236 $( '.articleFeedbackv5-' + action + '-link' ).live( 'click', $.articleFeedbackv5special.actions[action].click );
237237 }
238 -
 238+
239239 // Bind submit actions on flyover panels (flag actions)
240240 $( '#articlefeedbackv5-noteflyover-submit' ).live( 'click', function( e ) {
241241 e.preventDefault();
@@ -243,24 +243,24 @@
244244 $( e.target ).attr( 'action' ),
245245 $( '#articlefeedbackv5-noteflyover-note' ).attr( 'value' ),
246246 { } );
247 -
 247+
248248 // hide tipsy
249249 $( '#' + $.articleFeedbackv5special.currentPanelHostId ).tipsy( 'hide' );
250250 $.articleFeedbackv5special.currentPanelHostId = undefined;
251251 } );
252 -
 252+
253253 // bind flyover panel close button
254254 $( '#articlefeedbackv5-noteflyover-close' ).live( 'click', function( e ) {
255255 e.preventDefault();
256256 $( '#' + $.articleFeedbackv5special.currentPanelHostId ).tipsy( 'hide' );
257257 $.articleFeedbackv5special.currentPanelHostId = undefined;
258258 } );
259 -
 259+
260260 }
261261 // }}}
262262 // {{{ bindPanels
263263 /**
264 - * Bind panels to controls - that cannot be 'live' events due to jQuery.typsy
 264+ * Bind panels to controls - that cannot be 'live' events due to jQuery.tipsy
265265 * limitations. This function should be invoked after feedback posts are loaded,
266266 * without parameters. The function should be invoked with the id parameter set
267267 * after an action is executed and its link is replaced with reverse action.
@@ -268,10 +268,10 @@
269269 * @param id post id to bind panels for. If none is supplied, bind entire list.
270270 */
271271 $.articleFeedbackv5special.bindPanels = function( id ) {
272 - // single post or entire list?
 272+ // single post or entire list?
273273 var $selector = !id ? $( '#articleFeedbackv5-show-feedback' ) : $( '.articleFeedbackv5-feedback[rel="' + id + '"]' );
274 -
275 - for( var action in $.articleFeedbackv5special.actions ) {
 274+
 275+ for ( var action in $.articleFeedbackv5special.actions ) {
276276 $selector.find( '.articleFeedbackv5-' + action + '-link' )
277277 .attr( 'action', action )
278278 .tipsy( {
@@ -282,13 +282,14 @@
283283 }
284284 }
285285 // }}}
286 -
 286+
287287 // }}}
288288 // {{{ Utility methods
289 -
 289+
290290 // {{{ toggleTipsy
 291+
291292 /**
292 - * Toggles tipsy display for an action link
 293+ * Utility method: Toggles tipsy display for an action link
293294 *
294295 * @param e event
295296 * @returns true if showing tipsy, false if hiding
@@ -297,7 +298,7 @@
298299 e.preventDefault();
299300 var $l = $( e.target );
300301 // are we hiding the current tipsy?
301 - if( $l.attr( 'id' ) == $.articleFeedbackv5special.currentPanelHostId ) {
 302+ if ( $l.attr( 'id' ) == $.articleFeedbackv5special.currentPanelHostId ) {
302303 $l.tipsy( 'hide' );
303304 $.articleFeedbackv5special.currentPanelHostId = undefined;
304305 return false;
@@ -311,12 +312,17 @@
312313 return true;
313314 }
314315 }
 316+
315317 // }}}
 318+ // {{{ toggleComment
316319
317 - // {{{ toggleComment
318 - $.articleFeedbackv5special.toggleComment = function( id ) {
319 - if( $( '#articleFeedbackv5-comment-toggle-' + id ).text()
320 - == mw.msg( 'articlefeedbackv5-comment-more' ) ) {
 320+ /**
 321+ * Utility method: Toggles a comment between short and full displays
 322+ *
 323+ * @param id string the comment id
 324+ */
 325+ $.articleFeedbackv5special.toggleComment = function( id ) {
 326+ if ( $( '#articleFeedbackv5-comment-toggle-' + id ).text() == mw.msg( 'articlefeedbackv5-comment-more' ) ) {
321327 $( '#articleFeedbackv5-comment-short-' + id ).hide();
322328 $( '#articleFeedbackv5-comment-full-' + id ).show();
323329 $( '#articleFeedbackv5-comment-toggle-' + id ).text(
@@ -334,7 +340,11 @@
335341 // }}}
336342 // {{{ drawSortArrow
337343
338 - $.articleFeedbackv5special.drawSortArrow = function() {
 344+ /**
 345+ * Utility method: Resets the sort arrows according to the currently selected
 346+ * sort and direction
 347+ */
 348+ $.articleFeedbackv5special.drawSortArrow = function() {
339349 var id = $.articleFeedbackv5special.listControls.sort,
340350 dir = $.articleFeedbackv5special.listControls.sortDirection;
341351
@@ -351,7 +361,9 @@
352362 // }}}
353363 // {{{ stripID
354364
355 - // Utility method for stripping long IDs down to the specific bits we care about.
 365+ /**
 366+ * Utility method: Strips long IDs down to the specific bits we care about
 367+ */
356368 $.articleFeedbackv5special.stripID = function( object, toRemove ) {
357369 return $( object ).attr( 'id' ).replace( toRemove, '' );
358370 };
@@ -464,12 +476,15 @@
465477
466478 // }}}
467479 // {{{ markHidden
 480+
468481 /**
469482 * Utility method: Marks a feedback row hidden
470483 *
471 - * @param $row element the feedback row
 484+ * @param $row element the feedback row
 485+ * @param hide_user string the user's name, linked to their user page
 486+ * @param hide_timestamp string the timestamp
472487 */
473 - $.articleFeedbackv5special.markHidden = function ( $row, $hide_user, $hide_timestamp ) {
 488+ $.articleFeedbackv5special.markHidden = function ( $row, hide_user, hide_timestamp ) {
474489 if ( $row.data( 'deleted' ) ) {
475490 $.articleFeedbackv5special.unmarkDeleted( $row );
476491 }
@@ -480,13 +495,15 @@
481496 .data( 'hidden', true );
482497 $row.find( '.articleFeedbackv5-comment-wrap' ).addClass( 'articleFeedbackv5-h3-push');
483498 $( '<span class="articleFeedbackv5-feedback-hidden-marker"></span>' )
484 - // this is on purpose html not text- $hide_user is a link
485 - .html( mw.msg( 'articlefeedbackv5-hidden', $hide_user, $hide_timestamp) )
 499+ // this is on purpose html not text- hide_user is a link
 500+ .html( mw.msg( 'articlefeedbackv5-hidden', hide_user, hide_timestamp) )
486501 .insertBefore( $row.find( '.articleFeedbackv5-comment-wrap' ) );
487502 $.articleFeedbackv5special.maskPost( $row, 'hidden');
488503 };
 504+
489505 // }}}
490506 // {{{ unmarkHidden
 507+
491508 /**
492509 * Utility method: Unmarks as hidden a feedback row
493510 *
@@ -498,14 +515,23 @@
499516 $row.find( '.articleFeedbackv5-feedback-hidden-marker' ).remove();
500517 $row.find( '.articleFeedbackv5-comment-wrap' ).removeClass( 'articleFeedbackv5-h3-push');
501518 };
 519+
502520 // }}}
503521 // {{{ maskPost
504 - $.articleFeedbackv5special.maskPost = function( $row, $type ) {
 522+
 523+ /**
 524+ * Utility method: Masks a comment that's been marked
 525+ * hidden/oversighted/etc.
 526+ *
 527+ * @param $row element the feedback row
 528+ * @param type string the mask type
 529+ */
 530+ $.articleFeedbackv5special.maskPost = function( $row, type ) {
505531 var $screen = $row.find( '.articleFeedbackv5-post-screen' );
506532 if( 0 == $screen.length ) {
507533 $screen = $( $.articleFeedbackv5special.maskHtmlTemplate );
508534 $screen.find( '.articleFeedbackv5-mask-text' )
509 - .text( mw.msg( 'articlefeedbackv5-mask-text-' + $type ) );
 535+ .text( mw.msg( 'articlefeedbackv5-mask-text-' + type ) );
510536 $screen.find( '.articleFeedbackv5-mask-postid' )
511537 .text( mw.msg( 'articlefeedbackv5-mask-postnumber', $row.attr( 'rel' ) ) );
512538 $row.prepend( $screen );
@@ -518,14 +544,18 @@
519545 $screen.find( '.articleFeedbackv5-mask-text-wrapper')
520546 .css( 'top', $screen.innerHeight() / 2 - 12 );
521547 }
 548+
522549 // }}}
523550 // {{{ markDeleted
 551+
524552 /**
525553 * Utility method: Marks a feedback row deleted
526554 *
527 - * @param $row element the feedback row
 555+ * @param $row element the feedback row
 556+ * @param oversight_user string the user's name, linked to their user page
 557+ * @param oversight_timestamp string the timestamp
528558 */
529 - $.articleFeedbackv5special.markDeleted = function ( $row , $oversight_user, $oversight_timestamp) {
 559+ $.articleFeedbackv5special.markDeleted = function ( $row, oversight_user, oversight_timestamp ) {
530560 if ( $row.data( 'deleted' ) ) {
531561 $.articleFeedbackv5special.unmarkDeleted( $row );
532562 }
@@ -535,8 +565,8 @@
536566 $row.addClass( 'articleFeedbackv5-feedback-deleted' )
537567 .data( 'deleted', true );
538568 var $marker = $( '<span class="articleFeedbackv5-feedback-deleted-marker"></span>' )
539 - // this is on purpose html not text- $oversight_user is a link
540 - .html( mw.msg( 'articlefeedbackv5-deleted', $oversight_user, $oversight_timestamp ) )
 569+ // this is on purpose html not text- oversight_user is a link
 570+ .html( mw.msg( 'articlefeedbackv5-deleted', oversight_user, oversight_timestamp ) )
541571 .insertBefore( $row.find( '.articleFeedbackv5-comment-wrap' ) );
542572 $row.find( '.articleFeedbackv5-comment-wrap' ).addClass( 'articleFeedbackv5-h3-push');
543573 $.articleFeedbackv5special.maskPost( $row, 'oversight' );
@@ -544,6 +574,7 @@
545575
546576 // }}}
547577 // {{{ unmarkDeleted
 578+
548579 /**
549580 * Utility method: Unmarks as deleted a feedback row
550581 *
@@ -555,9 +586,17 @@
556587 $row.find( '.articleFeedbackv5-feedback-deleted-marker' ).remove();
557588 $row.find( '.articleFeedbackv5-comment-wrap' ).removeClass( 'articleFeedbackv5-h3-push');
558589 };
 590+
559591 // }}}
560 -
561592 // {{{ setActivityFlag
 593+
 594+ /**
 595+ * Utility method: Sets an activity flag
 596+ *
 597+ * @param id string the feedback id
 598+ * @param flag string the flag name
 599+ * @param value string the value
 600+ */
562601 $.articleFeedbackv5special.setActivityFlag = function( id, flag, value ) {
563602 // no activity for this post yet, create default structure
564603 if ( !( id in $.articleFeedbackv5special.activity ) ) {
@@ -565,9 +604,10 @@
566605 }
567606 $.articleFeedbackv5special.activity[id][flag] = value;
568607 $.articleFeedbackv5special.storeActivity();
569 - }
 608+ };
 609+
570610 // }}}
571 -
 611+
572612 // }}}
573613 // {{{ Process methods
574614
@@ -584,22 +624,22 @@
585625 $.articleFeedbackv5special.flagFeedback = function ( id, action, note, options ) {
586626 // default parameters
587627 note = typeof note !== undefined ? note : '';
588 -
 628+
589629 if( $.articleFeedbackv5special.listControls.disabled ) {
590630 return false;
591631 }
592 -
593 - // This was causing problems with eg 'clicking helpful when the cookie
594 - // already says unhelpful', which is a case where two ajax requests
 632+
 633+ // This was causing problems with eg 'clicking helpful when the cookie
 634+ // already says unhelpful', which is a case where two ajax requests
595635 // is perfectly legitimate.
596636 // Check another global variable to not disable ajax in that case.
597637 if( !$.articleFeedbackv5special.listControls.allowMultiple ) {
598 - // Put a lock on ajax requests to prevent another one from going
 638+ // Put a lock on ajax requests to prevent another one from going
599639 // through while this is still running. Prevents manic link-clicking
600640 // messing up the counts, and generally seems like a good idea.
601641 $.articleFeedbackv5special.listControls.disabled = true;
602642 }
603 -
 643+
604644 // Merge request data and options objects (flat)
605645 var requestData = {
606646 'pageid' : $.articleFeedbackv5special.page,
@@ -626,10 +666,10 @@
627667 if( undefined != $.articleFeedbackv5special.actions[action].onSuccess ) {
628668 $.articleFeedbackv5special.actions[action].onSuccess( id, data );
629669 }
630 -
 670+
631671 // Re-enable ajax flagging.
632672 $.articleFeedbackv5special.listControls.disabled = false;
633 -
 673+
634674 // re-bind panels (tipsies)
635675 $.articleFeedbackv5special.bindPanels( id );
636676 return true;
@@ -652,6 +692,7 @@
653693
654694 // }}}
655695 // {{{ loadActivityLog
 696+
656697 /**
657698 * Load the activity log for a feedback post item
658699 *
@@ -695,10 +736,10 @@
696737 $( '#articlefeedbackv5-activity-log' ).text( mw.msg( 'articleFeedbackv5-view-activity-error' ) );
697738 }
698739 } );
699 -
 740+
700741 return false;
701 - }
702 -
 742+ };
 743+
703744 // }}}
704745 // {{{ loadFeedback
705746
@@ -763,7 +804,7 @@
764805 }
765806 $l.attr( 'id', 'articleFeedbackv5-unabuse-link-' + id )
766807 .removeClass( 'articleFeedbackv5-abuse-link' )
767 - .addClass( 'articleFeedbackv5-unabuse-link' );
 808+ .addClass( 'articleFeedbackv5-unabuse-link' );
768809 }
769810 }
770811
@@ -793,7 +834,7 @@
794835 } );
795836
796837 return false;
797 - }
 838+ };
798839
799840 // }}}
800841 // {{{ loadActivity
@@ -806,10 +847,11 @@
807848 if ( flatActivity ) {
808849 $.articleFeedbackv5special.activity = $.articleFeedbackv5special.decodeActivity( flatActivity );
809850 }
810 - }
 851+ };
811852
812853 // }}}
813854 // {{{ storeActivity
 855+
814856 /**
815857 * Stores the user activity to the cookie
816858 */
@@ -820,23 +862,26 @@
821863 flatActivity,
822864 { 'expires': 365, 'path': '/' }
823865 );
824 - }
 866+ };
 867+
825868 // }}}
826869 // {{{ canBeFlagged
 870+
827871 /**
828872 * Returns true if the post can be flagged
829873 */
830874 $.articleFeedbackv5special.canBeFlagged = function( $post ) {
831875 return !$post.data( 'hidden' ) && !$post.data( 'deleted' );
832 - }
 876+ };
 877+
833878 // }}}
834879
835880 // }}}
836 -
837881 // {{{ Actions
 882+
838883 /**
839884 * Actions - available actions on the page.
840 - *
 885+ *
841886 * Each action is an object with the following properties:
842887 * hasTipsy - true if the action needs a flyover panel
843888 * tipsyHtml - html for the corresponding flyover panel
@@ -848,7 +893,7 @@
849894 * data - any data returned by the AJAX call
850895 */
851896 $.articleFeedbackv5special.actions = {
852 -
 897+
853898 // Vote helpful
854899 'helpful': {
855900 'hasTipsy': false,
@@ -881,7 +926,7 @@
882927 $.articleFeedbackv5special.setActivityFlag( id, 'helpful', true );
883928 }
884929 },
885 -
 930+
886931 // Un-vote helpful
887932 'reversehelpful': {
888933 'hasTipsy': false,
@@ -905,7 +950,7 @@
906951 $.articleFeedbackv5special.setActivityFlag( id, 'helpful', false );
907952 }
908953 },
909 -
 954+
910955 // Vote unhelpful
911956 'unhelpful': {
912957 'hasTipsy': false,
@@ -938,7 +983,7 @@
939984 $.articleFeedbackv5special.setActivityFlag( id, 'unhelpful', true );
940985 }
941986 },
942 -
 987+
943988 // Un-vote unhelpful
944989 'reverseunhelpful': {
945990 'hasTipsy': false,
@@ -1001,7 +1046,7 @@
10021047 $.articleFeedbackv5special.setActivityFlag( id, 'abuse', true );
10031048 }
10041049 },
1005 -
 1050+
10061051 // Unflag post as abusive
10071052 'unabuse': {
10081053 'hasTipsy': false,
@@ -1040,7 +1085,7 @@
10411086 $.articleFeedbackv5special.setActivityFlag( id, 'abuse', false );
10421087 }
10431088 },
1044 -
 1089+
10451090 // Hide post action
10461091 'hide': {
10471092 'hasTipsy': true,
@@ -1062,7 +1107,7 @@
10631108 $.articleFeedbackv5special.setActivityFlag( id, 'hide', true );
10641109 }
10651110 },
1066 -
 1111+
10671112 // Show post action
10681113 'show': {
10691114 'hasTipsy': true,
@@ -1081,7 +1126,7 @@
10821127 $.articleFeedbackv5special.setActivityFlag( id, 'hide', false );
10831128 }
10841129 },
1085 -
 1130+
10861131 // Request oversight action
10871132 'requestoversight': {
10881133 'hasTipsy': true,
@@ -1112,7 +1157,7 @@
11131158 }
11141159 }
11151160 },
1116 -
 1161+
11171162 // Cancel oversight request action
11181163 'unrequestoversight': {
11191164 'hasTipsy': true,
@@ -1129,7 +1174,7 @@
11301175 .addClass( 'articleFeedbackv5-requestoversight-link');
11311176 }
11321177 },
1133 -
 1178+
11341179 // Oversight post action
11351180 'oversight': {
11361181 'hasTipsy': true,
@@ -1162,7 +1207,7 @@
11631208 $.articleFeedbackv5special.setActivityFlag( id, 'delete', true );
11641209 }
11651210 },
1166 -
 1211+
11671212 // Un-oversight action
11681213 'unoversight': {
11691214 'hasTipsy': true,
@@ -1184,7 +1229,7 @@
11851230 $.articleFeedbackv5special.setActivityFlag( id, 'delete', false );
11861231 }
11871232 },
1188 -
 1233+
11891234 // Decline oversight action
11901235 'declineoversight': {
11911236 'hasTipsy': true,
@@ -1196,7 +1241,7 @@
11971242 $( '#articleFeedbackv5-declineoversight-link-' + id ).remove();
11981243 }
11991244 },
1200 -
 1245+
12011246 // View activity log action
12021247 'activity': {
12031248 'hasTipsy': true,
@@ -1215,8 +1260,9 @@
12161261 }
12171262 }
12181263 }
1219 -
 1264+
12201265 };
 1266+
12211267 // }}}
12221268
12231269 // }}}
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.js
@@ -77,7 +77,7 @@
7878 /**
7979 * Are we in debug mode?
8080 */
81 - $.articleFeedbackv5.debug = mw.config.get( 'wgArticleFeedbackv5Debug' ) ? true : ( mw.util.getParamValue( 'debug' ) ? true : false );
 81+ $.articleFeedbackv5.debug = mw.config.get( 'wgArticleFeedbackv5Debug' ) ? true : false;
8282
8383 /**
8484 * Are we tracking clicks?
@@ -452,7 +452,7 @@
453453 $block.find( '.articleFeedbackv5-submit' )
454454 .click( function ( e ) {
455455 e.preventDefault();
456 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-submit-' +
 456+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-submit-' +
457457 ( $.articleFeedbackv5.inDialog ? 'overlay' : 'bottom' ) );
458458 $.articleFeedbackv5.submitForm();
459459 } );
@@ -506,1340 +506,6 @@
507507
508508 // }}}
509509
510 - },
511 -
512 - // }}}
513 - // {{{ Bucket 2
514 -
515 - /**
516 - * Bucket 2: Help Improve This Article
517 - */
518 - '2': {
519 -
520 - // {{{ properties
521 -
522 - /**
523 - * The tags available for this bucket
524 - */
525 - tagInfo: mw.config.get( 'wgArticleFeedbackv5Bucket2TagNames' ),
526 -
527 - /**
528 - * The comment default text, by tag (filled by buildForm)
529 - */
530 - commentDefault: {},
531 -
532 - /**
533 - * Currently displayed placeholder text. This is a workaround for Chrome/FF
534 - * automatic focus in overlays.
535 - */
536 - currentDefaultText: '',
537 -
538 - // }}}
539 - // {{{ templates
540 -
541 - /**
542 - * Pull out the markup so it's easy to find
543 - */
544 - templates: {
545 -
546 - /**
547 - * The template for the whole block
548 - */
549 - block: '\
550 - <form>\
551 - <div class="articleFeedbackv5-top-error"></div>\
552 - <div>\
553 - <div class="articleFeedbackv5-tags">\
554 - <ul></ul>\
555 - <div class="clear"></div>\
556 - </div>\
557 - <div class="clear"></div>\
558 - </div>\
559 - <div class="articleFeedbackv5-comment">\
560 - <textarea id="articleFeedbackv5-find-feedback" class="feedback-text" name="comment"></textarea>\
561 - </div>\
562 - <div class="articleFeedbackv5-disclosure">\
563 - <!-- <p class="articlefeedbackv5-shared-on-feedback"></p> -->\
564 - <p class="articlefeedbackv5-transparency-terms"></p>\
565 - </div>\
566 - <button class="articleFeedbackv5-submit" type="submit" disabled="disabled" id="articleFeedbackv5-submit-bttn">\
567 - <html:msg key="bucket2-form-submit" />\
568 - </button>\
569 - <div class="clear"></div>\
570 - </form>\
571 - ',
572 -
573 -
574 - /**
575 - * The template for a single tag
576 - */
577 - tag: '\
578 - <li>\
579 - <span class="tag-selector"></span>\
580 - <label class="articleFeedbackv5-tag-label"></label>\
581 - <input name="articleFeedbackv5-bucket2-tag" type="radio" class="articleFeedbackv5-tag-input" />\
582 - <span class="clear"></span>\
583 - </li>\
584 - '
585 -
586 - },
587 -
588 - // }}}
589 - // {{{ getTitle
590 -
591 - /**
592 - * Gets the title
593 - *
594 - * @return string the title
595 - */
596 - getTitle: function () {
597 - return mw.msg( 'articlefeedbackv5-bucket2-title' );
598 - },
599 -
600 - // }}}
601 - // {{{ buildForm
602 -
603 - /**
604 - * Builds the empty form
605 - *
606 - * @return Element the form
607 - */
608 - buildForm: function () {
609 -
610 - // Start up the block to return
611 - var $block = $( $.articleFeedbackv5.currentBucket().templates.block );
612 -
613 - // Add the tags from the options
614 - $block.find( '.articleFeedbackv5-tags ul' ).each( function () {
615 - var info = $.articleFeedbackv5.currentBucket().tagInfo;
616 - var tabIndex = 1;
617 - for ( var i = 0; i < info.length; i++ ) {
618 - var key = info[i];
619 - var comm_def_msg = 'articlefeedbackv5-bucket2-' + key + '-comment-default';
620 - $.articleFeedbackv5.currentBucket().commentDefault[key] = mw.msg( comm_def_msg );
621 - var label_msg = 'articlefeedbackv5-bucket2-' + key + '-label';
622 - var tag_id = 'articleFeedbackv5-bucket2-' + key;
623 - var $tag = $( $.articleFeedbackv5.currentBucket().templates.tag );
624 - $tag.attr( 'rel', key );
625 - $tag.find( '.articleFeedbackv5-tag-input' )
626 - .attr( 'id', tag_id )
627 - .attr( 'tabindex', tabIndex++)
628 - .val( key );
629 - $tag.find( '.articleFeedbackv5-tag-label' )
630 - .addClass( 'articleFeedbackv5-bucket2-' + key + '-label' )
631 - .attr( 'for', tag_id )
632 - .text( mw.msg( label_msg ) );
633 - $tag.appendTo( $( this ) );
634 - }
635 - $( $.articleFeedbackv5.templates.clear ).appendTo( $( this ) );
636 - } );
637 -
638 - // Fill in the disclosure text
639 - $block.find( '.articlefeedbackv5-shared-on-feedback' )
640 - .html( $.articleFeedbackv5.buildLink(
641 - 'articlefeedbackv5-shared-on-feedback',
642 - {
643 - href: mw.config.get( 'wgScript' ) + '?' + $.param( {
644 - title: mw.config.get( 'wgPageName' ),
645 - action: 'feedback'
646 - } ),
647 - text: 'articlefeedbackv5-shared-on-feedback-linktext',
648 - target: '_blank'
649 - } ) );
650 - $block.find( '.articlefeedbackv5-transparency-terms' )
651 - .html( $.articleFeedbackv5.buildLink(
652 - 'articlefeedbackv5-transparency-terms',
653 - {
654 - href: mw.config.get( 'wgArticleFeedbackv5TermsPage' ),
655 - text: 'articlefeedbackv5-transparency-terms-linktext',
656 - target: '_blank'
657 - } ) );
658 -
659 - // Turn the submit into a slick button
660 - $block.find( '.articleFeedbackv5-submit' )
661 - .button()
662 - .addClass( 'ui-button-blue' )
663 -
664 - return $block;
665 - },
666 -
667 - // }}}
668 - // {{{ bindEvents
669 -
670 - /**
671 - * Binds any events
672 - *
673 - * @param $block element the form block
674 - */
675 - bindEvents: function ( $block ) {
676 -
677 - // Enable submission and switch out the comment default on toggle selection
678 - $block.find( '.articleFeedbackv5-tags li' )
679 - .click( function ( e ) {
680 - var new_val = $( this ).attr( 'rel' );
681 - $.articleFeedbackv5.currentBucket().selectTag( new_val );
682 - $.articleFeedbackv5.enableSubmission( true );
683 - } );
684 -
685 - // Clear out the question on focus
686 - $block.find( '.articleFeedbackv5-comment textarea' )
687 - .focus( function () {
688 - if ( $( this ).val() == $.articleFeedbackv5.currentBucket().currentDefaultText ) {
689 - $( this ).val( '' );
690 - $(this).addClass( 'active' );
691 - }
692 - })
693 - .keyup ( function () {
694 - if( $( this ).val().length > 0 ) {
695 - $.articleFeedbackv5.enableSubmission( true );
696 - }
697 - } )
698 - .blur( function () {
699 - var key = $.articleFeedbackv5.find( '.articleFeedbackv5-tags input[checked]' ).val();
700 - var def_msg = $.articleFeedbackv5.currentBucket().commentDefault[key];
701 - if ( $( this ).val() == '' ) {
702 - $( this ).val( def_msg );
703 - $(this).removeClass( 'active' );
704 - } else {
705 - $.articleFeedbackv5.enableSubmission( true );
706 - }
707 - } );
708 -
709 - // Attach the submit
710 - $block.find( '.articleFeedbackv5-submit' )
711 - .click( function ( e ) {
712 - e.preventDefault();
713 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-submit-' +
714 - ( $.articleFeedbackv5.inDialog ? 'overlay' : 'bottom' ) );
715 - $.articleFeedbackv5.submitForm();
716 - } );
717 -
718 - },
719 -
720 - // }}}
721 - // {{{ afterBuild
722 -
723 - /**
724 - * Handles any setup that has to be done once the markup is in the
725 - * holder
726 - */
727 - afterBuild: function () {
728 - // Default to 'suggestion'
729 - $.articleFeedbackv5.currentBucket().selectTag( 'suggestion' );
730 - },
731 -
732 - // }}}
733 - // {{{ selectTag
734 -
735 - /**
736 - * Selects a particular tag
737 - *
738 - * @param tag string the tag
739 - */
740 - selectTag: function ( tag ) {
741 - var $c = $.articleFeedbackv5.find( '.articleFeedbackv5-comment textarea' );
742 - $.articleFeedbackv5.find( '.articleFeedbackv5-tags li' ).each( function () {
743 - var key = $( this ).attr( 'rel' );
744 - if ( key == tag ) {
745 - // Set checked
746 - $( this ).find( 'input' ).attr( 'checked', 'checked' );
747 - // Set active
748 - $( this ).addClass( 'active' );
749 - // Change out comment text
750 - var empty = false;
751 - if ( $c.val() == '') {
752 - empty = true;
753 - } else {
754 - for ( var t in $.articleFeedbackv5.currentBucket().commentDefault ) {
755 - if ( $c.val() == $.articleFeedbackv5.currentBucket().commentDefault[t] ) {
756 - empty = true;
757 - }
758 - }
759 - }
760 - if ( empty ) {
761 - $c.val( $.articleFeedbackv5.currentBucket().commentDefault[key] );
762 - // Store default text, workaround for overlay bug in Chrome/FF
763 - $.articleFeedbackv5.currentBucket().currentDefaultText = $.articleFeedbackv5.currentBucket().commentDefault[key];
764 - }
765 - } else {
766 - // Clear checked
767 - $( this ).find( 'input' ).removeAttr( 'checked' );
768 - // Remove active
769 - $( this ).removeClass( 'active' );
770 - }
771 - } );
772 - },
773 -
774 - // }}}
775 - // {{{ getFormData
776 -
777 - /**
778 - * Pulls down form data
779 - *
780 - * @return object the form data
781 - */
782 - getFormData: function () {
783 - var data = {};
784 - data.tag = $.articleFeedbackv5.find( '.articleFeedbackv5-tags input[checked]' ).val();
785 - data.comment = $.articleFeedbackv5.find( '.articleFeedbackv5-comment textarea' ).val();
786 - for ( var t in $.articleFeedbackv5.currentBucket().commentDefault ) {
787 - if ( data.comment == $.articleFeedbackv5.currentBucket().commentDefault[t] ) {
788 - data.comment = '';
789 - }
790 - }
791 - return data;
792 - },
793 -
794 - // }}}
795 - // {{{ localValidation
796 -
797 - /**
798 - * Performs any local validation
799 - *
800 - * @param object formdata the form data
801 - * @return mixed if ok, false; otherwise, an object as { 'field name' : 'message' }
802 - */
803 - localValidation: function ( formdata ) {
804 - var error = {};
805 - var ok = true;
806 - if ( !( 'comment' in formdata ) || formdata.comment == '' ) {
807 - $.articleFeedbackv5.enableSubmission( false );
808 - error.nofeedback = mw.msg( 'articlefeedbackv5-error-nofeedback' );
809 - ok = false;
810 - }
811 - return ok ? false : error;
812 - }
813 -
814 - // }}}
815 -
816 - },
817 -
818 - // }}}
819 - // {{{ Bucket 3
820 -
821 - /**
822 - * Bucket 3: Help Improve This Article
823 - */
824 - '3': {
825 -
826 - // {{{ templates
827 -
828 - /**
829 - * Pull out the markup so it's easy to find
830 - */
831 - templates: {
832 -
833 - /**
834 - * The template for the whole block
835 - */
836 - block: '\
837 - <form>\
838 - <div class="articleFeedbackv5-top-error"></div>\
839 - <div>\
840 - <p class="instructions-left"><html:msg key="bucket3-rating-question" /></p>\
841 - <div class="articleFeedbackv5-rating articleFeedbackv5-rating-new">\
842 - <input type="hidden" name="rating" value="0">\
843 - <div class="bucket3-rating">\
844 - <div class="articleFeedbackv5-rating-labels articleFeedbackv5-visibleWith-form">\
845 - <div class="articleFeedbackv5-rating-label" rel="1"></div>\
846 - <div class="articleFeedbackv5-rating-label" rel="2"></div>\
847 - <div class="articleFeedbackv5-rating-label" rel="3"></div>\
848 - <div class="articleFeedbackv5-rating-label" rel="4"></div>\
849 - <div class="articleFeedbackv5-rating-label" rel="5"></div>\
850 - <div class="articleFeedbackv5-rating-clear" style="display: none;"></div>\
851 - </div>\
852 - </div>\
853 - <div style="clear:both;"></div>\
854 - <div class="articleFeedbackv5-visibleWith-form">\
855 - <div class="articleFeedbackv5-rating-tooltip"></div>\
856 - </div>\
857 - </div>\
858 - <div class="clear"></div>\
859 - </div>\
860 - <div class="articleFeedbackv5-comment">\
861 - <textarea id="articleFeedbackv5-find-feedback" class="feedback-text" name="comment"></textarea>\
862 - </div>\
863 - <div class="articleFeedbackv5-disclosure">\
864 - <!-- <p class="articlefeedbackv5-shared-on-feedback"></p> -->\
865 - <p class="articlefeedbackv5-transparency-terms"></p>\
866 - </div>\
867 - <button class="articleFeedbackv5-submit" type="submit" disabled="disabled" id="articleFeedbackv5-submit-bttn"><html:msg key="bucket3-form-submit" /></button>\
868 - <div class="clear"></div>\
869 - </form>\
870 - '
871 -
872 - },
873 -
874 - // }}}
875 - // {{{ getTitle
876 -
877 - /**
878 - * Gets the title
879 - *
880 - * @return string the title
881 - */
882 - getTitle: function () {
883 - return mw.msg( 'articlefeedbackv5-bucket3-title' );
884 - },
885 -
886 - // }}}
887 - // {{{ buildForm
888 -
889 - /**
890 - * Builds the empty form
891 - *
892 - * @return Element the form
893 - */
894 - buildForm: function () {
895 -
896 - // Start up the block to return
897 - var $block = $( $.articleFeedbackv5.currentBucket().templates.block );
898 -
899 - // Fill in the rating clear title
900 - var clear_msg = mw.msg( 'articlefeedbackv5-bucket3-clear-rating' );
901 - $block.find( '.articleFeedbackv5-rating-clear' )
902 - .attr( 'title', clear_msg );
903 -
904 - // Activate tooltips
905 - $block.find( '[title]' )
906 - .tipsy( {
907 - 'gravity': 'sw',
908 - 'center': false,
909 - 'fade': true,
910 - 'delayIn': 300,
911 - 'delayOut': 100
912 - } );
913 -
914 - // Fill in the disclosure text
915 - $block.find( '.articlefeedbackv5-shared-on-feedback' )
916 - .html( $.articleFeedbackv5.buildLink(
917 - 'articlefeedbackv5-shared-on-feedback',
918 - {
919 - href: mw.config.get( 'wgScript' ) + '?' + $.param( {
920 - title: mw.config.get( 'wgPageName' ),
921 - action: 'feedback'
922 - } ),
923 - text: 'articlefeedbackv5-shared-on-feedback-linktext',
924 - target: '_blank'
925 - } ) );
926 - $block.find( '.articlefeedbackv5-transparency-terms' )
927 - .html( $.articleFeedbackv5.buildLink(
928 - 'articlefeedbackv5-transparency-terms',
929 - {
930 - href: mw.config.get( 'wgArticleFeedbackv5TermsPage' ),
931 - text: 'articlefeedbackv5-transparency-terms-linktext',
932 - target: '_blank'
933 - } ) );
934 -
935 - // Start with a default comment
936 - $block.find( '.articleFeedbackv5-comment textarea' )
937 - .val( mw.msg( 'articlefeedbackv5-bucket3-comment-default' ) );
938 -
939 - // Turn the submit into a slick button
940 - $block.find( '.articleFeedbackv5-submit' )
941 - .button()
942 - .addClass( 'ui-button-blue' )
943 -
944 - return $block;
945 - },
946 -
947 - // }}}
948 - // {{{ bindEvents
949 -
950 - /**
951 - * Binds any events
952 - *
953 - * @param $block element the form block
954 - */
955 - bindEvents: function ( $block ) {
956 -
957 - // Set up rating behavior
958 - var rlabel = $block.find( '.articleFeedbackv5-rating-label' );
959 - rlabel.hover( function () {
960 - // mouse on
961 - var $el = $( this );
962 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
963 - $el.addClass( 'articleFeedbackv5-rating-label-hover-head' );
964 - $el.prevAll( '.articleFeedbackv5-rating-label' )
965 - .addClass( 'articleFeedbackv5-rating-label-hover-tail' );
966 - $rating.find( '.articleFeedbackv5-rating-tooltip' )
967 - .text( mw.msg( 'articlefeedbackv5-bucket3-rating-tooltip-' + $el.attr( 'rel' ) ) )
968 - .show();
969 - }, function () {
970 - // mouse off
971 - var $el = $( this );
972 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
973 - $el.removeClass( 'articleFeedbackv5-rating-label-hover-head' );
974 - $el.prevAll( '.articleFeedbackv5-rating-label' )
975 - .removeClass( 'articleFeedbackv5-rating-label-hover-tail' );
976 - $rating.find( '.articleFeedbackv5-rating-tooltip' )
977 - .hide();
978 - $.articleFeedbackv5.currentBucket().updateRating( $rating );
979 - });
980 - rlabel.mousedown( function () {
981 - $.articleFeedbackv5.enableSubmission( true );
982 - var $ui = $.articleFeedbackv5.find( 'articleFeedbackv5-ui' );
983 - if ( $ui.hasClass( 'articleFeedbackv5-expired' ) ) {
984 - // Changing one means the rest will get submitted too
985 - $ui.removeClass( 'articleFeedbackv5-expired' );
986 - $ui.find( '.articleFeedbackv5-rating' )
987 - .addClass( 'articleFeedbackv5-rating-new' );
988 - }
989 - var $el = $( this );
990 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
991 - $rating.addClass( 'articleFeedbackv5-rating-new' );
992 - $rating.find( 'input:hidden' ).val( $el.attr( 'rel' ) );
993 - $el.addClass( 'articleFeedbackv5-rating-label-down' );
994 - $el.nextAll()
995 - .removeClass( 'articleFeedbackv5-rating-label-full' );
996 - $el.parent().find( '.articleFeedbackv5-rating-clear' ).show();
997 - } );
998 - rlabel.mouseup( function () {
999 - $(this).removeClass( 'articleFeedbackv5-rating-label-down' );
1000 - } );
1001 -
1002 - // Icon to clear out the ratings
1003 - $block.find( '.articleFeedbackv5-rating-clear' )
1004 - .click( function () {
1005 - $.articleFeedbackv5.enableSubmission( true );
1006 - $(this).hide();
1007 - var $rating = $(this).closest( '.articleFeedbackv5-rating' );
1008 - $rating.find( 'input:hidden' ).val( 0 );
1009 - $.articleFeedbackv5.currentBucket().updateRating( $rating );
1010 - } );
1011 -
1012 - // Clear out the question on focus
1013 - $block.find( '.articleFeedbackv5-comment textarea' )
1014 - .focus( function () {
1015 - var def_msg = mw.msg( 'articlefeedbackv5-bucket3-comment-default' );
1016 - if ( $( this ).val() == def_msg ) {
1017 - $( this ).val( '' );
1018 - $(this).addClass( 'active' );
1019 - }
1020 - })
1021 - .keyup ( function () {
1022 - if( $( this ).val().length > 0 ) {
1023 - $.articleFeedbackv5.enableSubmission( true );
1024 - }
1025 - } )
1026 - .blur( function () {
1027 - var def_msg = mw.msg( 'articlefeedbackv5-bucket3-comment-default' );
1028 - if ( $( this ).val() == '' ) {
1029 - $( this ).val( def_msg );
1030 - $(this).removeClass( 'active' );
1031 - } else {
1032 - $.articleFeedbackv5.enableSubmission( true );
1033 - }
1034 - } );
1035 -
1036 -
1037 - // Attach the submit
1038 - $block.find( '.articleFeedbackv5-submit' )
1039 - .click( function ( e ) {
1040 - e.preventDefault();
1041 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + 'submit-' +
1042 - ( $.articleFeedbackv5.inDialog ? 'overlay' : 'bottom' ) );
1043 - $.articleFeedbackv5.submitForm();
1044 - } );
1045 -
1046 - },
1047 -
1048 - // }}}
1049 - // {{{ updateRating
1050 -
1051 - /**
1052 - * Updates the stars to match the associated hidden field
1053 - *
1054 - * @param $rating the rating block
1055 - */
1056 - updateRating: function ( $rating ) {
1057 - $rating.find( '.articleFeedbackv5-rating-label' )
1058 - .removeClass( 'articleFeedbackv5-rating-label-full' );
1059 - var val = $rating.find( 'input:hidden' ).val();
1060 - var $label = $rating.find( '.articleFeedbackv5-rating-label[rel="' + val + '"]' );
1061 - if ( $label.length ) {
1062 - $label.prevAll( '.articleFeedbackv5-rating-label' )
1063 - .add( $label )
1064 - .addClass( 'articleFeedbackv5-rating-label-full' );
1065 - $label.nextAll( '.articleFeedbackv5-rating-label' )
1066 - .removeClass( 'articleFeedbackv5-rating-label-full' );
1067 - $rating.find( '.articleFeedbackv5-rating-clear' ).show();
1068 - } else {
1069 - $rating.find( '.articleFeedbackv5-rating-clear' ).hide();
1070 - }
1071 - },
1072 -
1073 - // }}}
1074 - // {{{ getFormData
1075 -
1076 - /**
1077 - * Pulls down form data
1078 - *
1079 - * @return object the form data
1080 - */
1081 - getFormData: function () {
1082 - var data = {};
1083 - var rating = $.articleFeedbackv5.find( '.articleFeedbackv5-rating input:hidden' ).val();
1084 - if ( '0' != rating ) {
1085 - data.rating = rating;
1086 - }
1087 - data.comment = $.articleFeedbackv5.find( '.articleFeedbackv5-comment textarea' ).val();
1088 - if ( data.comment == mw.msg( 'articlefeedbackv5-bucket3-comment-default' ) ) {
1089 - data.comment = '';
1090 - }
1091 - return data;
1092 - },
1093 -
1094 - // }}}
1095 - // {{{ localValidation
1096 -
1097 - /**
1098 - * Performs any local validation
1099 - *
1100 - * @param object formdata the form data
1101 - * @return mixed if ok, false; otherwise, an object as { 'field name' : 'message' }
1102 - */
1103 - localValidation: function ( formdata ) {
1104 - var error = {};
1105 - var ok = true;
1106 - if ( ( !( 'comment' in formdata ) || formdata.comment == '' )
1107 - && ( !( 'rating' in formdata ) || formdata.rating < 1 ) ) {
1108 - $.articleFeedbackv5.enableSubmission( false );
1109 - error.nofeedback = mw.msg( 'articlefeedbackv5-error-nofeedback' );
1110 - ok = false;
1111 - }
1112 - return ok ? false : error;
1113 - }
1114 -
1115 - // }}}
1116 -
1117 - },
1118 -
1119 - // }}}
1120 - // {{{ Bucket 4
1121 -
1122 - /**
1123 - * Bucket 4: Help Improve This Article
1124 - */
1125 - '4': {
1126 -
1127 - // {{{ templates
1128 -
1129 - /**
1130 - * Pull out the markup so it's easy to find
1131 - */
1132 - templates: {
1133 -
1134 - /**
1135 - * The template for the whole block
1136 - */
1137 - block: '\
1138 - <div>\
1139 - <div class="form-row articleFeedbackv5-bucket4-toggle">\
1140 - <p class="sub-header"><strong><html:msg key="bucket4-subhead" /></strong></p>\
1141 - <p class="instructions-left"><html:msg key="bucket4-teaser-line1" /><br />\
1142 - <html:msg key="bucket4-teaser-line2" /></p>\
1143 - </div>\
1144 - <div class="articleFeedbackv5-disclosure">\
1145 - <p><a class="articleFeedbackv5-learn-to-edit" target="_blank"><html:msg key="bucket4-learn-to-edit" /> &raquo;</a></p>\
1146 - </div>\
1147 - <a class="articleFeedbackv5-submit" id="articleFeedbackv5-submit-bttn"><html:msg key="bucket4-form-submit" /></a>\
1148 - <div class="clear"></div>\
1149 - </div>\
1150 - '
1151 -
1152 - },
1153 -
1154 - // }}}
1155 - // {{{ getTitle
1156 -
1157 - /**
1158 - * Gets the title
1159 - *
1160 - * @return string the title
1161 - */
1162 - getTitle: function () {
1163 - return mw.msg( 'articlefeedbackv5-bucket4-title' );
1164 - },
1165 -
1166 - // }}}
1167 - // {{{ buildForm
1168 -
1169 - /**
1170 - * Builds the empty form
1171 - *
1172 - * @return Element the form
1173 - */
1174 - buildForm: function () {
1175 -
1176 - // Start up the block to return
1177 - var $block = $( $.articleFeedbackv5.currentBucket().templates.block );
1178 -
1179 - // Fill in the learn to edit link
1180 - $block.find( '.articleFeedbackv5-learn-to-edit' )
1181 - .attr( 'href', mw.config.get( 'wgArticleFeedbackv5LearnToEdit' ) );
1182 -
1183 - // Fill in the edit link
1184 - var edit_track_id = $.articleFeedbackv5.bucketName() + '-button_click-' +
1185 - ( $.articleFeedbackv5.inDialog ? 'overlay' : 'bottom' );
1186 - $block.find( '.articleFeedbackv5-cta-button' )
1187 - .attr( 'href', $.articleFeedbackv5.editUrl( edit_track_id ) );
1188 -
1189 - // Turn the submit into a slick button
1190 - $block.find( '.articleFeedbackv5-submit' )
1191 - .button()
1192 - .addClass( 'ui-button-blue' )
1193 -
1194 - return $block;
1195 - },
1196 -
1197 - // }}}
1198 - // {{{ afterBuild
1199 -
1200 - /**
1201 - * Handles any setup that has to be done once the markup is in the
1202 - * holder
1203 - */
1204 - afterBuild: function () {
1205 - // Set a custom message
1206 - $.articleFeedbackv5.$holder
1207 - .add( $.articleFeedbackv5.$dialog)
1208 - .find( '.articleFeedbackv5-tooltip-info' )
1209 - .text( mw.msg( 'articlefeedbackv5-bucket4-help-tooltip-info' ) );
1210 - }
1211 -
1212 - // }}}
1213 -
1214 - },
1215 -
1216 - // }}}
1217 - // {{{ Bucket 5
1218 -
1219 - /**
1220 - * Bucket 5: Old ratings form
1221 - */
1222 - '5': {
1223 -
1224 - // {{{ properties
1225 -
1226 - /**
1227 - * The ratings right now are coming from the config, but they really
1228 - * can't be configured. Eventually, these should just be hardcoded.
1229 - */
1230 - ratingInfo: mw.config.get( 'wgArticleFeedbackv5Bucket5RatingCategories' ),
1231 -
1232 - /**
1233 - * Only certain users can see the expertise checkboxes and email
1234 - * (bucketed on init)
1235 - */
1236 - showOptions: false,
1237 -
1238 - /**
1239 - * Whether we need to load the aggregate ratings the next time the button is
1240 - * clicked. This is initially set to true, turned to false after the first
1241 - * time, then turned back to true on form submission, in case the user wants
1242 - * to go back and see the ratings with theirs included.
1243 - */
1244 - loadAggregate: true,
1245 -
1246 - /**
1247 - * Whether we're currently looking at the report
1248 - */
1249 - inReport: false,
1250 -
1251 - // }}}
1252 - // {{{ templates
1253 -
1254 - /**
1255 - * Pull out the markup so it's easy to find
1256 - */
1257 - templates: {
1258 -
1259 - /**
1260 - * The template for the whole block
1261 - */
1262 - block: '\
1263 - <form id="articleFeedbackv5-bucket5">\
1264 - <div class="articleFeedbackv5-switch articleFeedbackv5-switch-report articleFeedbackv5-visibleWith-form" rel="report"><html:msg key="bucket5-report-switch-label" /></div>\
1265 - <div class="articleFeedbackv5-switch articleFeedbackv5-switch-form articleFeedbackv5-visibleWith-report" rel="form"><html:msg key="bucket5-form-switch-label" /></div>\
1266 - <div class="articleFeedbackv5-explanation articleFeedbackv5-visibleWith-form"><a class="articleFeedbackv5-explanation-link"><html:msg key="bucket5-form-panel-explanation" /></a></div>\
1267 - <div class="articleFeedbackv5-description articleFeedbackv5-visibleWith-report"><html:msg key="bucket5-report-panel-description" /></div>\
1268 - <div style="clear:both;"></div>\
1269 - <div class="articleFeedbackv5-ratings"></div>\
1270 - <div style="clear:both;"></div>\
1271 - <div class="articleFeedbackv5-options">\
1272 - <div class="articleFeedbackv5-expertise articleFeedbackv5-visibleWith-form" >\
1273 - <input type="checkbox" value="general" disabled="disabled" /><label class="articleFeedbackv5-expertise-disabled"><html:msg key="bucket5-form-panel-expertise" /></label>\
1274 - <div class="articleFeedbackv5-expertise-options">\
1275 - <div><input type="checkbox" value="studies" /><label><html:msg key="bucket5-form-panel-expertise-studies" /></label></div>\
1276 - <div><input type="checkbox" value="profession" /><label><html:msg key="bucket5-form-panel-expertise-profession" /></label></div>\
1277 - <div><input type="checkbox" value="hobby" /><label><html:msg key="bucket5-form-panel-expertise-hobby" /></label></div>\
1278 - <div><input type="checkbox" value="other" /><label><html:msg key="bucket5-form-panel-expertise-other" /></label></div>\
1279 - <div class="articleFeedbackv5-helpimprove">\
1280 - <input type="checkbox" value="helpimprove-email" />\
1281 - <label><html:msg key="bucket5-form-panel-helpimprove" /></label>\
1282 - <input type="text" placeholder="" class="articleFeedbackv5-helpimprove-email" />\
1283 - <div class="articleFeedbackv5-helpimprove-note"></div>\
1284 - </div>\
1285 - </div>\
1286 - </div>\
1287 - <div style="clear:both;"></div>\
1288 - </div>\
1289 - <button class="articleFeedbackv5-submit articleFeedbackv5-visibleWith-form" id="articleFeedbackv5-submit-bttn5" type="submit" disabled="disabled"><html:msg key="bucket5-form-panel-submit" /></button>\
1290 - <div class="articleFeedbackv5-pending articleFeedbackv5-visibleWith-form"><span><html:msg key="bucket5-form-panel-pending" /></span></div>\
1291 - <div style="clear:both;"></div>\
1292 - <div class="articleFeedbackv5-notices articleFeedbackv5-visibleWith-form">\
1293 - <div class="articleFeedbackv5-expiry">\
1294 - <div class="articleFeedbackv5-expiry-title"><html:msg key="bucket5-form-panel-expiry-title" /></div>\
1295 - <div class="articleFeedbackv5-expiry-message"><html:msg key="bucket5-form-panel-expiry-message" /></div>\
1296 - </div>\
1297 - </div>\
1298 - </form>\
1299 - ',
1300 -
1301 -
1302 - /**
1303 - * The template for a single rating
1304 - */
1305 - rating: '\
1306 - <div class="articleFeedbackv5-rating">\
1307 - <div class="articleFeedbackv5-label"></div>\
1308 - <input type="hidden" name="" />\
1309 - <div class="articleFeedbackv5-rating-labels articleFeedbackv5-visibleWith-form">\
1310 - <div class="articleFeedbackv5-rating-label" rel="1"></div>\
1311 - <div class="articleFeedbackv5-rating-label" rel="2"></div>\
1312 - <div class="articleFeedbackv5-rating-label" rel="3"></div>\
1313 - <div class="articleFeedbackv5-rating-label" rel="4"></div>\
1314 - <div class="articleFeedbackv5-rating-label" rel="5"></div>\
1315 - <div class="articleFeedbackv5-rating-clear"></div>\
1316 - </div>\
1317 - <div class="articleFeedbackv5-visibleWith-form">\
1318 - <div class="articleFeedbackv5-rating-tooltip"></div>\
1319 - </div>\
1320 - <div class="articleFeedbackv5-rating-average articleFeedbackv5-visibleWith-report"></div>\
1321 - <div class="articleFeedbackv5-rating-meter articleFeedbackv5-visibleWith-report"><div></div></div>\
1322 - <div class="articleFeedbackv5-rating-count articleFeedbackv5-visibleWith-report"></div>\
1323 - <div style="clear:both;"></div>\
1324 - </div>\
1325 - '
1326 -
1327 - },
1328 -
1329 - // }}}
1330 - // {{{ init
1331 -
1332 - /**
1333 - * Initializes the bucket
1334 - */
1335 - init: function () {
1336 - var opt = mw.user.bucket( 'ext.articleFeedbackv5-options', mw.config.get( 'wgArticleFeedbackv5Options' ) )
1337 - $.articleFeedbackv5.currentBucket().showOptions = ( 'show' === opt );
1338 - },
1339 -
1340 - // }}}
1341 - // {{{ getTitle
1342 -
1343 - /**
1344 - * Gets the title
1345 - *
1346 - * @return string the title
1347 - */
1348 - getTitle: function () {
1349 - if ( $.articleFeedbackv5.buckets[5].inReport ) {
1350 - return mw.msg( 'articlefeedbackv5-bucket5-report-panel-title' );
1351 - } else {
1352 - return mw.msg( 'articlefeedbackv5-bucket5-form-panel-title' );
1353 - }
1354 - },
1355 -
1356 - // }}}
1357 - // {{{ buildForm
1358 -
1359 - /**
1360 - * Builds the empty form
1361 - *
1362 - * @return Element the form
1363 - */
1364 - buildForm: function () {
1365 -
1366 - // Start up the block to return
1367 - var $block = $( $.articleFeedbackv5.currentBucket().templates.block );
1368 -
1369 - // Add the ratings from the options
1370 - $block.find( '.articleFeedbackv5-ratings' ).each( function () {
1371 - var info = $.articleFeedbackv5.currentBucket().ratingInfo;
1372 - for ( var i = 0; i < info.length; i++ ) {
1373 - var key = info[i];
1374 - var tip_msg = 'articlefeedbackv5-bucket5-' + key + '-tip';
1375 - var label_msg = 'articlefeedbackv5-bucket5-' + key + '-label';
1376 - var $rtg = $( $.articleFeedbackv5.currentBucket().templates.rating );
1377 - $rtg.attr( 'rel', key );
1378 - $rtg.find( '.articleFeedbackv5-label' )
1379 - .attr( 'title', mw.msg( tip_msg ) )
1380 - .text( mw.msg( label_msg ) );
1381 - $rtg.find( '.articleFeedbackv5-rating-clear' )
1382 - .attr( 'title', mw.msg( 'articlefeedbackv5-bucket5-form-panel-clear' ) );
1383 - $rtg.appendTo( $(this) );
1384 - }
1385 - } );
1386 -
1387 - // Fill in the link to the What's This page
1388 - $block.find( '.articleFeedbackv5-explanation-link' )
1389 - .attr(
1390 - 'href',
1391 - mw.util.wikiGetlink( mw.config.get( 'wgArticleFeedbackv5WhatsThisPage' ) ) // TODO: Make this work
1392 - );
1393 -
1394 - // Fill in the Help Improve message and links
1395 - $block.find( '.articleFeedbackv5-helpimprove-note' )
1396 - .html( $.articleFeedbackv5.buildLink(
1397 - 'articlefeedbackv5-bucket5-form-panel-helpimprove-note',
1398 - {
1399 - href: mw.config.get( 'wgArticleFeedbackv5TermsPage' ), // TODO: Make this work
1400 - text: 'articlefeedbackv5-bucket5-form-panel-helpimprove-privacy',
1401 - target: '_blank'
1402 - }
1403 - ) );
1404 -
1405 - $block.find( '.articleFeedbackv5-helpimprove-email' )
1406 - .attr( 'placeholder', mw.msg( 'articlefeedbackv5-bucket5-form-panel-helpimprove-email-placeholder' ) )
1407 - .placeholder(); // back. compat. for older browsers
1408 -
1409 - // Activate tooltips
1410 - $block.find( '[title]' )
1411 - .tipsy( {
1412 - 'gravity': 'sw',
1413 - 'center': false,
1414 - 'fade': true,
1415 - 'delayIn': 300,
1416 - 'delayOut': 100
1417 - } );
1418 -
1419 - // Set id and for on expertise checkboxes
1420 - $block.find( '.articleFeedbackv5-expertise input:checkbox' )
1421 - .each( function () {
1422 - var id = 'articleFeedbackv5-expertise-' + $(this).attr( 'value' );
1423 - $(this).attr( 'id', id );
1424 - $(this).next().attr( 'for', id );
1425 - } );
1426 - $block.find( '.articleFeedbackv5-helpimprove > input:checkbox' )
1427 - .each( function () {
1428 - var id = 'articleFeedbackv5-expertise-' + $(this).attr( 'value' );
1429 - $(this).attr( 'id', id );
1430 - $(this).next().attr( 'for', id );
1431 - })
1432 -
1433 - // Turn the submit into a slick button
1434 - $block.find( '.articleFeedbackv5-submit' )
1435 - .button()
1436 - .addClass( 'ui-button-blue' )
1437 -
1438 - // Hide report elements initially
1439 - $block.find( '.articleFeedbackv5-visibleWith-report' ).hide();
1440 -
1441 - // Name the hidden rating fields
1442 - $block.find( '.articleFeedbackv5-rating' )
1443 - .each( function () {
1444 - $(this).find( 'input:hidden' ) .attr( 'name', $(this).attr( 'rel' ) );
1445 - } );
1446 -
1447 - // Hide the additional options, if the user's in a bucket that
1448 - // requires it
1449 - if ( !$.articleFeedbackv5.currentBucket().showOptions ) {
1450 - $block.find( '.articleFeedbackv5-options' ).hide();
1451 - }
1452 -
1453 - return $block;
1454 - },
1455 -
1456 - // }}}
1457 - // {{{ bindEvents
1458 -
1459 - /**
1460 - * Binds any events
1461 - *
1462 - * @param $block element the form block
1463 - */
1464 - bindEvents: function ( $block ) {
1465 -
1466 - // On-blur validity check for Help Improve email field
1467 - $block.find( '.articleFeedbackv5-helpimprove-email' )
1468 - .one( 'blur', function () {
1469 - var $el = $(this);
1470 - var bucket = $.articleFeedbackv5.currentBucket();
1471 - bucket.updateMailValidityLabel( $el.val() );
1472 - $el.keyup( function () {
1473 - bucket.updateMailValidityLabel( $el.val() );
1474 - } );
1475 - } );
1476 -
1477 - // Slide-down for the expertise checkboxes
1478 - $block.find( '.articleFeedbackv5-expertise > input:checkbox' )
1479 - .change( function () {
1480 - var $options = $.articleFeedbackv5.find( '.articleFeedbackv5-expertise-options' );
1481 - if ( $(this).is( ':checked' ) ) {
1482 - $options.slideDown( 'fast' );
1483 - } else {
1484 - $options.slideUp( 'fast', function () {
1485 - $options.find( 'input:checkbox' ).attr( 'checked', false );
1486 - } );
1487 - }
1488 - } );
1489 -
1490 - // Enable submission when at least one rating is set
1491 - $block.find( '.articleFeedbackv5-expertise input:checkbox' )
1492 - .each( function () {
1493 - var id = 'articleFeedbackv5-expertise-' + $(this).attr( 'value' );
1494 - $(this).click( function () {
1495 - $.articleFeedbackv5.enableSubmission( true );
1496 - } );
1497 - } );
1498 -
1499 - // Clicking on the email field checks the associted box
1500 - $block.find( '.articleFeedbackv5-helpimprove-email' )
1501 - .bind( 'mousedown click', function ( e ) {
1502 - $(this).closest( '.articleFeedbackv5-helpimprove' )
1503 - .find( 'input:checkbox' )
1504 - .attr( 'checked', true );
1505 - } );
1506 -
1507 - // Attach the submit
1508 - $block.find( '.articleFeedbackv5-submit' )
1509 - .click( function ( e ) {
1510 - e.preventDefault();
1511 - $.articleFeedbackv5.submitForm();
1512 - } );
1513 -
1514 - // Set up form/report switch behavior
1515 - $block.find( '.articleFeedbackv5-switch' )
1516 - .click( function ( e ) {
1517 - var which = $( this ).attr( 'rel' );
1518 - if ( which == 'report' && $.articleFeedbackv5.currentBucket().loadAggregate ) {
1519 - $.articleFeedbackv5.currentBucket().loadAggregateRatings();
1520 - $.articleFeedbackv5.currentBucket().loadAggregate = false;
1521 - }
1522 - $.articleFeedbackv5.find( '.articleFeedbackv5-visibleWith-' + which ).show();
1523 - $.articleFeedbackv5.find( '.articleFeedbackv5-switch' )
1524 - .not( $( this ) )
1525 - .each( function () {
1526 - $.articleFeedbackv5.find( '.articleFeedbackv5-visibleWith-' + $( this ).attr( 'rel' ) ).hide();
1527 - } );
1528 - $.articleFeedbackv5.currentBucket().inReport = which == 'report';
1529 - e.preventDefault();
1530 - return false;
1531 - } );
1532 -
1533 - // Set up rating behavior
1534 - var rlabel = $block.find( '.articleFeedbackv5-rating-label' );
1535 - rlabel.hover( function () {
1536 - // mouse on
1537 - var $el = $( this );
1538 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
1539 - $el.addClass( 'articleFeedbackv5-rating-label-hover-head' );
1540 - $el.prevAll( '.articleFeedbackv5-rating-label' )
1541 - .addClass( 'articleFeedbackv5-rating-label-hover-tail' );
1542 - $rating.find( '.articleFeedbackv5-rating-tooltip' )
1543 - .text( mw.msg( 'articlefeedbackv5-bucket5-' + $rating.attr( 'rel' ) + '-tooltip-' + $el.attr( 'rel' ) ) )
1544 - .show();
1545 - }, function () {
1546 - // mouse off
1547 - var $el = $( this );
1548 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
1549 - $el.removeClass( 'articleFeedbackv5-rating-label-hover-head' );
1550 - $el.prevAll( '.articleFeedbackv5-rating-label' )
1551 - .removeClass( 'articleFeedbackv5-rating-label-hover-tail' );
1552 - $rating.find( '.articleFeedbackv5-rating-tooltip' )
1553 - .hide();
1554 - var bucket = $.articleFeedbackv5.currentBucket();
1555 - bucket.updateRating( $rating );
1556 - });
1557 - rlabel.mousedown( function () {
1558 - $.articleFeedbackv5.enableSubmission( true );
1559 - var $ui = $.articleFeedbackv5.find( '.articleFeedbackv5-ui' );
1560 - if ( $ui.hasClass( 'articleFeedbackv5-expired' ) ) {
1561 - // Changing one means the rest will get submitted too
1562 - $ui.removeClass( 'articleFeedbackv5-expired' );
1563 - $ui.find( '.articleFeedbackv5-rating' )
1564 - .addClass( 'articleFeedbackv5-rating-new' );
1565 - }
1566 - $ui.find( '.articleFeedbackv5-expertise' )
1567 - .each( function () {
1568 - $.articleFeedbackv5.currentBucket().enableExpertise( $(this) );
1569 - } );
1570 - var $el = $( this );
1571 - var $rating = $el.closest( '.articleFeedbackv5-rating' );
1572 - $rating.addClass( 'articleFeedbackv5-rating-new' );
1573 - $rating.find( 'input:hidden' ).val( $el.attr( 'rel' ) );
1574 - $el.addClass( 'articleFeedbackv5-rating-label-down' );
1575 - $el.nextAll()
1576 - .removeClass( 'articleFeedbackv5-rating-label-full' );
1577 - $el.parent().find( '.articleFeedbackv5-rating-clear' ).show();
1578 - } );
1579 - rlabel.mouseup( function () {
1580 - $(this).removeClass( 'articleFeedbackv5-rating-label-down' );
1581 - } );
1582 -
1583 - // Icon to clear out the ratings
1584 - $block.find( '.articleFeedbackv5-rating-clear' )
1585 - .click( function () {
1586 - $.articleFeedbackv5.enableSubmission( true );
1587 - $(this).hide();
1588 - var $rating = $(this).closest( '.articleFeedbackv5-rating' );
1589 - $rating.find( 'input:hidden' ).val( 0 );
1590 - $.articleFeedbackv5.currentBucket().updateRating( $rating );
1591 - } );
1592 -
1593 - },
1594 -
1595 - // }}}
1596 - // {{{ afterBuild
1597 -
1598 - /**
1599 - * Handles any setup that has to be done once the markup is in the
1600 - * holder
1601 - */
1602 - afterBuild: function () {
1603 - // Drop the tooltip trigger
1604 - $.articleFeedbackv5.$holder
1605 - .add( $.articleFeedbackv5.$dialog)
1606 - .find( '.articleFeedbackv5-tooltip-trigger' ).hide();
1607 - },
1608 -
1609 - // }}}
1610 - // {{{ updateRating
1611 -
1612 - /**
1613 - * Updates the stars to match the associated hidden field
1614 - *
1615 - * @param $rating the rating block
1616 - */
1617 - updateRating: function ( $rating ) {
1618 - $rating.find( '.articleFeedbackv5-rating-label' )
1619 - .removeClass( 'articleFeedbackv5-rating-label-full' );
1620 - var val = $rating.find( 'input:hidden' ).val();
1621 - var $label = $rating.find( '.articleFeedbackv5-rating-label[rel="' + val + '"]' );
1622 - if ( $label.length ) {
1623 - $label.prevAll( '.articleFeedbackv5-rating-label' )
1624 - .add( $label )
1625 - .addClass( 'articleFeedbackv5-rating-label-full' );
1626 - $label.nextAll( '.articleFeedbackv5-rating-label' )
1627 - .removeClass( 'articleFeedbackv5-rating-label-full' );
1628 - $rating.find( '.articleFeedbackv5-rating-clear' ).show();
1629 - } else {
1630 - $rating.find( '.articleFeedbackv5-rating-clear' ).hide();
1631 - }
1632 - },
1633 -
1634 - // }}}
1635 - // {{{ enableSubmission
1636 -
1637 - /**
1638 - * Enables or disables submission of the form
1639 - *
1640 - * @param state bool true to enable; false to disable
1641 - */
1642 - enableSubmission: function ( state ) {
1643 - if ( state ) {
1644 - $.articleFeedbackv5.find( '.articleFeedbackv5-pending span' ).fadeIn( 'fast' );
1645 - } else {
1646 - $.articleFeedbackv5.find( '.articleFeedbackv5-pending span' ).hide();
1647 - }
1648 - $.articleFeedbackv5.submissionEnabled = state;
1649 - },
1650 -
1651 - // }}}
1652 - // {{{ enableExpertise
1653 -
1654 - /**
1655 - * Enables the expertise checkboxes
1656 - *
1657 - * @param element $el the element containing checkboxes to enable
1658 - */
1659 - enableExpertise: function ( $el ) {
1660 - $el.find( 'input:checkbox[value=general]' )
1661 - .attr( 'disabled', false )
1662 - $el.find( '.articleFeedbackv5-expertise-disabled' )
1663 - .removeClass( 'articleFeedbackv5-expertise-disabled' );
1664 - },
1665 -
1666 - // }}}
1667 - // {{{ updateMailValidityLabel
1668 -
1669 - /**
1670 - * Given an email sting, gets validity status (true, false, null) and updates
1671 - * the label's CSS class
1672 - *
1673 - * @param string mail the email address
1674 - */
1675 - updateMailValidityLabel: function ( mail ) {
1676 - var isValid = mw.util.validateEmail( mail );
1677 - var $label = $.articleFeedbackv5.find( '.articleFeedbackv5-helpimprove-email' );
1678 - if ( isValid === null ) { // empty address
1679 - $label.removeClass( 'valid invalid' );
1680 - } else if ( isValid ) {
1681 - $label.addClass( 'valid' ).removeClass( 'invalid' );
1682 - } else {
1683 - $label.addClass( 'invalid' ).removeClass( 'valid' );
1684 - }
1685 - },
1686 -
1687 - // }}}
1688 - // {{{ loadAggregateRatings
1689 -
1690 - /**
1691 - * Pulls the aggregate ratings via ajax request
1692 - * the label's CSS class
1693 - */
1694 - loadAggregateRatings: function () {
1695 - $.ajax( {
1696 - 'url': $.articleFeedbackv5.apiUrl,
1697 - 'type': 'GET',
1698 - 'dataType': 'json',
1699 - 'data': {
1700 - 'action': 'query',
1701 - 'format': 'json',
1702 - 'list': 'articlefeedbackv5-view-ratings',
1703 - 'afpageid': $.articleFeedbackv5.pageId,
1704 - 'maxage': 0,
1705 - 'smaxage': mw.config.get( 'wgArticleFeedbackv5SMaxage' )
1706 - },
1707 - 'success': function ( data ) {
1708 - // Get data
1709 - if (
1710 - !( 'query' in data )
1711 - || !( 'articlefeedbackv5-view-ratings' in data.query )
1712 - || !( 'rollup' in data.query['articlefeedbackv5-view-ratings'] )
1713 - ) {
1714 - mw.log( mw.msg ( 'articlefeedbackv5-error-response' ) );
1715 - var msg = mw.msg ( 'articlefeedbackv5-error-response' );
1716 - if ( 'error' in data && 'info' in data.error ) {
1717 - msg = data.error.info;
1718 - } else {
1719 - aft5_debug(data);
1720 - }
1721 - $.articleFeedbackv5.markShowstopperError( msg );
1722 - return;
1723 - }
1724 - var rollup = data.query['articlefeedbackv5-view-ratings'].rollup;
1725 -
1726 - // Ratings
1727 - $.articleFeedbackv5.find( '.articleFeedbackv5-rating' ).each( function () {
1728 - var name = $(this).attr( 'rel' );
1729 - var rating = name in rollup ? rollup[name] : null;
1730 - if (
1731 - rating !== null
1732 - && 'total' in rating
1733 - && 'count' in rating
1734 - && rating.total > 0
1735 - ) {
1736 - var average = Math.round( ( rating.total / rating.count ) * 10 ) / 10;
1737 - $(this).find( '.articleFeedbackv5-rating-average' )
1738 - .text( mw.language.convertNumber( average + ( average % 1 === 0 ? '.0' : '' ) , false ) );
1739 - $(this).find( '.articleFeedbackv5-rating-meter div' )
1740 - .css( 'width', Math.round( average * 21 ) + 'px' );
1741 - $(this).find( '.articleFeedbackv5-rating-count' )
1742 - .text( mw.msg( 'articlefeedbackv5-bucket5-report-ratings', rating.count ) );
1743 - } else {
1744 - // Special case for no ratings
1745 - $(this).find( '.articleFeedbackv5-rating-average' )
1746 - .html( '&nbsp;' );
1747 - $(this).find( '.articleFeedbackv5-rating-meter div' )
1748 - .css( 'width', 0 );
1749 - $(this).find( '.articleFeedbackv5-rating-count' )
1750 - .text( mw.msg( 'articlefeedbackv5-bucket5-report-empty' ) );
1751 - }
1752 - } );
1753 -
1754 - // Status change - un-new the rating controls
1755 - $.articleFeedbackv5.find( '.articleFeedbackv5-rating-new' )
1756 - .removeClass( 'articleFeedbackv5-rating-new' );
1757 - },
1758 - 'error': function () {
1759 - mw.log( 'Report loading error' );
1760 - $.articleFeedbackv5.currentBucket().markShowstopperError( 'Report loading error' );
1761 - $.articleFeedbackv5.find( '.articleFeedbackv5-error' ).show();
1762 - }
1763 - } );
1764 -
1765 - },
1766 -
1767 - // }}}
1768 - // {{{ getFormData
1769 -
1770 - /**
1771 - * Pulls down form data
1772 - *
1773 - * @return object the form data
1774 - */
1775 - getFormData: function () {
1776 - var data = {};
1777 - var info = $.articleFeedbackv5.currentBucket().ratingInfo;
1778 - for ( var i = 0; i < info.length; i++ ) {
1779 - var key = info[i];
1780 - var val = $.articleFeedbackv5.find( 'input[name="' + key + '"]' ).val();
1781 - if ( '0' != val ) {
1782 - data[key] = val;
1783 - }
1784 - }
1785 - $.articleFeedbackv5.find( '.articleFeedbackv5-expertise input:checked' ).each( function () {
1786 - data['expertise-' + $( this ).val()] = 1;
1787 - } );
1788 - if ( $.articleFeedbackv5.find( '.articleFeedbackv5-helpimprove input:checked' ).length > 0 ) {
1789 - data.email = $.articleFeedbackv5.find( '.articleFeedbackv5-helpimprove-email' ).val();
1790 - }
1791 - return data;
1792 - },
1793 -
1794 - // }}}
1795 - // {{{ localValidation
1796 -
1797 - /**
1798 - * Performs any local validation
1799 - *
1800 - * @param object formdata the form data
1801 - * @return mixed if ok, false; otherwise, an object as { 'field name' : 'message' }
1802 - */
1803 - localValidation: function ( formdata ) {
1804 - var error = {};
1805 - var ok = true;
1806 - if ( $.articleFeedbackv5.find( '.articleFeedbackv5-helpimprove input:checked' ).length > 0 ) {
1807 - if ( 'email' in formdata && !mw.util.validateEmail( formdata.email ) ) {
1808 - error.helpimprove_email = mw.msg( 'articlefeedbackv5-error-email' );
1809 - ok = false;
1810 - }
1811 - }
1812 - return ok ? false : error;
1813 - },
1814 -
1815 - // }}}
1816 - // {{{ markFormErrors
1817 -
1818 - /**
1819 - * Marks any errors on the form
1820 - *
1821 - * @param object errors errors, indexed by field name
1822 - */
1823 - markFormErrors: function ( errors ) {
1824 - if ( 'helpimprove_email' in errors ) {
1825 - $.articleFeedbackv5.find( '.articleFeedbackv5-helpimprove-email' )
1826 - .addClass( 'invalid' )
1827 - .removeClass( 'valid' );
1828 - }
1829 - },
1830 -
1831 - // }}}
1832 - // {{{ onSubmit
1833 -
1834 - /**
1835 - * Sends off the email tracking request alongside the regular form
1836 - * submit
1837 - */
1838 - onSubmit: function () {
1839 - $.articleFeedbackv5.currentBucket().loadAggregate = true;
1840 - }
1841 -
1842 - // }}}
1843 -
1844510 }
1845511
1846512 // }}}
@@ -1958,7 +624,7 @@
1959625 .attr( 'href', mw.msg( 'articlefeedbackv5-cta1-learn-how-url' ) );
1960626
1961627 // Fill in the link
1962 - var edit_track_id = $.articleFeedbackv5.bucketName() + '-' +
 628+ var edit_track_id = $.articleFeedbackv5.experiment() + '-' +
1963629 $.articleFeedbackv5.ctaName() + '-button_click-' +
1964630 ( $.articleFeedbackv5.inDialog ? 'overlay': 'bottom' );
1965631 $block.find( '.articleFeedbackv5-cta-button' )
@@ -2025,7 +691,7 @@
2026692
2027693 // Fill in the button link
2028694 var learn_url = mw.msg( 'articlefeedbackv5-cta1-learn-how-url' );
2029 - var learn_track_id = $.articleFeedbackv5.bucketName() + '-' +
 695+ var learn_track_id = $.articleFeedbackv5.experiment() + '-' +
2030696 $.articleFeedbackv5.ctaName() + '-button_click-' +
2031697 ( $.articleFeedbackv5.inDialog ? 'overlay': 'bottom' );
2032698 $block.find( '.articleFeedbackv5-cta-button' )
@@ -2105,7 +771,7 @@
2106772 // Fill in the go-to-survey link
2107773 var survey_url = $.articleFeedbackv5.currentCTA().getSurveyUrl();
2108774 if ( survey_url ) {
2109 - var survey_track_id = $.articleFeedbackv5.bucketName() + '-' +
 775+ var survey_track_id = $.articleFeedbackv5.experiment() + '-' +
2110776 $.articleFeedbackv5.ctaName() + '-button_click-' +
2111777 ( $.articleFeedbackv5.inDialog ? 'overlay': 'bottom' );
2112778 $block.find( '.articleFeedbackv5-cta-button' )
@@ -2723,6 +1389,11 @@
27241390 $.articleFeedbackv5.init = function ( $el, config ) {
27251391 $.articleFeedbackv5.$holder = $el;
27261392 $.articleFeedbackv5.config = config;
 1393+ // Debug mode
 1394+ var reqDebug = mw.util.getParamValue( 'debug' );
 1395+ if ( reqDebug ) {
 1396+ $.articleFeedbackv5.debug = reqDebug == 'false' ? false : true;
 1397+ }
27271398 // Are we tracking clicks?
27281399 $.articleFeedbackv5.clickTracking = $.articleFeedbackv5.checkClickTracking();
27291400 // Has the user already submitted ratings for this page at this revision?
@@ -2737,7 +1408,7 @@
27381409 $.articleFeedbackv5.$holder.appear( function () {
27391410 if ( !$.articleFeedbackv5.isLoaded ) {
27401411 $.articleFeedbackv5.load( 'auto', 'bottom' );
2741 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-impression-bottom' );
 1412+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-impression-bottom' );
27421413 }
27431414 } );
27441415 // Keep track of links that must be removed after a successful submission
@@ -2747,7 +1418,7 @@
27481419 $.articleFeedbackv5.addTriggerLinks();
27491420 // Track init at 1%
27501421 if ( Math.random() * 100 < 1 ) {
2751 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-init' );
 1422+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-init' );
27521423 }
27531424 };
27541425
@@ -2766,10 +1437,10 @@
27671438 // 1. Requested in query string (debug only)
27681439 // 2. From cookie (see below)
27691440 // 3. Core bucketing
2770 - var knownBuckets = { '0': true, '1': true, '2': true, '3': true, '4': true, '5': true };
2771 - var requested = mw.util.getParamValue( 'bucket' );
 1441+ var knownBuckets = { '0': true, '1': true };
 1442+ var requested = mw.util.getParamValue( 'aftv5_form' );
27721443 var cookieval = $.cookie( $.articleFeedbackv5.prefix( 'display-bucket' ) );
2773 - if ( $.articleFeedbackv5.debug && requested in knownBuckets ) {
 1444+ if ( requested in knownBuckets ) {
27741445 $.articleFeedbackv5.bucketId = requested;
27751446 } else if ( cookieval in knownBuckets ) {
27761447 $.articleFeedbackv5.bucketId = cookieval;
@@ -2778,7 +1449,7 @@
27791450 'ext.articleFeedbackv5-display',
27801451 mw.config.get( 'wgArticleFeedbackv5DisplayBuckets' )
27811452 );
2782 - var nameMap = { zero: '0', one: '1', two: '2', three: '3', four: '4', five: '5' };
 1453+ var nameMap = { zero: '0', one: '1' };
27831454 $.articleFeedbackv5.bucketId = nameMap[bucketName];
27841455 }
27851456 // Drop in a cookie to keep track of their display bucket;
@@ -2835,14 +1506,14 @@
28361507 if ( 'buckets' in cfg ) {
28371508 var knownBuckets = cfg.buckets;
28381509 var requested = mw.util.getParamValue( 'aftv5_link' );
2839 - if ( $.articleFeedbackv5.inDebug() && ( requested in knownBuckets || requested == 'X' ) ) {
 1510+ if ( requested in knownBuckets || requested == 'X' ) {
28401511 bucketedLink = requested;
28411512 } else {
28421513 bucketedLink = mw.user.bucket( 'ext.articleFeedbackv5-links', cfg );
28431514 }
28441515 }
28451516 }
2846 - if ( $.articleFeedbackv5.inDebug() ) {
 1517+ if ( $.articleFeedbackv5.debug ) {
28471518 aft5_debug( 'Using link option ' + bucketedLink );
28481519 }
28491520 $.articleFeedbackv5.floatingLinkId = bucketedLink;
@@ -3001,14 +1672,14 @@
30021673 };
30031674
30041675 // }}}
3005 - // {{{ bucketName
 1676+ // {{{ experiment
30061677
30071678 /**
3008 - * Utility method: Gets the name of the current bucket
 1679+ * Utility method: Gets the name of the current experiment
30091680 *
3010 - * @return string the bucket name
 1681+ * @return string the experiment (e.g. "option1A")
30111682 */
3012 - $.articleFeedbackv5.bucketName = function () {
 1683+ $.articleFeedbackv5.experiment = function () {
30131684 return 'option' + $.articleFeedbackv5.bucketId + $.articleFeedbackv5.floatingLinkId;
30141685 };
30151686
@@ -3290,6 +1961,7 @@
32911962 'pageid': $.articleFeedbackv5.pageId,
32921963 'revid': $.articleFeedbackv5.revisionId,
32931964 'bucket': $.articleFeedbackv5.bucketId,
 1965+ 'experiment': $.articleFeedbackv5.experiment().replace( 'option', '' ),
32941966 'link': $.articleFeedbackv5.submittedLinkId
32951967 } );
32961968
@@ -3328,11 +2000,18 @@
33292001 } else {
33302002 msg = mw.msg( data.error );
33312003 }
 2004+ } else if ( 'warning' in data ) {
 2005+ // NB: Warnings come from the AbuseFilter and are
 2006+ // already translated.
 2007+ msg = data.warning;
33322008 } else {
33332009 msg = { info: mw.msg( 'articlefeedbackv5-error-unknown' ) };
33342010 }
33352011 $.articleFeedbackv5.markFormErrors( { _api : msg } );
33362012 $.articleFeedbackv5.unlockForm();
 2013+ if ( $.articleFeedbackv5.inDialog ) {
 2014+ $.articleFeedbackv5.setDialogDimensions();
 2015+ }
33372016 }
33382017 },
33392018 'error': function () {
@@ -3443,7 +2122,7 @@
34442123 $.articleFeedbackv5.setDialogDimensions();
34452124
34462125 // Track the event
3447 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-' +
 2126+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-' +
34482127 $.articleFeedbackv5.ctaName() + '-impression-' + from );
34492128
34502129 $.articleFeedbackv5.nowShowing = 'cta';
@@ -3567,7 +2246,7 @@
35682247 var prefLink = mw.config.get( 'wgScript' ) + '?' +
35692248 $.param( { title: 'Special:Preferences' } ) +
35702249 '#mw-prefsection-rendering';
3571 - var prefTrackId = $.articleFeedbackv5.bucketName() + '-disable_gotoprefs_click';
 2250+ var prefTrackId = $.articleFeedbackv5.experiment() + '-disable_gotoprefs_click';
35722251 $flyover.find( '.articleFeedbackv5-disable-flyover-button' )
35732252 .attr( 'href', $.articleFeedbackv5.trackingUrl( prefLink, prefTrackId ) )
35742253 .button()
@@ -3577,7 +2256,7 @@
35782257 .attr( 'href', '#hello' )
35792258 .attr( 'rel', linkId );
35802259
3581 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-disable_flyover-impression' );
 2260+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-disable_flyover-impression' );
35822261 return $flyover.html();
35832262 }
35842263 } )
@@ -3591,7 +2270,7 @@
35922271 } else {
35932272 $host.tipsy( 'show' );
35942273 $wrap.addClass( 'articleFeedbackv5-tipsy-active' );
3595 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-disable_button_click' );
 2274+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-disable_button_click' );
35962275 }
35972276 } );
35982277 };
@@ -3802,7 +2481,7 @@
38032482 $.articleFeedbackv5.setLinkId( $link.data( 'linkId' ) );
38042483
38052484 // Track the impression
3806 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-impression-overlay' );
 2485+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-impression-overlay' );
38072486
38082487 // Hide the panel
38092488 $.articleFeedbackv5.$holder.hide();
@@ -3820,9 +2499,9 @@
38212500 $.articleFeedbackv5.closeAsModal = function () {
38222501 if ( $.articleFeedbackv5.inDialog ) {
38232502 if ( 'form' == $.articleFeedbackv5.nowShowing ) {
3824 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-close-overlay' );
 2503+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-close-overlay' );
38252504 } else if ('cta' == $.articleFeedbackv5.nowShowing ) {
3826 - $.articleFeedbackv5.trackClick( $.articleFeedbackv5.bucketName() + '-' +
 2505+ $.articleFeedbackv5.trackClick( $.articleFeedbackv5.experiment() + '-' +
38272506 $.articleFeedbackv5.ctaName() + '-close-overlay' );
38282507 }
38292508 $.articleFeedbackv5.setLinkId( 'X' );
@@ -3931,7 +2610,7 @@
39322611 * @param $link Element the trigger link
39332612 */
39342613 $.articleFeedbackv5.clickTriggerLink = function( $link ) {
3935 - var tracking_id = $.articleFeedbackv5.bucketName() +
 2614+ var tracking_id = $.articleFeedbackv5.experiment() +
39362615 '-trigger' + $link.data( 'linkId' ) +
39372616 '-click-overlay';
39382617 $.articleFeedbackv5.trackClick( tracking_id );
@@ -3960,7 +2639,7 @@
39612640 inDebug: { args: 0, ret: true },
39622641 nowShowing: { args: 0, ret: true },
39632642 prefix: { args: 1, ret: true },
3964 - bucketName: { args: 0, ret: true },
 2643+ experiment: { args: 0, ret: true },
39652644 addToRemovalQueue: { args: 1, ret: false },
39662645 openAsModal: { args: 1, ret: false },
39672646 closeAsModal: { args: 0, ret: true },
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/ArticleFeedbackv5.php
@@ -273,6 +273,7 @@
274274 $wgAutoloadClasses['ApiFlagFeedbackArticleFeedbackv5'] = $dir . 'api/ApiFlagFeedbackArticleFeedbackv5.php';
275275 $wgAutoloadClasses['ApiViewActivityArticleFeedbackv5'] = $dir . 'api/ApiViewActivityArticleFeedbackv5.php';
276276 $wgAutoloadClasses['ArticleFeedbackv5Hooks'] = $dir . 'ArticleFeedbackv5.hooks.php';
 277+$wgAutoloadClasses['ArticleFeedbackv5Flagging'] = $dir . 'ArticleFeedbackv5.flagging.php';
277278 $wgAutoloadClasses['ArticleFeedbackv5MailerJob'] = $dir . 'ArticleFeedbackv5MailerJob.php';
278279 $wgAutoloadClasses['SpecialArticleFeedbackv5'] = $dir . 'SpecialArticleFeedbackv5.php';
279280 $wgExtensionMessagesFiles['ArticleFeedbackv5'] = $dir . 'ArticleFeedbackv5.i18n.php';
@@ -321,3 +322,10 @@
322323 $wgLogActions['suppress/unrequest'] = 'articlefeedbackv5-log-unrequest';
323324 $wgLogActions['articlefeedbackv5/flag'] = 'articlefeedbackv5-log-flag';
324325 $wgLogActions['articlefeedbackv5/unflag'] = 'articlefeedbackv5-log-unflag';
 326+
 327+// Add custom action handlers for AbuseFilter
 328+// Not for this release
 329+// $wgAbuseFilterAvailableActions[] = 'aftv5flagabuse';
 330+// $wgAbuseFilterAvailableActions[] = 'aftv5hide';
 331+// $wgAbuseFilterAvailableActions[] = 'aftv5requestoversight';
 332+
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiFlagFeedbackArticleFeedbackv5.php
@@ -30,394 +30,22 @@
3131 */
3232 public function execute() {
3333
34 - // default values for information to be filled in
35 - $filters = array();
36 - $update = array();
37 - $results = array();
38 -
3934 // get important values from our parameters
4035 $params = $this->extractRequestParams();
4136 $pageId = $params['pageid'];
4237 $feedbackId = $params['feedbackid'];
4338 $flag = $params['flagtype'];
4439 $notes = $params['note'];
45 - $toggle = $params['toggle'];
4640 $direction = isset( $params['direction'] ) ? $params['direction'] : 'increase';
47 - $where = array( 'af_id' => $feedbackId );
 41+ $toggle = $params['toggle'];
4842
4943 // woah, we were not checking for permissions (that could have been script kiddy bad)
5044 global $wgUser;
5145
52 - // we may not actually use this, but don't want to repeat this a million times
53 - $default_user = wfMessage( 'articlefeedbackv5-default-user' )->text();
 46+ // Fire up the flagging object
 47+ $flagger = new ArticleFeedbackv5Flagging( $wgUser, $pageId, $feedbackId );
 48+ $results = $flagger->run( $flag, $notes, $direction, $toggle );
5449
55 - // we use ONE db connection that talks to master
56 - $dbw = wfGetDB( DB_MASTER );
57 - $dbw->begin();
58 - $timestamp = $dbw->timestamp();
59 -
60 - // load feedback record, bail if we don't have one
61 - $record = $this->fetchRecord( $dbw, $feedbackId );
62 -
63 - if ( $record === false || !$record->af_id ) {
64 - // no-op, because this is already broken
65 - $error = 'articlefeedbackv5-invalid-feedback-id';
66 -
67 - } elseif ( 'delete' == $flag && $wgUser->isAllowed( 'aftv5-delete-feedback' ) ) {
68 -
69 - // deleting means to "mark as oversighted" and "delete" it
70 - // oversighting also auto-hides the item
71 -
72 - // increase means "oversight this"
73 - if ( $direction == 'increase' ) {
74 - $activity = 'oversight';
75 -
76 - // delete
77 - $update['af_is_deleted'] = true;
78 - $update['af_is_undeleted'] = false;
79 - // only store the oversighter on delete/oversight
80 - $update['af_oversight_user_id'] = $wgUser->getId();
81 - $update['af_oversight_timestamp'] = $timestamp;
82 - // delete specific filters
83 - $filters['deleted'] = 1;
84 - $filters['notdeleted'] = -1;
85 - if ( true == $record->af_is_undeleted ) {
86 - $filters['undeleted'] = -1;
87 - }
88 -
89 - // This is data for the "hidden by, oversighted by" red line
90 - $results['oversight-user'] = ApiArticleFeedbackv5Utils::getUserLink( $wgUser );
91 - $results['oversight-timestamp'] = wfTimestamp( TS_RFC2822, $timestamp );
92 -
93 - // autohide if not hidden
94 - if ( false == $record->af_is_hidden ) {
95 - $update['af_is_hidden'] = true;
96 - $update['af_is_unhidden'] = false;
97 - $filters = $this->changeFilterCounts( $record, $filters, 'hide' );
98 - // 0 is used for "autohidden" purposes, we'll explicitly set it to overwrite last hider
99 - $update['af_hide_user_id'] = 0;
100 - $update['af_hide_timestamp'] = $timestamp;
101 - $implicit_hide = true; // for logging
102 - // tell front-end autohiding was done
103 - $results['autohidden'] = 1;
104 - // This is data for the "hidden by, oversighted by" red line
105 - $results['hide-user'] = ApiArticleFeedbackv5Utils::getUserLink( null, $default_user );
106 - $results['hide-timestamp'] = wfTimestamp( TS_RFC2822, $timestamp );
107 - }
108 -
109 - } else {
110 - // decrease means "unoversight this" but does NOT auto-unhide
111 - $activity = 'unoversight';
112 - $update['af_is_deleted'] = false;
113 - $update['af_is_undeleted'] = true;
114 - // increment "undeleted", decrement "deleted"
115 - // NOTE: we do not touch visible, since hidden controls visiblity
116 - $filters['deleted'] = -1;
117 - $filters['undeleted'] = 1;
118 - // increment "notdeleted" for count of everything but oversighted
119 - $filters['notdeleted'] = 1;
120 - }
121 -
122 - } elseif ( 'hide' == $flag && $wgUser->isAllowed( 'aftv5-hide-feedback' ) ) {
123 -
124 - // increase means "hide this"
125 - if ( $direction == 'increase' ) {
126 - $activity = 'hidden';
127 -
128 - // hide
129 - $update['af_is_hidden'] = true;
130 - $update['af_is_unhidden'] = false;
131 - // only store the hider on hide not show
132 - $update['af_hide_user_id'] = $wgUser->getId();
133 - $update['af_hide_timestamp'] = $timestamp;
134 - $filters = $this->changeFilterCounts( $record, $filters, 'hide' );
135 -
136 - // This is data for the "hidden by, oversighted by" red line
137 - $results['hide-user'] = ApiArticleFeedbackv5Utils::getUserLink( $wgUser );
138 - $results['hide-timestamp'] = wfTimestamp( TS_RFC2822, $timestamp );
139 -
140 - } else {
141 - // decrease means "unhide this"
142 - $activity = 'unhidden';
143 -
144 - $update['af_is_hidden'] = false;
145 - $update['af_is_unhidden'] = true;
146 -
147 - $filters = $this->changeFilterCounts( $record, $filters, 'show' );
148 - }
149 -
150 - } elseif ( 'resetoversight' === $flag && $wgUser->isAllowed( 'aftv5-delete-feedback' ) ) {
151 -
152 - $activity = 'decline';
153 - // oversight request count becomes 0
154 - $update['af_oversight_count'] = 0;
155 - // declined oversight is flagged
156 - $update['af_is_declined'] = true;
157 - $filters['declined'] = 1;
158 - // if the oversight count was greater then 1
159 - if ( 0 < $record->af_oversight_count ) {
160 - $filters['needsoversight'] = -1;
161 - }
162 -
163 - } elseif ( 'abuse' === $flag ) {
164 -
165 - // Conditional formatting for abuse flag
166 - global $wgArticleFeedbackv5AbusiveThreshold,
167 - $wgArticleFeedbackv5HideAbuseThreshold;
168 -
169 - $results['abuse_count'] = $record->af_abuse_count;
170 -
171 - // Make the abuse count in the result reflect this vote.
172 - if ( $direction == 'increase' ) {
173 - $results['abuse_count']++;
174 - } else {
175 - $results['abuse_count']--;
176 - }
177 - // no negative numbers
178 - $results['abuse_count'] = max( 0, $results['abuse_count'] );
179 -
180 - // Return a flag in the JSON, that turns the link red.
181 - if ( $results['abuse_count'] >= $wgArticleFeedbackv5AbusiveThreshold ) {
182 - $results['abusive'] = 1;
183 - }
184 -
185 - // Adding a new abuse flag: abusive++
186 - if ( $direction == 'increase' ) {
187 - $activity = 'flag';
188 - $filters['abusive'] = 1;
189 - // NOTE: we are bypassing traditional sql escaping here
190 - $update[] = "af_abuse_count = af_abuse_count + 1";
191 -
192 - // Auto-hide after threshold flags
193 - if ( $record->af_abuse_count > $wgArticleFeedbackv5HideAbuseThreshold
194 - && false == $record->af_is_hidden ) {
195 - // hide
196 - $update['af_is_hidden'] = true;
197 - $update['af_is_unhidden'] = false;
198 - // 0 is used for "autohidden" purposes, we'll explicitly set it to overwrite last hider
199 - $update['af_hide_user_id'] = 0;
200 - $update['af_hide_timestamp'] = $timestamp;
201 -
202 - $filters = $this->changeFilterCounts( $record, $filters, 'hide' );
203 - $results['abuse-hidden'] = 1;
204 - $implicit_hide = true;
205 -
206 - // tell front-end autohiding was done
207 - $results['autohidden'] = 1;
208 - // This is data for the "hidden by, oversighted by" red line
209 - $results['hide-user'] = ApiArticleFeedbackv5Utils::getUserLink( null, $default_user );
210 - $results['hide-timestamp'] = wfTimestamp( TS_RFC2822, $timestamp );
211 - }
212 - }
213 -
214 - // Removing the last abuse flag: abusive--
215 - elseif ( $direction == 'decrease' ) {
216 - $activity = 'unflag';
217 - $filters['abusive'] = -1;
218 - // NOTE: we are bypassing traditional sql escaping here
219 - $update[] = "af_abuse_count = GREATEST(CONVERT(af_abuse_count, SIGNED) -1, 0)";
220 -
221 - // Un-hide if we don't have 5 flags anymore
222 - if ( $record->af_abuse_count == 5 && true == $record->af_is_hidden ) {
223 - $update['af_is_hidden'] = false;
224 - $update['af_is_unhidden'] = true;
225 -
226 - $filters = $this->changeFilterCounts( $record, $filters, 'show' );
227 -
228 - $implicit_unhide = true;
229 - }
230 - } else {
231 - // TODO: real error here?
232 - $error = 'articlefeedbackv5-invalid-feedback-flag';
233 - }
234 -
235 - // NOTE: this is actually request/unrequest oversight and works similar to abuse
236 - } elseif ( 'oversight' === $flag && $wgUser->isAllowed( 'aftv5-hide-feedback' ) ) {
237 -
238 - if ( $direction == 'increase' ) {
239 - $activity = 'request';
240 - $filters['needsoversight'] = 1;
241 - // NOTE: we are bypassing traditional sql escaping here
242 - $update[] = "af_oversight_count = af_oversight_count + 1";
243 -
244 - // autohide if not hidden
245 - if ( false == $record->af_is_hidden ) {
246 - $update['af_is_hidden'] = true;
247 - $update['af_is_unhidden'] = false;
248 - // 0 is used for "autohidden" purposes, we'll explicitly set it to overwrite last hider
249 - $update['af_hide_user_id'] = 0;
250 - $update['af_hide_timestamp'] = $timestamp;
251 -
252 - $filters = $this->changeFilterCounts( $record, $filters, 'hide' );
253 - $implicit_hide = true; // for logging
254 - // tell front-end autohiding was done
255 - $results['autohidden'] = 1;
256 - // This is data for the "hidden by, oversighted by" red line
257 - $results['hide-user'] = ApiArticleFeedbackv5Utils::getUserLink( null, $default_user );
258 - $results['hide-timestamp'] = wfTimestamp( TS_RFC2822, $timestamp );
259 - }
260 -
261 - // IF the previous setting was 0, send an email
262 - if ( $record->af_oversight_count < 1 ) {
263 -
264 - $this->sendOversightEmail( $record->af_page_id , $feedbackId );
265 -
266 - }
267 - } elseif ( $direction == 'decrease' ) {
268 - $activity = 'unrequest';
269 - $filters['needsoversight'] = -1;
270 - // NOTE: we are bypassing traditional sql escaping here
271 - $update[] = "af_oversight_count = GREATEST(CONVERT(af_oversight_count, SIGNED) - 1, 0)";
272 - } else {
273 - // TODO: real error here?
274 - $error = 'articlefeedbackv5-invalid-feedback-flag';
275 - }
276 -
277 - // helpful and unhelpful flagging
278 - } elseif ( 'unhelpful' === $flag || 'helpful' === $flag ) {
279 -
280 - $results['toggle'] = $toggle;
281 - $helpful = $record->af_helpful_count;
282 - $unhelpful = $record->af_unhelpful_count;
283 -
284 - // if toggle is on, we are decreasing one and increasing the other atomically
285 - // means one less http request and the counts don't mess up
286 - if ( true == $toggle ) {
287 -
288 - if ( ( ( $flag == 'helpful' && $direction == 'increase' )
289 - || ( $flag == 'unhelpful' && $direction == 'decrease' ) )
290 - ) {
291 - // NOTE: we are bypassing traditional sql escaping here
292 - $update[] = "af_helpful_count = af_helpful_count + 1";
293 - $update[] = "af_unhelpful_count = GREATEST(0, CONVERT(af_unhelpful_count, SIGNED) - 1)";
294 - $helpful++;
295 - $unhelpful--;
296 -
297 - } elseif ( ( ( $flag == 'unhelpful' && $direction == 'increase' )
298 - || ( $flag == 'helpful' && $direction == 'decrease' ) )
299 - ) {
300 - // NOTE: we are bypassing traditional sql escaping here
301 - $update[] = "af_unhelpful_count = af_unhelpful_count + 1";
302 - $update[] = "af_helpful_count = GREATEST(0, CONVERT(af_helpful_count, SIGNED) - 1)";
303 - $helpful--;
304 - $unhelpful++;
305 - }
306 -
307 - } else {
308 -
309 - if ( 'unhelpful' === $flag && $direction == 'increase' ) {
310 - // NOTE: we are bypassing traditional sql escaping here
311 - $update[] = "af_unhelpful_count = af_unhelpful_count + 1";
312 - $unhelpful++;
313 - } elseif ( 'unhelpful' === $flag && $direction == 'decrease' ) {
314 - // NOTE: we are bypassing traditional sql escaping here
315 - $update[] = "af_unhelpful_count = GREATEST(0, CONVERT(af_unhelpful_count, SIGNED) - 1)";
316 - $unhelpful--;
317 - } elseif ( $flag == 'helpful' && $direction == 'increase' ) {
318 - // NOTE: we are bypassing traditional sql escaping here
319 - $update[] = "af_helpful_count = af_helpful_count + 1";
320 - $helpful++;
321 - } elseif ( $flag == 'helpful' && $direction == 'decrease' ) {
322 - // NOTE: we are bypassing traditional sql escaping here
323 - $update[] = "af_helpful_count = GREATEST(0, CONVERT(af_helpful_count, SIGNED) - 1)";
324 - $helpful--;
325 - }
326 -
327 - }
328 -
329 - $netHelpfulness = $helpful - $unhelpful;
330 -
331 - // increase helpful OR decrease unhelpful
332 - if ( ( ( $flag == 'helpful' && $direction == 'increase' )
333 - || ( $flag == 'unhelpful' && $direction == 'decrease' ) )
334 - ) {
335 - // net was -1: no longer unhelpful
336 - if ( $netHelpfulness == -1 ) {
337 - $filters['unhelpful'] = -1;
338 - }
339 -
340 - // net was 0: now helpful
341 - if ( $netHelpfulness == 0 ) {
342 - $filters['helpful'] = 1;
343 - }
344 - }
345 -
346 - // increase unhelpful OR decrease unhelpful
347 - if ( ( ( $flag == 'unhelpful' && $direction == 'increase' )
348 - || ( $flag == 'helpful' && $direction == 'decrease' ) )
349 - ) {
350 - // net was 1: no longer helpful
351 - if ( $netHelpfulness == 1 ) {
352 - $filters['helpful'] = -1;
353 - }
354 -
355 - // net was 0: now unhelpful
356 - if ( $netHelpfulness == 0 ) {
357 - $filters['unhelpful'] = 1;
358 - }
359 - }
360 -
361 - } else {
362 - $error = 'articlefeedbackv5-invalid-feedback-flag';
363 - }
364 -
365 - // we were valid
366 - if ( !isset( $error ) ) {
367 -
368 - $success = $dbw->update(
369 - 'aft_article_feedback',
370 - $update,
371 - $where,
372 - __METHOD__
373 - );
374 -
375 - // Update the filter count rollups.
376 - ApiArticleFeedbackv5Utils::updateFilterCounts( $dbw, $pageId, $filters );
377 -
378 - $dbw->commit(); // everything went well, so we commit our db changes
379 -
380 - // helpfulness counts are NOT logged, no activity is set
381 - if ( isset( $activity ) ) {
382 - ApiArticleFeedbackv5Utils::logActivity( $activity , $pageId, $feedbackId, $notes );
383 - }
384 -
385 - // handle implicit hide/show logging
386 - if ( isset( $implicit_hide ) && $implicit_hide ) {
387 - ApiArticleFeedbackv5Utils::logActivity( 'hidden' , $pageId, $feedbackId, '', true );
388 - }
389 -
390 - // Update helpful/unhelpful display count after submission.
391 - if ( $flag == 'helpful' || $flag == 'unhelpful' ) {
392 -
393 - // no negative numbers please
394 - $helpful = max( 0, $helpful );
395 - $unhelpful = max( 0, $unhelpful );
396 -
397 - $results['helpful'] = wfMessage(
398 - 'articlefeedbackv5-form-helpful-votes',
399 - $helpful, $unhelpful
400 - )->escaped();
401 -
402 - // Update net_helpfulness after flagging as helpful/unhelpful.
403 - $dbw->update(
404 - 'aft_article_feedback',
405 - array( 'af_net_helpfulness = CONVERT(af_helpful_count, SIGNED) - CONVERT(af_unhelpful_count, SIGNED)' ),
406 - array(
407 - 'af_id' => $params['feedbackid'],
408 - ),
409 - __METHOD__
410 - );
411 - }
412 - }
413 -
414 - if ( $error ) {
415 - $results['result'] = 'Error';
416 - $results['reason'] = $error;
417 - } else {
418 - $results['result'] = 'Success';
419 - $results['reason'] = null;
420 - }
421 -
42250 $this->getResult()->addValue(
42351 null,
42452 $this->getModuleName(),
@@ -426,87 +54,6 @@
42755 }
42856
42957 /**
430 - * Helper function to grab a record from the database with information
431 - * about the current feedback row
432 - *
433 - * @param object $dbw connection to database
434 - * @param int $id id of the feedback to fetch
435 - * @return object database record
436 - */
437 - private function fetchRecord( $dbw, $id ) {
438 - $record = $dbw->selectRow(
439 - 'aft_article_feedback',
440 - array(
441 - 'af_id',
442 - 'af_page_id',
443 - 'af_abuse_count',
444 - 'af_is_hidden',
445 - 'af_helpful_count',
446 - 'af_unhelpful_count',
447 - 'af_is_deleted',
448 - 'af_net_helpfulness',
449 - 'af_is_unhidden',
450 - 'af_is_undeleted',
451 - 'af_is_declined',
452 - 'af_has_comment',
453 - 'af_oversight_count' ),
454 - array( 'af_id' => $id )
455 - );
456 - return $record;
457 - }
458 -
459 - /**
460 - * Helper function to manipulate all flags when hiding/showing a piece of feedback
461 - *
462 - * @param object $record existing feedback database record
463 - * @param array $filters existing filters
464 - * @param string $action 'hide' or 'show'
465 - * @return array the filter array with new filter choices added
466 - */
467 - protected function changeFilterCounts( $record, $filters, $action ) {
468 - // only filters that hide shouldn't manipulate are
469 - // all, deleted, undeleted, and notdeleted
470 -
471 - // use -1 (decrement) for hide, 1 for increment (show) - default is hide
472 - switch( $action ) {
473 - case 'show':
474 - $int = 1;
475 - // if we're showing, this will increment
476 - $filters['unhidden'] = 1;
477 - break;
478 - default:
479 - // if we're hiding, and was unhidden, decrement
480 - if ( true == $record->af_is_unhidden ) {
481 - $filters['unhidden'] = -1;
482 - }
483 - $int = -1;
484 - break;
485 - }
486 -
487 - // visible, invisible, unhidden
488 - $filters['visible'] = $int;
489 - $filters['invisible'] = -$int; // opposite of int
490 -
491 - // comment
492 - if ( true == $record->af_has_comment ) {
493 - $filters['comment'] = $int;
494 - }
495 -
496 - // abusive
497 - if ( $record->af_abuse_count > 1 ) {
498 - $filters['abusive'] = $int;
499 - }
500 - // helpful and unhelpful
501 - if ( $record->af_net_helpfulness > 1 ) {
502 - $filters['helpful'] = $int;
503 - } elseif ( $record->af_net_helpfulness < 1 ) {
504 - $filters['unhelpful'] = $int;
505 - }
506 -
507 - return $filters;
508 - }
509 -
510 - /**
51158 * Gets the allowed parameters
51259 *
51360 * @return array the params info, indexed by allowed key
@@ -544,7 +91,7 @@
54592 ApiBase::PARAM_REQUIRED => false,
54693 ApiBase::PARAM_ISMULTI => false,
54794 ApiBase::PARAM_TYPE => 'boolean'
548 - )
 95+ ),
54996 );
55097 }
55198
@@ -558,7 +105,7 @@
559106 'feedbackid' => 'FeedbackID to flag',
560107 'type' => 'Type of flag to apply - hide or abuse',
561108 'note' => 'Information on why the feedback activity occurred',
562 - 'toggle' => 'The flag is being toggled atomically, only useful for (un)helpful'
 109+ 'toggle' => 'The flag is being toggled atomically, only useful for (un)helpful',
563110 );
564111 }
565112
@@ -607,52 +154,7 @@
608155 }
609156
610157 public function isWriteMode() { return true; }
 158+
611159 public function mustBePosted() { return true; }
612160
613 - /**
614 - * Helper function to dig out page url and title, feedback permalink, and
615 - * requestor page url and name - if all this data can be retrieved properly
616 - * it shoves an email job into the queue for sending to the oversighters'
617 - * mailing list - only called for NEW oversight requests
618 - *
619 - * @param int $page_id page id to grab info on
620 - * @param int $feedback_id identifier for the feedback item
621 - */
622 - protected function sendOversightEmail( $page_id, $feedback_id ) {
623 - global $wgUser;
624 -
625 - // jobs need a title object
626 - $title_object = Title::newFromID( $page_id );
627 -
628 - if ( !$title_object ) {
629 - return; // no title object, no mail
630 - }
631 -
632 - // get the string name of the page
633 - $page_name = $title_object->getDBKey();
634 -
635 - // make a title out of our user (sigh)
636 - $user_page = Title::makeTitle( NS_USER, $wgUser->getName() );
637 -
638 - if ( !$user_page ) {
639 - return; // no user title object, no mail
640 - }
641 -
642 - // to build our permalink, use the feedback entry key + the page name (isn't page name a title? but title is an object? confusing)
643 - $permalink = SpecialPage::getTitleFor( 'ArticleFeedbackv5', "$page_name/$feedback_id" );
644 -
645 - if ( !$permalink ) {
646 - return; // no proper permalink? no mail
647 - }
648 -
649 - // build our params
650 - $params = array( 'user_name' => $wgUser->getName(),
651 - 'user_url' => $user_page->getCanonicalUrl(),
652 - 'page_name' => $title_object->getText(),
653 - 'page_url' => $title_object->getCanonicalUrl(),
654 - 'permalink' => $permalink->getCanonicalUrl() );
655 -
656 - $job = new ArticleFeedbackv5MailerJob( $title_object, $params );
657 - $job->insert();
658 - }
659161 }
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiArticleFeedbackv5.php
@@ -19,6 +19,12 @@
2020 // Cache this, so we don't have to look it up every time.
2121 private $revision_limit = null;
2222
 23+ // Allow auto-flagging of feedback
 24+ private $autoFlag = array();
 25+
 26+ // Warn for abuse?
 27+ private $warnForAbuse = false;
 28+
2329 /**
2430 * Constructor
2531 */
@@ -41,7 +47,6 @@
4248 return;
4349 }
4450
45 -
4651 // Anon token check
4752 $token = $this->getAnonToken( $params );
4853
@@ -53,8 +58,10 @@
5459 $dbw = wfGetDB( DB_MASTER );
5560 $pageId = $params['pageid'];
5661 $bucket = $params['bucket'];
 62+ $experiment = $params['experiment'];
5763 $revisionId = $params['revid'];
5864 $error = null;
 65+ $warning = null;
5966 $userAnswers = array();
6067 $fields = ApiArticleFeedbackv5Utils::getFields();
6168 $emailData = array(
@@ -81,7 +88,11 @@
8289 }
8390 if ( $wgArticleFeedbackv5AbuseFiltering && 'text' == $type
8491 && $this->findAbuse( $value, $pageId ) ) {
85 - $error = 'articlefeedbackv5-error-abuse';
 92+ if ( $this->warnForAbuse ) {
 93+ $warning = $this->warnForAbuse;
 94+ } else {
 95+ $error = 'articlefeedbackv5-error-abuse';
 96+ }
8697 break;
8798 }
8899 $data = array( 'aa_field_id' => $field['afi_id'] );
@@ -96,6 +107,10 @@
97108 $this->getResult()->addValue( null, 'error', $error );
98109 return;
99110 }
 111+ if ( $warning ) {
 112+ $this->getResult()->addValue( null, 'warning', $warning );
 113+ return;
 114+ }
100115
101116 // all write actions should be done under the same transaction
102117 // or counts will be messed up
@@ -138,6 +153,26 @@
139154
140155 wfRunHooks( 'ArticleFeedbackChangeRating', array( $params ) );
141156
 157+ // Are we set to auto-flag?
 158+ $flagger = new ArticleFeedbackv5Flagging( 0, $pageId, $feedbackId );
 159+ foreach ( $this->autoFlag as $flag => $rule_desc ) {
 160+ $msg = 'articlefeedbackv5-abusefilter-note-aftv5';
 161+ if ( $flag == 'abuse' ) {
 162+ $msg .= 'flagabuse';
 163+ } elseif ( $flag == 'hide' ) {
 164+ $msg .= 'hide';
 165+ } elseif ( $flag == 'oversight' ) {
 166+ $msg .= 'requestoversight';
 167+ } else {
 168+ continue;
 169+ }
 170+ $notes = wfMsgExt( $msg, 'parseinline', array( $rule_desc ) );
 171+ $res = $flagger->run( $flag, $notes );
 172+ if ( 'Error' == $res['result'] ) {
 173+ // TODO: Log somewhere?
 174+ }
 175+ }
 176+
142177 $this->getResult()->addValue(
143178 null,
144179 $this->getModuleName(),
@@ -247,9 +282,6 @@
248283 }
249284 }
250285
251 - // Create a fake title so we can pretend this is an article edit
252 - $title = Title::newFromText( '__article_feedback_5__' );
253 -
254286 // Check SpamBlacklist, if installed
255287 if ( function_exists( 'wfSpamBlacklistObject' ) ) {
256288 $spam = wfSpamBlacklistObject();
@@ -257,6 +289,7 @@
258290 $spam = BaseBlacklist::getInstance( 'spam' );
259291 }
260292 if ( $spam ) {
 293+ $title = Title::newFromText( 'ArticleFeedbackv5_' . $pageId );
261294 $ret = $spam->filter( $title, $value, '' );
262295 if ( $ret !== false ) {
263296 return true;
@@ -266,21 +299,103 @@
267300 // Check AbuseFilter, if installed
268301 if ( class_exists( 'AbuseFilter' ) ) {
269302 global $wgUser;
 303+
 304+ // Set up variables
 305+ $title = Title::newFromID( $pageId );
270306 $vars = new AbuseFilterVariableHolder;
271307 $vars->addHolder( AbuseFilter::generateUserVars( $wgUser ) );
272 - $vars->addHolder( AbuseFilter::generateTitleVars( $title, 'FEEDBACK' ) );
 308+ $vars->addHolder( AbuseFilter::generateTitleVars( $title , 'ARTICLE' ) );
273309 $vars->setVar( 'SUMMARY', 'Article Feedback 5' );
274310 $vars->setVar( 'ACTION', 'feedback' );
275 - $vars->setVar( 'old_wikitext', '' );
276311 $vars->setVar( 'new_wikitext', $value );
277 - $vars->addHolder( AbuseFilter::getEditVars( $title ) );
278 - $filter_result = AbuseFilter::filterAction( $vars, $title );
279 - return $filter_result != '' && $filter_result !== true;
 312+ $vars->setLazyLoadVar( 'new_size', 'length', array( 'length-var' => 'new_wikitext' ) );
 313+
 314+ // Add custom action handlers
 315+ global $wgAbuseFilterCustomActionsHandlers;
 316+ $flagCallback = array( $this, 'callbackAbuseActionFlag' );
 317+ // Not for this release
 318+ // $wgAbuseFilterCustomActionsHandlers['aftv5flagabuse'] = $flagCallback;
 319+ // $wgAbuseFilterCustomActionsHandlers['aftv5hide'] = $flagCallback;
 320+ // $wgAbuseFilterCustomActionsHandlers['aftv5requestoversight'] = $flagCallback;
 321+
 322+ // Check the filters (mimics AbuseFilter::filterAction)
 323+ $vars->setVar( 'context', 'filter' );
 324+ $vars->setVar( 'timestamp', time() );
 325+ $results = AbuseFilter::checkAllFilters( $vars );
 326+ if ( count( array_filter( $results ) ) == 0 ) {
 327+ return false;
 328+ }
 329+
 330+ // Abuse filter consequences
 331+ $matched = array_keys( array_filter( $results ) );
 332+ list( $actions_taken, $error_msg ) = AbuseFilter::executeFilterActions(
 333+ $matched, $title, $vars );
 334+
 335+ // Send to the abuse filter log
 336+ $dbr = wfGetDB( DB_SLAVE );
 337+ $log_template = array(
 338+ 'afl_user' => $wgUser->getId(),
 339+ 'afl_user_text' => $wgUser->getName(),
 340+ 'afl_timestamp' => $dbr->timestamp( wfTimestampNow() ),
 341+ 'afl_namespace' => $title->getNamespace(),
 342+ 'afl_title' => $title->getDBkey(),
 343+ 'afl_ip' => wfGetIP()
 344+ );
 345+ $action = $vars->getVar( 'ACTION' )->toString();
 346+ AbuseFilter::addLogEntries( $actions_taken, $log_template, $action, $vars );
 347+
 348+ // Local consequences (right now: disallow only)
 349+ $disallow = false;
 350+ $warn = false;
 351+ foreach ( $actions_taken as $id => $actions ) {
 352+ foreach ( $actions as $action ) {
 353+ if ( 'disallow' == $action) {
 354+ $disallow = true;
 355+ }
 356+ if ( 'warn' == $action ) {
 357+ $warn = true;
 358+ }
 359+ }
 360+ }
 361+ if ( $warn ) {
 362+ $this->warnForAbuse = $error_msg;
 363+ return true;
 364+ }
 365+ if ( $disallow ) {
 366+ return true;
 367+ }
280368 }
281369
282370 return false;
283371 }
284372
 373+ /**
 374+ * AbuseFilter callback: flag feedback (abuse, oversight, hide, etc.)
 375+ *
 376+ * @param string $action the action name (AF)
 377+ * @param array $parameters the action parameters (AF)
 378+ * @param Title $title the title passed in
 379+ * @param AbuseFilterVariableHolder $vars the variables passed in
 380+ * @param string $rule_desc the rule description
 381+ */
 382+ public function callbackAbuseActionFlag( $action, $parameters,
 383+ $title, $vars, $rule_desc ) {
 384+ switch ( $action ) {
 385+ case 'aftv5flagabuse':
 386+ $this->autoFlag['abuse'] = $rule_desc;
 387+ break;
 388+ case 'aftv5hide':
 389+ $this->autoFlag['hide'] = $rule_desc;
 390+ break;
 391+ case 'aftv5requestoversight':
 392+ $this->autoFlag['oversight'] = $rule_desc;
 393+ break;
 394+ default:
 395+ // Fall through silently
 396+ break;
 397+ }
 398+ }
 399+
285400 public function updateFilterCounts( $dbw, $pageId, $answers ) {
286401
287402 // a new item should be in all and visible by default, increment those counters
@@ -566,13 +681,14 @@
567682 private function saveUserRatings( $dbw, $data, $bucket, $params ) {
568683 global $wgUser, $wgArticleFeedbackv5LinkBuckets;
569684
570 - $ctaId = $this->getCTAId( $data, $bucket );
571 - $revId = $params['revid'];
572 - $bucket = $params['bucket'];
573 - $linkName = $params['link'];
574 - $token = $this->getAnonToken( $params );
575 - $timestamp = $dbw->timestamp();
576 - $ip = null;
 685+ $ctaId = $this->getCTAId( $data, $bucket );
 686+ $revId = $params['revid'];
 687+ $bucket = $params['bucket'];
 688+ $experiment = $params['experiment'];
 689+ $linkName = $params['link'];
 690+ $token = $this->getAnonToken( $params );
 691+ $timestamp = $dbw->timestamp();
 692+ $ip = null;
577693
578694 if ( !$wgUser ) {
579695 $this->dieUsage( 'User info is missing', 'missinguser' );
@@ -618,7 +734,8 @@
619735 'af_user_id' => $wgUser->getId(),
620736 'af_user_ip' => $ip,
621737 'af_user_anon_token' => $token,
622 - 'af_bucket_id' => $bucket,
 738+ 'af_form_id' => $bucket,
 739+ 'af_experiment' => $experiment,
623740 'af_link_id' => $linkId,
624741 'af_has_comment' => $has_comment,
625742 ) );
@@ -696,7 +813,6 @@
697814 );
698815 }
699816
700 -
701817 /**
702818 * Picks a CTA to send the user to
703819 *
@@ -756,6 +872,9 @@
757873 ApiBase::PARAM_TYPE => 'string',
758874 ApiBase::PARAM_REQUIRED => true,
759875 ),
 876+ 'experiment' => array(
 877+ ApiBase::PARAM_TYPE => 'string',
 878+ ),
760879 'email' => array(
761880 ApiBase::PARAM_TYPE => 'string',
762881 )
@@ -780,11 +899,12 @@
781900 */
782901 public function getParamDescription() {
783902 $ret = array(
784 - 'pageid' => 'Page ID to submit feedback for',
785 - 'revid' => 'Revision ID to submit feedback for',
786 - 'anontoken' => 'Token for anonymous users',
787 - 'bucket' => 'Which feedback widget was shown to the user',
788 - 'link' => 'Which link the user clicked on to get to the widget',
 903+ 'pageid' => 'Page ID to submit feedback for',
 904+ 'revid' => 'Revision ID to submit feedback for',
 905+ 'anontoken' => 'Token for anonymous users',
 906+ 'bucket' => 'Which feedback form was shown to the user',
 907+ 'experiment' => 'Which experiment was shown to the user',
 908+ 'link' => 'Which link the user clicked on to get to the widget',
789909 );
790910 $fields = ApiArticleFeedbackv5Utils::getFields();
791911 foreach ( $fields as $f ) {
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/api/ApiViewFeedbackArticleFeedbackv5.php
@@ -140,7 +140,7 @@
141141 $where[] = $continueSql;
142142 }
143143 // Only show bucket 1 (per Fabrice on 1/25)
144 - $where['af_bucket_id'] = 1;
 144+ $where['af_form_id'] = 1;
145145
146146 // Fetch the feedback IDs we need.
147147 /* I'd really love to do this in one big query, but MySQL
@@ -204,7 +204,7 @@
205205 'aft_article_field',
206206 'aft_article_field_option', 'user', 'page'
207207 ),
208 - array( 'af_id', 'af_bucket_id', 'afi_name', 'afo_name',
 208+ array( 'af_id', 'af_form_id', 'afi_name', 'afo_name',
209209 'answer.aa_response_text', 'answer.aa_response_boolean',
210210 'answer.aa_response_rating', 'answer.aa_response_option_id',
211211 'afi_data_type', 'af_created', 'user_name',
@@ -331,7 +331,7 @@
332332 global $wgUser, $wgLang;
333333 $id = $record[0]->af_id;
334334
335 - switch( $record[0]->af_bucket_id ) {
 335+ switch( $record[0]->af_form_id ) {
336336 case 1: $content .= $this->renderBucket1( $record ); break;
337337 case 2: $content .= $this->renderBucket2( $record ); break;
338338 case 3: $content .= $this->renderBucket3( $record ); break;
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/sql/ArticleFeedbackv5.sql
@@ -26,12 +26,14 @@
2727 -- Foreign key to revision.rev_id
2828 af_revision_id integer unsigned NOT NULL,
2929 -- Which feedback widget the user was given. Default of 0 is "none".
30 - af_bucket_id integer unsigned NOT NULL DEFAULT 0,
 30+ af_form_id integer unsigned NOT NULL DEFAULT 0,
3131 -- Which CTA widget was displayed to the user. 0 is "none",
3232 -- Which would come up if they got the edit page CTA, and couldn't edit.
3333 af_cta_id integer unsigned NOT NULL DEFAULT 0,
3434 -- Which link the user clicked on to get to the widget. Default of 0 is "none".
3535 af_link_id integer unsigned NOT NULL DEFAULT 0,
 36+ -- Which experiment this feedback is a part of (matches clicktracking).
 37+ af_experiment varchar(32) NULL,
3638 -- Creation timetamp
3739 af_created binary(14) NOT NULL DEFAULT '',
3840 -- Number of times the feedback was hidden or marked as abusive.
Index: branches/wmf/1.19wmf1/extensions/ArticleFeedbackv5/sql/alter.sql
@@ -148,3 +148,13 @@
149149 -- make sure all old feedback has dates, even if they're wrong
150150 UPDATE aft_article_feedback SET af_hide_timestamp = NOW() WHERE af_is_hidden IS TRUE AND af_hide_timestamp = '';
151151 UPDATE aft_article_feedback SET af_oversight_timestamp = NOW() WHERE af_is_deleted IS TRUE AND af_oversight_timestamp = '';
 152+
 153+-- Added 3/29 (reha)
 154+ALTER TABLE /*_*/aft_article_feedback CHANGE COLUMN af_bucket_id af_form_id INTEGER UNSIGNED NOT NULL DEFAULT 0;
 155+ALTER TABLE /*_*/aft_article_feedback ADD COLUMN af_experiment varchar(32) NULL;
 156+CREATE INDEX /*_*/af_experiment ON /*_*/aft_article_feedback (af_experiment);
 157+UPDATE /*_*/aft_article_feedback SET af_experiment = af_form_id WHERE DATE(af_created) <= '2012-03-21';
 158+UPDATE /*_*/aft_article_feedback SET af_experiment = CONCAT(af_form_id, 'A') WHERE DATE(af_created) > '2012-03-21' AND af_link_id = 1;
 159+UPDATE /*_*/aft_article_feedback SET af_experiment = CONCAT(af_form_id, 'E') WHERE DATE(af_created) > '2012-03-21' AND af_link_id = 5;
 160+UPDATE /*_*/aft_article_feedback SET af_experiment = CONCAT(af_form_id, '?') WHERE DATE(af_created) > '2012-03-21' AND af_link_id = 0;
 161+

Status & tagging log