Requirements: basic css, image editing, basic theming
The fivestar module provides a tidy little voting widget that allows users to vote on nodes. It also provides a CCK field of the type "Fivestar Rating", which can be used to rate a node on multiple criteria.
One of my pet projects, Hill Bomb, requires just this functionality. It's a maps-mashup site for downhill riders, for example skateboarders or mountain bikers, to upload details and maps of awesome hills around the world. Here are the some of the ratings that users can give to hills:

As one of my friendly beta testers pointed out to me, it's difficult to judge what "more flames" means in each individual context. For example, does having more flames mean a better road surface, or more danger to the rider?
It would be better to have different image sets for each rating that clearly indicates what more and less actually means. Fivestar module doesn't provide this functionality out of the box - you can only choose one set of icons for all your Fivestar fields. So, I had to do a bit of modification to get it working.
Here's a tutorial with the examples from Hill Bomb. I only add one different image set here, but it can easily be extended for multiple.
Step 1: The images
Create the icon sets for each field. I chose small cars for the traffic fields. It's hopefully obvious that more cars = more traffic!
Making this image was easy in the fantastic open-source image manipulation program Paint.NET. It's just a matter of finding a cool icon, extending it's canvas to be 3 times it's original height, then duplicating it twice, and finally editing each one as preferred. The top image is the deactivated state, the second is the activated state, and the third is the roll-over state. Here I just left the roll-over to be identical to the activated state.
I also made this two-state "delete vote" image, the bottom one being the roll-over.
Step 2: The custom widget
Next, go to sites/all/modules/fivestar/widgets, where you'll find a directory for each available widget set. Create a new directory that will contain your new image set. In this case, I called it "hillbomb", after the site.
Now copy the contents of another widget directory, that will act as the base or fallback icons. These will be displayed by default, except where you specify your new icon set to be shown. I chose the flames icons, and copied over the three files (two sprite images and a .css). I renamed the .css from flames.css to the name of the folder, hillbomb.css.
Also copy in your new images that were made in step 1.
Step 3: The images in the css
Edit the css file in your new widget directory (in my example, hillbomb.css). Select everything, and copy-paste a duplicate of it at the end of the file. The original section should be left alone - this will ensure that the base icons (the flames) still work everywhere.
In this duplicated css, you need to change all references to the image flames.png to your new image, then do the same for the "delete vote" image (delete.png).
So:
background: url(flame.png) no-repeat 0 0;
becomes
background: url(cars.png) no-repeat 0 0;
Now change all height/width values in the duplicated css to match your new images. It's easy to work out - where the original css file has the width/height of a single sprite from the old image, then use the width/height from the new. If it has double the old image's height, then enter double the new image's height.
Step 4: Specify which CCK Fivestar field has our custom images
In order to make our new image sets display only on certain CCK fields, we need to make sure the scope of the css selector is correct. In my example case, I just add div.field-field-day-traffic before each of the static view selectors, because that's the class given to the div surrounding the field:
/*Override the traffic fields */
/* Static View-only Star Version - TRAFFIC (CARS)*/
div.field-field-day-traffic div.fivestar-widget-static .star {
width: 20px;
height: 20px;
background: url(cars.png) no-repeat 0 0;
}Note this will only work for the static view (i.e. when a fivestar field is displayed in the node's body), not for the node edit form. We get to that shortly.
Step 5: Give it a quick test
You should now be able to choose your new widget in your Fivestar settings, located at admin/settings/fivestar.
Test to make sure it works - make sure you try it out on a node with a Fivestar CCK field of the same name you specified in the css of Step 4. View the body of the node (static view of the Fivestar rating), and the node edit form (Javascript view).
Of course, the Javascript view of the widget will still display the flames instead of the cars, and also presents a small problem. If you use Firebug to inspect the HTML of the widgets, you'll notice there's no way to discern which CCK field each one belongs to, and thus no way to select just that field for our new images. In my example, there's no way of telling just the Javascript traffic field to display the cars instead of flames - it's all or nothing. Each one is just a <div class="fivestar-form-item fivestar-labels-hover">.
Step 6: Override the theme function for Javascript Fivestar
The simplest solution I found was to override the theme function, enclosing the widget in a div with a more descriptive class. This goes in template.php:
<?php
/**
* Customise the fivestar element, add another div so we know which icons to draw
*/
function phptemplate_fivestar($element) {
// Add necessary css.
fivestar_add_css();
// Add necessary Javascript.
if ($element['#widget'] == 'stars' ) {
fivestar_add_js();
}
return '<div class="fivestar-' . $element['#parents'][0] . '">' . theme('form_element', $element, $element['#children']) . '</div>';
}
?>The only difference from the original function is that instead of returning just theme('form_element', $element, $element['#children']), I enclose it in a div with the name of the field as a class. The name of the field is pulled from $element['#parents'][0]. The result of this for the traffic field will be:
<div class="fivestar-field_day_traffic">...widget...</div>
It's definitely not the most elegant solution, but unfortunately the Fivestar source code is a bit quirky. It doesn't really provide an opportunity to theme the widget's individual bits, as the work gets done in the function fivestar_expand(). I will provide a patch for the function, but until that gets committed, the easiest method is to use the above theme override.
Step 7: Add specific selectors into the css for the Javascript images
Back at the css file, you can now go to the section for the Javascript images, and using the new enclosing
div.fivestar-field_day_traffic div.fivestar-widget .cancel,
div.fivestar-field_day_traffic div.fivestar-widget .star {
width: 20px;
height: 20px;
}And that should do it! Refresh the browser on the node edit page, and you'll see your new images used for the Javascript widget.
Here's screenshot of Hill Bomb's images. I use little road rollers for the road quality rating, as well as the cars and flames:

There has been a suggestion that Fivestar be modified to allow this functionality to be set easily for each field, however it was marked as "won't fix" by the maintainers, for fairly sound reasons I think.
You could also argue that the custom CSS and images should be in the theme folder rather than the widget's folder, but I'll leave that issue alone. Just don't forget about the custom widget when you upgrade the Fivestar module... ;)












Excellent Tutorial
Great tutorial! Can I entice you to give this as a lesson in the Drupal Dojo sometime? Hit me up via email or in IRC (josh_k) if you're game. :)
Fantastic
Thanks for writing up this excellent tutorial. Step 6 sounds like something we could improve upon in Fivestar, that class should be added for you, especially if I'm going around telling people to buckle-down and learn CSS ;)
Anyway, fantastic. Thanks again!
Thanks, my pleasure! I have
Thanks, my pleasure! I have tried to patch the function as I mentioned in the tutorial, but I can't quite work out the best place. Will create an issue sometime soon.
Multi-criteria Fivestar ratings?
Great tutorial! I was wondering if you could also give me some ideas as to how to set up a multi-criteria Fivestar rating system like you have (daytime traffic, night time traffic, etc.). Thanks!
CCK
Just use CCK - add each "criteria" as a different field.
Fantastic post. Bookmarked
Fantastic post. Bookmarked this site and emailed it to a few friends, your post was that great, keep it up.
Very good post, thanks a lot.
Very good post, thanks a lot.
Multi-criteria Fivestar ratings?
Thanks for this post. Great !!
different icons for votes
How can I go one step ahead, and display different icons for 1 star, 2 star & so on?
What I need is a 2 star field with 1st icon as thumbs up & second one as thumbs down.
Good Job
markers are very good rating. css and php code with the support of your presentation very successful.
great tutorial - can you tell me how to use rules with fivestar
I'm a grad student at UNC and working on a (really cool) project to start an undergraduate writing journal and peer review mechanism powered by Drupal. We're in the final stages and trying to hit our go live deadline.
Right now, we have a content type "submission" created where authors will submit articles for review. We are using fivestar to allow the community to rate the submissions and help us filter out the best ones to promote them for editorial review. This is where we are running into problems. Workflow is set up and working. But I can't manage to get rules to work with fivestar as the event to trigger a workflow action.
Any ideas how to do this?
Fivestar 6.x-2.x-dev and different icons
Hi Mark, very good work.
I ho pe I can use it in my situation :)
I'm trying to implement the solution you described with the following configuration:
+ Drupal 6.19
+ Fivestar 6.x-2.x-dev
I suppose that the function that I need to override is actually "theme_fivestar" :
http://drupalcontrib.org/api/function/theme_fivestar/5
I've tried to setup all the necessary, but no modifications to the resulting fivestar css; it seems like the function is never called. I've checked all many times, Drupal cache is deactivated, with no results. I'm missing something?
It's no clear for me the cascade of functions calling for generating this damn css.
I would be grateful if you could help me
PS Probably an error in Section 4:
div.field-field-day-traffic
would be
div.fivestar-field-day-traffic
Post new comment