AJAX star rating plugin
2 : Configuration
In the days of social networks, you often want to give users the possibility to rate things and to show the average rating. The most common user interface implementation is a star rating system. This plugin offers you an easy, customizable way to enable your users to star rate any CakePHP model you want.
rating/config/plugin.rating.php
<?php
/**
* Config file for the AJAX star rating plugin.
*
* @author Michael Schneidt <michael.schneidt@arcor.de>
* @copyright Copyright 2009, Michael Schneidt
* @license http://www.opensource.org/licenses/mit-license.php
* @link http://bakery.cakephp.org/articles/view/ajax-star-rating-plugin-1
* @version 2.2
*/
/**
* Disable the user rating.
*/
$config['Rating.disable'] = false;
/**
* Show errors and warnings that should help to setup the plugin.
*/
$config['Rating.showHelp'] = true;
/**
* CakePHP app root.
*
* If you access your app like http://yourdomain/mycake then /mycake/ is your app root.
*/
$config['Rating.appRoot'] = '';
/**
* Show a flash message after rating.
*
* (displays 'Rating.flashMessage')
*/
$config['Rating.flash'] = false;
/**
* Message shown on flash.
*
* (depends on 'Rating.flash')
*/
$config['Rating.flashMessage'] = __('Your rating has been saved.', true);
/**
* Enable fallback for disabled javascript.
*
* (this inserts additional html code)
*/
$config['Rating.fallback'] = true;
/**
* Show flash message on fallback save redirect.
*
* (displays 'Rating.flashMessage')
*/
$config['Rating.fallbackFlash'] = false;
/**
* User id location in the session data.
*/
$config['Rating.sessionUserId'] = 'User.id';
/**
* Enable Guest rating. (ignores 'Rating.sessionUserId')
*
* Guest access is stored in cookie to prevent multiple ratings (not secure!)
*/
$config['Rating.guest'] = false;
/**
* Guest cookie duration time. (interpreted with strtotime())
*/
$config['Rating.guestDuration'] = '1 week';
/**
* Maximum rating.
*/
$config['Rating.maxRating'] = 5;
/**
* Location of the full star image.
*/
$config['Rating.starFullImageLocation'] = 'rating/img/star-full.png';
/**
* Location of the empty star image.
*/
$config['Rating.starEmptyImageLocation'] = 'rating/img/star-empty.png';
/**
* Location of the half star image.
*/
$config['Rating.starHalfImageLocation'] = 'rating/img/star-half.png';
/**
* Save the average rating and vote count to the rated model.
*
* This may speed up loading, because the values must not be
* calculated from the ratings on every access. This is also
* helpful if you want to sort the model by rating data, e.g.
* using pagination sort.
*
* This config only works, if you use no more than one rating
* element (name parameter) for each model id and no different
* config files (config parameter) with same field names set.
*
* If set to true, you have to add the 'Rating.modelAverageField'
* and 'Rating.modelVotesField' to your rated models.
*/
$config['Rating.saveToModel'] = false;
/**
* Field name in models for the average rating.
*
* SQL: ALTER TABLE <model_table> ADD <Rating.modelAverageField> decimal(3,1) unsigned default '0.0';
*
* (depends on 'Rating.saveToModel')
*/
$config['Rating.modelAverageField'] = 'rating';
/**
* Field name in models for the rating votes.
*
* SQL: ALTER TABLE <model_table> ADD <Rating.modelVotesField> int(11) unsigned default '0';
*
* (depends on 'Rating.saveToModel')
*/
$config['Rating.modelVotesField'] = 'votes';
/**
* Allow users to change their ratings.
*/
$config['Rating.allowChange'] = true;
/**
* Allow users to delete their ratings by
* deselecting the current rating.
*
* (depends on 'Rating.allowChange')
*/
$config['Rating.allowDelete'] = true;
/**
* Display the user rating in stars instead of the average rating.
*/
$config['Rating.showUserRatingStars'] = false;
/**
* Show a mark to indicate the user rating.
*
* (change mark in /vendors/css/rating.css .rating-user)
*/
$config['Rating.showUserRatingMark'] = true;
/**
* Define the text beside the stars.
*
* %AVG% Average rating
* %MAX% Maximum rating
* %VOTES% Number of votes
* %RATING% User rating
*/
$config['Rating.statusText'] = '%AVG% / %MAX% (%VOTES%)';
/**
* Show 'Rating.mouseOverMessages' on mouseover.
*/
$config['Rating.showMouseOverMessages'] = true;
/**
* Messages that are showing on mouseover.
*
* If you want to put links into the messages like for login, you have
* to do that manually, because the CakePHP helpers don't work here yet.
*
* 'login' this message appears if the user is not signed in.
* 'rated' this message appears if the user rated already.
* 'delete' this message appears if the user mouseovers his rating and 'Rating.allowDelete' is set true.
* '1' to 'Rating.maxRating' represent the different rating values.
*
* (depends on 'Rating.showMouseOverMessages')
*/
$config['Rating.mouseOverMessages'] = array('login' => __('Please login to rate', true),
'rated' => __('Thanks for your rating', true),
'delete' => __('Click to remove your rating', true),
'1' => __('Really bad', true),
'2' => __('Bad', true),
'3' => __('Average', true),
'4' => __('Good', true),
'5' => __('Really good', true));
?>
Comments
Comment
1 Super Cool!
Thanks
Comment
2 Little Help with Plugin Models
I keep getting model_id does not exist when I use a plugin model. I enter the model as 'Plugin.Model' eg. 'News.Article'
I've confirmed the model_id exists in d database, I've also tested the same code used in rating_controller->view()
$model = 'News.Article';
$modelInstance = ClassRegistry::init($model);
$modelInstance->id = '3ad66b8f-55ce-11de-bb54-9f1eb639fa3a';
var_dump($modelInstance->exists(true));
In my test, it returns true, but in the the rating_controller it returns false.
I'd really appreciate some help. Thanks in advance.
Comment
3 Little Help with Plugin Models
thx for your comment! I tried to reproduce your problem and created a dummy "news" plugin with a model "article" and also put an article with your uuid into the db "articles", but I didn't experience the problem. Your code snippet shows true on top of the ratings_controller->view().
Do you use the latest CakePHP release? Did you test it with the demo? Atm I have no other idea, but I will let you know if I come up with something.
Regards
Comment
4 Little Help with Plugin Models
I just tested it with the demo and I still get the same error. With models in app/models it works well.
I'm using cake 1.2.3.8166.
Here is how I called the rating plugin.
echo $this->element('rating', array('plugin' => 'rating',
'model' => 'News.Article',
'id' => $article['Article']['id'])
);
?>
Thanks for your help.
Comment
5 Little Help with Plugin Models
ok now I got it too and could figure it out. The problem is, that the sanitization encodes the incoming uuid, that's why the model_id is not found. Further more a rating cannot be saved in the ratings table, because the model_id is an integer field.
I will add the support in the next version, so long you can change the model_id from int to varchar and disable the encoding on Sanitize::clean (in view() and save()) as follows:
Sanitize::clean($id, array('encode' => false));
I really didn't think of uuids, so tanks for your comment.
Comment
6 Little Help with Plugin Models
Comment
7 Error when rating...
Warning (512): DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[User] => Array
(
[id] => 3
[username] => test
[group_id] => 2
[created] => 2009-07-22 17:35:44
[modified] => 2009-07-22 17:35:44
)
)
Aco: controllers/Ratings/save [CORE/cake/libs/controller/components/acl.php, line 239]
Its seems to have something to do with the DBACl http://book.cakephp.org/view/644/Initialize-the-Db-Acl-tables but cannot really figure it our. Thanks for your help!
Question
8 Error when rating...
Warning (512): DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[User] => Array
(
[id] => 3
[username] => test
[group_id] => 2
[created] => 2009-07-22 17:35:44
[modified] => 2009-07-22 17:35:44
)
)
Aco: controllers/Ratings/save [CORE/cake/libs/controller/components/acl.php, line 239]
Its seems to have something to do with the DBACl http://book.cakephp.org/view/644/Initialize-the-Db-Acl-tables but cannot really figure it our. Thanks for your help!
Question
9 re: Error when rating...
I discovered the mistake myself. I did not have the permission to rate, so had to allow the ratings_controller.php to do this by adding:
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allowedActions = array('*');
}
Comment
10 Good Job!
1) Javascript need to be declared before the element instaciation: not a big deal, but i follow the Steve Souders´ performance tips, so, i prefer to declare my javascript on the end of document. Actually, it shows an error if I do this. A solution is simple - instead of call ratingInit directly, do this:
$(window).load(function() {
renderInit( // arguments, etc..
}
So, the code will be executed after document load. If you have a public svn or git repo, i would like to help you with this.
Thx,
Yuri Teixeira.
Comment
11 Re: Good Job!
Maybe something like this:
rating_prototype.js
function ratingInit(element, data, options, config, enabled) {
Event.observe(window, 'load', function() {
...
});
}
rating_jquery.js
function ratingInit(element, data, options, config, enabled) {
$(window).load(function () {
...
});
}
Regards
Comment
12 Re: Re: Good Job!
I currently see no way how to handle that, if you find something working let me know.
Comment
13 Thanks!
I wanted to let you know that I found a little bug.
If you are using a custom primary key field in your model (not "id"), and you set Rating.saveAverageToModel to true, it won't work.
To make it work I just modified line 105 in ratings_controller.php from this:
$avgRating = $modelInstance->field(Configure::read('Rating.modelAverageField'), array($model.'.id' => $id));
to this:
$avgRating = $modelInstance->field(Configure::read('Rating.modelAverageField'), array($model.'.'.$modelInstance->primaryKey => $id));
Thanks again for sharing it! It saved me a lot of work :)
Nahuel
Comment
14 Images Missing
user/rating/image/star-full.png
it now points at:-
user/rating/img/star-full.png
And when I click on a one of the image place holders I get this error:-
Error: The action rating is not defined in controller UserController
I assume I've missed something simple here since everyone else seems to get it working so easily. I'm using cake 1.2.5, and rating 2.0 with JQuery.
Anyone have any ideas what the problem could be ?
Comment
15 Re: Images Missing
Where does the "user/" come from in your case? I thought it is your appRoot, but in this case there shouldn't be the controller error.
Maybe it would help you to download the working demo and setup a similar configuration of your situation.
Comment
16 flash msg
The flash messages work very well too, although as I already have flash messages in my website, I end up with some general flash messages appearing in the ratings boxes instead of in the initial place where I put my $session->flash().
Would you have any suggestions on how to separate my own flash messages (and decide of the place in the view where it gets displayed), from the ratings flash?
Comment
17 Re: flash msg
To solve the flash collisions you simply have to use a flash key, as shown in the following code.
rating\controllers\ratings_controller.php line 258
$this->Session->setFlash(Configure::read('Rating.flashMessage'),
'default',
array('class' => 'rating-flash'),
'rating');
rating\views\ratings\view.ctp line 95
$session->flash('rating');
Because of ajax reloading, the flash message element has to be within the plugin's view and cannot be placed elsewhere in your view. However it may help you to relocate the div.rating-flash via css in the rating\vendors\css\rating.css
Comment
18 Re: Re: flash msg
Works like a charm. Thanks!
Comment
19 lot of thx
very useful...
Bug
20 Problem loading config values - SOLVED
My biggest (only) problem was that I didn't copy the plugin.rating.php config file into /app/config/plugin.rating.php
None of the Rating config values were set. All were NULL. I wasn't expecting to have to copy config files around. I thought I was diligent about reading the docs, but I must have missed something.
If for no other reason that to make me look foolish, could someone post a link to the documentation that says to copy the config file?
In addition, I think there is a 'bug' in the RatingsController where it loads the config file.
Check this out from line 45 in the RatingsController:
// load config
if (Configure::load($config) === false && Configure::read('Rating.showHelp')) {
echo 'Error: The '.$config.'.php was not found in your app/config directory. Please copy it from rating/config/plugin.rating.php';
exit;
}
Is the logic really saying if I can't load the config file AND the config value to display help is true, then display an error message?
It would seem correct to remove the AND showHelp part of the logic. Yes?
Comment
21 Re: protoaculous
find ich toll, dass du im Rahmen der FH die Möglichkeit hast CakePHP zu verwenden.
Das Plugin hat als Grundvoraussetzung dass entweder Prototype oder jQuery geladen ist, steht auch oben unter Requirements. Ist aber zugegeben etwas kurz und natürlich nicht klar was man machen muss, wenn man die beiden Frameworks gar nicht kennt. Werde ich mal überarbeiten, danke auf jeden Fall für das Feedback.
Gruß,
Michael
Comment
22 Re: Problem loading config values
somehow you missed step 3 in the installation instructions in this article as well as in the install.nfo file. Anyway, I aggree that the plugin could load some default configurations when there is no config file found.
There should be an error message, but as you correctly found out, the condition is crap, to read the showHelp config doesn't make any sense when there are no configs loaded. ;-)
Thx for that!
Question
23 vertical to horizontal layout
Comment
24 Re: vertical to horizontal layout
div.fallback label {
display: inline;
.
.
}
div.rating label {
display: inline;
}
Bug
25 Problems with custom routes
there should be an error in the redirect with custom routes (named controllers) after saving the rate.
Example:
Router::connect(
'/government',
array('controller' => 'products', 'action' => 'display', 5)
);
There return an error view by calling the XMLHttpRequest.responseText…
Comment
26 bugfix in js
url: ratingSettings[element]['config']['appRoot'] + '/rating/ratings/save/' + data[0] + '/' + data[3] + '/' + value + '?' + Math.floor(Math.random() * 999999),
Comment
27 Great work! ...
first of all i wish to thank Michael for this awesome piece of code.
Got it, had to use the element like this:
<?php
echo $this->element('rating', array('plugin' => 'rating',
'model' => 'Provider',
'id' => $provider['Provider']['id']));
?>
Just can repeat myself: Great work! Thanks a lot.
Comment
28 If you have custom catchall routes
Router::connect('/rating/ratings/:action/*', array('controller' => 'ratings','plugin'=>'rating'));Comment
29 "field ambiguous" error thrown for "view" method
Controller Class:
<?php// $values = $modelInstance->find(array($modelInstance->primaryKey => $id), //causes "field 'id' is ambiguous" warning
//
//fix:
//
$values = $modelInstance->find(array($modelInstance->name.".".$modelInstance->primaryKey => $id),
array(Configure::read('Rating.modelAverageField'),
Configure::read('Rating.modelVotesField')),
null,
-1);
?>
Comment
30 Not Working
help me to crack
Thanks in Advances..
Comment
31 RE: Not Working
Question
32 Use Auth component
$config['Rating.sessionUserId'] = 'Auth.User.id';
does not work for me
Every time a see: Warning: No valid user id was found at "Auth.User.id" in the session
Comment
33 Re: Use Auth component
If that warning goes away once you log in, then just turn off the help text in the plugin's config file.
Comment
34 stars disappear
When I disable javascript I see the star ratings, status text, radio buttons, vote button, and mouseover messages. Also, when javascript is disabled I can vote and it works fine, adding my vote to the database. So my guess is something is happening in the rating_query.js that fouls up the display.
Any thoughts on how to fix this would be greatly appreciated.
Comment
35 Re: stars disappear