2
// $Id: fivestar.module,v 1.13.2.56 2009/04/30 20:58:31 quicksketch Exp $
6
* A simple n-star voting widget, usable in other forms.
9
function fivestar_help($path, $arg) {
12
case 'admin/settings/fivestar':
13
$output = t('This page is used to configure site-wide features of the fivestar module. To setup fivestar to rate content:');
15
t('Configure site-wide settings for fivestar below.'),
16
t('Go to <a href="!types">admin/content/types</a> and edit the type you would like to rate.', array('!types' => url('admin/content/types'))),
17
t('On the settings page for the content type, a set of options is available for fivestar, where you can enable rating for that type and set rating options.'),
19
$output .= theme('item_list', $steps, NULL, 'ol');
25
* Implementation of hook_menu().
27
* Provides a callback url where votes can be submitted by the client-side
30
function fivestar_menu() {
32
$items['admin/settings/fivestar'] = array(
33
'title' => 'Fivestar',
34
'description' => 'Configure site-wide widgets used for Fivestar rating.',
35
'page callback' => 'drupal_get_form',
36
'page arguments' => array('fivestar_settings'),
37
'access callback' => 'user_access',
38
'access arguments' => array('administer site configuration'),
39
'type' => MENU_NORMAL_ITEM,
40
'file' => 'fivestar_color.inc',
42
$items['fivestar/preview/color'] = array(
43
'page callback' => 'fivestar_preview_color',
44
'access callback' => 'user_access',
45
'access arguments' => array('administer site configuration'),
46
'type' => MENU_CALLBACK,
47
'file' => 'fivestar_color.inc',
49
$items['fivestar/preview/node'] = array(
50
'page callback' => 'fivestar_preview',
51
'access callback' => 'user_access',
52
'access arguments' => array('administer content types'),
53
'type' => MENU_CALLBACK,
55
$items['fivestar/vote'] = array(
56
'page callback' => 'fivestar_vote',
57
'access callback' => 'user_access',
58
'access arguments' => array('rate content'),
59
'type' => MENU_CALLBACK,
65
* Implementation of hook_init().
67
* These includes do not need to be loaded for cached pages.
69
function fivestar_init() {
70
if (module_exists('content')) {
71
module_load_include('inc', 'fivestar', 'fivestar_field');
74
// Add necessary CSS and JS.
75
// TODO: These shouldn't be loaded on every page, but block caching omits
76
// CSS and JS files that would be otherwise added.
82
* Implementation of hook_perm().
84
* Exposes permissions for rating content, viewing aggregate ratings, and using PHP
85
* snippets when configuring fivestar CCK fields.
87
function fivestar_perm() {
88
return array('rate content', 'use PHP for fivestar target');
92
* Implementation of hook_theme().
94
function fivestar_theme() {
96
// Fivestar theme functions.
98
'arguments' => array('element' => NULL),
100
'fivestar_node_type_form' => array(
101
'arguments' => array('form' => NULL),
103
'fivestar_preview' => array(
104
'arguments' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL, 'labels_enable' => TRUE, 'labels' => array()),
106
'fivestar_preview_widget' => array(
107
'arguments' => array('css_file' => NULL),
109
'fivestar_preview_wrapper' => array(
110
'arguments' => array('content' => NULL, 'type' => 'direct'),
112
'fivestar_select' => array(
113
'arguments' => array('element' => NULL),
115
'fivestar_settings' => array(
116
'arguments' => array('form' => NULL),
118
'fivestar_static' => array(
119
'arguments' => array('rating' => NULL, 'stars' => 5),
121
'fivestar_static_element' => array(
122
'arguments' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL),
124
'fivestar_summary' => array(
125
'arguments' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5),
127
'fivestar_widget' => array(
128
'arguments' => array('form' => NULL),
130
// fivestar_color.inc.
131
'fivestar_color_form' => array(
132
'arguments' => array('form' => NULL),
134
// fivestar_field.inc.
135
'fivestar_formatter_default' => array(
136
'arguments' => array('element' => NULL),
138
'fivestar_formatter_rating' => array(
139
'arguments' => array('element' => NULL),
141
'fivestar_formatter_percentage' => array(
142
'arguments' => array('element' => NULL),
148
* Implementation of hook_form_alter().
150
* Adds fivestar enaable and position to the node-type configuration form.
152
function fivestar_form_alter(&$form, &$form_state, $form_id) {
153
if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
154
// Goofy hack to get the buttons at the end of the array.
155
$form['workflow']['#weight'] = isset($form['workflow']['#weight']) ? $form['workflow']['#weight'] + 1 : 1;
156
$form['submit']['#weight'] = isset($form['submit']['#weight']) ? $form['submit']['#weight'] + 1 : 1;
157
$form['delete']['#weight'] = isset($form['delete']['#weight']) ? $form['delete']['#weight'] + 1 : 1;
159
$form['fivestar'] = array(
160
'#type' => 'fieldset',
161
'#title' => t('Fivestar ratings'),
162
'#collapsible' => TRUE,
163
'#collapsed' => !variable_get('fivestar_'. $form['#node_type']->type, 0),
164
'#description' => t('To rate this content, enable Fivestar rating below. These settings will be used for both comments (if available) and direct rating.'),
165
'#theme' => 'fivestar_node_type_form',
166
'#attributes' => array('id' => 'fivestar-node-type-form'),
169
$form['fivestar']['fivestar'] = array(
170
'#type' => 'checkbox',
171
'#title' => t('Enable Fivestar rating'),
172
'#default_value' => variable_get('fivestar_'. $form['#node_type']->type, 0),
173
'#return_value' => 1,
177
$form['fivestar']['fivestar_stars'] = array(
179
'#title' => t('Number of stars'),
180
'#options' => drupal_map_assoc(range(1, 10)),
181
'#default_value' => variable_get('fivestar_stars_'. $form['#node_type']->type, 5),
186
$form['fivestar']['labels'] = array(
187
'#type' => 'fieldset',
188
'#title' => t('Star Labels'),
189
'#collapsible' => TRUE,
190
'#collapsed' => TRUE,
191
'#description' => t('These star labels appear as the link title when javascript is enabled as well as the select list options when javascript is disabled.'),
194
$form['fivestar']['labels']['fivestar_labels_enable'] = array(
195
'#type' => 'checkbox',
196
'#title' => t('Display labels on mouse over'),
197
'#default_value' => variable_get('fivestar_labels_enable_'. $form['#node_type']->type, 1),
198
'#return_value' => 1,
200
'#description' => t('When enabled, the star labels will dynamically appear underneath the stars as the user hovers over each star to provide a more descriptive qualitative meaning for each star value.'),
203
// Create the Mouseover text forms for each of the rating options
204
// This form depends on the number of stars, and these extra textfields will be hidden with javascript
205
$star_count = variable_get('fivestar_stars_'. $form['#node_type']->type, 5);
206
$labels = variable_get('fivestar_labels_'. $form['#node_type']->type, array());
207
for ($n = 0; $n <= 10; $n++) {
208
if ($star_count == 5 && $n <= 5) {
209
// If the default 5 stars are chosen, then use these five default label values.
210
$default_labels = array(t('Cancel rating'), t('Poor'), t('Okay'), t('Good'), t('Great'), t('Awesome'));
213
$default_labels[$n] = t('Cancel rating');
216
$default_labels[$n] = t('Give it @star/@count');
218
$form['fivestar']['labels']['fivestar_label_'. $n] = array(
219
'#type' => 'textfield',
220
'#title' => $n > 0 ? t('Star @star label', array('@star' => $n)) : t('Cancel label'),
221
'#default_value' => isset($labels[$n]) ? $labels[$n] : $default_labels[$n],
222
'#prefix' => '<div id="fivestar-label-'. $n .'" class="fivestar-label">',
223
'#suffix' => '</div>',
228
$form['fivestar']['direct'] = array(
229
'#type' => 'fieldset',
230
'#title' => t('Direct rating widget'),
231
'#collapsible' => FALSE,
232
'#description' => t('These settings allow you to display a rating widget to your users while they are viewing content of this type. Rating will immediately register a vote for that piece of content.'),
236
$form['fivestar']['direct']['fivestar_style'] = array(
238
'#title' => t('Star display style'),
239
'#default_value' => variable_get('fivestar_style_'. $form['#node_type']->type, 'average'),
241
'average' => t('Display average vote value'),
242
'user' => t('Display user vote value'),
243
'smart' => t('User vote if available, average otherwise'),
244
'dual' => t('Both user and average vote'),
248
$form['fivestar']['direct']['fivestar_text'] = array(
250
'#title' => t('Text display style'),
251
'#default_value' => variable_get('fivestar_text_'. $form['#node_type']->type, 'dual'),
253
'none' => t('Display no text beneath stars'),
254
'average' => t('Current average in text'),
255
'user' => t('User current vote in text'),
256
'smart' => t('User vote if available, average otherwise'),
257
'dual' => t('Both user and average vote'),
261
$form['fivestar']['direct']['fivestar_title'] = array(
262
'#type' => 'checkbox',
263
'#title' => t('Show widget title'),
264
'#default_value' => variable_get('fivestar_title_'. $form['#node_type']->type, 1),
265
'#return_value' => 1,
268
$form['fivestar']['direct']['fivestar_feedback'] = array(
269
'#type' => 'checkbox',
270
'#title' => t('Enable feedback during vote saving and deletion'),
271
'#default_value' => variable_get('fivestar_feedback_'. $form['#node_type']->type, 1),
275
$form['fivestar']['direct']['fivestar_unvote'] = array(
276
'#type' => 'checkbox',
277
'#title' => t('Allow users to undo their votes'),
278
'#default_value' => variable_get('fivestar_unvote_'. $form['#node_type']->type, 0),
279
'#return_value' => 1,
282
$form['fivestar']['direct']['fivestar_position_teaser'] = array(
284
'#title' => t('Teaser display'),
285
'#default_value' => variable_get('fivestar_position_teaser_'. $form['#node_type']->type, 'hidden'),
287
'above' => t('Clickable widget above teaser'),
288
'below' => t('Clickable widget below teaser'),
289
'above_static' => t('Static display above teaser'),
290
'below_static' => t('Static display below teaser'),
291
'link' => t('Teaser link to full node widget'),
292
'hidden' => '<'. t('hidden') .'>',
296
$form['fivestar']['direct']['fivestar_position'] = array(
298
'#title' => t('Full node display'),
299
'#default_value' => variable_get('fivestar_position_'. $form['#node_type']->type, 'below'),
301
'above' => t('Clickable widget above node body'),
302
'below' => t('Clickable widget below node body'),
303
'above_static' => t('Static display above node body'),
304
'below_static' => t('Static display below node body'),
305
'hidden' => '<'. t('hidden') .'>',
309
$form['fivestar']['direct']['fivestar_direct_preview'] = array(
311
'#title' => t('Direct rating widget preview'),
314
$form['fivestar']['direct']['fivestar_style']['#default_value'],
315
$form['fivestar']['direct']['fivestar_text']['#default_value'],
316
$form['fivestar']['fivestar_stars']['#default_value'],
317
$form['fivestar']['direct']['fivestar_unvote']['#default_value'],
318
$form['fivestar']['direct']['fivestar_title']['#default_value'] ? NULL : FALSE,
319
$form['fivestar']['labels']['fivestar_labels_enable']['#default_value'],
320
variable_get('fivestar_labels_'. $form['#node_type']->type, array())
323
if (!$form['fivestar']['fivestar']['#default_value']) {
324
$form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', '');
327
$form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', $form['fivestar']['direct']['fivestar_direct_preview']['#value']);
330
$form['#submit'][] = 'fivestar_node_type_form_submit';
335
* Additional submit handler for the node type form.
337
function fivestar_node_type_form_submit($form, &$form_state) {
338
// Do not save any fivestar variables if fivestar is disabled.
339
if (isset($form_state['values']['fivestar']) && $form_state['values']['fivestar'] === 0) {
340
foreach ($form_state['values'] as $key => $value) {
341
if (strpos($key, 'fivestar') === 0) {
342
variable_del($key .'_'. $form_state['values']['type']);
346
// Merge labels into a single variable.
348
for ($n = 0; $n <= 10; $n++) {
349
$labels[] = $form_state['values']['fivestar_label_'. $n];
350
variable_del('fivestar_label_'. $n .'_'. $form_state['values']['type']);
352
variable_del('fivestar_labels_'. $form_state['values']['type']);
353
if ($form_state['values']['fivestar_labels_enable']) {
354
variable_set('fivestar_labels_'. $form_state['values']['type'], $labels);
359
* Theme function to add the Fivestar preview to the node type form.
361
function theme_fivestar_node_type_form($form) {
362
drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar-admin.js');
363
drupal_add_js(array('fivestar' => array('preview_url' => url('fivestar/preview/node'))), 'setting');
364
drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar-admin.css', 'module', 'all', FALSE);
367
$output .= drupal_render($form['fivestar']);
368
$output .= drupal_render($form['fivestar_stars']);
371
$output .= drupal_render($form['labels']);
373
// Direct rating settings form.
375
$direct .= '<div id="fivestar-direct-form">';
376
$direct .= drupal_render($form['direct']['fivestar_style']);
377
$direct .= drupal_render($form['direct']['fivestar_text']);
378
$direct .= drupal_render($form['direct']['fivestar_title']);
379
$direct .= drupal_render($form['direct']['fivestar_unvote']);
380
$direct .= drupal_render($form['direct']['fivestar_feedback']);
381
$direct .= drupal_render($form['direct']['fivestar_position_teaser']);
382
$direct .= drupal_render($form['direct']['fivestar_position']);
384
$direct .= '<div id="fivestar-direct-preview">';
385
$direct .= drupal_render($form['direct']['fivestar_direct_preview']);
388
$form['direct']['#children'] = $direct;
389
$output .= drupal_render($form['direct']);
391
// Comment settings form.
392
if (module_exists('fivestar_comment')) {
394
$comment .= '<div id="fivestar-comment-form">';
395
$comment .= drupal_render($form['comment']['fivestar_comment']);
396
$comment .= '</div>';
397
$comment .= '<div id="fivestar-comment-preview">';
398
$comment .= drupal_render($form['comment']['fivestar_comment_preview']);
399
$comment .= '</div>';
401
$form['comment']['#children'] = $comment;
402
$output .= drupal_render($form['comment']);
405
// Any remaining cruft (should be empty).
406
$output .= drupal_render($form);
411
* Implementation of hook_node_types().
413
function fivestar_node_type($op, $info) {
415
$variables = array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser');
417
// Be responsible and cleanup unneeded variables.
418
if ($op == 'delete') {
419
foreach ($variables as $variable) {
420
variable_del($variable .'_'. $type);
423
// When changing the type name, update the variables.
424
elseif ($op == 'update' && !empty($info->old_type) && $info->old_type != $info->type) {
425
foreach ($variables as $variable) {
426
$value = variable_get($variable .'_'. $type, -1);
428
variable_del($variable .'_'. $type);
429
variable_set($variable .'_'. $type, $value);
436
* Callback function for admin/settings/fivestar. Display the settings form.
438
function fivestar_settings() {
441
$form['widget'] = array(
443
'#type' => 'fieldset',
444
'#title' => t('Widget display'),
445
'#description' => t('Choose a widget set to be used on your site. Widgets supporting custom colors can be further customized by adjusting the color scheme.'),
449
$widgets = module_invoke_all('fivestar_widgets');
450
$classic_widgets = array();
451
$color_widgets = array();
452
foreach ($widgets as $path => $name) {
453
$directory = dirname($path);
454
$matches = file_scan_directory($directory, '-template.');
455
if (empty($matches)) {
456
$classic_widgets[$path] = $name;
459
$color_widgets[$path] = $name;
463
// If using a color widget, set the default value to the original path.
464
$default_value = variable_get('fivestar_widget', 'default');
465
foreach ($color_widgets as $path => $name) {
466
if (basename($path) == basename($default_value)) {
467
$default_value = $path;
471
$form['widget']['fivestar_widget'] = array(
473
'#options' => array('default' => t('Default')) + $classic_widgets + $color_widgets,
474
'#default_value' => $default_value,
475
'#attributes' => array('class' => 'fivestar-widgets'),
478
$form['widget']['fivestar_color_widget'] = array(
480
'#title' => t('Custom color widgets'),
481
'#options' => $color_widgets,
482
'#attributes' => array('class' => 'fivestar-widgets'),
485
$form['color'] = fivestar_color_form();
486
$form['#validate'][] = 'fivestar_color_form_validate';
487
$form['#submit'][] = 'fivestar_color_form_submit';
488
$form['#submit'][] ='fivestar_settings_submit';
490
$form['submit'] = array(
492
'#value' => t('Save configuration'),
499
function fivestar_settings_submit($form, &$form_state) {
500
variable_set('fivestar_widget', $form_state['values']['fivestar_widget']);
503
function theme_fivestar_settings($form) {
504
drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar-admin.css', 'module', 'all', FALSE);
505
drupal_set_title(t('Fivestar Settings'));
508
$form['widget']['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':<br />'. theme('fivestar_preview_widget', 'default');
510
// Preview for each classic widget.
511
foreach (element_children($form['widget']['fivestar_widget']) as $widget_key) {
512
if ($widget_key != 'default') {
513
$form['widget']['fivestar_widget'][$widget_key]['#description'] = $form['widget']['fivestar_widget'][$widget_key]['#title'] .' '. t('Preview') .':<br />'. theme('fivestar_preview_widget', $widget_key);
517
// Preview for each color-enabled widget.
518
foreach (element_children($form['widget']['fivestar_color_widget']) as $widget_key) {
519
$form['widget']['fivestar_color_widget'][$widget_key] = $form['widget']['fivestar_widget'][$widget_key];
520
$form['widget']['fivestar_color_widget'][$widget_key]['#description'] = $form['widget']['fivestar_color_widget'][$widget_key]['#title'] .' '. t('Preview') .':<br />'. theme('fivestar_preview_widget', $widget_key);
521
unset($form['widget']['fivestar_widget'][$widget_key]);
524
// Add the new styles to the page.
525
drupal_set_html_head("<style type=\"text/css\" media=\"all\">\n". fivestar_get_inline_css() ."</style>");
527
$form['widget']['fivestar_widget']['#attributes']['class'] .= ' clear-block';
528
$form['widget']['fivestar_color_widget']['#attributes']['class'] .= ' fivestar-color-widgets clear-block';
530
return drupal_render($form);
533
function theme_fivestar_preview_widget($css_file) {
534
static $default_css_added = FALSE;
536
// Add the default CSS to the page to ensure the defaults take precedence.
537
if (!$default_css_added) {
538
$css = file_get_contents(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');
539
// Prepend the classes with the unique widget div.
540
$css = preg_replace('/((div)?\.fivestar-widget)/', 'div.fivestar-widgets $1', $css);
541
// Update relative URLs with absolute locations.
542
$css = preg_replace('/url\(\.\.\/(.*?)\)/', 'url('. base_path() . drupal_get_path('module', 'fivestar') .'/$1)', $css);
543
fivestar_add_inline_css('default', $css);
544
$default_css_added = TRUE;
547
// Add widget specific CSS to the page.
548
$widget_name = str_replace('.css', '', basename($css_file));
549
$widget_path = dirname($css_file);
550
if ($widget_name != 'default') {
551
$css = file_get_contents($css_file);
552
// Prepend the classes with the unique widget div.
553
$css = preg_replace('/((div)?\.fivestar-widget)/', 'div#fivestar-preview-'. $widget_name .' $1', $css);
554
// Update relative URLs with absolute locations.
555
$css = preg_replace('/url\((.*?)\)/', 'url('. base_path() . $widget_path .'/$1)', $css);
556
fivestar_add_inline_css($widget_name, $css);
560
$form_state = array();
561
$form['vote'] = array(
562
'#type' => 'fivestar',
564
'#auto_submit' => FALSE,
565
'#allow_clear' => TRUE,
568
$form = form_builder('fivestar_preview', $form, $form_state);
570
$output = '<div class="fivestar-star-preview" id="fivestar-preview-'. $widget_name .'">';
571
$output .= drupal_render($form);
578
* Callback function for fivestar/preview/color.
580
* Outputs a dynamically generated star or cancel png.
582
function fivestar_preview_color() {
583
$args = func_get_args();
584
// Remove query string if it gets passed in as argument.
585
$filename = preg_replace('/\?.*$/', '', array_pop($args));
586
$type = array_pop($args);
587
$widget = array_pop($args);
589
// Convert args to our color scheme.
590
$color_scheme = array(
593
'hover1' => $args[2],
594
'hover2' => $args[3],
600
// Find the source location of the desired widget.
601
$widgets = module_invoke_all('fivestar_widgets');
602
foreach ($widgets as $key => $name) {
603
if (drupal_strtolower($name) == $widget) {
604
$source_directory = str_replace($widget .'.css', '', $key);
609
// Generate the requested image and exit.
610
$image = _fivestar_color_render($source_directory . str_replace('.png', '-template.png', $filename), $color_scheme, $type);
611
drupal_set_header('Content-type: image/png');
612
drupal_set_header("Expires: ". gmdate("D, d M Y H:i:s", time() + 300) ." GMT");
613
drupal_set_header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
614
drupal_set_header("Cache-Control: max-age=300");
620
* Callback function for fivestar/preview/node. Outputs a JSON page containing
621
* a Fivestar preview of a node rating widget.
623
function fivestar_preview() {
624
// Perform a few basic security checks.
625
$style = check_plain($_POST['style']);
626
$text = check_plain($_POST['text']);
627
$stars = (int)$_POST['stars'];
628
$unvote = (boolean)$_POST['unvote'];
629
$title = (boolean)$_POST['title'];
630
$feedback_enable = (boolean)$_POST['feedback'];
631
$labels_enable = (boolean)$_POST['labels_enable'];
632
$labels = (array)$_POST['labels'];
633
foreach ($labels as $key => $label) {
634
$labels[$key] = filter_xss_admin($label);
637
$output = theme('fivestar_preview', $style, $text, $stars, $unvote, $title ? NULL : FALSE, $feedback_enable, $labels_enable, $labels);
638
drupal_set_header('Content-Type: text/javascript; charset=utf-8');
639
print drupal_to_js(array('status' => TRUE, 'data' => $output));
642
function theme_fivestar_preview($style = NULL, $text = NULL, $stars = NULL, $unvote = NULL, $title = NULL, $feedback_enable = TRUE, $labels_enable = TRUE, $labels = array()) {
650
'allow_clear' => $unvote,
654
'autosubmit' => FALSE,
655
'feedback_enable' => $feedback_enable,
656
'labels_enable' => $labels_enable,
660
$form = drupal_get_form('fivestar_custom_widget', $values, $settings);
661
// This regex is sadly necessary because having duplicate form_tokens or
662
// form_id elements can cause the content type form to choke. Forms inside of
663
// forms is also frowned upon, so this removes the wrapping form tag as well.
664
$form = str_replace(array('<form', '</form>'), array('<div', '</div>'), $form);
665
$form = preg_replace('/( method=".*?")|( action=".*?")|(<input.*?name="(form_token|form_id|destination|form_build_id)".*?\/>)/', '', $form);
669
function theme_fivestar_preview_wrapper($content, $type = 'direct') {
670
return '<div class="fivestar-preview fivestar-preview-'. $type .'">'. $content .'</div>';
674
* Callback function for fivestar/vote.
677
* A content-type to log the vote to. 'node' is the most common.
679
* A content id to log the vote to. This would be a node ID, a comment ID, etc.
681
* Multi-axis tag to allow multiple votes per node. 'vote' is the most common.
683
* A value from 1-100, representing the vote cast for the content.
685
* An XML chunk containing the results of the vote, for use by the client-side
688
function fivestar_vote($type, $cid, $tag, $value) {
689
drupal_set_header("Content-Type: text/xml");
691
$output .= '<?xml version="1.0" encoding="UTF-8"?>';
693
// Rebuild the #auto_submit_path that was used as the token seed.
694
$path = preg_replace('/\/'. $value .'$/', '', $_GET['q']);
695
if (!isset($_GET['token']) || !fivestar_check_token($_GET['token'], $path)) {
696
$output .= '<xml><error>'. t('Invalid token') .'</error></xml>';
700
$result = _fivestar_cast_vote($type, $cid, $value, $tag, NULL, TRUE);
701
votingapi_recalculate_results($type, $cid);
703
if ($type == 'node') {
704
$node = node_load($cid);
706
$stars = variable_get('fivestar_stars_'. (!isset($node) ? 'default' : $node->type), 5);
707
$feedback_enable = variable_get('fivestar_feedback_'. (!isset($node) ? 'default' : $node->type), 1);
709
$output .= '<xml><result>';
711
if (count($result)) {
712
foreach ($result as $data) {
713
if ($data['tag'] == $tag) {
714
$output .= '<'. $data['function'] .'>'. $data['value'] .'</'. $data['function'] .'>';
715
$summary[$data['tag']][$data['function']] = $data['value'];
719
$output .= '<summary>';
720
$output .= '<average><![CDATA['. theme('fivestar_summary', NULL, $summary[$tag]['average'], NULL, $stars, $feedback_enable) .']]></average>';
721
$output .= '<average_count><![CDATA['. theme('fivestar_summary', NULL, $summary[$tag]['average'], $summary[$tag]['count'], $stars, $feedback_enable) .']]></average_count>';
722
$output .= '<user><![CDATA['. theme('fivestar_summary', $value, NULL, NULL, $stars, $feedback_enable) .']]></user>';
723
$output .= '<user_count><![CDATA['. theme('fivestar_summary', $value, NULL, $summary[$tag]['count'], $stars, $feedback_enable) .']]></user_count>';
724
$output .= '<combo><![CDATA['. theme('fivestar_summary', $value, $summary[$tag]['average'], $summary[$tag]['count'], $stars, $feedback_enable) .']]></combo>';
725
$output .= '<count><![CDATA['. theme('fivestar_summary', NULL, NULL, $summary[$tag]['count'], $stars, $feedback_enable) .']]></count>';
726
$output .= '</summary>';
727
$output .= '</result>';
730
$output .= '<value>'. $value .'</value>';
731
$output .= '<type>'. $type .'</type>';
732
$output .= '<id>'. $cid .'</id>';
733
$output .= '<tag>'. $tag .'</tag>';
734
$output .= '</vote></xml>';
736
drupal_set_header("Content-Type: text/xml");
741
* Internal function to handle vote casting, flood control, XSS, IP based
744
function _fivestar_cast_vote($type, $cid, $value, $tag = NULL, $uid = NULL, $result = FALSE, $skip_validation = FALSE) {
746
$tag = empty($tag) ? 'vote' : $tag;
747
$uid = empty($uid) ? $user->uid : $uid;
749
// Bail out if the user's trying to vote on an invalid object.
750
if (!$skip_validation && !fivestar_validate_target($type, $cid, $uid)) {
754
// Sanity-check the incoming values.
755
if (is_numeric($cid) && is_numeric($value)) {
760
// Get the user's current vote.
761
$criteria = array('content_type' => $type, 'content_id' => $cid, 'tag' => $tag, 'uid' => $uid);
762
// Get the unique identifier for the user (IP Address if anonymous).
763
$user_criteria = votingapi_current_user_identifier();
764
$user_votes = votingapi_select_votes($criteria + $user_criteria);
767
votingapi_delete_votes($user_votes);
770
$votes = $criteria += array('value' => $value);
771
votingapi_set_votes($votes, $user_votes);
773
return fivestar_get_votes($type, $cid, $tag, $uid);
777
function fivestar_get_votes($type, $cid, $tag = 'vote', $uid = NULL) {
784
'content_type' => $type,
785
'content_id' => $cid,
786
'value_type' => 'percent',
791
'average' => array(),
796
$results = votingapi_select_results($criteria);
797
foreach ($results as $result) {
798
if ($result['function'] == 'average') {
799
$votes['average'] = $result;
801
if ($result['function'] == 'count') {
802
$votes['count'] = $result;
806
$user_vote = votingapi_select_votes($criteria += array('uid' => $uid));
808
$votes['user'] = $user_vote[0];
809
$votes['user']['function'] = 'user';
813
// If the user is anonymous, we never bother loading their existing votes.
814
// Not only would it be hit-or-miss, it would break page caching. Safer to always
815
// show the 'fresh' version to anon users.
816
$votes['user'] = array('value' => 0);
823
* Check that an item being voted upon is a valid vote.
826
* Type of target (currently only node is supported).
828
* Identifier within the type (in this case nid).
830
* The user trying to cast the vote.
834
function fivestar_validate_target($type, $id, $uid = NULL) {
835
if (!isset($account)) {
836
$uid = $GLOBALS['user']->uid;
839
$access = module_invoke_all('fivestar_access', $type, $id, $uid);
840
foreach ($access as $result) {
841
if ($result == TRUE) {
844
if ($result === FALSE) {
851
* Implementation of hook_fivestar_access().
853
* This hook is called before every vote is cast through Fivestar. It allows
854
* modules to allow voting on any type of content, such as nodes, users, or
855
* comments, even though only nodes are supported by Fivestar directly.
858
* Type of target (currently only node is supported).
860
* Identifier within the type (in this case nid).
862
* The user trying to cast the vote.
864
* @return boolean or NULL
865
* Returns TRUE if voting is supported on this object.
866
* Returns NULL if voting is not supported on this object by this module.
867
* If needing to absolutely deny all voting on this object, regardless
868
* of permissions defined in other modules, return FALSE. Note if all
869
* modules return NULL, stating no preference, then access will be denied.
871
function fivestar_fivestar_access($type, $id, $account) {
872
if ($type == 'node' && $node = node_load($id)) {
873
if (variable_get('fivestar_'. $node->type, 0)) {
880
* Implementation of hook_fivestar_widgets().
882
* This hook allows other modules to create additional custom widgets for
883
* the fivestar module.
886
* An array of key => value pairs suitable for inclusion as the #options in a
887
* select or radios form element. Each key must be the location of a css
888
* file for a fivestar widget. Each value should be the name of the widget.
890
function fivestar_fivestar_widgets() {
891
$widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
892
$files = file_scan_directory($widgets_directory, '\.css$');
895
foreach ($files as $file) {
896
if (strpos($file->filename, '-rtl.css') === FALSE) {
897
$widgets[$file->filename] = drupal_ucfirst(str_replace('-color', '', $file->name));
904
* Implementation of hook_nodeapi().
906
* Adds the fievestar widget to the node view.
908
function fivestar_nodeapi(&$node, $op, $teaser, $page) {
911
if (!in_array($node->build_mode, array(NODE_BUILD_PREVIEW, NODE_BUILD_SEARCH_INDEX)) && !isset($node->modr8_form_teaser) && variable_get('fivestar_'. $node->type, 0)) {
913
$position = variable_get('fivestar_position_teaser_'. $node->type, 'above');
916
$position = variable_get('fivestar_position_'. $node->type, 'above');
921
if (user_access('rate content')) {
922
$node->content['fivestar_widget'] = array(
923
'#value' => fivestar_widget_form($node),
924
'#weight' => $position == 'above' ? -10 : 50,
927
} // Fall through to static if not allowed to rate.
928
$position .= '_static';
931
$stars = variable_get('fivestar_stars_'. $node->type, 5);
932
$node->content['fivestar_widget'] = array(
933
'#value' => fivestar_static('node', $node->nid, NULL, $node->type),
934
'#weight' => strpos($position, 'above') === 0 ? -10 : 50,
948
* Implementation of hook_link().
950
* Add a "rate" link to node teaser.
952
function fivestar_link($type, $node = NULL, $teaser = FALSE) {
954
if ($type == "node" && $teaser) {
955
if (variable_get('fivestar_position_teaser_'. $node->type, 'above') == "link") {
956
$links['rate'] = array(
957
'title' => t('Rate'),
958
'href' => 'node/'. $node->nid,
959
'fragment' => 'fivestar-form-node-'. $node->nid,
960
'attributes' => array('title' => t('Rate this @type', array('@type' => node_get_types('name', $node->type)))),
968
function fivestar_block($op = 'list', $delta = 0, $edit = array()) {
972
$blocks[0]['info'] = t('Fivestar: Rate this node');
976
if (user_access('access content') && user_access('rate content')) {
977
if (arg(0) == 'node' && is_numeric(arg(1)) && (arg(2) == '' || arg(2) == 'view')) {
978
$node = node_load(arg(1));
979
if (fivestar_validate_target('node', $node->nid)) {
980
$block['subject'] = t('Rate This');
981
$block['content'] = fivestar_widget_form($node);
990
function fivestar_widget_form($node) {
991
return drupal_get_form('fivestar_form_node_'. $node->nid, 'node', $node->nid);
995
* Get a private token used to protect links from spoofing - CSRF.
997
function fivestar_get_token($seed) {
998
return drupal_get_token($seed);
1002
* Check to see if a token value matches the specified node.
1004
function fivestar_check_token($token, $seed) {
1005
return drupal_get_token($seed) == $token;
1009
* Implementation of hook_forms().
1011
* This is necessary when multiple fivestar forms appear on the same page, each
1012
* requiring a separate form_id, but all using the same underlying callbacks.
1014
function fivestar_forms($form_id, $args) {
1015
if (strpos($form_id, 'fivestar_form') !== FALSE) {
1016
if ($form_id == 'fivestar_form_'. $args[0] .'_'. $args[1]) {
1017
$forms[$form_id] = array('callback' => 'fivestar_form');
1024
* Create the fivestar form for the current item.
1025
* Note that this is not an implementation of hook_form(). We should probably
1026
* change the function to reflect that.
1028
function fivestar_form(&$form_state, $content_type, $content_id) {
1031
if ($content_type == 'node') {
1032
if (is_numeric($content_id)) {
1033
$node = node_load($content_id);
1041
$votes = fivestar_get_votes($content_type, $content_id);
1044
'user' => isset($votes['user']['value']) ? $votes['user']['value'] : 0,
1045
'average' => isset($votes['average']['value']) ? $votes['average']['value'] : 0,
1046
'count' => isset($votes['count']['value']) ? $votes['count']['value'] : 0,
1050
'stars' => variable_get('fivestar_stars_'. $node->type, 5),
1051
'allow_clear' => variable_get('fivestar_unvote_'. $node->type, FALSE),
1052
'style' => variable_get('fivestar_style_'. $node->type, 'average'),
1053
'text' => variable_get('fivestar_text_'. $node->type, 'dual'),
1054
'content_type' => $content_type,
1055
'content_id' => $content_id,
1057
'autosubmit' => TRUE,
1058
'title' => variable_get('fivestar_title_'. $node->type, 1) ? NULL : FALSE,
1059
'feedback_enable' => variable_get('fivestar_feedback_'. $node->type, 1),
1060
'labels_enable' => variable_get('fivestar_labels_enable_'. $node->type, 1),
1061
'labels' => variable_get('fivestar_labels_'. $node->type, array()),
1064
return fivestar_custom_widget($form_state, $values, $settings);
1067
function fivestar_static($content_type, $content_id, $tag = 'vote', $node_type = NULL) {
1071
'content_type' => $content_type,
1072
'content_id' => $content_id,
1073
'value_type' => 'percent',
1077
$votes = fivestar_get_votes($content_type, $content_id, $tag);
1079
if ($content_type == 'node') {
1080
// Content type should always be passed to avoid this node load.
1081
if (!isset($node_type)) {
1082
$node = node_load($content_id);
1083
$node_type = $node->type;
1086
$star_display = variable_get('fivestar_style_'. $node_type, 'average');
1087
$text_display = variable_get('fivestar_text_'. $node_type, 'dual');
1088
$title_display = variable_get('fivestar_title_'. $node_type, 1);
1090
$stars = variable_get('fivestar_stars_'. $node_type, 5);
1091
switch ($star_display) {
1094
$star_value = $votes['average']['value'];
1095
$title = $title_display ? t('Average') : NULL;
1098
$star_value = $votes['user']['value'];
1099
$title = $title_display ? t('Your rating') : NULL;
1102
$star_value = $votes['user']['value'] ? $votes['user']['value'] : $votes['average']['value'];
1103
$title = $title_display ? $votes['user']['value'] ? t('Your rating') : t('Average') : NULL;
1107
// Set all text values, then unset the unnecessary ones.
1108
$user_value = $votes['user']['value'];
1109
$average_value = $votes['average']['value'];
1110
$count_value = $votes['count']['value'];
1111
switch ($text_display) {
1116
$average_value = NULL;
1119
if ($votes['user']['value']) {
1120
$average_value = NULL;
1128
// Possibly add other content types here (comment, user, etc).
1131
$star_value = $votes['average']['value'];
1132
$user_value = $votes['user']['value'];
1133
$average_value = $votes['average']['value'];
1134
$count_value = $votes['count']['value'];
1137
$star_display = theme('fivestar_static', $star_value, $stars);
1138
$text_display = $text_display == 'none' ? NULL : theme('fivestar_summary', $user_value, $average_value, $count_value, $stars, FALSE);
1140
return theme('fivestar_static_element', $star_display, $title, $text_display);
1143
function fivestar_custom_widget(&$form_state, $values, $settings) {
1145
'#attributes' => array('class' => 'fivestar-widget'),
1146
'#redirect' => FALSE,
1147
'#theme' => 'fivestar_widget',
1149
$form['#submit'][] = 'fivestar_form_submit';
1151
if (isset($settings['content_type'])) {
1152
$form['content_type'] = array(
1153
'#type' => 'hidden',
1154
'#value' => $settings['content_type'],
1155
'#id' => $settings['content_id'] ? 'edit-content-type-'. $settings['content_id'] : NULL,
1159
if (isset($settings['content_id'])) {
1160
$form['content_id'] = array(
1161
'#type' => 'hidden',
1162
'#value' => $settings['content_id'],
1163
'#id' => $settings['content_id'] ? 'edit-content-id-'. $settings['content_id'] : NULL,
1167
$form['vote'] = array(
1168
'#type' => 'fivestar',
1169
'#stars' => $settings['stars'],
1170
'#vote_count' => $values['count'],
1171
'#vote_average' => $values['average'],
1172
'#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE,
1173
'#auto_submit_path' => (!isset($settings['autosubmit']) || $settings['autosubmit']) ? 'fivestar/vote/'. $settings['content_type'] .'/'. $settings['content_id'] .'/'. $settings['tag'] : NULL,
1174
'#allow_clear' => $settings['allow_clear'],
1175
'#content_id' => isset($settings['content_id']) ? $settings['content_id'] : NULL,
1176
'#required' => isset($settings['required']) ? $settings['required'] : FALSE,
1177
'#feedback_enable' => isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE,
1178
'#labels_enable' => isset($settings['labels_enable']) ? $settings['labels_enable'] : TRUE,
1179
'#labels' => isset($settings['labels']) ? $settings['labels'] : NULL,
1180
'#tag' => isset($settings['tag']) ? $settings['tag'] : 'vote',
1183
$form['destination'] = array(
1184
'#type' => 'hidden',
1185
'#value' => $_GET['q'],
1186
'#id' => isset($settings['content_id']) ? 'edit-destination-'. $settings['content_id'] : NULL,
1189
$form['fivestar_submit'] = array(
1190
'#type' => 'submit',
1191
'#value' => t('Rate'),
1192
'#attributes' => array('class' => 'fivestar-submit'),
1193
'#id' => isset($settings['content_id']) ? 'edit-fivestar-submit-'. $settings['content_id'] : NULL,
1196
$form['vote']['#attributes']['class'] = isset($form['vote']['#attributes']['class']) ? $form['vote']['#attributes']['class'] : '';
1197
$settings['feedback_enable'] = isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE;
1198
switch ($settings['text']) {
1200
$form['vote']['#description'] = theme('fivestar_summary', $values['user'], NULL, $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
1201
$form['vote']['#attributes']['class'] .= ' fivestar-user-text';
1204
$form['vote']['#description'] = $settings['style'] == 'dual' ? NULL : theme('fivestar_summary', NULL, $values['average'], $values['count'], $settings['stars'], $settings['feedback_enable']);
1205
$form['vote']['#attributes']['class'] .= ' fivestar-average-text';
1208
$form['vote']['#description'] = ($settings['style'] == 'dual' && !$values['user']) ? NULL : theme('fivestar_summary', $values['user'], $values['user'] ? NULL : $values['average'], $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
1209
$form['vote']['#attributes']['class'] .= ' fivestar-smart-text '. ($values['user'] ? 'fivestar-user-text' : 'fivestar-average-text');
1212
$form['vote']['#description'] = theme('fivestar_summary', $values['user'], $settings['style'] == 'dual' ? NULL : $values['average'], $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
1213
$form['vote']['#attributes']['class'] .= ' fivestar-combo-text';
1217
switch ($settings['style']) {
1219
$form['vote']['#title'] = t('Average');
1220
$form['vote']['#default_value'] = $values['average'];
1221
$form['vote']['#attributes']['class'] .= ' fivestar-average-stars';
1224
$form['vote']['#title'] = t('Your rating');
1225
$form['vote']['#default_value'] = $values['user'];
1226
$form['vote']['#attributes']['class'] .= ' fivestar-user-stars';
1229
$form['vote']['#title'] = $values['user'] ? t('Your rating') : t('Average');
1230
$form['vote']['#default_value'] = $values['user'] ? $values['user'] : $values['average'];
1231
$form['vote']['#attributes']['class'] .= ' fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars');
1234
$form['vote']['#title'] = t('Your rating');
1235
$form['vote']['#default_value'] = $values['user'];
1236
$form['vote']['#attributes']['class'] .= ' fivestar-combo-stars';
1237
$form['#attributes']['class'] .= ' fivestar-combo-stars';
1238
$static_average = theme('fivestar_static', $values['average'], $settings['stars'], $settings['tag']);
1239
if ($settings['text'] == 'none' && !$settings['labels_enable'] && !$settings['feedback_enable']) {
1240
$static_description = NULL;
1242
elseif ($settings['text'] != 'none') {
1243
$static_description = theme('fivestar_summary', NULL, $settings['text'] == 'user' ? NULL : (isset($values['average']) ? $values['average'] : 0), isset($values['count']) ? $values['count'] : 0, $settings['stars'], FALSE);
1246
$static_description = ' ';
1248
$form['average'] = array(
1249
'#type' => 'markup',
1250
'#value' => theme('fivestar_static_element', $static_average, $settings['title'] !== FALSE ? t('Average') : NULL, $static_description),
1256
// Set an over-ridding title if passed in.
1257
// An empty title won't change the default, a string will set a new title,
1258
// and title === FALSE will unset the title entirely.
1259
if (isset($settings['title'])) {
1260
if ($settings['title'] !== FALSE) {
1261
$form['vote']['#title'] = $settings['title'];
1264
unset($form['vote']['#title']);
1265
unset($form['average']['#title']);
1273
* Submit handler for the above form (non-javascript version).
1275
function fivestar_form_submit($form, &$form_state) {
1276
if ($form_state['values']['form_id'] == 'fivestar_form_'. $form_state['values']['content_type'] .'_'. $form_state['values']['content_id']) {
1278
_fivestar_cast_vote($form_state['values']['content_type'], $form_state['values']['content_id'], $form_state['values']['vote']);
1279
votingapi_recalculate_results($form_state['values']['content_type'], $form_state['values']['content_id']);
1281
// Set a message that the vote was received.
1282
if ($form_state['values']['vote'] === '0') {
1283
drupal_set_message(t('Your vote has been cleared.'));
1285
elseif (is_numeric($form_state['values']['vote'])) {
1286
drupal_set_message(t('Thank you for your vote.'));
1288
// Regenerate the page with a drupal_goto() to update the current values.
1294
* Implementation of hook_elements().
1296
* Defines 'fivestar' form element type
1298
function fivestar_elements() {
1299
$type['fivestar'] = array(
1302
'#widget' => 'stars',
1303
'#allow_clear' => FALSE,
1304
'#auto_submit' => FALSE,
1305
'#auto_submit_path' => '',
1306
'#labels_enable' => TRUE,
1307
'#feedback_enable' => TRUE,
1308
'#process' => array('fivestar_expand'),
1314
* Theme the fivestar form element by adding necessary css and javascript.
1316
function theme_fivestar($element) {
1317
if (empty($element['#description'])) {
1318
if ($element['#feedback_enable']) {
1319
$element['#description'] = '<div class="fivestar-summary fivestar-feedback-enabled"> </div>';
1321
elseif ($element['#labels_enable']) {
1322
$element['#description'] = '<div class="fivestar-summary"> </div>';
1326
return theme('form_element', $element, $element['#children']);
1330
* Theme the straight HTML version of the fivestar select list. This is used
1331
* to remove the wrapping 'form-item' div from the select list.
1333
function theme_fivestar_select($element) {
1335
$size = $element['#size'] ? ' size="'. $element['#size'] .'"' : '';
1336
_form_set_class($element, array('form-select'));
1337
$multiple = isset($element['#multiple']) && $element['#multiple'];
1338
return '<select name="'. $element['#name'] .''. ($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="'. $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>';
1342
* Theme an entire fivestar widget, including the submit button and the normal
1343
* fivestar widget themed in the theme_fivestar() function.
1345
function theme_fivestar_widget($form) {
1346
// Only print out the summary if text is being displayed or using rollover text.
1347
if (empty($form['vote']['#description']) && strpos($form['vote']['#prefix'], 'fivestar-labels-hover') === FALSE) {
1348
unset($form['vote']['#description']);
1351
$class = 'fivestar-form';
1352
$class .= '-'. (isset($form['vote']['#tag']) ? $form['vote']['#tag'] : 'vote');
1353
$class .= '-'. (isset($form['content_id']['#value']) ? $form['content_id']['#value'] : 0);
1356
$output .= '<div class="'. $class .' clear-block">';
1357
$output .= drupal_render($form);
1358
$output .= '</div>';
1363
* Display a plain HTML VIEW ONLY version of the widget
1364
* with the specified rating
1367
* The desired rating to display out of 100 (i.e. 80 is 4 out of 5 stars)
1369
* The total number of stars this rating is out of
1371
* Allows multiple ratings per node
1373
* A themed HTML string representing the star widget
1376
function theme_fivestar_static($rating, $stars = 5, $tag = 'vote') {
1378
$output .= '<div class="fivestar-widget-static fivestar-widget-static-'. $tag .' fivestar-widget-static-'. $stars .' clear-block">';
1379
$numeric_rating = $rating/(100/$stars);
1380
for ($n=1; $n <= $stars; $n++) {
1381
$star_value = ceil((100/$stars) * $n);
1382
$prev_star_value = ceil((100/$stars) * ($n-1));
1383
$zebra = ($n % 2 == 0) ? 'even' : 'odd';
1384
$first = $n == 1 ? ' star-first' : '';
1385
$last = $n == $stars ? ' star-last' : '';
1386
$output .= '<div class="star star-'. $n .' star-'. $zebra . $first . $last .'">';
1387
if ($rating < $star_value && $rating > $prev_star_value) {
1388
$percent = (($rating - $prev_star_value) / ($star_value - $prev_star_value)) * 100;
1389
$output .= '<span class="on" style="width: '. $percent .'%">';
1391
elseif ($rating >= $star_value) {
1392
$output .= '<span class="on">';
1395
$output .= '<span class="off">';
1397
if ($n == 1)$output .= $numeric_rating;
1398
$output .= '</span></div>';
1400
$output .= '</div>';
1404
function theme_fivestar_summary($user_rating, $average_rating, $votes, $stars = 5, $feedback = TRUE) {
1407
if (isset($user_rating)) {
1408
$div_class = isset($votes) ? 'user-count' : 'user';
1409
$user_stars = round(($user_rating * $stars) / 100, 1);
1410
$output .= '<span class="user-rating">'. t('Your rating: <span>!stars</span>', array('!stars' => $user_rating ? $user_stars : t('None'))) .'</span>';
1412
if (isset($user_rating) && isset($average_rating)) {
1415
if (isset($average_rating)) {
1416
$div_class = isset($votes) ? 'average-count' : 'average';
1417
$average_stars = round(($average_rating * $stars) / 100, 1);
1418
$output .= '<span class="average-rating">'. t('Average: <span>!stars</span>', array('!stars' => $average_stars)) .'</span>';
1420
if (isset($user_rating) && isset($average_rating)) {
1421
$div_class = 'combo';
1424
if (isset($votes) && !(isset($user_rating) || isset($average_rating))) {
1425
$output .= ' <span class="total-votes">'. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .'</span>';
1426
$div_class = 'count';
1428
elseif (isset($votes)) {
1429
$output .= ' <span class="total-votes">('. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .')</span>';
1433
$output = '<span class="empty">'. t('No votes yet') .'</span>';
1436
$output = '<div class="fivestar-summary fivestar-summary-'. $div_class . ($feedback ? ' fivestar-feedback-enabled' : '') .'">'. $output .'</div>';
1441
* Display a static fivestar value as stars with a title and description.
1443
function theme_fivestar_static_element($value, $title = NULL, $description = NULL) {
1445
$output .= '<div class="fivestar-static-form-item">';
1449
'#description' => $description,
1452
$output .= theme('form_element', $element, $value);
1453
$output .= '</div>';
1458
* Fetch the necessary CSS files to render the fivestar widget.
1460
function fivestar_add_css($widget_css = NULL) {
1461
// Add fivestar CSS.
1462
drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');
1464
// Add widget specific CSS.
1465
if (!isset($widget_css)) {
1466
$widget_css = variable_get('fivestar_widget', 'default');
1469
if ($widget_css != 'default') {
1470
drupal_add_css($widget_css);
1475
* Add necessary JS files and settings to render the fivestar widget.
1477
function fivestar_add_js() {
1478
static $js_added = FALSE;
1480
// Add necessary javascript only once per page.
1483
'titleUser' => t('Your rating') .': ',
1484
'titleAverage' => t('Average') .': ',
1485
'feedbackSavingVote' => t('Saving your vote...'),
1486
'feedbackVoteSaved' => t('Your vote has been saved.'),
1487
'feedbackDeletingVote' => t('Deleting your vote...'),
1488
'feedbackVoteDeleted' => t('Your vote has been deleted.'),
1491
drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar.js');
1492
drupal_add_js(array('fivestar' => $settings), 'setting');
1499
* Add Inline CSS to the page, only used on admin/settings/fivestar page.
1501
function fivestar_add_inline_css($widget_key = NULL, $css = NULL, $reset = FALSE) {
1504
if (!isset($inline_css) || $reset) {
1505
$inline_css = array();
1508
if (isset($widget_key) && isset($inline_css)) {
1509
$inline_css[$widget_key] = $css;
1516
* Retrieve a list of all inline CSS to be added to the page.
1518
function fivestar_get_inline_css() {
1519
$inline_css = fivestar_add_inline_css();
1520
return implode("\n", $inline_css);
1524
* Process callback for fivestar_element -- see fivestar_element()
1526
function fivestar_expand($element) {
1527
static $fivestar_id = 0;
1529
if (isset($element['#vote_count'])) {
1530
$element['vote_count'] = array(
1531
'#type' => 'hidden',
1532
'#value' => $element['#vote_count'],
1533
'#id' => 'edit-vote-count-'. $fivestar_id,
1537
if (isset($element['#vote_average'])) {
1538
$element['vote_average'] = array(
1539
'#type' => 'hidden',
1540
'#value' => $element['#vote_average'],
1541
'#id' => 'edit-vote-average-'. $fivestar_id,
1545
if ($element['#auto_submit'] && !empty($element['#auto_submit_path'])) {
1546
$element['auto_submit_path'] = array(
1547
'#type' => 'hidden',
1548
'#value' => url($element['#auto_submit_path']),
1549
'#attributes' => array('class' => 'fivestar-path'),
1550
'#id' => 'edit-auto-submit-path-'. $fivestar_id,
1552
$element['auto_submit_token'] = array(
1553
'#type' => 'hidden',
1554
'#value' => fivestar_get_token($element['#auto_submit_path']),
1555
'#attributes' => array('class' => 'fivestar-token'),
1556
'#id' => 'edit-auto-submit-token-'. $fivestar_id,
1560
if (!isset($element['#default_value'])) {
1561
$element['#default_value'] = 0;
1564
$options = array('-' => t('Select rating'));
1565
$default_value = $element['#default_value'];
1566
for ($i = 0; $i <= $element['#stars']; $i++) {
1567
$this_value = ceil($i * 100/$element['#stars']);
1568
$next_value = ceil(($i+1) * 100/$element['#stars']);
1570
// Display clear button only if enabled.
1571
if ($element['#allow_clear'] == TRUE && $i == 0) {
1572
$options[$this_value] = isset($element['#labels'][$i]) ? t(filter_xss_admin($element['#labels'][$i])) : t('Cancel rating');
1574
// Display a normal star value.
1576
if (isset($element['#labels'][$i])) {
1577
$options[$this_value] = $element['#labels'][$i] == '' ? $i : t(filter_xss_admin($element['#labels'][$i]), array('@star' => $i, '@count' => $element['#stars']));
1580
$options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
1583
// Round up the default value to the next exact star value if needed.
1584
if ($this_value < $element['#default_value'] && $next_value > $element['#default_value']) {
1585
$default_value = $next_value;
1589
$element['vote'] = array(
1590
'#type' => 'select',
1591
'#options' => $options,
1592
'#required' => $element['#required'],
1593
'#default_value' => $default_value,
1594
'#parents' => $element['#parents'],
1595
'#id' => 'edit-vote-'. $fivestar_id,
1596
'#theme' => 'fivestar_select',
1597
'#weight' => $element['#weight'],
1600
// If a default value is not exactly on a radio value, round up to the next one
1601
if ($element['#default_value'] > $this_value && $element['#default_value'] <= $next_value) {
1602
$element['vote']['#default_value'] = $next_value;
1605
// Set a class for the display of label text on hover.
1606
$label_class = $element['#labels_enable'] ? ' fivestar-labels-hover' : '';
1608
$element['#id'] = 'edit-vote-'. $fivestar_id;
1609
$element['#prefix'] = '<div class="fivestar-form-item '. (isset($element['#attributes']['class']) ? $element['#attributes']['class'] : '') . $label_class .'">';
1610
$element['#suffix'] = '</div>';
1612
// Add validation function that considers a 0 value as empty.
1613
$element['#element_validate'] = array('fivestar_validate');
1619
function fivestar_validate($element, &$form_state) {
1620
if ($element['#required'] && (empty($element['#value']) || $element['#value'] == '-')) {
1621
form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
1625
function fivestar_votingapi_views_formatters($details = array()) {
1626
if ($details->field == 'value') {
1628
'fivestar_views_value_display_handler' => t('Fivestar Stars (display only)'),
1629
'fivestar_views_widget_compact_handler' => t('Fivestar Stars (clickable, no text)'),
1630
'fivestar_views_widget_normal_handler' => t('Fivestar Stars (clickable, with text)'),
1635
function fivestar_views_value_display_handler($value, $field, $columns) {
1636
// Determine number of stars to display
1637
if ($field->view->base_table == 'node') {
1638
if (isset($columns->node_type)) {
1639
$stars = variable_get('fivestar_stars_'. $columns->node_type, 5);
1642
$node_type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $columns->nid));
1643
$stars = variable_get('fivestar_stars_'. (!isset($node_type) ? 'default' : $node_type), 5);
1646
// Find the VotingAPI tag for this field.
1647
foreach ($field->query->table_queue[$field->relationship]['join']->extra as $votingapi_setting) {
1648
if ($votingapi_setting['field'] == 'tag') {
1649
$tag = $votingapi_setting['value'];
1658
return theme('fivestar_static', $value, $stars, $tag);
1661
function fivestar_views_widget_compact_handler($value, $field, $columns) {
1662
return fivestar_views_widget_handler($value, $field, $columns, FALSE);
1665
function fivestar_views_widget_normal_handler($value, $field, $columns) {
1666
return fivestar_views_widget_handler($value, $field, $columns, TRUE);
1669
function fivestar_views_widget_handler($value, $field, $columns, $summary) {
1671
// If the user can't rate, use the display handler.
1672
if (!user_access('rate content')) {
1673
return fivestar_views_value_display_handler($value, $field, $columns);
1676
if ($field->view->base_table == 'node') {
1678
// Find the VotingAPI tag for this field.
1679
foreach ($field->query->table_queue[$field->relationship]['join']->extra as $votingapi_setting) {
1680
if ($votingapi_setting['field'] == 'tag') {
1681
$tag = $votingapi_setting['value'];
1685
$content_type = 'node';
1686
$content_id = $columns->nid;
1687
$node_type = isset($columns->node_type) ? $columns->node_type : db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $columns->nid));
1689
$votes = fivestar_get_votes($content_type, $content_id, $tag);
1692
'user' => isset($votes['user']['value']) ? $votes['user']['value'] : 0,
1693
'average' => isset($votes['average']['value']) ? $votes['average']['value'] : 0,
1694
'count' => isset($votes['count']['value']) ? $votes['count']['value'] : 0,
1698
'stars' => variable_get('fivestar_stars_'. $node_type, 5),
1699
'allow_clear' => variable_get('fivestar_unvote_'. $node_type, FALSE),
1700
// If the user has setup this content type to use smart stars, display
1701
// the smart version instead of just the average.
1702
'style' => variable_get('fivestar_style_'. $node_type, 'average') != 'smart' ? 'average' : 'smart',
1703
'text' => $summary ? variable_get('fivestar_text_'. $node_type, 'dual') : 'none',
1704
'content_type' => $content_type,
1705
'content_id' => $content_id,
1707
'autosubmit' => TRUE,
1709
'feedback_enable' => $summary ? variable_get('fivestar_feedback_'. $node_type, 1) : FALSE,
1710
'labels_enable' => $summary ? variable_get('fivestar_labels_enable_'. $node_type, 1) : FALSE,
1711
'labels' => $summary ? variable_get('fivestar_labels_'. $node_type, array()) : array(),
1714
return drupal_get_form('fivestar_custom_widget', $values, $settings);
1717
return theme('fivestar_static', $value, 5);