Version 2.8: extension registration support, replace AJAX stuff with an API module

Also some related cleanup (removal of wfProfile* stuff, etc.).

Change-Id: Id8a993e71ddca7bfbf3452725300167698ce3135
This commit is contained in:
Jack Phoenix 2016-04-24 21:26:42 +03:00
parent 9f54e77e0f
commit 1e23ff6a07
8 changed files with 272 additions and 204 deletions

143
ApiVoteNY.php Normal file
View file

@ -0,0 +1,143 @@
<?php
/**
* VoteNY API module
*
* @file
* @ingroup API
* @date 21 November 2015
* @see https://www.mediawiki.org/wiki/API:Extensions#ApiSampleApiExtension.php
*/
class ApiVoteNY extends ApiBase {
/**
* @var Vote|VoteStars Instance of the Vote or VoteStars class, set in execute() below
*/
private $vote;
/**
* Main entry point.
*/
public function execute() {
$user = $this->getUser();
// Get the request parameters
$params = $this->extractRequestParams();
$action = $params['what'];
// If the "what" param isn't present, we don't know what to do!
if ( !$action || $action === null ) {
$this->dieUsageMsg( 'missingparam' );
}
// Need to have sufficient user rights to proceed...
if ( !$user->isAllowed( 'voteny' ) ) {
$this->dieUsageMsg( 'badaccess-group0' );
}
// Ensure that the page ID is present and that it really is numeric
$pageId = $params['pageId'];
if ( !$pageId || $pageId === null || !is_numeric( $pageId ) ) {
$this->dieUsageMsg( array( 'missingparam', 'pageId' ) );
}
// Vote value is needed for actual vote actions, i.e. everything but "delete"
$voteValue = $params['voteValue'];
if ( !( $voteValue || $voteValue === null ) && $action !== 'delete' ) {
$this->dieUsageMsg( array( 'missingparam', 'voteValue' ) );
}
// Set the private class member variable and do something...
if ( isset( $params['type'] ) && $params['type'] && $params['type'] == 'stars' ) {
$this->vote = new VoteStars( $pageId );
switch ( $action ) {
case 'delete':
$this->vote->delete();
$output = $this->vote->display();
break;
case 'multi':
if ( $this->vote->UserAlreadyVoted() ) {
$this->vote->delete();
}
$this->vote->insert( $voteValue );
$output = $this->vote->displayScore();
break;
case 'vote':
default:
if ( $this->vote->UserAlreadyVoted() ) {
$this->vote->delete();
}
$this->vote->insert( $voteValue );
$output = $this->vote->display( $voteValue );
break;
}
} else {
$this->vote = new Vote( $pageId );
switch ( $action ) {
case 'delete':
$this->vote->delete();
$output = $this->vote->count( 1 );
break;
case 'vote':
default:
$this->vote->insert( $voteValue );
$output = $this->vote->count( 1 );
break;
}
}
// Top level
$this->getResult()->addValue( null, $this->getModuleName(),
array( 'result' => $output )
);
return true;
}
public function needsToken() {
return 'csrf';
}
public function isWriteMode() {
return true;
}
/**
* @return array
*/
public function getAllowedParams() {
return array(
'what' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
'pageId' => array(
ApiBase::PARAM_TYPE => 'integer',
ApiBase::PARAM_REQUIRED => true
),
'voteValue' => array(
ApiBase::PARAM_TYPE => 'integer',
),
'type' => array(
ApiBase::PARAM_TYPE => 'string',
)
);
}
/**
* @see ApiBase::getExamplesMessages()
*/
protected function getExamplesMessages() {
return array(
'action=voteny&what=vote&pageId=666' => 'apihelp-voteny-example-1',
'action=voteny&what=delete&pageId=666' => 'apihelp-voteny-example-2',
'action=voteny&what=vote&type=stars&pageId=666&voteValue=3' => 'apihelp-voteny-example-3',
'action=voteny&what=delete&type=stars&pageId=666' => 'apihelp-voteny-example-4',
'action=voteny&what=multi&type=stars&pageId=666&voteValue=4' => 'apihelp-voteny-example-5'
);
}
}

69
Vote.js
View file

@ -25,14 +25,14 @@ var VoteNY = function VoteNY() {
* @param PageID Integer: internal ID number of the current article
*/
this.clickVote = function( TheVote, PageID ) {
$.post(
mw.util.wikiScript(), {
action: 'ajax',
rs: 'wfVoteClick',
rsargs: [ TheVote, PageID ]
}
).done( function( data ) {
$( '#PollVotes' ).html( ( data || '0' ) );
( new mw.Api() ).postWithToken( 'csrf', {
action: 'voteny',
format: 'json',
what: 'vote',
pageId: PageID,
voteValue: TheVote
} ).done( function( data ) {
$( '#PollVotes' ).html( data.voteny.result );
$( '#Answer' ).html(
'<a href="javascript:void(0);" class="vote-unvote-link">' +
mediaWiki.msg( 'voteny-unvote-link' ) + '</a>'
@ -44,17 +44,15 @@ var VoteNY = function VoteNY() {
* Called when removing your vote through the green square voting box
*
* @param PageID Integer: internal ID number of the current article
* @param mk Mixed: random token
*/
this.unVote = function( PageID ) {
$.post(
mw.util.wikiScript(), {
action: 'ajax',
rs: 'wfVoteDelete',
rsargs: [ PageID ]
}
).done( function( data ) {
$( '#PollVotes' ).html( ( data || '0' ) );
( new mw.Api() ).postWithToken( 'csrf', {
action: 'voteny',
format: 'json',
what: 'delete',
pageId: PageID
} ).done( function( data ) {
$( '#PollVotes' ).html( data.voteny.result );
$( '#Answer' ).html(
'<a href="javascript:void(0);" class="vote-vote-link">' +
mediaWiki.msg( 'voteny-link' ) + '</a>'
@ -71,22 +69,22 @@ var VoteNY = function VoteNY() {
*/
this.clickVoteStars = function( TheVote, PageID, id, action ) {
this.voted_new[id] = TheVote;
var rsfun;
var actionName;
if ( action == 3 ) {
rsfun = 'wfVoteStars';
actionName = 'stars'; // all other values but 'multi' are ignored anyway
}
if ( action == 5 ) {
rsfun = 'wfVoteStarsMulti';
actionName = 'multi';
}
$.post(
mw.util.wikiScript(), {
action: 'ajax',
rs: rsfun,
rsargs: [ TheVote, PageID ]
}
).done( function( data ) {
$( '#rating_' + id ).html( data );
( new mw.Api() ).postWithToken( 'csrf', {
action: 'voteny',
type: 'stars',
what: actionName,
voteValue: TheVote,
pageId: PageID
} ).done( function( data ) {
$( '#rating_' + id ).html( data.voteny.result );
} );
};
@ -97,14 +95,13 @@ var VoteNY = function VoteNY() {
* @param id Integer: ID of the current rating star
*/
this.unVoteStars = function( PageID, id ) {
$.post(
mw.util.wikiScript(), {
action: 'ajax',
rs: 'wfVoteStarsDelete',
rsargs: [ PageID ]
}
).done( function( data ) {
$( '#rating_' + id ).html( data );
( new mw.Api() ).postWithToken( 'csrf', {
action: 'voteny',
what: 'delete',
type: 'stars',
pageId: PageID
} ).done( function( data ) {
$( '#rating_' + id ).html( data.voteny.result );
} );
};

View file

@ -1,4 +0,0 @@
<?php
// The entry point has been moved to VoteNY.php
require_once( __DIR__ . '/VoteNY.php' );

View file

@ -29,8 +29,6 @@ class VoteHooks {
public static function renderVote( $input, $args, $parser ) {
global $wgOut, $wgUser;
wfProfileIn( __METHOD__ );
// Disable parser cache (sadly we have to do this, because the caching is
// messing stuff up; we want to show an up-to-date rating instead of old
// or totally wrong rating, i.e. another page's rating...)
@ -72,8 +70,6 @@ class VoteHooks {
$output = $vote->display();
}
wfProfileOut( __METHOD__ );
return $output;
}
@ -174,7 +170,7 @@ class VoteHooks {
* @param string $pagename Page name
* @return int Amount of votes for the given page
*/
static function getNumberOfVotesPageParser( $parser, $pagename ) {
public static function getNumberOfVotesPageParser( $parser, $pagename ) {
$title = Title::newFromText( $pagename );
if ( !$title instanceof Title ) {
@ -202,7 +198,7 @@ class VoteHooks {
* @param Parser $parser
* @return bool
*/
static function setupNumberOfVotesPageParser( &$parser ) {
public static function setupNumberOfVotesPageParser( &$parser ) {
$parser->setFunctionHook( 'NUMBEROFVOTESPAGE', 'VoteHooks::getNumberOfVotesPageParser', Parser::SFH_NO_HASH );
return true;
}

View file

@ -1,70 +0,0 @@
<?php
/**
* Vote extension - JavaScript-based voting with the <vote> tag
*
* @file
* @ingroup Extensions
* @author Aaron Wright <aaron.wright@gmail.com>
* @author David Pean <david.pean@gmail.com>
* @author Jack Phoenix <jack@countervandalism.net>
* @link https://www.mediawiki.org/wiki/Extension:VoteNY Documentation
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
// Extension credits that show up on Special:Version
$wgExtensionCredits['parserhook'][] = array(
'name' => 'Vote',
'version' => '2.7',
'author' => array( 'Aaron Wright', 'David Pean', 'Jack Phoenix' ),
'descriptionmsg' => 'voteny-desc',
'url' => 'https://www.mediawiki.org/wiki/Extension:VoteNY'
);
// Path to Vote extension files
$wgVoteDirectory = "$IP/extensions/VoteNY";
// New user right
$wgAvailableRights[] = 'voteny';
$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';
// 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';
// Set up the new special page, Special:TopRatings, which shows top rated pages
// based on given criteria
$wgAutoloadClasses['SpecialTopRatings'] = __DIR__ . '/SpecialTopRatings.php';
$wgSpecialPages['TopRatings'] = 'SpecialTopRatings';
// Hooked functions
$wgAutoloadClasses['VoteHooks'] = __DIR__ . '/VoteHooks.php';
$wgHooks['ParserFirstCallInit'][] = 'VoteHooks::registerParserHook';
$wgHooks['RenameUserSQL'][] = 'VoteHooks::onUserRename';
$wgHooks['ParserGetVariableValueSwitch'][] = 'VoteHooks::assignValueToMagicWord';
$wgHooks['MagicWordwgVariableIDs'][] = 'VoteHooks::registerVariableId';
$wgHooks['ParserFirstCallInit'][] = 'VoteHooks::setupNumberOfVotesPageParser';
$wgHooks['LoadExtensionSchemaUpdates'][] = 'VoteHooks::addTable';
// ResourceLoader support for MediaWiki 1.17+
$wgResourceModules['ext.voteNY.styles'] = array(
'styles' => 'Vote.css',
'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'
);

View file

@ -1,88 +0,0 @@
<?php
/**
* AJAX functions used by Vote extension.
*/
$wgAjaxExportList[] = 'wfVoteClick';
function wfVoteClick( $voteValue, $pageId ) {
global $wgUser;
if ( !$wgUser->isAllowed( 'voteny' ) ) {
return '';
}
if ( is_numeric( $pageId ) && ( is_numeric( $voteValue ) ) ) {
$vote = new Vote( $pageId );
$vote->insert( $voteValue );
return $vote->count( 1 );
} else {
return 'error';
}
}
$wgAjaxExportList[] = 'wfVoteDelete';
function wfVoteDelete( $pageId ) {
global $wgUser;
if ( !$wgUser->isAllowed( 'voteny' ) ) {
return '';
}
if ( is_numeric( $pageId ) ) {
$vote = new Vote( $pageId );
$vote->delete();
return $vote->count( 1 );
} else {
return 'error';
}
}
$wgAjaxExportList[] = 'wfVoteStars';
function wfVoteStars( $voteValue, $pageId ) {
global $wgUser;
if ( !$wgUser->isAllowed( 'voteny' ) ) {
return '';
}
$vote = new VoteStars( $pageId );
if ( $vote->UserAlreadyVoted() ) {
$vote->delete();
}
$vote->insert( $voteValue );
return $vote->display( $voteValue );
}
$wgAjaxExportList[] = 'wfVoteStarsMulti';
function wfVoteStarsMulti( $voteValue, $pageId ) {
global $wgUser;
if ( !$wgUser->isAllowed( 'voteny' ) ) {
return '';
}
$vote = new VoteStars( $pageId );
if ( $vote->UserAlreadyVoted() ) {
$vote->delete();
}
$vote->insert( $voteValue );
return $vote->displayScore();
}
$wgAjaxExportList[] = 'wfVoteStarsDelete';
function wfVoteStarsDelete( $pageId ) {
global $wgUser;
if ( !$wgUser->isAllowed( 'voteny' ) ) {
return '';
}
$vote = new VoteStars( $pageId );
$vote->delete();
return $vote->display();
}

84
extension.json Normal file
View file

@ -0,0 +1,84 @@
{
"name": "VoteNY",
"version": "2.8",
"author": [
"Aaron Wright",
"David Pean",
"Jack Phoenix"
],
"license-name": "GPL-2.0+",
"url": "https://www.mediawiki.org/wiki/Extension:VoteNY",
"descriptionmsg": "voteny-desc",
"type": "parserhook",
"SpecialPages": {
"TopRatings": "SpecialTopRatings"
},
"MessagesDirs": {
"VoteNY": [
"i18n"
]
},
"APIModules": {
"voteny": "ApiVoteNY"
},
"ExtensionMessagesFiles": {
"VoteNYAlias": "VoteNY.alias.php",
"VoteNYMagic": "VoteNY.i18n.magic.php"
},
"AutoloadClasses": {
"ApiVoteNY": "ApiVoteNY.php",
"Vote": "VoteClass.php",
"VoteStars": "VoteClass.php",
"SpecialTopRatings": "SpecialTopRatings.php",
"VoteHooks": "VoteHooks.php"
},
"Hooks": {
"ParserFirstCallInit": [
"VoteHooks::registerParserHook",
"VoteHooks::setupNumberOfVotesPageParser"
],
"RenameUserSQL": [
"VoteHooks::onUserRename"
],
"ParserGetVariableValueSwitch": [
"VoteHooks::assignValueToMagicWord"
],
"MagicWordwgVariableIDs": [
"VoteHooks::registerVariableId"
],
"LoadExtensionSchemaUpdates": [
"VoteHooks::addTable"
]
},
"ResourceFileModulePaths": {
"localBasePath": "",
"remoteExtPath": "VoteNY"
},
"ResourceModules": {
"ext.voteNY.scripts": {
"scripts": "Vote.js",
"messages": [
"voteny-link",
"voteny-unvote-link"
],
"dependencies": [ "mediawiki.api" ],
"position": "bottom"
},
"ext.voteNY.styles": {
"styles": "Vote.css",
"position": "top"
}
},
"AvailableRights": [
"voteny"
],
"GroupPermissions": {
"*": {
"voteny": false
},
"user": {
"voteny": true
}
},
"manifest_version": 1
}

View file

@ -5,6 +5,16 @@
"David Pean <david.pean@gmail.com>"
]
},
"apihelp-voteny-description": "VoteNY API module",
"apihelp-voteny-param-what": "Action to take; valid values are 'vote' (green voting box), 'multi' (voting stars) or 'delete' (delete a previously given vote)",
"apihelp-voteny-param-pageId": "Page ID of the page where the voting box/stars is/are",
"apihelp-voteny-param-voteValue": "Numerical vote value between 1 and 5",
"apihelp-voteny-param-type": "Set this to 'stars' to call the voting stars (VoteStars PHP class), otherwise the green vote box (Vote PHP class) is used",
"apihelp-voteny-example-1": "Cast a vote for the page which has the ID number 666",
"apihelp-voteny-example-2": "Delete your vote from the page which has the ID number 666",
"apihelp-voteny-example-3": "Cast a vote (3 stars out of 5) for the page which has the ID number 666",
"apihelp-voteny-example-4": "Delete your vote from the page which has the ID number 666 which is using the star rating",
"apihelp-voteny-example-5": "Cast a vote (4 stars out of 5) for the page which has the ID number 666, deleting your previous vote, if any",
"voteny-desc": "JavaScript-based voting with the <tt>&lt;vote&gt;</tt> tag",
"voteny-link": "Vote",
"voteny-unvote-link": "unvote",