Projet

Général

Profil

Paste
Télécharger (25,3 ko) Statistiques
| Branche: | Révision:

root / htmltest / sites / all / modules / fivestar / fivestar.module @ 3cb08e71

1
<?php
2

    
3
/**
4
 * @file
5
 * A simple n-star voting widget, usable in other forms.
6
 */
7

    
8
include_once dirname(__FILE__) . '/includes/fivestar.field.inc';
9

    
10
/**
11
 * Implementation of hook_help().
12
 */
13
function fivestar_help($path, $arg) {
14
  $output = '';
15
  switch ($path) {
16
    case 'admin/config/content/fivestar':
17
      $output = t('This page is used to configure site-wide features of the Fivestar module.');
18
    break;
19
  }
20
  return $output;
21
}
22

    
23
/**
24
 * Implementation of hook_menu().
25
 */
26
function fivestar_menu() {
27
  $items = array();
28
  $items['admin/config/content/fivestar'] = array(
29
    'title'             => 'Fivestar',
30
    'description'       => 'Configure site-wide widgets used for Fivestar rating.',
31
    'page callback'     => 'drupal_get_form',
32
    'page arguments'    => array('fivestar_settings'),
33
    'access callback'   => 'user_access',
34
    'access arguments'  => array('administer site configuration'),
35
    'type'              => MENU_NORMAL_ITEM,
36
    'file'              => 'includes/fivestar.admin.inc',
37
  );
38
  $items['fivestar/preview/color'] = array(
39
    'page callback'     => 'fivestar_preview_color',
40
    'access callback'   => 'user_access',
41
    'access arguments'  => array('administer site configuration'),
42
    'type'              => MENU_CALLBACK,
43
    'file'              => 'includes/fivestar.color.inc',
44
  );
45

    
46
  return $items;
47
}
48

    
49
/**
50
 * Implements hook_microdata_suggestions().
51
 */
52
function fivestar_microdata_suggestions() {
53
  $mappings = array();
54

    
55
  // Add the review mapping for Schema.org
56
  $mappings['fields']['fivestar']['schema.org'] = array(
57
    '#itemprop' => array('aggregateRating'),
58
    '#is_item' => TRUE,
59
    '#itemtype' => array('http://schema.org/AggregateRating'),
60
    'average_rating' => array(
61
      '#itemprop' => array('ratingValue'),
62
    ),
63
  );
64

    
65
  return $mappings;
66
}
67

    
68
/**
69
 * Implementation of hook_permission().
70
 *
71
 * Exposes permissions for rating content.
72
 */
73
function fivestar_permission() {
74
  return array(
75
    'rate content' => array(
76
      'title' => t('rate content'),
77
    ),
78
  );
79
}
80

    
81
/**
82
 * Implementation of hook_theme().
83
 */
84
function fivestar_theme() {
85
  return array(
86
    // Fivestar theme functions.
87
    'fivestar' => array(
88
      'render element' => 'element',
89
      'file' => 'includes/fivestar.theme.inc',
90
    ),
91
    'fivestar_select' => array(
92
      'render element' => 'element',
93
      'file' => 'includes/fivestar.theme.inc',
94
    ),
95
    'fivestar_static' => array(
96
      'variables' => array('rating' => NULL, 'stars' => 5, 'tag' => 'vote', 'widget' => array('name' => 'default', 'css' => '')),
97
      'file' => 'includes/fivestar.theme.inc',
98
    ),
99
    'fivestar_static_element' => array(
100
      'variables' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL),
101
      'file' => 'includes/fivestar.theme.inc',
102
    ),
103
    'fivestar_summary' => array(
104
      'variables' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5, 'microdata' => array()),
105
      'file' => 'includes/fivestar.theme.inc',
106
    ),
107
    // fivestar.admin.inc.
108
    'fivestar_preview' => array(
109
      'variables' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL),
110
      'file' => 'includes/fivestar.theme.inc',
111
    ),
112
    'fivestar_preview_widget' => array(
113
      'variables' => array('css' => NULL, 'name' => NULL),
114
      'file' => 'includes/fivestar.theme.inc',
115
    ),
116
    'fivestar_preview_wrapper' => array(
117
      'variables' => array('content' => NULL, 'type' => 'direct'),
118
      'file' => 'includes/fivestar.theme.inc',
119
    ),
120
    'fivestar_settings' => array(
121
      'render element' => 'form',
122
      'file' => 'includes/fivestar.theme.inc',
123
    ),
124
    'fivestar_color_form' => array(
125
      'render element' => 'form',
126
      'file' => 'includes/fivestar.theme.inc',
127
    ),
128
    'fivestar_formatter_default' => array(
129
      'render element' => 'element',
130
      'file' => 'includes/fivestar.theme.inc',
131
    ),
132
    'fivestar_formatter_rating' => array(
133
      'render element' => 'element',
134
      'file' => 'includes/fivestar.theme.inc',
135
    ),
136
    'fivestar_formatter_percentage' => array(
137
      'render element' => 'element',
138
      'file' => 'includes/fivestar.theme.inc',
139
    ),
140
  );
141
}
142

    
143
function _fivestar_variables() {
144
  return array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser', 'fivestar_labels_enable', 'fivestar_labels', 'fivestar_text', 'fivestar_title', 'fivestar_feedback');
145
}
146

    
147
/**
148
 * Internal function to handle vote casting, flood control, XSS, IP based
149
 * voting, etc...
150
 */
151

    
152
function _fivestar_cast_vote($entity_type, $id, $value, $tag = NULL, $uid = NULL, $skip_validation = FALSE) {
153
  global $user;
154
  $tag = empty($tag) ? 'vote' : $tag;
155
  $uid = isset($uid) ? $uid : $user->uid;
156
  // Bail out if the user's trying to vote on an invalid object.
157
  if (!$skip_validation && !fivestar_validate_target($entity_type, $id, $tag, $uid)) {
158
    return array();
159
  }
160
  // Sanity-check the incoming values.
161
  if (is_numeric($id) && is_numeric($value)) {
162
    if ($value > 100) {
163
      $value = 100;
164
    }
165

    
166
    // Get the user's current vote.
167
    $criteria = array('entity_type' => $entity_type, 'entity_id' => $id, 'tag' => $tag, 'uid' => $uid);
168
    // Get the unique identifier for the user (IP Address if anonymous).
169
    $user_criteria = votingapi_current_user_identifier();
170
    $user_votes = votingapi_select_votes($criteria + $user_criteria);
171

    
172
    if ($value == 0) {
173
      votingapi_delete_votes($user_votes);
174
    }
175
    else {
176
      $votes = $criteria += array('value' => $value);
177
      votingapi_set_votes($votes);
178
    }
179

    
180
    // Moving the calculationg after saving/deleting the vote but before getting the votes.
181
    votingapi_recalculate_results($entity_type, $id);    
182
    return fivestar_get_votes($entity_type, $id, $tag, $uid);
183
  }
184
}
185

    
186
/**
187
 * Utility function to retrieve VotingAPI votes.
188
 *
189
 * Note that this should not be used for general vote retrieval, instead the
190
 * VotingAPI function votingapi_select_results() should be used, which is more
191
 * efficient when retrieving multiple votes.
192
 *
193
 * @param $entity_type
194
 *   The Entity type for which to retrieve votes.
195
 * @param $id
196
 *   The ID for which to retrieve votes.
197
 * @param $tag
198
 *   The VotingAPI tag for which to retrieve votes.
199
 * @param $uid
200
 *   Optional. A user ID for which to retrieve votes.
201
 * @return
202
 *   An array of the following keys:
203
 *   - average: An array of VotingAPI results, including the average 'value'.
204
 *   - count: An array of VotingAPI results, including the count 'value'.
205
 *   - user: An array of VotingAPI results, including the user's vote 'value'.
206
 */
207
function fivestar_get_votes($entity_type, $id, $tag = 'vote', $uid = NULL) {
208
  global $user;
209

    
210
  if (!isset($uid)) {
211
    $uid = $user->uid;
212
  }
213

    
214
  $criteria = array(
215
    'entity_type' => $entity_type,
216
    'entity_id' => $id,
217
    'value_type' => 'percent',
218
    'tag' => $tag,
219
  );
220

    
221
  $votes = array(
222
    'average' => array(),
223
    'count' => array(),
224
    'user' => array(),
225
  );
226

    
227
  $results = votingapi_select_results($criteria);
228
  foreach ($results as $result) {
229
    if ($result['function'] == 'average') {
230
      $votes['average'] = $result;
231
    }
232
    if ($result['function'] == 'count') {
233
      $votes['count'] = $result;
234
    }
235
  }
236
  if ($uid) {
237
    $user_vote = votingapi_select_votes($criteria += array('uid' => $uid));
238
    if ($user_vote) {
239
      $votes['user'] = $user_vote[0];
240
      $votes['user']['function'] = 'user';
241
    }
242
  }
243
  else {
244
    // If the user is anonymous, we never bother loading their existing votes.
245
    // Not only would it be hit-or-miss, it would break page caching. Safer to always
246
    // show the 'fresh' version to anon users.
247
    $votes['user'] = array('value' => 0);
248
  }
249

    
250
  return $votes;
251
}
252

    
253
/**
254
 * Check that an item being voted upon is a valid vote.
255
 *
256
 * @param $entity_type
257
 *   Type of target.
258
 * @param $id
259
 *   Identifier within the type.
260
 * @param $tag
261
 *   The VotingAPI tag string.
262
 * @param $uid
263
 *   The user trying to cast the vote.
264
 *
265
 * @return boolean
266
 */
267
function fivestar_validate_target($entity_type, $id, $tag, $uid = NULL) {
268
  if (!isset($uid)) {
269
    $uid = $GLOBALS['user']->uid;
270
  }
271

    
272
  $access = module_invoke_all('fivestar_access', $entity_type, $id, $tag, $uid);
273
  foreach ($access as $result) {
274
    if ($result == TRUE) {
275
      return TRUE;
276
    }
277
    if ($result === FALSE) {
278
      return FALSE;
279
    }
280
  }
281
}
282

    
283
/**
284
 * Implementation of hook_fivestar_access().
285
 *
286
 * This hook is called before every vote is cast through Fivestar. It allows
287
 * modules to allow voting on any type of entity, such as nodes, users, or
288
 * comments.
289
 *
290
 * @param $entity_type
291
 *   Type entity.
292
 * @param $id
293
 *   Identifier within the type.
294
 * @param $tag
295
 *   The VotingAPI tag string.
296
 * @param $uid
297
 *   The user ID trying to cast the vote.
298
 *
299
 * @return boolean or NULL
300
 *   Returns TRUE if voting is supported on this object.
301
 *   Returns NULL if voting is not supported on this object by this module.
302
 *   If needing to absolutely deny all voting on this object, regardless
303
 *   of permissions defined in other modules, return FALSE. Note if all
304
 *   modules return NULL, stating no preference, then access will be denied.
305
 */
306
function fivestar_fivestar_access($entity_type, $id, $tag, $uid) {
307
  // Check to see if there is a field instance on this entity.
308
  $fields = field_read_fields(array('module' => 'fivestar'));
309
  foreach($fields as $field) {
310
    if ($field['settings']['axis'] == $tag) {
311
      $params = array(
312
        'entity_type' => $entity_type,
313
        'field_name' => $field['field_name'],
314
      );
315
      $instance = field_read_instances($params);
316
      if(!empty($instance)) {
317
        return TRUE;
318
      }
319
    }
320
  }
321
}
322

    
323
/**
324
 * Implementation of hook_fivestar_widgets().
325
 *
326
 * This hook allows other modules to create additional custom widgets for
327
 * the fivestar module.
328
 *
329
 * @return array
330
 *   An array of key => value pairs suitable for inclusion as the #options in a
331
 *   select or radios form element. Each key must be the location of a css
332
 *   file for a fivestar widget. Each value should be the name of the widget.
333
 */
334
function fivestar_fivestar_widgets() {
335
  $files = &drupal_static(__FUNCTION__);
336
  if (!isset($files)) {
337
    $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
338
    $files = file_scan_directory($widgets_directory, '/\.css$/');
339
  }
340
  $widgets = array();
341
  foreach ($files as $file) {
342
    if (strpos($file->filename, '-rtl.css') === FALSE) {
343
      $widgets[$file->uri] = drupal_ucfirst(str_replace('-color', '', $file->name));
344
    }
345
  }
346
  return $widgets;
347
}
348

    
349
/**
350
 * Form builder; Build a custom Fivestar rating widget with arbitrary settings.
351
 *
352
 * This function is usually not called directly, instead call
353
 * drupal_get_form('fivestar_custom_widget', $values, $settings) when wanting
354
 * to display a widget.
355
 *
356
 * @param $form_state
357
 *   The form state provided by Form API.
358
 * @param $values
359
 *   An array of current vote values from 0 to 100, with the following array
360
 *   keys:
361
 *   - user: The user's current vote.
362
 *   - average: The average vote value.
363
 *   - count: The total number of votes so far on this content.
364
 * @param $settings
365
 *   An array of settings that configure the properties of the rating widget.
366
 *   Available keys for the settings include:
367
 *   - content_type: The type of content which will be voted upon.
368
 *   - content_id: The content ID which will be voted upon.
369
 *   - stars: The number of stars to display in this widget, from 2 to 10.
370
 *     Defaults to 5.
371
 *   - autosubmit: Whether the form should be submitted upon star selection.
372
 *     Defaults to TRUE.
373
 *   - allow_clear: Whether or not to show the "Clear current vote" icon when
374
 *     showing the widget. Defaults to FALSE.
375
 *   - required: Whether this field is required before the form can be
376
 *     submitted. Defaults to FALSE.
377
 *   - tag: The VotingAPI tag that will be registered by this widget. Defaults
378
 *     to "vote".
379
 */
380
function fivestar_custom_widget($form, &$form_state, $values, $settings) {
381
  $form = array(
382
    '#attributes' => array(
383
      'class' => array('fivestar-widget')
384
    ),
385
  );
386
  $form['#submit'][] = 'fivestar_form_submit';
387

    
388
  $form_state['settings'] = $settings;
389

    
390
  $form['vote'] = array(
391
    '#type' => 'fivestar',
392
    '#stars' => $settings['stars'],
393
    '#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE,
394
    '#allow_clear' => $settings['allow_clear'],
395
    '#required' => isset($settings['required']) ? $settings['required'] : FALSE,
396
    '#widget' => isset($settings['widget']) ? $settings['widget'] : array('name' => 'default', 'css' => 'default'),
397
    '#values' => $values,
398
    '#settings' => $settings,
399
  );
400

    
401
  $form['fivestar_submit'] = array(
402
    '#type' => 'submit',
403
    '#value' => t('Rate'),
404
    '#attributes' => array('class' => array('fivestar-submit')),
405
  );
406

    
407
  return $form;
408
}
409

    
410
/**
411
 * Submit handler for the above form (non-javascript version).
412
 */
413
function fivestar_form_submit($form, &$form_state) {
414
  // Cast the vote.
415
  _fivestar_cast_vote($form_state['settings']['content_type'], $form_state['settings']['content_id'], $form_state['values']['vote'], $form_state['settings']['tag']);
416

    
417
  // Set a message that the vote was received.
418
  if ($form_state['values']['vote'] === '0') {
419
    drupal_set_message(t('Your vote has been cleared.'));
420
  }
421
  elseif (is_numeric($form_state['values']['vote'])) {
422
    drupal_set_message(t('Thank you for your vote.'));
423
  }
424
}
425

    
426
/**
427
 * AJAX submit handler for fivestar_custom_widget
428
 */
429
function fivestar_ajax_submit($form, $form_state) {
430
  _fivestar_update_field_value($form_state['settings']['content_type'], $form_state['settings']['entity'], $form_state['settings']['field_name'], $form_state['settings']['langcode'], $form_state['values']['vote']);
431
  $votes = _fivestar_cast_vote($form_state['settings']['content_type'], $form_state['settings']['content_id'], $form_state['values']['vote'], $form_state['settings']['tag']);
432

    
433
  $values = array();
434
  $values['user'] = isset($votes['user']['value']) ? $votes['user']['value'] : 0;
435
  $values['average'] = isset($votes['average']['value']) ? $votes['average']['value'] : 0;
436
  $values['count'] = isset($votes['count']['value']) ? $votes['count']['value'] : 0;
437

    
438
  // We need to process the 'fivestar' element with the new values.
439
  $form['vote']['#values'] = $values;
440
  $new_element = fivestar_expand($form['vote']);
441
  // Update the description. Since it doesn't account of our cast vote above.
442
  // TODO: Look into all this, I honestly don't like it and it's a bit weird.
443
  $form['vote']['vote']['#description'] = $new_element['vote']['#description'];
444

    
445
  return array(
446
    '#type' => 'ajax',
447
    '#commands' => array(
448
      array(
449
        'command' => 'fivestarUpdate',
450
        'data' => drupal_render($form['vote']),
451
      ),
452
    ),
453
  );
454
}
455

    
456
/**
457
 * Implementation of hook_elements().
458
 *
459
 * Defines 'fivestar' form element type
460
 */
461
function fivestar_element_info() {
462
  $type['fivestar'] = array(
463
    '#input' => TRUE,
464
    '#stars' => 5,
465
    '#allow_clear' => FALSE,
466
    '#auto_submit' => FALSE,
467
    '#process' => array('fivestar_expand'),
468
    '#theme_wrappers' => array('form_element'),
469
    '#widget' => array(
470
      'name' => 'default',
471
      'css' => 'default',
472
    ),
473
    '#values' => array(
474
      'user' => 0,
475
      'average' => 0,
476
      'count' => 0,
477
    ),
478
    '#settings' => array(
479
      'style' => 'user',
480
      'text' => 'none',
481
    ),
482
  );
483
  return $type;
484
}
485

    
486
/**
487
 * Process callback for fivestar_element -- see fivestar_element()
488
 */
489
function fivestar_expand($element) {
490
  // Add CSS and JS
491
  $path = drupal_get_path('module', 'fivestar');
492
  $element['#attached']['js'][] = $path . '/js/fivestar.js';
493
  $element['#attached']['css'][] = $path . '/css/fivestar.css';
494
  $settings = $element['#settings'];
495
  $values = $element['#values'];
496
  $class[] = 'clearfix';
497

    
498
  $options = array('-' => t('Select rating'));
499
  for ($i = 1; $i <= $element['#stars']; $i++) {
500
    $this_value = ceil($i * 100/$element['#stars']);
501
    $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
502
  }
503
  // Display clear button only if enabled.
504
  if ($element['#allow_clear'] == TRUE) {
505
    $options[0] = t('Cancel rating');
506
  }
507

    
508
  $element['vote'] = array(
509
    '#type' => 'select',
510
    '#options' => $options,
511
    '#required' => $element['#required'],
512
    '#theme' => 'fivestar_select',
513
    '#default_value' => _fivestar_get_element_default_value($element),
514
    '#weight' => -2,
515
  );
516

    
517
  if (isset($element['#parents'])) {
518
    $element['vote']['#parents'] = $element['#parents'];
519
  }
520

    
521
  switch ($settings['text']) {
522
    case 'user':
523
      $element['vote']['#description'] = theme('fivestar_summary', array(
524
        'user_rating' => $values['user'],
525
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
526
        'stars' => $settings['stars'],
527
        'microdata' => $settings['microdata'],
528
      ));
529
      $class[] = 'fivestar-user-text';
530
      break;
531
    case 'average':
532
      $element['vote']['#description'] = $settings['style'] == 'dual' ? NULL : theme('fivestar_summary', array(
533
        'average_rating' => $values['average'],
534
        'votes' => $values['count'],
535
        'stars' => $settings['stars'],
536
        'microdata' => $settings['microdata'],
537
      ));
538
      $class[] = 'fivestar-average-text';
539
      break;
540
    case 'smart':
541
      $element['vote']['#description'] = ($settings['style'] == 'dual' && !$values['user']) ? NULL : theme('fivestar_summary', array(
542
        'user_rating' => $values['user'],
543
        'average_rating' => $values['user'] ? NULL : $values['average'],
544
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
545
        'stars' => $settings['stars'],
546
        'microdata' => $settings['microdata'],
547
      ));
548
      $class[] = 'fivestar-smart-text';
549
      $class[] = $values['user'] ? 'fivestar-user-text' : 'fivestar-average-text';
550
      break;
551
    case 'dual':
552
      $element['vote']['#description'] = theme('fivestar_summary', array(
553
        'user_rating' => $values['user'],
554
        'average_rating' => $settings['style'] == 'dual' ? NULL : $values['average'],
555
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
556
        'stars' => $settings['stars'],
557
        'microdata' => $settings['microdata'],
558
      ));
559
      $class[] = ' fivestar-combo-text';
560
      break;
561
  }
562

    
563
  switch ($settings['style']) {
564
    case 'average':
565
      $class[] = 'fivestar-average-stars';
566
      break;
567
    case 'user':
568
      $class[] = 'fivestar-user-stars';
569
      break;
570
    case 'smart':
571
      $class[] = 'fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars');
572
      break;
573
    case 'dual':
574
      $class[] = 'fivestar-combo-stars';
575
      $static_average = theme('fivestar_static', array(
576
        'rating' => $values['average'],
577
        'stars' => $settings['stars'],
578
        'tag' => $settings['tag'],
579
        'widget' => $settings['widget'],
580
      ));
581
      if ($settings['text'] != 'none') {
582
        $static_description = theme('fivestar_summary', array(
583
          'average_rating' => $settings['text'] == 'user' ? NULL : (isset($values['average']) ? $values['average'] : 0),
584
          'votes' => isset($values['count']) ? $values['count'] : 0,
585
          'stars' => $settings['stars'],
586
        ));
587
      }
588
      else {
589
        $static_description = '&nbsp;';
590
      }
591
      $element['average'] = array(
592
        '#type' => 'markup',
593
        '#markup' => theme('fivestar_static_element', array(
594
          'star_display' => $static_average,
595
          'title' => '',
596
          'description' => $static_description,
597
        )),
598
        '#weight' => -1,
599
      );
600
      break;
601
  }
602
  $class[] = 'fivestar-form-item';
603
  $class[] = 'fivestar-' . $element['#widget']['name'];
604
  if ($element['#widget']['name'] != 'default') {
605
    $element['#attached']['css'][] = $element['#widget']['css'];
606
  }
607
  $element['#prefix'] = '<div ' . drupal_attributes(array('class' => $class)) . '>';
608
  $element['#suffix'] = '</div>';
609

    
610
  // Add AJAX handling if necessary.
611
  if (!empty($element['#auto_submit'])) {
612
    $element['vote']['#ajax'] = array(
613
      'callback' => 'fivestar_ajax_submit',
614
    );
615
    $element['vote']['#attached']['js'][] = $path . '/js/fivestar.ajax.js';
616
  }
617

    
618
  if (empty($element['#input'])) {
619
    $static_stars = theme('fivestar_static', array(
620
      'rating' => $element['vote']['#default_value'],
621
      'stars' => $settings['stars'],
622
      'tag' => $settings['tag'],
623
      'widget' => $settings['widget'],
624
    ));
625

    
626
    $element['vote'] = array(
627
      '#type' => 'markup',
628
      '#markup' => theme('fivestar_static_element', array(
629
        'star_display' => $static_stars,
630
        'title' => '',
631
        'description' => $element['vote']['#description'],
632
      )),
633
    );
634
  }
635

    
636
  // Add validation function that considers a 0 value as empty.
637
  $element['#element_validate'] = array('fivestar_validate');
638

    
639
  return $element;
640
}
641

    
642
/**
643
 * Helper function to get the correct default value for a fivestar element.
644
 *
645
 * @param $element
646
 *   The fivestar element
647
 * @return
648
 *   The default value for the element.
649
 */
650
function _fivestar_get_element_default_value($element) {
651
  if (isset($element['#default_value'])) {
652
    $default_value = $element['#default_value'];
653
  }
654
  else {
655
    switch ($element['#settings']['style']) {
656
      case 'average':
657
        $default_value = $element['#values']['average'];
658
        break;
659
      case 'user':
660
        $default_value = $element['#values']['user'];
661
        break;
662
      case 'smart':
663
        $default_value = (!empty($element['#values']['user']) ? $element['#values']['user'] : $element['#values']['average']);
664
        break;
665
      case 'dual':
666
        $default_value = $element['#values']['user'];
667
        break;
668
      default:
669
        $default_value = $element['#values']['average'];
670
    }
671
  }
672

    
673
  for ($i = 0; $i <= $element['#stars']; $i++) {
674
    $this_value = ceil($i * 100/$element['#stars']);
675
    $next_value = ceil(($i+1) * 100/$element['#stars']);
676

    
677
    // Round up the default value to the next exact star value if needed.
678
    if ($this_value < $default_value && $next_value > $default_value) {
679
      $default_value = $next_value;
680
    }
681
  }
682

    
683
  return $default_value;
684
}
685

    
686
/**
687
 * An #element_validate function for "fivestar" elements.
688
 */
689
function fivestar_validate($element, &$form_state) {
690
  if ($element['#required'] && (empty($element['#value']) || $element['#value'] == '-')) {
691
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
692
  }
693
}
694

    
695
/**
696
 * Implementation of hook_votingapi_metadata_alter().
697
 */
698
function fivestar_votingapi_metadata_alter(&$data) {
699
  foreach (fivestar_get_tags() as $tag) {
700
    // Add several custom tags that are being used by fivestar.
701
    $data['tags'][$tag] = array(
702
      'name' => t($tag),
703
      'description' => t('@tag used by fivestar.', array('@tag' => $tag)),
704
      'module' => 'fivestar',
705
    );
706
  }
707
}
708

    
709
function fivestar_get_tags() {
710
  $tags_txt = variable_get('fivestar_tags', 'vote');
711
  $tags_exploded = explode(',', $tags_txt);
712

    
713
  $tags = array();
714
  $got_vote = false;
715
  foreach ($tags_exploded as $tag) {
716
    $tag_trimmed = trim($tag);
717
    if ($tag_trimmed) {
718
      $tags[$tag_trimmed] = $tag_trimmed;
719
      if ($tag_trimmed == 'vote') {
720
        $got_vote = true;
721
      }
722
    }
723
  }
724

    
725
  if (!$got_vote) {
726
    $tags['vote'] = 'vote';
727
  }
728
  return $tags;
729
}
730

    
731
function fivestar_get_targets($field, $instance, $key = FALSE, $entity = FALSE, $langcode = LANGUAGE_NONE) {
732
  $options = array();
733
  $targets = module_invoke_all('fivestar_target_info', $field, $instance);
734
  if ($key == FALSE) {
735
    foreach ($targets as $target => $info) {
736
      $options[$target] = $info['title'];
737
    }
738
    return $options;
739
  }
740
  else {
741
    if (isset($targets[$key]) && !empty($targets[$key]['callback']) && function_exists($targets[$key]['callback'])) {
742
      return call_user_func($targets[$key]['callback'], $entity, $field, $instance, $langcode);
743
    }
744
  }
745
}
746

    
747
/**
748
 * Implements hook_fivestar_target_info().
749
 */
750
function fivestar_fivestar_target_info($field, $instance) {
751
  $entity_type = $instance['entity_type'];
752
  $bundle = $instance['bundle'];
753
  $options = array(
754
    'none' => array(
755
      'title' => t('None'),
756
    ),
757
  );
758
  // Add node_referrence support.
759
  if (module_exists('node_reference')) {
760
    $field_names = array_keys(field_read_fields(array('module' => 'node_reference')));
761
    if (!empty($field_names)) {
762
      $field_instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle, 'field_name' => $field_names));
763
      if (!empty($field_instances)) {
764
        foreach ($field_instances as $field_instance) {
765
          $options[$field_instance['field_name']] = array(
766
            'title' => t('Node reference: @field', array('@field' => $field_instance['field_name'])),
767
            'callback' => '_fivestar_target_node_reference'
768
          );
769
        }
770
      }
771
    }
772
  }
773

    
774
  // Add comment module support.
775
  if ($instance['entity_type'] == 'comment') {
776
    $options['parent_node'] = array(
777
      'title' => t('Parent Node'),
778
      'callback' => '_fivestar_target_comment_parent_node'
779
    );
780
  }
781

    
782
  return $options;
783
}
784

    
785
/**
786
 *
787
 * @return (array) array('entity_type', 'entity_id')
788
 */
789
function _fivestar_target_node_reference($entity, $field, $instance, $langcode) {
790
  $target = array();
791

    
792
  $node_reference = $instance['settings']['target'];
793
  if (isset($entity->{$node_reference}[$langcode][0]) && is_numeric($entity->{$node_reference}[$langcode][0]['nid'])) {
794
    $target['entity_id'] = $entity->{$node_reference}[$langcode][0]['nid'];
795
    $target['entity_type'] = 'node';
796
  }
797

    
798
  return $target;
799
}
800

    
801
function _fivestar_target_comment_parent_node($entity, $field, $instance, $langcode) {
802
  return array(
803
    'entity_id' => $entity->nid,
804
    'entity_type' => 'node',
805
  );
806
}