From 33f50151978ddfc0bd4271168d0536526304eea0 Mon Sep 17 00:00:00 2001 From: Jack Phoenix Date: Thu, 14 May 2015 00:00:34 +0300 Subject: [PATCH] Version 2.7: various JS fixes and stuff * CSS and JS are now separate RL modules * JS rewritten to use jQuery instead of sajax * Fixed a scoping bug from the previous patchset * Fixed other scoping bugs so that you can revoke your vote w/o reloading the page, no matter what * JS module is not loaded for users w/o the 'voteny' user right * JS is not loaded on Special:TopRatings anymore, it was only loaded there ever back when this extension had inline JS to prevent over 9000 JS errors, plus it wouldn't have worked because wgArticleId is 0 for special pages * Special page alias file added Change-Id: I6668238a0ab6272b0a7b3b167832ebb2d3abe2c2 --- SpecialTopRatings.php | 28 +++++++--- Vote.js | 118 ++++++++++++++++++++++++++---------------- VoteHooks.php | 7 ++- VoteNY.alias.php | 14 +++++ VoteNY.php | 25 +++++---- 5 files changed, 127 insertions(+), 65 deletions(-) create mode 100644 VoteNY.alias.php diff --git a/SpecialTopRatings.php b/SpecialTopRatings.php index 86e1850..202e3bc 100644 --- a/SpecialTopRatings.php +++ b/SpecialTopRatings.php @@ -30,6 +30,9 @@ class SpecialTopRatings extends IncludableSpecialPage { // Set the page title, robot policies, etc. $this->setHeaders(); + $out = $this->getOutput(); + $user = $this->getUser(); + $categoryName = $namespace = ''; // Parse the parameters passed to the special page @@ -47,9 +50,13 @@ class SpecialTopRatings extends IncludableSpecialPage { $limit = 50; } - // Add JS (and CSS) -- needed so that users can vote on this page and - // so that their browsers' consoles won't be filled with JS errors ;-) - $this->getOutput()->addModules( 'ext.voteNY' ); + // Add CSS + $out->addModuleStyles( 'ext.voteNY.styles' ); + /* scroll down some lines to see why I'm not including JS here anymore + if ( $user->isAllowed( 'voteny' ) ) { + $out->addModules( 'ext.voteNY.scripts' ); + } + */ $ratings = array(); $output = ''; @@ -106,12 +113,21 @@ class SpecialTopRatings extends IncludableSpecialPage { // If we have some ratings, start building HTML output if ( !empty( $ratings ) ) { /* XXX dirrrrrrty hack! because when we include this page, the JS - is not included, but we want things to work still */ - if ( $this->including() ) { + * is not included, but we want things to work still + * Actually, this is way harder than what it looks like. + * The JS uses wgArticleId but when directly viewing Special:TopRatings, + * wgArticleId is zero, because special pages aren't articles. + * As for including the special page, then wgArticleId would likely + * point at the ID of the page that includes {{Special:TopRatings}}, + * which would be stupid and wrong. + * Besides, shouldn't you check out the images/pages that you're gonna + * vote for? Yeah, that's what I thought, too. + if ( $this->including() && $user->isAllowed( 'voteny' ) ) { global $wgExtensionAssetsPath; $output .= ''; } + */ // yes, array_keys() is needed foreach ( array_keys( $ratings ) as $discardThis => $pageId ) { @@ -147,7 +163,7 @@ class SpecialTopRatings extends IncludableSpecialPage { } // Output everything! - $this->getOutput()->addHTML( $output ); + $out->addHTML( $output ); } /** diff --git a/Vote.js b/Vote.js index d3b6742..70226cd 100644 --- a/Vote.js +++ b/Vote.js @@ -1,8 +1,9 @@ /** * JavaScript functions for Vote extension. * - * TODO: Should refactor this into a jQuery widget. The widget should get a PageID in its - * constructor so it can work on any page for any page and with multiple instances per page. + * TODO: Should refactor this into a jQuery widget. + * The widget should get a PageID in its constructor so it can work on any page + * for any page and with multiple instances per page. * * @constructor * @@ -24,12 +25,18 @@ var VoteNY = function VoteNY() { * @param PageID Integer: internal ID number of the current article */ this.clickVote = function( TheVote, PageID ) { - sajax_request_type = 'POST'; - sajax_do_call( 'wfVoteClick', [ TheVote, PageID ], function( request ) { - document.getElementById( 'PollVotes' ).innerHTML = request.responseText; - document.getElementById( 'Answer' ).innerHTML = + $.post( + mw.util.wikiScript(), { + action: 'ajax', + rs: 'wfVoteClick', + rsargs: [ TheVote, PageID ] + } + ).done( function( data ) { + $( '#PollVotes' ).html( ( data || '0' ) ); + $( '#Answer' ).html( '' + - mediaWiki.msg( 'voteny-unvote-link' ) + ''; + mediaWiki.msg( 'voteny-unvote-link' ) + '' + ); } ); }; @@ -40,12 +47,18 @@ var VoteNY = function VoteNY() { * @param mk Mixed: random token */ this.unVote = function( PageID ) { - sajax_request_type = 'POST'; - sajax_do_call( 'wfVoteDelete', [ PageID ], function( request ) { - document.getElementById( 'PollVotes' ).innerHTML = request.responseText; - document.getElementById( 'Answer' ).innerHTML = + $.post( + mw.util.wikiScript(), { + action: 'ajax', + rs: 'wfVoteDelete', + rsargs: [ PageID ] + } + ).done( function( data ) { + $( '#PollVotes' ).html( ( data || '0' ) ); + $( '#Answer' ).html( '' + - mediaWiki.msg( 'voteny-link' ) + ''; + mediaWiki.msg( 'voteny-link' ) + '' + ); } ); }; @@ -59,16 +72,22 @@ var VoteNY = function VoteNY() { this.clickVoteStars = function( TheVote, PageID, id, action ) { this.voted_new[id] = TheVote; var rsfun; - if( action == 3 ) { + if ( action == 3 ) { rsfun = 'wfVoteStars'; } - if( action == 5 ) { + if ( action == 5 ) { rsfun = 'wfVoteStarsMulti'; } - var resultElement = document.getElementById( 'rating_' + id ); - sajax_request_type = 'POST'; - sajax_do_call( rsfun, [ TheVote, PageID ], resultElement ); + $.post( + mw.util.wikiScript(), { + action: 'ajax', + rs: rsfun, + rsargs: [ TheVote, PageID ] + } + ).done( function( data ) { + $( '#rating_' + id ).html( data ); + } ); }; /** @@ -78,60 +97,67 @@ var VoteNY = function VoteNY() { * @param id Integer: ID of the current rating star */ this.unVoteStars = function( PageID, id ) { - var resultElement = document.getElementById( 'rating_' + id ); - sajax_request_type = 'POST'; - sajax_do_call( 'wfVoteStarsDelete', [ PageID ], resultElement ); + $.post( + mw.util.wikiScript(), { + action: 'ajax', + rs: 'wfVoteStarsDelete', + rsargs: [ PageID ] + } + ).done( function( data ) { + $( '#rating_' + id ).html( data ); + } ); }; this.startClearRating = function( id, rating, voted ) { + var voteNY = this; this.clearRatingTimer = setTimeout( function() { - this.clearRating( id, 0, rating, voted ); + voteNY.clearRating( id, 0, rating, voted ); }, 200 ); }; this.clearRating = function( id, num, prev_rating, voted ) { - if( this.voted_new[id] ) { + if ( this.voted_new[id] ) { voted = this.voted_new[id]; } - for( var x = 1; x <= this.MaxRating; x++ ) { + for ( var x = 1; x <= this.MaxRating; x++ ) { var star_on, old_rating; - if( voted ) { + if ( voted ) { star_on = 'voted'; old_rating = voted; } else { star_on = 'on'; old_rating = prev_rating; } - var ratingElement = document.getElementById( 'rating_' + id + '_' + x ); - if( !num && old_rating >= x ) { - ratingElement.src = this.imagePath + 'star_' + star_on + '.gif'; + var ratingElement = $( '#rating_' + id + '_' + x ); + if ( !num && old_rating >= x ) { + ratingElement.attr( 'src', this.imagePath + 'star_' + star_on + '.gif' ); } else { - ratingElement.src = this.imagePath + 'star_off.gif'; + ratingElement.attr( 'src', this.imagePath + 'star_off.gif' ); } } }; this.updateRating = function( id, num, prev_rating ) { - if( this.clearRatingTimer && this.last_id == id ) { + if ( this.clearRatingTimer && this.last_id == id ) { clearTimeout( this.clearRatingTimer ); } this.clearRating( id, num, prev_rating ); - for( var x = 1; x <= num; x++ ) { - document.getElementById( 'rating_' + id + '_' + x ).src = this.imagePath + 'star_voted.gif'; + for ( var x = 1; x <= num; x++ ) { + $( '#rating_' + id + '_' + x ).attr( 'src', this.imagePath + 'star_voted.gif' ); } this.last_id = id; }; }; -// TODO:Mmake event handlers part of a widget as described in the VoteNY's TODO and reduce this +// TODO: Make event handlers part of a widget as described in the VoteNY's TODO and reduce this // code to instantiating such a widget for the current wiki page if required. -jQuery( document ).ready( function() { +$( function() { var vote = new VoteNY(); // Green voting box's link - jQuery( '.vote-action' ).on( 'click', '> a', function( event ) { - if( jQuery( this ).hasClass( 'vote-unvote-link' ) ) { + $( '.vote-action' ).on( 'click', '> a', function( event ) { + if ( $( this ).hasClass( 'vote-unvote-link' ) ) { vote.unVote( mw.config.get( 'wgArticleId' ) ); } else { vote.clickVote( 1, mw.config.get( 'wgArticleId' ) ); @@ -139,23 +165,27 @@ jQuery( document ).ready( function() { } ); // Rating stars - jQuery( 'img.vote-rating-star' ).click( function() { - var that = jQuery( this ); + // Note: this uses $( 'body' ).on( 'actionName', 'selector' + // instead of $( 'selector' ).actionName so that the hover effects work + // correctly even *after* you've voted (say, if you wanted to change your + // vote with the star ratings without reloading the page). + $( 'body' ).on( 'click', '.vote-rating-star', function() { + var that = $( this ); vote.clickVoteStars( that.data( 'vote-the-vote' ), mw.config.get( 'wgArticleId' ), that.data( 'vote-id' ), that.data( 'vote-action' ) ); - } ).mouseover( function() { - var that = jQuery( this ); + } ).on( 'mouseover', '.vote-rating-star', function() { + var that = $( this ); vote.updateRating( that.data( 'vote-id' ), that.data( 'vote-the-vote' ), that.data( 'vote-rating' ) ); - } ).mouseout( function() { - var that = jQuery( this ); + } ).on( 'mouseout', '.vote-rating-star', function() { + var that = $( this ); vote.startClearRating( that.data( 'vote-id' ), that.data( 'vote-rating' ), @@ -164,10 +194,10 @@ jQuery( document ).ready( function() { } ); // Remove vote (rating stars) - jQuery( 'a.vote-remove-stars-link' ).click( function() { + $( 'body' ).on( 'click', '.vote-remove-stars-link', function() { vote.unVoteStars( mw.config.get( 'wgArticleId' ), - jQuery( this ).data( 'vote-id' ) + $( this ).data( 'vote-id' ) ); } ); -} ); +} ); \ No newline at end of file diff --git a/VoteHooks.php b/VoteHooks.php index c1de3e9..ae86e21 100644 --- a/VoteHooks.php +++ b/VoteHooks.php @@ -27,7 +27,7 @@ class VoteHooks { * @return string HTML */ public static function renderVote( $input, $args, $parser ) { - global $wgOut; + global $wgOut, $wgUser; wfProfileIn( __METHOD__ ); @@ -39,7 +39,10 @@ class VoteHooks { // Add CSS & JS // In order for us to do this *here* instead of having to do this in // registerParserHook(), we must've disabled parser cache - $wgOut->addModules( 'ext.voteNY' ); + $parser->getOutput()->addModuleStyles( 'ext.voteNY.styles' ); + if ( $wgUser->isAllowed( 'voteny' ) ) { + $parser->getOutput()->addModules( 'ext.voteNY.scripts' ); + } // Define variable - 0 means that we'll get that green voting box by default $type = 0; diff --git a/VoteNY.alias.php b/VoteNY.alias.php new file mode 100644 index 0000000..63b8654 --- /dev/null +++ b/VoteNY.alias.php @@ -0,0 +1,14 @@ + array( 'TopRatings' ), +); \ No newline at end of file diff --git a/VoteNY.php b/VoteNY.php index fdbe1f8..f5c0f45 100644 --- a/VoteNY.php +++ b/VoteNY.php @@ -11,18 +11,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ -/** - * Protect against register_globals vulnerabilities. - * This line must be present before any global variable is referenced. - */ -if ( !defined( 'MEDIAWIKI' ) ) { - die( "This is not a valid entry point.\n" ); -} - // Extension credits that show up on Special:Version $wgExtensionCredits['parserhook'][] = array( 'name' => 'Vote', - 'version' => '2.6.1', + 'version' => '2.7', 'author' => array( 'Aaron Wright', 'David Pean', 'Jack Phoenix' ), 'descriptionmsg' => 'voteny-desc', 'url' => 'https://www.mediawiki.org/wiki/Extension:VoteNY' @@ -37,11 +29,13 @@ $wgGroupPermissions['*']['voteny'] = false; // Anonymous users cannot vote $wgGroupPermissions['user']['voteny'] = true; // Registered users can vote // AJAX functions needed by this extension -require_once( 'Vote_AjaxFunctions.php' ); +require_once 'Vote_AjaxFunctions.php'; // Autoload classes and set up i18n $wgMessagesDirs['VoteNY'] = __DIR__ . '/i18n'; +$wgExtensionMessagesFiles['VoteNYAlias'] = __DIR__ . '/VoteNY.alias.php'; $wgExtensionMessagesFiles['VoteNYMagic'] = __DIR__ . '/VoteNY.i18n.magic.php'; + $wgAutoloadClasses['Vote'] = __DIR__ . '/VoteClass.php'; $wgAutoloadClasses['VoteStars'] = __DIR__ . '/VoteClass.php'; @@ -61,11 +55,16 @@ $wgHooks['ParserFirstCallInit'][] = 'VoteHooks::setupNumberOfVotesPageParser'; $wgHooks['LoadExtensionSchemaUpdates'][] = 'VoteHooks::addTable'; // ResourceLoader support for MediaWiki 1.17+ -$wgResourceModules['ext.voteNY'] = array( +$wgResourceModules['ext.voteNY.styles'] = array( 'styles' => 'Vote.css', - 'scripts' => 'Vote.js', - 'messages' => array( 'voteny-link', 'voteny-unvote-link' ), 'localBasePath' => __DIR__, 'remoteExtPath' => 'VoteNY', 'position' => 'top' // available since r85616 ); + +$wgResourceModules['ext.voteNY.scripts'] = array( + 'scripts' => 'Vote.js', + 'messages' => array( 'voteny-link', 'voteny-unvote-link' ), + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'VoteNY' +);