The article rating widget cannot record a vote. Every click is answered with "Invalid Token", so the star rating
feature is inert on a default install with article ratings enabled.
templates/shaper_helixultimate/js/main.js:301-334 builds the AJAX payload:
$('.article-ratings .rating-star').on('click', function (event) {
event.preventDefault();
var $parent = $(this).closest('.article-ratings');
var request = {
option: 'com_ajax',
template: template,
action: 'rating',
rating: $(this).data('number'),
article_id: $parent.data('id'),
format: 'json',
};
$.ajax({ type: 'POST', data: request, ... });
});
The payload carries no CSRF token, but the endpoint it reaches requires one. templates/shaper_helixultimate/helper.php:27:
Session::checkToken() or die(json_encode($output));
Session::checkToken() (libraries/src/Session/Session.php:64-88) looks for the token in the X-CSRF-Token header or in
the POST body; neither is present, so it returns false and the handler dies with {"status":false,"message":"Invalid Token"}.
Reproduced from a normal browser session on an article page, same session for both requests:
POST option=com_ajax&template=shaper_helixultimate&action=rating&rating=5&article_id=45&format=json
-> {"status":false,"message":"Invalid Token"}
POST option=com_ajax&template=shaper_helixultimate&action=rating&rating=5&article_id=45&format=json&<token>=1
-> {"status":true,"message":"Thanks for your rating.","rating_count":1,"ratings":"<span class=\"rating-star\" ..."}
The only difference between the two is the token field, and Joomla already publishes it on every page in
joomla-script-options under csrf.token, so the fix is three lines in the same handler:
var csrfToken = window.Joomla && Joomla.getOptions ? Joomla.getOptions('csrf.token', '') : '';
if (csrfToken) {
request[csrfToken] = 1;
}
Two smaller things in the same endpoint, which only become reachable once the token is sent:
-
helper.php:32 takes rating as a plain (int) and helper.php:86 adds it straight into rating_sum with no
range check. A request with rating=99999 inflates the stored average for that article. Clamping to 1..5 is enough.
-
helper.php:31 takes article_id as a plain (int) and helper.php:94 inserts a #__content_rating row without
checking that the article exists or is published, so rows can be created for ids that are not articles.
Thank you.