~spreadubuntu/spreadubuntu/devel-drupal6

« back to all changes in this revision

Viewing changes to modules/fivestar/fivestar.module

  • Committer: ruben
  • Date: 2009-06-08 09:38:49 UTC
  • Revision ID: ruben@captive-20090608093849-s1qtsyctv2vwp1x1
SpreadUbuntu moving to Drupal6. Based on ubuntu-drupal theme and adding our modules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
// $Id: fivestar.module,v 1.13.2.56 2009/04/30 20:58:31 quicksketch Exp $
 
3
 
 
4
/**
 
5
 * @file
 
6
 * A simple n-star voting widget, usable in other forms.
 
7
 */
 
8
 
 
9
function fivestar_help($path, $arg) {
 
10
  $output = '';
 
11
  switch ($path) {
 
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:');
 
14
      $steps = array(
 
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.'),
 
18
      );
 
19
      $output .= theme('item_list', $steps, NULL, 'ol');
 
20
  }
 
21
  return $output;
 
22
}
 
23
 
 
24
/**
 
25
 * Implementation of hook_menu().
 
26
 *
 
27
 * Provides a callback url where votes can be submitted by the client-side
 
28
 * javascript.
 
29
 */
 
30
function fivestar_menu() {
 
31
  $items = array();
 
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',
 
41
  );
 
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',
 
48
  );
 
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,
 
54
  );
 
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,
 
60
  );
 
61
  return $items;
 
62
}
 
63
 
 
64
/**
 
65
 * Implementation of hook_init().
 
66
 *
 
67
 * These includes do not need to be loaded for cached pages.
 
68
 */
 
69
function fivestar_init() {
 
70
  if (module_exists('content')) {
 
71
    module_load_include('inc', 'fivestar', 'fivestar_field');
 
72
  }
 
73
 
 
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.
 
77
  fivestar_add_js();
 
78
  fivestar_add_css();
 
79
}
 
80
 
 
81
/**
 
82
 * Implementation of hook_perm().
 
83
 *
 
84
 * Exposes permissions for rating content, viewing aggregate ratings, and using PHP
 
85
 * snippets when configuring fivestar CCK fields.
 
86
 */
 
87
function fivestar_perm() {
 
88
  return array('rate content', 'use PHP for fivestar target');
 
89
}
 
90
 
 
91
/**
 
92
 * Implementation of hook_theme().
 
93
 */
 
94
function fivestar_theme() {
 
95
  return array(
 
96
    // Fivestar theme functions.
 
97
    'fivestar' => array(
 
98
      'arguments' => array('element' => NULL),
 
99
    ),
 
100
    'fivestar_node_type_form' => array(
 
101
      'arguments' => array('form' => NULL),
 
102
    ),
 
103
    'fivestar_preview' => array(
 
104
      'arguments' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL, 'labels_enable' => TRUE, 'labels' => array()),
 
105
    ),
 
106
    'fivestar_preview_widget' => array(
 
107
      'arguments' => array('css_file' => NULL),
 
108
    ),
 
109
    'fivestar_preview_wrapper' => array(
 
110
      'arguments' => array('content' => NULL, 'type' => 'direct'),
 
111
    ),
 
112
    'fivestar_select' => array(
 
113
      'arguments' => array('element' => NULL),
 
114
    ),
 
115
    'fivestar_settings' => array(
 
116
      'arguments' => array('form' => NULL),
 
117
    ),
 
118
    'fivestar_static' => array(
 
119
      'arguments' => array('rating' => NULL, 'stars' => 5),
 
120
    ),
 
121
    'fivestar_static_element' => array(
 
122
      'arguments' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL),
 
123
    ),
 
124
    'fivestar_summary' => array(
 
125
      'arguments' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5),
 
126
    ),
 
127
    'fivestar_widget' => array(
 
128
      'arguments' => array('form' => NULL),
 
129
    ),
 
130
    // fivestar_color.inc.
 
131
    'fivestar_color_form' => array(
 
132
      'arguments' => array('form' => NULL),
 
133
    ),
 
134
    // fivestar_field.inc.
 
135
    'fivestar_formatter_default' => array(
 
136
      'arguments' => array('element' => NULL),
 
137
    ),
 
138
    'fivestar_formatter_rating' => array(
 
139
      'arguments' => array('element' => NULL),
 
140
    ),
 
141
    'fivestar_formatter_percentage' => array(
 
142
      'arguments' => array('element' => NULL),
 
143
    ),
 
144
  );
 
145
}
 
146
 
 
147
/**
 
148
 * Implementation of hook_form_alter().
 
149
 *
 
150
 * Adds fivestar enaable and position to the node-type configuration form.
 
151
 */
 
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;
 
158
 
 
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'),
 
167
    );
 
168
 
 
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,
 
174
      '#weight' => -5,
 
175
    );
 
176
 
 
177
    $form['fivestar']['fivestar_stars'] = array(
 
178
      '#type' => 'select',
 
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),
 
182
      '#weight' => -4,
 
183
    );
 
184
 
 
185
 
 
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.'),
 
192
    );
 
193
 
 
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,
 
199
      '#weight' => -5,
 
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.'),
 
201
    );
 
202
 
 
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'));
 
211
      }
 
212
      elseif ($n == 0) {
 
213
        $default_labels[$n] = t('Cancel rating');
 
214
      }
 
215
      else {
 
216
        $default_labels[$n] = t('Give it @star/@count');
 
217
      }
 
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>',
 
224
        '#size' => 30,
 
225
      );
 
226
    }
 
227
 
 
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.'),
 
233
      '#weight' => 2,
 
234
    );
 
235
 
 
236
    $form['fivestar']['direct']['fivestar_style'] = array(
 
237
      '#type' => 'select',
 
238
      '#title' => t('Star display style'),
 
239
      '#default_value' => variable_get('fivestar_style_'. $form['#node_type']->type, 'average'),
 
240
      '#options' => array(
 
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'),
 
245
      ),
 
246
    );
 
247
 
 
248
    $form['fivestar']['direct']['fivestar_text'] = array(
 
249
      '#type' => 'select',
 
250
      '#title' => t('Text display style'),
 
251
      '#default_value' => variable_get('fivestar_text_'. $form['#node_type']->type, 'dual'),
 
252
      '#options' => array(
 
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'),
 
258
      ),
 
259
    );
 
260
 
 
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,
 
266
    );
 
267
 
 
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),
 
272
      '#return_value' => 1
 
273
    );
 
274
 
 
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,
 
280
    );
 
281
 
 
282
    $form['fivestar']['direct']['fivestar_position_teaser'] = array(
 
283
      '#type' => 'select',
 
284
      '#title' => t('Teaser display'),
 
285
      '#default_value' => variable_get('fivestar_position_teaser_'. $form['#node_type']->type, 'hidden'),
 
286
      '#options' => array(
 
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') .'>',
 
293
      ),
 
294
    );
 
295
 
 
296
    $form['fivestar']['direct']['fivestar_position'] = array(
 
297
      '#type' => 'select',
 
298
      '#title' => t('Full node display'),
 
299
      '#default_value' => variable_get('fivestar_position_'. $form['#node_type']->type, 'below'),
 
300
      '#options' => array(
 
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') .'>',
 
306
      ),
 
307
    );
 
308
 
 
309
    $form['fivestar']['direct']['fivestar_direct_preview'] = array(
 
310
      '#type' => 'item',
 
311
      '#title' => t('Direct rating widget preview'),
 
312
      '#value' => theme(
 
313
        'fivestar_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())
 
321
      ),
 
322
    );
 
323
    if (!$form['fivestar']['fivestar']['#default_value']) {
 
324
      $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', '');
 
325
    }
 
326
    else {
 
327
      $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', $form['fivestar']['direct']['fivestar_direct_preview']['#value']);
 
328
    }
 
329
 
 
330
    $form['#submit'][] = 'fivestar_node_type_form_submit';
 
331
  }
 
332
}
 
333
 
 
334
/**
 
335
 * Additional submit handler for the node type form.
 
336
 */
 
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']);
 
343
      }
 
344
    }
 
345
  }
 
346
  // Merge labels into a single variable.
 
347
  $labels = array();
 
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']);
 
351
  }
 
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);
 
355
  }
 
356
}
 
357
 
 
358
/**
 
359
 * Theme function to add the Fivestar preview to the node type form.
 
360
 */
 
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);
 
365
 
 
366
  $output = '';
 
367
  $output .= drupal_render($form['fivestar']);
 
368
  $output .= drupal_render($form['fivestar_stars']);
 
369
 
 
370
  // Star labels.
 
371
  $output .= drupal_render($form['labels']);
 
372
 
 
373
  // Direct rating settings form.
 
374
  $direct = '';
 
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']);
 
383
  $direct .= '</div>';
 
384
  $direct .= '<div id="fivestar-direct-preview">';
 
385
  $direct .= drupal_render($form['direct']['fivestar_direct_preview']);
 
386
  $direct .= '</div>';
 
387
 
 
388
  $form['direct']['#children'] = $direct;
 
389
  $output .= drupal_render($form['direct']);
 
390
 
 
391
  // Comment settings form.
 
392
  if (module_exists('fivestar_comment')) {
 
393
    $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>';
 
400
 
 
401
    $form['comment']['#children'] = $comment;
 
402
    $output .= drupal_render($form['comment']);
 
403
  }
 
404
 
 
405
  // Any remaining cruft (should be empty).
 
406
  $output .= drupal_render($form);
 
407
  return $output;
 
408
}
 
409
 
 
410
/**
 
411
 * Implementation of hook_node_types().
 
412
 */
 
413
function fivestar_node_type($op, $info) {
 
414
  $type = $info->type;
 
415
  $variables = array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser');
 
416
 
 
417
  // Be responsible and cleanup unneeded variables.
 
418
  if ($op == 'delete') {
 
419
    foreach ($variables as $variable) {
 
420
      variable_del($variable .'_'. $type);
 
421
    }
 
422
  }
 
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);
 
427
      if ($value != -1) {
 
428
        variable_del($variable .'_'. $type);
 
429
        variable_set($variable .'_'. $type, $value);
 
430
      }
 
431
    }
 
432
  }
 
433
}
 
434
 
 
435
/**
 
436
 * Callback function for admin/settings/fivestar. Display the settings form.
 
437
 */
 
438
function fivestar_settings() {
 
439
  $form = array();
 
440
 
 
441
  $form['widget'] = array(
 
442
    '#tree' => FALSE,
 
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.'),
 
446
    '#weight' => -2,
 
447
  );
 
448
 
 
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;
 
457
    }
 
458
    else {
 
459
      $color_widgets[$path] = $name;
 
460
    }
 
461
  }
 
462
 
 
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;
 
468
    }
 
469
  }
 
470
 
 
471
  $form['widget']['fivestar_widget'] = array(
 
472
    '#type' => 'radios',
 
473
    '#options' => array('default' => t('Default')) + $classic_widgets + $color_widgets,
 
474
    '#default_value' => $default_value,
 
475
    '#attributes' => array('class' => 'fivestar-widgets'),
 
476
  );
 
477
 
 
478
  $form['widget']['fivestar_color_widget'] = array(
 
479
    '#type' => 'radios',
 
480
    '#title' => t('Custom color widgets'),
 
481
    '#options' => $color_widgets,
 
482
    '#attributes' => array('class' => 'fivestar-widgets'),
 
483
  );
 
484
 
 
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';
 
489
 
 
490
  $form['submit'] = array(
 
491
    '#type' => 'submit',
 
492
    '#value' => t('Save configuration'),
 
493
    '#weight' => 45,
 
494
  );
 
495
 
 
496
  return $form;
 
497
}
 
498
 
 
499
function fivestar_settings_submit($form, &$form_state) {
 
500
  variable_set('fivestar_widget', $form_state['values']['fivestar_widget']);
 
501
}
 
502
 
 
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'));
 
506
 
 
507
  // Default preview.
 
508
  $form['widget']['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':<br />'. theme('fivestar_preview_widget', 'default');
 
509
 
 
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);
 
514
    }
 
515
  }
 
516
 
 
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]);
 
522
  }
 
523
 
 
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>");
 
526
 
 
527
  $form['widget']['fivestar_widget']['#attributes']['class'] .= ' clear-block';
 
528
  $form['widget']['fivestar_color_widget']['#attributes']['class'] .= ' fivestar-color-widgets clear-block';
 
529
 
 
530
  return drupal_render($form);
 
531
}
 
532
 
 
533
function theme_fivestar_preview_widget($css_file) {
 
534
  static $default_css_added = FALSE;
 
535
 
 
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;
 
545
  }
 
546
 
 
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);
 
557
  }
 
558
 
 
559
  $form = array();
 
560
  $form_state = array();
 
561
  $form['vote'] = array(
 
562
    '#type' => 'fivestar',
 
563
    '#stars' => 5,
 
564
    '#auto_submit' => FALSE,
 
565
    '#allow_clear' => TRUE,
 
566
  );
 
567
 
 
568
  $form = form_builder('fivestar_preview', $form, $form_state);
 
569
 
 
570
  $output = '<div class="fivestar-star-preview" id="fivestar-preview-'. $widget_name .'">';
 
571
  $output .= drupal_render($form);
 
572
  $output .= '</div>';
 
573
 
 
574
  return $output;
 
575
}
 
576
 
 
577
/**
 
578
 * Callback function for fivestar/preview/color.
 
579
 *
 
580
 * Outputs a dynamically generated star or cancel png.
 
581
 */
 
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);
 
588
 
 
589
  // Convert args to our color scheme.
 
590
  $color_scheme = array(
 
591
    'on1' => $args[0],
 
592
    'on2' => $args[1],
 
593
    'hover1' => $args[2],
 
594
    'hover2' => $args[3],
 
595
    'off1' => $args[4],
 
596
    'off2' => $args[5],
 
597
    'matte' => $args[6],
 
598
  );
 
599
 
 
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);
 
605
      break;
 
606
    }
 
607
  }
 
608
 
 
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");
 
615
  imagepng($image);
 
616
  exit();
 
617
}
 
618
 
 
619
/**
 
620
 * Callback function for fivestar/preview/node. Outputs a JSON page containing
 
621
 * a Fivestar preview of a node rating widget.
 
622
 */
 
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);
 
635
  }
 
636
 
 
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));
 
640
}
 
641
 
 
642
function theme_fivestar_preview($style = NULL, $text = NULL, $stars = NULL, $unvote = NULL, $title = NULL, $feedback_enable = TRUE, $labels_enable = TRUE, $labels = array()) {
 
643
  $values = array(
 
644
    'average' => 50,
 
645
    'user' => 80,
 
646
    'count' => 20,
 
647
  );
 
648
  $settings = array(
 
649
    'stars' => $stars,
 
650
    'allow_clear' => $unvote,
 
651
    'style' => $style,
 
652
    'text' => $text,
 
653
    'title' => $title,
 
654
    'autosubmit' => FALSE,
 
655
    'feedback_enable' => $feedback_enable,
 
656
    'labels_enable' => $labels_enable,
 
657
    'labels' => $labels,
 
658
  );
 
659
 
 
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);
 
666
  return $form;
 
667
}
 
668
 
 
669
function theme_fivestar_preview_wrapper($content, $type = 'direct') {
 
670
  return '<div class="fivestar-preview fivestar-preview-'. $type .'">'. $content .'</div>';
 
671
}
 
672
 
 
673
/**
 
674
 * Callback function for fivestar/vote.
 
675
 *
 
676
 * @param type
 
677
 *   A content-type to log the vote to. 'node' is the most common.
 
678
 * @param cid
 
679
 *   A content id to log the vote to. This would be a node ID, a comment ID, etc.
 
680
 * @param tag
 
681
 *   Multi-axis tag to allow multiple votes per node. 'vote' is the most common.
 
682
 * @param value
 
683
 *   A value from 1-100, representing the vote cast for the content.
 
684
 * @return
 
685
 *  An XML chunk containing the results of the vote, for use by the client-side
 
686
 *  javascript code.
 
687
 */
 
688
function fivestar_vote($type, $cid, $tag, $value) {
 
689
  drupal_set_header("Content-Type: text/xml");
 
690
  $output = '';
 
691
  $output .= '<?xml version="1.0" encoding="UTF-8"?>';
 
692
 
 
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>';
 
697
    exit($output);
 
698
  }
 
699
 
 
700
  $result = _fivestar_cast_vote($type, $cid, $value, $tag, NULL, TRUE);
 
701
  votingapi_recalculate_results($type, $cid);
 
702
 
 
703
  if ($type == 'node') {
 
704
    $node = node_load($cid);
 
705
  }
 
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);
 
708
 
 
709
  $output .= '<xml><result>';
 
710
 
 
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'];
 
716
      }
 
717
    }
 
718
  }
 
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>';
 
728
 
 
729
  $output .= '<vote>';
 
730
  $output .= '<value>'. $value .'</value>';
 
731
  $output .= '<type>'. $type .'</type>';
 
732
  $output .= '<id>'. $cid .'</id>';
 
733
  $output .= '<tag>'. $tag .'</tag>';
 
734
  $output .= '</vote></xml>';
 
735
 
 
736
  drupal_set_header("Content-Type: text/xml");
 
737
  exit($output);
 
738
}
 
739
 
 
740
/**
 
741
 * Internal function to handle vote casting, flood control, XSS, IP based
 
742
 * voting, etc...
 
743
 */
 
744
function _fivestar_cast_vote($type, $cid, $value, $tag = NULL, $uid = NULL, $result = FALSE, $skip_validation = FALSE) {
 
745
  global $user;
 
746
  $tag = empty($tag) ? 'vote' : $tag;
 
747
  $uid = empty($uid) ? $user->uid : $uid;
 
748
 
 
749
  // Bail out if the user's trying to vote on an invalid object.
 
750
  if (!$skip_validation && !fivestar_validate_target($type, $cid, $uid)) {
 
751
    return array();
 
752
  }
 
753
 
 
754
  // Sanity-check the incoming values.
 
755
  if (is_numeric($cid) && is_numeric($value)) {
 
756
    if ($value > 100) {
 
757
      $value = 100;
 
758
    }
 
759
 
 
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);
 
765
 
 
766
    if ($value == 0) {
 
767
      votingapi_delete_votes($user_votes);
 
768
    }
 
769
    else {
 
770
      $votes = $criteria += array('value' => $value);
 
771
      votingapi_set_votes($votes, $user_votes);
 
772
    }
 
773
    return fivestar_get_votes($type, $cid, $tag, $uid);
 
774
  }
 
775
}
 
776
 
 
777
function fivestar_get_votes($type, $cid, $tag = 'vote', $uid = NULL) {
 
778
  global $user;
 
779
  if (empty($uid)) {
 
780
    $uid = $user->uid;
 
781
  }
 
782
 
 
783
  $criteria = array(
 
784
    'content_type' => $type,
 
785
    'content_id' => $cid,
 
786
    'value_type' => 'percent',
 
787
    'tag' => $tag,
 
788
  );
 
789
 
 
790
  $votes = array(
 
791
    'average' => array(),
 
792
    'count' => array(),
 
793
    'user' => array(),
 
794
  );
 
795
 
 
796
  $results = votingapi_select_results($criteria);
 
797
  foreach ($results as $result) {
 
798
    if ($result['function'] == 'average') {
 
799
      $votes['average'] = $result;
 
800
    }
 
801
    if ($result['function'] == 'count') {
 
802
      $votes['count'] = $result;
 
803
    }
 
804
  }
 
805
  if ($uid) {
 
806
    $user_vote = votingapi_select_votes($criteria += array('uid' => $uid));
 
807
    if ($user_vote) {
 
808
      $votes['user'] = $user_vote[0];
 
809
      $votes['user']['function'] = 'user';
 
810
    }
 
811
  }
 
812
  else {
 
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);
 
817
  }
 
818
 
 
819
  return $votes;
 
820
}
 
821
 
 
822
/**
 
823
 * Check that an item being voted upon is a valid vote.
 
824
 *
 
825
 * @param $type
 
826
 *   Type of target (currently only node is supported).
 
827
 * @param $id
 
828
 *   Identifier within the type (in this case nid).
 
829
 * @param $uid
 
830
 *   The user trying to cast the vote.
 
831
 *
 
832
 * @return boolean
 
833
 */
 
834
function fivestar_validate_target($type, $id, $uid = NULL) {
 
835
  if (!isset($account)) {
 
836
    $uid = $GLOBALS['user']->uid;
 
837
  }
 
838
 
 
839
  $access = module_invoke_all('fivestar_access', $type, $id, $uid);
 
840
  foreach ($access as $result) {
 
841
    if ($result == TRUE) {
 
842
      return TRUE;
 
843
    }
 
844
    if ($result === FALSE) {
 
845
      return FALSE;
 
846
    }
 
847
  }
 
848
}
 
849
 
 
850
/**
 
851
 * Implementation of hook_fivestar_access().
 
852
 *
 
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.
 
856
 *
 
857
 * @param $type
 
858
 *   Type of target (currently only node is supported).
 
859
 * @param $id
 
860
 *   Identifier within the type (in this case nid).
 
861
 * @param $account
 
862
 *   The user trying to cast the vote.
 
863
 *
 
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.
 
870
 */
 
871
function fivestar_fivestar_access($type, $id, $account) {
 
872
  if ($type == 'node' && $node = node_load($id)) {
 
873
    if (variable_get('fivestar_'. $node->type, 0)) {
 
874
      return TRUE;
 
875
    }
 
876
  }
 
877
}
 
878
 
 
879
/**
 
880
 * Implementation of hook_fivestar_widgets().
 
881
 *
 
882
 * This hook allows other modules to create additional custom widgets for
 
883
 * the fivestar module.
 
884
 *
 
885
 * @return array
 
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.
 
889
 */
 
890
function fivestar_fivestar_widgets() {
 
891
  $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
 
892
  $files = file_scan_directory($widgets_directory, '\.css$');
 
893
 
 
894
  $widgets = array();
 
895
  foreach ($files as $file) {
 
896
    if (strpos($file->filename, '-rtl.css') === FALSE) {
 
897
      $widgets[$file->filename] = drupal_ucfirst(str_replace('-color', '', $file->name));
 
898
    }
 
899
  }
 
900
  return $widgets;
 
901
}
 
902
 
 
903
/**
 
904
 * Implementation of hook_nodeapi().
 
905
 *
 
906
 * Adds the fievestar widget to the node view.
 
907
 */
 
908
function fivestar_nodeapi(&$node, $op, $teaser, $page) {
 
909
  switch ($op) {
 
910
    case 'view':
 
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)) {
 
912
        if ($teaser) {
 
913
          $position = variable_get('fivestar_position_teaser_'. $node->type, 'above');
 
914
        }
 
915
        else {
 
916
          $position = variable_get('fivestar_position_'. $node->type, 'above');
 
917
        }
 
918
        switch ($position) {
 
919
          case 'above':
 
920
          case 'below':
 
921
            if (user_access('rate content')) {
 
922
              $node->content['fivestar_widget'] = array(
 
923
                '#value' => fivestar_widget_form($node),
 
924
                '#weight' => $position == 'above' ? -10 : 50,
 
925
              );
 
926
              break;
 
927
            } // Fall through to static if not allowed to rate.
 
928
            $position .= '_static';
 
929
          case 'above_static':
 
930
          case 'below_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,
 
935
            );
 
936
            break;
 
937
          default:
 
938
            // We'll do nothing.
 
939
            break;
 
940
        }
 
941
      }
 
942
      break;
 
943
  }
 
944
}
 
945
 
 
946
 
 
947
/**
 
948
 * Implementation of hook_link().
 
949
 *
 
950
 * Add a "rate" link to node teaser.
 
951
 */
 
952
function fivestar_link($type, $node = NULL, $teaser = FALSE) {
 
953
  $links = array();
 
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)))),
 
961
      );
 
962
    }
 
963
  }
 
964
  return $links;
 
965
}
 
966
 
 
967
 
 
968
function fivestar_block($op = 'list', $delta = 0, $edit = array()) {
 
969
  global $user;
 
970
  switch ($op) {
 
971
    case 'list':
 
972
      $blocks[0]['info'] = t('Fivestar: Rate this node');
 
973
      return $blocks;
 
974
 
 
975
    case 'view':
 
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);
 
982
            return $block;
 
983
          }
 
984
        }
 
985
      }
 
986
      break;
 
987
  }
 
988
}
 
989
 
 
990
function fivestar_widget_form($node) {
 
991
  return drupal_get_form('fivestar_form_node_'. $node->nid, 'node', $node->nid);
 
992
}
 
993
 
 
994
/**
 
995
 * Get a private token used to protect links from spoofing - CSRF.
 
996
 */
 
997
function fivestar_get_token($seed) {
 
998
  return drupal_get_token($seed);
 
999
}
 
1000
 
 
1001
/**
 
1002
 * Check to see if a token value matches the specified node.
 
1003
 */
 
1004
function fivestar_check_token($token, $seed) {
 
1005
  return drupal_get_token($seed) == $token;
 
1006
}
 
1007
 
 
1008
/**
 
1009
 * Implementation of hook_forms().
 
1010
 *
 
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.
 
1013
 */
 
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');
 
1018
      return $forms;
 
1019
    }
 
1020
  }
 
1021
}
 
1022
 
 
1023
/**
 
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.
 
1027
 */
 
1028
function fivestar_form(&$form_state, $content_type, $content_id) {
 
1029
  global $user;
 
1030
 
 
1031
  if ($content_type == 'node') {
 
1032
    if (is_numeric($content_id)) {
 
1033
      $node = node_load($content_id);
 
1034
    }
 
1035
    else {
 
1036
      return array();
 
1037
    }
 
1038
  }
 
1039
 
 
1040
 
 
1041
  $votes = fivestar_get_votes($content_type, $content_id);
 
1042
 
 
1043
  $values = array(
 
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,
 
1047
  );
 
1048
 
 
1049
  $settings = array(
 
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,
 
1056
    'tag' => 'vote',
 
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()),
 
1062
  );
 
1063
 
 
1064
  return fivestar_custom_widget($form_state, $values, $settings);
 
1065
}
 
1066
 
 
1067
function fivestar_static($content_type, $content_id, $tag = 'vote', $node_type = NULL) {
 
1068
  global $user;
 
1069
 
 
1070
  $criteria = array(
 
1071
    'content_type' => $content_type,
 
1072
    'content_id' => $content_id,
 
1073
    'value_type' => 'percent',
 
1074
    'tag' => 'vote',
 
1075
  );
 
1076
 
 
1077
  $votes = fivestar_get_votes($content_type, $content_id, $tag);
 
1078
 
 
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;
 
1084
    }
 
1085
 
 
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);
 
1089
 
 
1090
    $stars = variable_get('fivestar_stars_'. $node_type, 5);
 
1091
    switch ($star_display) {
 
1092
      case 'average':
 
1093
      case 'dual':
 
1094
        $star_value = $votes['average']['value'];
 
1095
        $title = $title_display ? t('Average') : NULL;
 
1096
        break;
 
1097
      case 'user':
 
1098
        $star_value = $votes['user']['value'];
 
1099
        $title = $title_display ? t('Your rating') : NULL;
 
1100
        break;
 
1101
      case 'smart':
 
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;
 
1104
        break;
 
1105
    }
 
1106
 
 
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) {
 
1112
      case 'average':
 
1113
        $user_value = NULL;
 
1114
        break;
 
1115
      case 'user':
 
1116
        $average_value = NULL;
 
1117
        break;
 
1118
      case 'smart':
 
1119
        if ($votes['user']['value']) {
 
1120
          $average_value = NULL;
 
1121
        }
 
1122
        else {
 
1123
          $user_value = NULL;
 
1124
        }
 
1125
        break;
 
1126
    }
 
1127
  }
 
1128
  // Possibly add other content types here (comment, user, etc).
 
1129
  else {
 
1130
    $stars = 5;
 
1131
    $star_value = $votes['average']['value'];
 
1132
    $user_value = $votes['user']['value'];
 
1133
    $average_value = $votes['average']['value'];
 
1134
    $count_value = $votes['count']['value'];
 
1135
  }
 
1136
 
 
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);
 
1139
 
 
1140
  return theme('fivestar_static_element', $star_display, $title, $text_display);
 
1141
}
 
1142
 
 
1143
function fivestar_custom_widget(&$form_state, $values, $settings) {
 
1144
  $form = array(
 
1145
    '#attributes' => array('class' => 'fivestar-widget'),
 
1146
    '#redirect' => FALSE,
 
1147
    '#theme' => 'fivestar_widget',
 
1148
  );
 
1149
  $form['#submit'][] = 'fivestar_form_submit';
 
1150
 
 
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,
 
1156
    );
 
1157
  }
 
1158
 
 
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,
 
1164
    );
 
1165
  }
 
1166
 
 
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',
 
1181
  );
 
1182
 
 
1183
  $form['destination'] = array(
 
1184
    '#type' => 'hidden',
 
1185
    '#value' => $_GET['q'],
 
1186
    '#id' => isset($settings['content_id']) ? 'edit-destination-'. $settings['content_id'] : NULL,
 
1187
  );
 
1188
 
 
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,
 
1194
  );
 
1195
 
 
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']) {
 
1199
    case 'user':
 
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';
 
1202
      break;
 
1203
    case 'average':
 
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';
 
1206
      break;
 
1207
    case 'smart':
 
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');
 
1210
      break;
 
1211
    case 'dual':
 
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';
 
1214
      break;
 
1215
  }
 
1216
 
 
1217
  switch ($settings['style']) {
 
1218
    case 'average':
 
1219
      $form['vote']['#title'] = t('Average');
 
1220
      $form['vote']['#default_value'] = $values['average'];
 
1221
      $form['vote']['#attributes']['class'] .= ' fivestar-average-stars';
 
1222
      break;
 
1223
    case 'user':
 
1224
      $form['vote']['#title'] = t('Your rating');
 
1225
      $form['vote']['#default_value'] = $values['user'];
 
1226
      $form['vote']['#attributes']['class'] .= ' fivestar-user-stars';
 
1227
      break;
 
1228
    case 'smart':
 
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');
 
1232
      break;
 
1233
    case 'dual':
 
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;
 
1241
      }
 
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);
 
1244
      }
 
1245
      else {
 
1246
        $static_description = '&nbsp;';
 
1247
      }
 
1248
      $form['average'] = array(
 
1249
        '#type' => 'markup',
 
1250
        '#value' => theme('fivestar_static_element', $static_average, $settings['title'] !== FALSE ? t('Average') : NULL, $static_description),
 
1251
        '#weight' => -1,
 
1252
      );
 
1253
      break;
 
1254
  }
 
1255
 
 
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'];
 
1262
    }
 
1263
    else {
 
1264
      unset($form['vote']['#title']);
 
1265
      unset($form['average']['#title']);
 
1266
    }
 
1267
  }
 
1268
 
 
1269
  return $form;
 
1270
}
 
1271
 
 
1272
/**
 
1273
 * Submit handler for the above form (non-javascript version).
 
1274
 */
 
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']) {
 
1277
    // Cast the vote.
 
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']);
 
1280
 
 
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.'));
 
1284
    }
 
1285
    elseif (is_numeric($form_state['values']['vote'])) {
 
1286
      drupal_set_message(t('Thank you for your vote.'));
 
1287
    }
 
1288
    // Regenerate the page with a drupal_goto() to update the current values.
 
1289
    drupal_goto();
 
1290
  }
 
1291
}
 
1292
 
 
1293
/**
 
1294
 * Implementation of hook_elements().
 
1295
 *
 
1296
 * Defines 'fivestar' form element type
 
1297
 */
 
1298
function fivestar_elements() {
 
1299
  $type['fivestar'] = array(
 
1300
    '#input' => TRUE,
 
1301
    '#stars' => 5,
 
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'),
 
1309
  );
 
1310
  return $type;
 
1311
}
 
1312
 
 
1313
/**
 
1314
 * Theme the fivestar form element by adding necessary css and javascript.
 
1315
 */
 
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">&nbsp;</div>';
 
1320
    }
 
1321
    elseif ($element['#labels_enable']) {
 
1322
      $element['#description'] = '<div class="fivestar-summary">&nbsp;</div>';
 
1323
    }
 
1324
  }
 
1325
 
 
1326
  return theme('form_element', $element, $element['#children']);
 
1327
}
 
1328
 
 
1329
/**
 
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.
 
1332
 */
 
1333
function theme_fivestar_select($element) {
 
1334
  $select = '';
 
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>';
 
1339
}
 
1340
 
 
1341
/**
 
1342
 * Theme an entire fivestar widget, including the submit button and the normal
 
1343
 * fivestar widget themed in the theme_fivestar() function.
 
1344
 */
 
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']);
 
1349
  }
 
1350
 
 
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);
 
1354
 
 
1355
  $output  = '';
 
1356
  $output .= '<div class="'. $class .' clear-block">';
 
1357
  $output .= drupal_render($form);
 
1358
  $output .= '</div>';
 
1359
  return $output;
 
1360
}
 
1361
 
 
1362
/**
 
1363
 * Display a plain HTML VIEW ONLY version of the widget
 
1364
 * with the specified rating
 
1365
 *
 
1366
 * @param $rating
 
1367
 *   The desired rating to display out of 100 (i.e. 80 is 4 out of 5 stars)
 
1368
 * @param $stars
 
1369
 *   The total number of stars this rating is out of
 
1370
 * @param $tag
 
1371
 *   Allows multiple ratings per node
 
1372
 * @return
 
1373
 *   A themed HTML string representing the star widget
 
1374
 *
 
1375
 */
 
1376
function theme_fivestar_static($rating, $stars = 5, $tag = 'vote') {
 
1377
  $output = '';
 
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 .'%">';
 
1390
    }
 
1391
    elseif ($rating >= $star_value) {
 
1392
      $output .= '<span class="on">';
 
1393
    }
 
1394
    else {
 
1395
      $output .= '<span class="off">';
 
1396
    }
 
1397
    if ($n == 1)$output .= $numeric_rating;
 
1398
    $output .= '</span></div>';
 
1399
  }
 
1400
  $output .= '</div>';
 
1401
  return $output;
 
1402
}
 
1403
 
 
1404
function theme_fivestar_summary($user_rating, $average_rating, $votes, $stars = 5, $feedback = TRUE) {
 
1405
  $output = '';
 
1406
  $div_class = '';
 
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>';
 
1411
  }
 
1412
  if (isset($user_rating) && isset($average_rating)) {
 
1413
    $output .= ' ';
 
1414
  }
 
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>';
 
1419
  }
 
1420
  if (isset($user_rating) && isset($average_rating)) {
 
1421
    $div_class = 'combo';
 
1422
  }
 
1423
 
 
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';
 
1427
  }
 
1428
  elseif (isset($votes)) {
 
1429
    $output .= ' <span class="total-votes">('. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .')</span>';
 
1430
  }
 
1431
 
 
1432
  if ($votes === 0) {
 
1433
    $output = '<span class="empty">'. t('No votes yet') .'</span>';
 
1434
  }
 
1435
 
 
1436
  $output = '<div class="fivestar-summary fivestar-summary-'. $div_class . ($feedback ? ' fivestar-feedback-enabled' : '') .'">'. $output .'</div>';
 
1437
  return $output;
 
1438
}
 
1439
 
 
1440
/**
 
1441
 * Display a static fivestar value as stars with a title and description.
 
1442
 */
 
1443
function theme_fivestar_static_element($value, $title = NULL, $description = NULL) {
 
1444
  $output = '';
 
1445
  $output .= '<div class="fivestar-static-form-item">';
 
1446
  $element = array(
 
1447
    '#type' => 'item',
 
1448
    '#title' => $title,
 
1449
    '#description' => $description,
 
1450
  );
 
1451
 
 
1452
  $output .= theme('form_element', $element, $value);
 
1453
  $output .= '</div>';
 
1454
  return $output;
 
1455
}
 
1456
 
 
1457
/**
 
1458
 * Fetch the necessary CSS files to render the fivestar widget.
 
1459
 */
 
1460
function fivestar_add_css($widget_css = NULL) {
 
1461
  // Add fivestar CSS.
 
1462
  drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');
 
1463
 
 
1464
  // Add widget specific CSS.
 
1465
  if (!isset($widget_css)) {
 
1466
    $widget_css = variable_get('fivestar_widget', 'default');
 
1467
  }
 
1468
 
 
1469
  if ($widget_css != 'default') {
 
1470
    drupal_add_css($widget_css);
 
1471
  }
 
1472
}
 
1473
 
 
1474
/**
 
1475
 * Add necessary JS files and settings to render the fivestar widget.
 
1476
 */
 
1477
function fivestar_add_js() {
 
1478
  static $js_added = FALSE;
 
1479
 
 
1480
  // Add necessary javascript only once per page.
 
1481
  if (!$js_added) {
 
1482
    $settings = array(
 
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.'),
 
1489
    );
 
1490
 
 
1491
    drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar.js');
 
1492
    drupal_add_js(array('fivestar' => $settings), 'setting');
 
1493
    $js_added = TRUE;
 
1494
  }
 
1495
}
 
1496
 
 
1497
 
 
1498
/**
 
1499
 * Add Inline CSS to the page, only used on admin/settings/fivestar page.
 
1500
 */
 
1501
function fivestar_add_inline_css($widget_key = NULL, $css = NULL, $reset = FALSE) {
 
1502
  static $inline_css;
 
1503
 
 
1504
  if (!isset($inline_css) || $reset) {
 
1505
    $inline_css = array();
 
1506
  }
 
1507
 
 
1508
  if (isset($widget_key) && isset($inline_css)) {
 
1509
    $inline_css[$widget_key] = $css;
 
1510
  }
 
1511
 
 
1512
  return $inline_css;
 
1513
}
 
1514
 
 
1515
/**
 
1516
 * Retrieve a list of all inline CSS to be added to the page.
 
1517
 */
 
1518
function fivestar_get_inline_css() {
 
1519
  $inline_css = fivestar_add_inline_css();
 
1520
  return implode("\n", $inline_css);
 
1521
}
 
1522
 
 
1523
/**
 
1524
 * Process callback for fivestar_element -- see fivestar_element()
 
1525
 */
 
1526
function fivestar_expand($element) {
 
1527
  static $fivestar_id = 0;
 
1528
 
 
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,
 
1534
    );
 
1535
  }
 
1536
 
 
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,
 
1542
    );
 
1543
  }
 
1544
 
 
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,
 
1551
    );
 
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,
 
1557
    );
 
1558
  }
 
1559
 
 
1560
  if (!isset($element['#default_value'])) {
 
1561
    $element['#default_value'] = 0;
 
1562
  }
 
1563
 
 
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']);
 
1569
 
 
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');
 
1573
    }
 
1574
    // Display a normal star value.
 
1575
    if ($i > 0) {
 
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']));
 
1578
      }
 
1579
      else {
 
1580
        $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
 
1581
      }
 
1582
    }
 
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;
 
1586
    }
 
1587
  }
 
1588
 
 
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'],
 
1598
  );
 
1599
 
 
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;
 
1603
  }
 
1604
 
 
1605
  // Set a class for the display of label text on hover.
 
1606
  $label_class = $element['#labels_enable'] ? ' fivestar-labels-hover' : '';
 
1607
 
 
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>';
 
1611
 
 
1612
  // Add validation function that considers a 0 value as empty.
 
1613
  $element['#element_validate'] = array('fivestar_validate');
 
1614
 
 
1615
  $fivestar_id++;
 
1616
  return $element;
 
1617
}
 
1618
 
 
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'])));
 
1622
  }
 
1623
}
 
1624
 
 
1625
function fivestar_votingapi_views_formatters($details = array()) {
 
1626
  if ($details->field == 'value') {
 
1627
    return array(
 
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)'),
 
1631
    );
 
1632
  }
 
1633
}
 
1634
 
 
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);
 
1640
    }
 
1641
    else {
 
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);
 
1644
    }
 
1645
 
 
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'];
 
1650
      }
 
1651
    }
 
1652
  }
 
1653
  else {
 
1654
    $stars = 5;
 
1655
    $tag = 'vote';
 
1656
  }
 
1657
 
 
1658
  return theme('fivestar_static', $value, $stars, $tag);
 
1659
}
 
1660
 
 
1661
function fivestar_views_widget_compact_handler($value, $field, $columns) {
 
1662
  return fivestar_views_widget_handler($value, $field, $columns, FALSE);
 
1663
}
 
1664
 
 
1665
function fivestar_views_widget_normal_handler($value, $field, $columns) {
 
1666
  return fivestar_views_widget_handler($value, $field, $columns, TRUE);
 
1667
}
 
1668
 
 
1669
function fivestar_views_widget_handler($value, $field, $columns, $summary) {
 
1670
 
 
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);
 
1674
  }
 
1675
 
 
1676
  if ($field->view->base_table == 'node') {
 
1677
 
 
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'];
 
1682
      }
 
1683
    }
 
1684
 
 
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));
 
1688
 
 
1689
    $votes = fivestar_get_votes($content_type, $content_id, $tag);
 
1690
 
 
1691
    $values = array(
 
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,
 
1695
    );
 
1696
 
 
1697
    $settings = array(
 
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,
 
1706
      'tag' => $tag,
 
1707
      'autosubmit' => TRUE,
 
1708
      'title' => FALSE,
 
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(),
 
1712
    );
 
1713
 
 
1714
    return drupal_get_form('fivestar_custom_widget', $values, $settings);
 
1715
  }
 
1716
  else {
 
1717
    return theme('fivestar_static', $value, 5);
 
1718
  }
 
1719
}