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:

ratings-1.png

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!

cars.png

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.

delete-cars.png

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

, specify the field that will use the new images. My field was called field_day_traffic, so here's an example of the modifications:

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:

final-ratings.png

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

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options