Projet

Général

Profil

Paste
Télécharger (24,7 ko) Statistiques
| Branche: | Révision:

root / htmltest / sites / all / modules / fivestar / fivestar.module @ dc45a079

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
 * Implementation of hook_permission().
51
 *
52
 * Exposes permissions for rating content.
53
 */
54
function fivestar_permission() {
55
  return array(
56
    'rate content' => array(
57
      'title' => t('rate content'),
58
    ),
59
  );
60
}
61

    
62
/**
63
 * Implementation of hook_theme().
64
 */
65
function fivestar_theme() {
66
  return array(
67
    // Fivestar theme functions.
68
    'fivestar' => array(
69
      'render element' => 'element',
70
      'file' => 'includes/fivestar.theme.inc',
71
    ),
72
    'fivestar_select' => array(
73
      'render element' => 'element',
74
      'file' => 'includes/fivestar.theme.inc',
75
    ),
76
    'fivestar_static' => array(
77
      'variables' => array('rating' => NULL, 'stars' => 5, 'tag' => 'vote', 'widget' => array('name' => 'default', 'css' => '')),
78
      'file' => 'includes/fivestar.theme.inc',
79
    ),
80
    'fivestar_static_element' => array(
81
      'variables' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL),
82
      'file' => 'includes/fivestar.theme.inc',
83
    ),
84
    'fivestar_summary' => array(
85
      'variables' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5, 'microdata' => array()),
86
      'file' => 'includes/fivestar.theme.inc',
87
    ),
88
    // fivestar.admin.inc.
89
    'fivestar_preview' => array(
90
      'variables' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL),
91
      'file' => 'includes/fivestar.theme.inc',
92
    ),
93
    'fivestar_preview_widget' => array(
94
      'variables' => array('css' => NULL, 'name' => NULL),
95
      'file' => 'includes/fivestar.theme.inc',
96
    ),
97
    'fivestar_preview_wrapper' => array(
98
      'variables' => array('content' => NULL, 'type' => 'direct'),
99
      'file' => 'includes/fivestar.theme.inc',
100
    ),
101
    'fivestar_settings' => array(
102
      'render element' => 'form',
103
      'file' => 'includes/fivestar.theme.inc',
104
    ),
105
    'fivestar_color_form' => array(
106
      'render element' => 'form',
107
      'file' => 'includes/fivestar.theme.inc',
108
    ),
109
    'fivestar_formatter_default' => array(
110
      'render element' => 'element',
111
      'file' => 'includes/fivestar.theme.inc',
112
    ),
113
    'fivestar_formatter_rating' => array(
114
      'render element' => 'element',
115
      'file' => 'includes/fivestar.theme.inc',
116
    ),
117
    'fivestar_formatter_percentage' => array(
118
      'render element' => 'element',
119
      'file' => 'includes/fivestar.theme.inc',
120
    ),
121
  );
122
}
123

    
124
function _fivestar_variables() {
125
  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');
126
}
127

    
128
/**
129
 * Internal function to handle vote casting, flood control, XSS, IP based
130
 * voting, etc...
131
 */
132

    
133
function _fivestar_cast_vote($entity_type, $id, $value, $tag = NULL, $uid = NULL, $skip_validation = FALSE) {
134
  global $user;
135
  $tag = empty($tag) ? 'vote' : $tag;
136
  $uid = isset($uid) ? $uid : $user->uid;
137
  // Bail out if the user's trying to vote on an invalid object.
138
  if (!$skip_validation && !fivestar_validate_target($entity_type, $id, $tag, $uid)) {
139
    return array();
140
  }
141
  // Sanity-check the incoming values.
142
  if (is_numeric($id) && is_numeric($value)) {
143
    if ($value > 100) {
144
      $value = 100;
145
    }
146

    
147
    // Get the user's current vote.
148
    $criteria = array('entity_type' => $entity_type, 'entity_id' => $id, 'tag' => $tag, 'uid' => $uid);
149
    // Get the unique identifier for the user (IP Address if anonymous).
150
    $user_criteria = votingapi_current_user_identifier();
151
    $user_votes = votingapi_select_votes($criteria + $user_criteria);
152

    
153
    if ($value == 0) {
154
      votingapi_delete_votes($user_votes);
155
    }
156
    else {
157
      $votes = $criteria += array('value' => $value);
158
      votingapi_set_votes($votes);
159
    }
160

    
161
    // Moving the calculationg after saving/deleting the vote but before getting the votes.
162
    votingapi_recalculate_results($entity_type, $id);    
163
    return fivestar_get_votes($entity_type, $id, $tag, $uid);
164
  }
165
}
166

    
167
/**
168
 * Utility function to retrieve VotingAPI votes.
169
 *
170
 * Note that this should not be used for general vote retrieval, instead the
171
 * VotingAPI function votingapi_select_results() should be used, which is more
172
 * efficient when retrieving multiple votes.
173
 *
174
 * @param $entity_type
175
 *   The Entity type for which to retrieve votes.
176
 * @param $id
177
 *   The ID for which to retrieve votes.
178
 * @param $tag
179
 *   The VotingAPI tag for which to retrieve votes.
180
 * @param $uid
181
 *   Optional. A user ID for which to retrieve votes.
182
 * @return
183
 *   An array of the following keys:
184
 *   - average: An array of VotingAPI results, including the average 'value'.
185
 *   - count: An array of VotingAPI results, including the count 'value'.
186
 *   - user: An array of VotingAPI results, including the user's vote 'value'.
187
 */
188
function fivestar_get_votes($entity_type, $id, $tag = 'vote', $uid = NULL) {
189
  global $user;
190

    
191
  if (!isset($uid)) {
192
    $uid = $user->uid;
193
  }
194

    
195
  $criteria = array(
196
    'entity_type' => $entity_type,
197
    'entity_id' => $id,
198
    'value_type' => 'percent',
199
    'tag' => $tag,
200
  );
201

    
202
  $votes = array(
203
    'average' => array(),
204
    'count' => array(),
205
    'user' => array(),
206
  );
207

    
208
  $results = votingapi_select_results($criteria);
209
  foreach ($results as $result) {
210
    if ($result['function'] == 'average') {
211
      $votes['average'] = $result;
212
    }
213
    if ($result['function'] == 'count') {
214
      $votes['count'] = $result;
215
    }
216
  }
217
  if ($uid) {
218
    $user_vote = votingapi_select_votes($criteria += array('uid' => $uid));
219
    if ($user_vote) {
220
      $votes['user'] = $user_vote[0];
221
      $votes['user']['function'] = 'user';
222
    }
223
  }
224
  else {
225
    // If the user is anonymous, we never bother loading their existing votes.
226
    // Not only would it be hit-or-miss, it would break page caching. Safer to always
227
    // show the 'fresh' version to anon users.
228
    $votes['user'] = array('value' => 0);
229
  }
230

    
231
  return $votes;
232
}
233

    
234
/**
235
 * Check that an item being voted upon is a valid vote.
236
 *
237
 * @param $entity_type
238
 *   Type of target.
239
 * @param $id
240
 *   Identifier within the type.
241
 * @param $tag
242
 *   The VotingAPI tag string.
243
 * @param $uid
244
 *   The user trying to cast the vote.
245
 *
246
 * @return boolean
247
 */
248
function fivestar_validate_target($entity_type, $id, $tag, $uid = NULL) {
249
  if (!isset($uid)) {
250
    $uid = $GLOBALS['user']->uid;
251
  }
252

    
253
  $access = module_invoke_all('fivestar_access', $entity_type, $id, $tag, $uid);
254
  foreach ($access as $result) {
255
    if ($result == TRUE) {
256
      return TRUE;
257
    }
258
    if ($result === FALSE) {
259
      return FALSE;
260
    }
261
  }
262
}
263

    
264
/**
265
 * Implementation of hook_fivestar_access().
266
 *
267
 * This hook is called before every vote is cast through Fivestar. It allows
268
 * modules to allow voting on any type of entity, such as nodes, users, or
269
 * comments.
270
 *
271
 * @param $entity_type
272
 *   Type entity.
273
 * @param $id
274
 *   Identifier within the type.
275
 * @param $tag
276
 *   The VotingAPI tag string.
277
 * @param $uid
278
 *   The user ID trying to cast the vote.
279
 *
280
 * @return boolean or NULL
281
 *   Returns TRUE if voting is supported on this object.
282
 *   Returns NULL if voting is not supported on this object by this module.
283
 *   If needing to absolutely deny all voting on this object, regardless
284
 *   of permissions defined in other modules, return FALSE. Note if all
285
 *   modules return NULL, stating no preference, then access will be denied.
286
 */
287
function fivestar_fivestar_access($entity_type, $id, $tag, $uid) {
288
  // Check to see if there is a field instance on this entity.
289
  $fields = field_read_fields(array('module' => 'fivestar'));
290
  foreach($fields as $field) {
291
    if ($field['settings']['axis'] == $tag) {
292
      $params = array(
293
        'entity_type' => $entity_type,
294
        'field_name' => $field['field_name'],
295
      );
296
      $instance = field_read_instances($params);
297
      if(!empty($instance)) {
298
        return TRUE;
299
      }
300
    }
301
  }
302
}
303

    
304
/**
305
 * Implementation of hook_fivestar_widgets().
306
 *
307
 * This hook allows other modules to create additional custom widgets for
308
 * the fivestar module.
309
 *
310
 * @return array
311
 *   An array of key => value pairs suitable for inclusion as the #options in a
312
 *   select or radios form element. Each key must be the location of a css
313
 *   file for a fivestar widget. Each value should be the name of the widget.
314
 */
315
function fivestar_fivestar_widgets() {
316
  $files = &drupal_static(__FUNCTION__);
317
  if (!isset($files)) {
318
    $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
319
    $files = file_scan_directory($widgets_directory, '/\.css$/');
320
  }
321
  $widgets = array();
322
  foreach ($files as $file) {
323
    if (strpos($file->filename, '-rtl.css') === FALSE) {
324
      $widgets[$file->uri] = drupal_ucfirst(str_replace('-color', '', $file->name));
325
    }
326
  }
327
  return $widgets;
328
}
329

    
330
/**
331
 * Form builder; Build a custom Fivestar rating widget with arbitrary settings.
332
 *
333
 * This function is usually not called directly, instead call
334
 * drupal_get_form('fivestar_custom_widget', $values, $settings) when wanting
335
 * to display a widget.
336
 *
337
 * @param $form_state
338
 *   The form state provided by Form API.
339
 * @param $values
340
 *   An array of current vote values from 0 to 100, with the following array
341
 *   keys:
342
 *   - user: The user's current vote.
343
 *   - average: The average vote value.
344
 *   - count: The total number of votes so far on this content.
345
 * @param $settings
346
 *   An array of settings that configure the properties of the rating widget.
347
 *   Available keys for the settings include:
348
 *   - content_type: The type of content which will be voted upon.
349
 *   - content_id: The content ID which will be voted upon.
350
 *   - stars: The number of stars to display in this widget, from 2 to 10.
351
 *     Defaults to 5.
352
 *   - autosubmit: Whether the form should be submitted upon star selection.
353
 *     Defaults to TRUE.
354
 *   - allow_clear: Whether or not to show the "Clear current vote" icon when
355
 *     showing the widget. Defaults to FALSE.
356
 *   - required: Whether this field is required before the form can be
357
 *     submitted. Defaults to FALSE.
358
 *   - tag: The VotingAPI tag that will be registered by this widget. Defaults
359
 *     to "vote".
360
 */
361
function fivestar_custom_widget($form, &$form_state, $values, $settings) {
362
  $form = array(
363
    '#attributes' => array(
364
      'class' => array('fivestar-widget')
365
    ),
366
  );
367
  $form['#submit'][] = 'fivestar_form_submit';
368

    
369
  $form_state['settings'] = $settings;
370

    
371
  $form['vote'] = array(
372
    '#type' => 'fivestar',
373
    '#stars' => $settings['stars'],
374
    '#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE,
375
    '#allow_clear' => $settings['allow_clear'],
376
    '#required' => isset($settings['required']) ? $settings['required'] : FALSE,
377
    '#widget' => isset($settings['widget']) ? $settings['widget'] : array('name' => 'default', 'css' => 'default'),
378
    '#values' => $values,
379
    '#settings' => $settings,
380
  );
381

    
382
  $form['fivestar_submit'] = array(
383
    '#type' => 'submit',
384
    '#value' => t('Rate'),
385
    '#attributes' => array('class' => array('fivestar-submit')),
386
  );
387

    
388
  return $form;
389
}
390

    
391
/**
392
 * Submit handler for the above form (non-javascript version).
393
 */
394
function fivestar_form_submit($form, &$form_state) {
395
  // Cast the vote.
396
  _fivestar_cast_vote($form_state['settings']['content_type'], $form_state['settings']['content_id'], $form_state['values']['vote'], $form_state['settings']['tag']);
397

    
398
  // Set a message that the vote was received.
399
  if ($form_state['values']['vote'] === '0') {
400
    drupal_set_message(t('Your vote has been cleared.'));
401
  }
402
  elseif (is_numeric($form_state['values']['vote'])) {
403
    drupal_set_message(t('Thank you for your vote.'));
404
  }
405
}
406

    
407
/**
408
 * AJAX submit handler for fivestar_custom_widget
409
 */
410
function fivestar_ajax_submit($form, $form_state) {
411
  _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']);
412
  $votes = _fivestar_cast_vote($form_state['settings']['content_type'], $form_state['settings']['content_id'], $form_state['values']['vote'], $form_state['settings']['tag']);
413

    
414
  $values = array();
415
  $values['user'] = isset($votes['user']['value']) ? $votes['user']['value'] : 0;
416
  $values['average'] = isset($votes['average']['value']) ? $votes['average']['value'] : 0;
417
  $values['count'] = isset($votes['count']['value']) ? $votes['count']['value'] : 0;
418

    
419
  // We need to process the 'fivestar' element with the new values.
420
  $form['vote']['#values'] = $values;
421
  $new_element = fivestar_expand($form['vote']);
422
  // Update the description. Since it doesn't account of our cast vote above.
423
  // TODO: Look into all this, I honestly don't like it and it's a bit weird.
424
  $form['vote']['vote']['#description'] = $new_element['vote']['#description'];
425

    
426
  return array(
427
    '#type' => 'ajax',
428
    '#commands' => array(
429
      array(
430
        'command' => 'fivestarUpdate',
431
        'data' => drupal_render($form['vote']),
432
      ),
433
    ),
434
  );
435
}
436

    
437
/**
438
 * Implementation of hook_elements().
439
 *
440
 * Defines 'fivestar' form element type
441
 */
442
function fivestar_element_info() {
443
  $type['fivestar'] = array(
444
    '#input' => TRUE,
445
    '#stars' => 5,
446
    '#allow_clear' => FALSE,
447
    '#auto_submit' => FALSE,
448
    '#process' => array('fivestar_expand'),
449
    '#theme_wrappers' => array('form_element'),
450
    '#widget' => array(
451
      'name' => 'default',
452
      'css' => 'default',
453
    ),
454
    '#values' => array(
455
      'user' => 0,
456
      'average' => 0,
457
      'count' => 0,
458
    ),
459
    '#settings' => array(
460
      'style' => 'user',
461
      'text' => 'none',
462
    ),
463
  );
464
  return $type;
465
}
466

    
467
/**
468
 * Process callback for fivestar_element -- see fivestar_element()
469
 */
470
function fivestar_expand($element) {
471
  // Add CSS and JS
472
  $path = drupal_get_path('module', 'fivestar');
473
  $element['#attached']['js'][] = $path . '/js/fivestar.js';
474
  $element['#attached']['css'][] = $path . '/css/fivestar.css';
475
  $settings = $element['#settings'];
476
  $values = $element['#values'];
477
  $class[] = 'clearfix';
478

    
479
  $options = array('-' => t('Select rating'));
480
  for ($i = 1; $i <= $element['#stars']; $i++) {
481
    $this_value = ceil($i * 100/$element['#stars']);
482
    $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
483
  }
484
  // Display clear button only if enabled.
485
  if ($element['#allow_clear'] == TRUE) {
486
    $options[0] = t('Cancel rating');
487
  }
488

    
489
  $element['vote'] = array(
490
    '#type' => 'select',
491
    '#options' => $options,
492
    '#required' => $element['#required'],
493
    '#theme' => 'fivestar_select',
494
    '#default_value' => _fivestar_get_element_default_value($element),
495
  );
496

    
497
  if (isset($element['#parents'])) {
498
    $element['vote']['#parents'] = $element['#parents'];
499
  }
500
  if (isset($element['#weight'])) {
501
    $element['vote']['#weight'] = $element['#weight'];
502
  }
503

    
504
  switch ($settings['text']) {
505
    case 'user':
506
      $element['vote']['#description'] = theme('fivestar_summary', array(
507
        'user_rating' => $values['user'],
508
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
509
        'stars' => $settings['stars'],
510
      ));
511
      $class[] = 'fivestar-user-text';
512
      break;
513
    case 'average':
514
      $element['vote']['#description'] = $settings['style'] == 'dual' ? NULL : theme('fivestar_summary', array(
515
        'average_rating' => $values['average'],
516
        'votes' => $values['count'],
517
        'stars' => $settings['stars'],
518
      ));
519
      $class[] = 'fivestar-average-text';
520
      break;
521
    case 'smart':
522
      $element['vote']['#description'] = ($settings['style'] == 'dual' && !$values['user']) ? NULL : theme('fivestar_summary', array(
523
        'user_rating' => $values['user'],
524
        'average_rating' => $values['user'] ? NULL : $values['average'],
525
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
526
        'stars' => $settings['stars'],
527
      ));
528
      $class[] = 'fivestar-smart-text';
529
      $class[] = $values['user'] ? 'fivestar-user-text' : 'fivestar-average-text';
530
      break;
531
    case 'dual':
532
      $element['vote']['#description'] = theme('fivestar_summary', array(
533
        'user_rating' => $values['user'],
534
        'average_rating' => $settings['style'] == 'dual' ? NULL : $values['average'],
535
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
536
        'stars' => $settings['stars'],
537
      ));
538
      $class[] = ' fivestar-combo-text';
539
      break;
540
  }
541

    
542
  switch ($settings['style']) {
543
    case 'average':
544
      $class[] = 'fivestar-average-stars';
545
      break;
546
    case 'user':
547
      $class[] = 'fivestar-user-stars';
548
      break;
549
    case 'smart':
550
      $class[] = 'fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars');
551
      break;
552
    case 'dual':
553
      $class[] = 'fivestar-combo-stars';
554
      $static_average = theme('fivestar_static', array(
555
        'rating' => $values['average'],
556
        'stars' => $settings['stars'],
557
        'tag' => $settings['tag'],
558
        'widget' => $settings['widget'],
559
      ));
560
      if ($settings['text'] != 'none') {
561
        $static_description = theme('fivestar_summary', array(
562
          'average_rating' => $settings['text'] == 'user' ? NULL : (isset($values['average']) ? $values['average'] : 0),
563
          'votes' => isset($values['count']) ? $values['count'] : 0,
564
          'stars' => $settings['stars'],
565
        ));
566
      }
567
      else {
568
        $static_description = '&nbsp;';
569
      }
570
      $element['average'] = array(
571
        '#type' => 'markup',
572
        '#markup' => theme('fivestar_static_element', array(
573
          'star_display' => $static_average,
574
          'title' => '',
575
          'description' => $static_description,
576
        )),
577
        '#weight' => -1,
578
      );
579
      break;
580
  }
581
  $class[] = 'fivestar-form-item';
582
  $class[] = 'fivestar-' . $element['#widget']['name'];
583
  if ($element['#widget']['name'] != 'default') {
584
    $element['#attached']['css'][] = $element['#widget']['css'];
585
  }
586
  $element['#prefix'] = '<div ' . drupal_attributes(array('class' => $class)) . '>';
587
  $element['#suffix'] = '</div>';
588

    
589
  // Add AJAX handling if necessary.
590
  if (!empty($element['#auto_submit'])) {
591
    $element['vote']['#ajax'] = array(
592
      'callback' => 'fivestar_ajax_submit',
593
    );
594
    $element['vote']['#attached']['js'][] = $path . '/js/fivestar.ajax.js';
595
  }
596

    
597
  if (empty($element['#input'])) {
598
    $static_stars = theme('fivestar_static', array(
599
      'rating' => $element['vote']['#default_value'],
600
      'stars' => $settings['stars'],
601
      'tag' => $settings['tag'],
602
      'widget' => $settings['widget'],
603
    ));
604

    
605
    $element['vote'] = array(
606
      '#type' => 'markup',
607
      '#markup' => theme('fivestar_static_element', array(
608
        'star_display' => $static_stars,
609
        'title' => '',
610
        'description' => $element['vote']['#description'],
611
      )),
612
    );
613
  }
614

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

    
618
  return $element;
619
}
620

    
621
/**
622
 * Helper function to get the correct default value for a fivestar element.
623
 *
624
 * @param $element
625
 *   The fivestar element
626
 * @return
627
 *   The default value for the element.
628
 */
629
function _fivestar_get_element_default_value($element) {
630
  if (isset($element['#default_value'])) {
631
    $default_value = $element['#default_value'];
632
  }
633
  else {
634
    switch ($element['#settings']['style']) {
635
      case 'average':
636
        $default_value = $element['#values']['average'];
637
        break;
638
      case 'user':
639
        $default_value = $element['#values']['user'];
640
        break;
641
      case 'smart':
642
        $default_value = (!empty($element['#values']['user']) ? $element['#values']['user'] : $element['#values']['average']);
643
        break;
644
      case 'dual':
645
        $default_value = $element['#values']['user'];
646
        break;
647
      default:
648
        $default_value = $element['#values']['average'];
649
    }
650
  }
651

    
652
  for ($i = 0; $i <= $element['#stars']; $i++) {
653
    $this_value = ceil($i * 100/$element['#stars']);
654
    $next_value = ceil(($i+1) * 100/$element['#stars']);
655

    
656
    // Round up the default value to the next exact star value if needed.
657
    if ($this_value < $default_value && $next_value > $default_value) {
658
      $default_value = $next_value;
659
    }
660
  }
661

    
662
  return $default_value;
663
}
664

    
665
/**
666
 * An #element_validate function for "fivestar" elements.
667
 */
668
function fivestar_validate($element, &$form_state) {
669
  if ($element['#required'] && (empty($element['#value']) || $element['#value'] == '-')) {
670
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
671
  }
672
}
673

    
674
/**
675
 * Implementation of hook_votingapi_metadata_alter().
676
 */
677
function fivestar_votingapi_metadata_alter(&$data) {
678
  foreach (fivestar_get_tags() as $tag) {
679
    // Add several custom tags that are being used by fivestar.
680
    $data['tags'][$tag] = array(
681
      'name' => t($tag),
682
      'description' => t('@tag used by fivestar.', array('@tag' => $tag)),
683
      'module' => 'fivestar',
684
    );
685
  }
686
}
687

    
688
function fivestar_get_tags() {
689
  $tags_txt = variable_get('fivestar_tags', 'vote');
690
  $tags_exploded = explode(',', $tags_txt);
691

    
692
  $tags = array();
693
  $got_vote = false;
694
  foreach ($tags_exploded as $tag) {
695
    $tag_trimmed = trim($tag);
696
    if ($tag_trimmed) {
697
      $tags[$tag_trimmed] = $tag_trimmed;
698
      if ($tag_trimmed == 'vote') {
699
        $got_vote = true;
700
      }
701
    }
702
  }
703

    
704
  if (!$got_vote) {
705
    $tags['vote'] = 'vote';
706
  }
707
  return $tags;
708
}
709

    
710
function fivestar_get_targets($field, $instance, $key = FALSE, $entity = FALSE, $langcode = LANGUAGE_NONE) {
711
  $options = array();
712
  $targets = module_invoke_all('fivestar_target_info', $field, $instance);
713
  if ($key == FALSE) {
714
    foreach ($targets as $target => $info) {
715
      $options[$target] = $info['title'];
716
    }
717
    return $options;
718
  }
719
  else {
720
    if (isset($targets[$key]) && !empty($targets[$key]['callback']) && function_exists($targets[$key]['callback'])) {
721
      return call_user_func($targets[$key]['callback'], $entity, $field, $instance, $langcode);
722
    }
723
  }
724
}
725

    
726
/**
727
 * Implements hook_fivestar_target_info().
728
 */
729
function fivestar_fivestar_target_info($field, $instance) {
730
  $entity_type = $instance['entity_type'];
731
  $bundle = $instance['bundle'];
732
  $options = array(
733
    'none' => array(
734
      'title' => t('None'),
735
    ),
736
  );
737
  // Add node_referrence support.
738
  if (module_exists('node_reference')) {
739
    $field_names = array_keys(field_read_fields(array('module' => 'node_reference')));
740
    if (!empty($field_names)) {
741
      $field_instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle, 'field_name' => $field_names));
742
      if (!empty($field_instances)) {
743
        foreach ($field_instances as $field_instance) {
744
          $options[$field_instance['field_name']] = array(
745
            'title' => t('Node reference: @field', array('@field' => $field_instance['field_name'])),
746
            'callback' => '_fivestar_target_node_reference'
747
          );
748
        }
749
      }
750
    }
751
  }
752

    
753
  // Add comment module support.
754
  if ($instance['entity_type'] == 'comment') {
755
    $options['parent_node'] = array(
756
      'title' => t('Parent Node'),
757
      'callback' => '_fivestar_target_comment_parent_node'
758
    );
759
  }
760

    
761
  return $options;
762
}
763

    
764
/**
765
 *
766
 * @return (array) array('entity_type', 'entity_id')
767
 */
768
function _fivestar_target_node_reference($entity, $field, $instance, $langcode) {
769
  $target = array();
770

    
771
  $node_reference = $instance['settings']['target'];
772
  if (isset($entity->{$node_reference}[$langcode][0]) && is_numeric($entity->{$node_reference}[$langcode][0]['nid'])) {
773
    $target['entity_id'] = $entity->{$node_reference}[$langcode][0]['nid'];
774
    $target['entity_type'] = 'node';
775
  }
776

    
777
  return $target;
778
}
779

    
780
function _fivestar_target_comment_parent_node($entity, $field, $instance, $langcode) {
781
  return array(
782
    'entity_id' => $entity->nid,
783
    'entity_type' => 'node',
784
  );
785
}