Projet

Général

Profil

Paste
Télécharger (32,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / fivestar / includes / fivestar.field.inc @ d50a36e0

1
<?php
2

    
3
/**
4
 * @file
5
 * Provides CCK integration for fivestar module.
6
 */
7

    
8
/**
9
 * Implements hook_field_info().
10
 */
11
function fivestar_field_info() {
12
  return array(
13
    'fivestar' => array(
14
      'label' => t('Fivestar Rating'),
15
      'description' => t('Store a rating for this piece of content.'),
16
      'default_widget' => 'exposed',
17
      'default_formatter' => 'fivestar_formatter_default',
18
      'settings' => array(
19
        'axis' => 'vote',
20
      ),
21
      'instance_settings' => array(
22
        'stars' => 5,
23
      ),
24
      'property_type' => 'fivestar',
25
      'property_callbacks' => array('fivestar_property_info_callback'),
26
      'microdata' => TRUE,
27
    ),
28
  );
29
}
30

    
31
/**
32
 *
33
 */
34
function fivestar_form_field_ui_field_edit_form_alter(&$form, $form_state) {
35
  $field = $form['#field'];
36
  $instance = $form['#instance'];
37
  if ($field['type'] == 'fivestar') {
38
    // Multiple values is not supported with Fivestar.
39
    $form['field']['cardinality']['#access'] = FALSE;
40
    $form['field']['cardinality']['#value'] = 1;
41
    // Setting "default value" here is confusing and for all practical purposes
42
    // with existing widgets provided by fivestar (and anything else available
43
    // in contrib) meaningless.
44
    $form['instance']['default_value_widget']['#access'] = FALSE;
45
  }
46
}
47

    
48
/**
49
 * Implements hook_field_settings_form().
50
 */
51
function fivestar_field_settings_form($field, $instance) {
52
  $form['axis'] = array(
53
    '#type' => 'select',
54
    '#required' => TRUE,
55
    '#title' => 'Voting Tag',
56
    '#options' => fivestar_get_tags(),
57
    '#description' => t('The tag this rating will affect. Enter a property on which that this rating will affect, such as <em>quality</em>, <em>satisfaction</em>, <em>overall</em>, etc.'),
58
    '#default_value' => isset($field['settings']['axis']) ? $field['settings']['axis'] : '',
59
    '#disabled' => field_has_data($field),
60
  );
61

    
62
  return $form;
63
}
64

    
65
/**
66
 *
67
 */
68
function fivestar_field_instance_settings_form($field, $instance) {
69
  $form = array();
70

    
71
  $widget_title = ($instance['widget']['type'] == 'select') ? t('Number of options') : t('Number of stars');
72
  $form['stars'] = array(
73
    '#type' => 'select',
74
    '#title' => check_plain($widget_title),
75
    '#options' => drupal_map_assoc(range(1, 10)),
76
    '#default_value' => isset($instance['settings']['stars']) ? $instance['settings']['stars'] : 5,
77
  );
78

    
79
  $form['allow_clear'] = array(
80
    '#type' => 'checkbox',
81
    '#title' => t('Allow users to cancel their ratings.'),
82
    '#default_value' => isset($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
83
    '#return_value' => 1,
84
  );
85

    
86
  $form['allow_revote'] = array(
87
    '#type' => 'checkbox',
88
    '#title' => t('Allow users to re-vote on already voted content.'),
89
    '#default_value' => isset($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : TRUE,
90
    '#return_value' => 1,
91
  );
92

    
93
  $form['allow_ownvote'] = array(
94
    '#type' => 'checkbox',
95
    '#title' => t('Allow users to vote on their own content.'),
96
    '#default_value' => isset($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : TRUE,
97
    '#return_value' => 1,
98
  );
99

    
100
  $options = fivestar_get_targets($field, $instance);
101
  $form['target'] = array(
102
    '#title' => t('Voting target'),
103
    '#type' => 'select',
104
    '#default_value' => (isset($instance['settings']['target']) && $instance['widget']['type'] != 'exposed') ? $instance['settings']['target'] : 'none',
105
    '#options' => $options,
106
    '#description' => t('The voting target will make the value of this field cast a vote on another node. Use node reference fields module to create advanced reviews. Use the Parent Node Target when using fivestar with comments. More information available on the <a href="http://drupal.org/handbook/modules/fivestar">Fivestar handbook page</a>.'),
107
    '#access' => (count($options) > 1 && $instance['widget']['type'] != 'exposed'),
108
  );
109

    
110
  return $form;
111
}
112

    
113
/**
114
 * Implements hook_field_insert().
115
 */
116
function fivestar_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
117
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items);
118
}
119

    
120
/**
121
 * Implements hook_field_update().
122
 */
123
function fivestar_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
124
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items);
125
}
126

    
127
/**
128
 * Implements hook_field_delete().
129
 */
130
function fivestar_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
131
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items, 'delete');
132
}
133

    
134
/**
135
 * Implements hook_field_views_data().
136
 */
137
function fivestar_field_views_data($field) {
138
  $data = field_views_field_default_views_data($field);
139
  foreach ($data as $table_name => $table_data) {
140
    // Add our handler to the rating views field.
141
    $data[$table_name][$field['field_name'] . '_rating']['filter']['handler'] = 'fivestar_views_handler_filter_in_operator';
142
  }
143
  return $data;
144
}
145

    
146
/**
147
 *
148
 */
149
function _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, &$items, $op = '') {
150
  foreach ($items as $delta => $item) {
151
    if ((isset($entity->status) && !$entity->status) || $op == 'delete') {
152
      $rating = 0;
153
    }
154
    else {
155
      $rating = (isset($items[$delta]['rating'])) ? $items[$delta]['rating'] : 0;
156
    }
157
    $target = _fivestar_field_target($entity, $field, $instance, $item, $langcode);
158
    if (!empty($target)) {
159
      if ($entity_type == 'comment' && $op == 'delete') {
160
        $target['vote_source'] = $entity->hostname;
161
      }
162
      else {
163
        $target['vote_source'] = NULL;
164
      }
165
      _fivestar_cast_vote($target['entity_type'], $target['entity_id'], $rating, $field['settings']['axis'], $entity->uid, TRUE, $target['vote_source']);
166
      votingapi_recalculate_results($target['entity_type'], $target['entity_id']);
167
    }
168
    // The original callback is only called for a single updated field, but the
169
    // Field API then updates all fields of the entity. For an update, the Field
170
    // API first deletes the equivalent row in the database and then adds a new
171
    // row based on the information in $items here. If there are multiple
172
    // Fivestar fields on an entity, the one being updated is handled OK
173
    // ('rating' having already been set to the new value), but other Fivestar
174
    // fields are set to NULL as 'rating' isn't set - not what an update would
175
    // be expected to do. So set 'rating' for all of the Fivestar fields from
176
    // the existing user data in $items. This preserves the user vote thru Field
177
    // API's delete/re-insert process.
178
    if (!isset($items[$delta]['rating'])) {
179
      $items[$delta]['rating'] = $items[$delta]['user'];
180
    }
181
  }
182
}
183

    
184
/**
185
 * Helper function to find the id that should be rated when a field is changed.
186
 */
187
function _fivestar_field_target($entity, $field, $instance, $item, $langcode) {
188
  if ($instance['widget']['type'] == 'exposed') {
189
    return NULL;
190
  }
191
  if (isset($instance['settings']['target'])) {
192
    $target = fivestar_get_targets($field, $instance, $instance['settings']['target'], $entity, $langcode);
193
  }
194
  else {
195
    // If all else fails, default to voting on the instance the field is attached to.
196
    list($id, $vid, $bundle) = entity_extract_ids($instance['entity_type'], $entity);
197
    $target = array(
198
      'entity_id' => $id,
199
      'entity_type' => $instance['entity_type'],
200
    );
201
  }
202
  return $target;
203
}
204

    
205
/**
206
 * Helper function to store a rating into the field storage.
207
 */
208
function _fivestar_update_field_value($entity_type, $entity, $field_name, $langcode, $value) {
209
  $entity->{$field_name}[$langcode][0]['rating'] = $value;
210
  $entity->original = isset($entity->original) ? $entity->original : NULL;
211
  field_attach_presave($entity_type, $entity);
212
  field_attach_update($entity_type, $entity);
213
}
214

    
215
/**
216
 * Implements hook_field_is_empty().
217
 */
218
function fivestar_field_is_empty($item, $field) {
219
  return empty($item['rating']) || $item['rating'] == '-';
220
}
221

    
222
/**
223
 * Implements hook_field_widget_info().
224
 */
225
function fivestar_field_widget_info() {
226
  return array(
227
    'exposed' => array(
228
      'label' => t('Stars (rated while viewing)'),
229
      'field types' => array('fivestar'),
230
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
231
    ),
232
    'stars' => array(
233
      'label' => t('Stars (rated while editing)'),
234
      'field types' => array('fivestar'),
235
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
236
      'settings' => array(
237
        'widget' => array(
238
          'fivestar_widget' => 'default',
239
        ),
240
      ),
241
    ),
242
    'fivestar_select' => array(
243
      'label' => t('Select list (rated while editing)'),
244
      'field types' => array('fivestar'),
245
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
246
    ),
247
  );
248
}
249

    
250
/**
251
 * Implements hook_field_widget_settings_form().
252
 */
253
function fivestar_field_widget_settings_form($field, $instance) {
254
  $form = array();
255
  if ($instance['widget']['type'] == 'stars') {
256
    $form['widget'] = array(
257
      '#tree' => TRUE,
258
      '#type' => 'fieldset',
259
      '#title' => t('Star display options'),
260
      '#description' => t('Choose a style for your widget.'),
261
      '#weight' => -2,
262
      '#collapsible' => TRUE,
263
      '#collapsed' => TRUE,
264
    );
265

    
266
    $widgets = module_invoke_all('fivestar_widgets');
267

    
268
    $form['widget']['fivestar_widget'] = array(
269
      '#type' => 'radios',
270
      '#options' => array('default' => t('Default')) + $widgets,
271
      '#default_value' => isset($instance['widget']['settings']['widget']['fivestar_widget']) ? $instance['widget']['settings']['widget']['fivestar_widget'] : 'default',
272
      '#attributes' => array('class' => array('fivestar-widgets', 'clearfix')),
273
      '#pre_render' => array('fivestar_previews_expand'),
274
      '#attached' => array('css' => array(drupal_get_path('module', 'fivestar') . '/css/fivestar-admin.css')),
275
    );
276
  }
277

    
278
  return $form;
279
}
280

    
281
/**
282
 *
283
 */
284
function fivestar_previews_expand($element) {
285

    
286
  foreach (element_children($element) as $css) {
287
    $vars = array(
288
      'css' => $css,
289
      'name' => strtolower($element[$css]['#title']),
290
    );
291
    $element[$css]['#description'] = theme('fivestar_preview_widget', $vars);
292
  }
293

    
294
  return $element;
295
}
296

    
297
/**
298
 * Implements hook_field_widget_form().
299
 */
300
function fivestar_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
301
  $element['#tree'] = TRUE;
302
  $i18n = function_exists('i18n_field_translate_property');
303
  if ($instance['widget']['type'] == 'fivestar_select' || ($instance['widget']['type'] == 'stars' && isset($form['#title']) && $form['#title'] == 'Default value')) {
304
    $options = array(0 => t('No stars'));
305
    if (empty($instance['settings']['stars'])) {
306
      $instance['settings']['stars'] = 5;
307
    }
308
    for ($i = 1; $i <= $instance['settings']['stars']; $i++) {
309
      $percentage = ceil($i * 100 / $instance['settings']['stars']);
310
      $options[$percentage] = format_plural($i, '1 star', '@count stars');
311
    }
312
    $element['rating'] = array(
313
      '#type' => 'select',
314
      '#title' => check_plain(isset($instance['label']) ? (($i18n) ? i18n_field_translate_property($instance, 'label') : t($instance['label'])) : FALSE),
315
      '#options' => $options,
316
      '#default_value' => isset($items[$delta]['rating']) ? $items[$delta]['rating'] : NULL,
317
      '#description' => check_plain(isset($instance['description']) ? (($i18n) ? i18n_field_translate_property($instance, 'description') : t($instance['description'])) : FALSE),
318
      '#required' => isset($instance['required']) ? $instance['required'] : FALSE,
319
    );
320
  }
321

    
322
  elseif ($instance['widget']['type'] == 'stars') {
323
    $widgets = module_invoke_all('fivestar_widgets');
324
    $active = isset($instance['widget']['settings']['widget']['fivestar_widget']) ? $instance['widget']['settings']['widget']['fivestar_widget'] : 'default';
325
    $widget = array(
326
      'name' => isset($widgets[$active]) ? strtolower($widgets[$active]) : 'default',
327
      'css' => $active,
328
    );
329

    
330
    $values = array(
331
      'user' => 0,
332
      'average' => 0,
333
      'count' => 0,
334
    );
335

    
336
    $settings = array(
337
      'stars' => $instance['settings']['stars'],
338
      'allow_clear' => !empty($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
339
      'allow_revote' => !empty($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : FALSE,
340
      'allow_ownvote' => !empty($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : FALSE,
341
      'style' => 'user',
342
      'text' => 'none',
343
      'widget' => $widget,
344
    );
345

    
346
    $element['rating'] = array(
347
      '#type' => 'fivestar',
348
      '#title' => check_plain(isset($instance['label']) ? (($i18n) ? i18n_field_translate_property($instance, 'label') : t($instance['label'])) : FALSE),
349
      '#stars' => isset($instance['settings']['stars']) ? $instance['settings']['stars'] : 5,
350
      '#allow_clear' => isset($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
351
      '#allow_revote' => isset($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : FALSE,
352
      '#allow_ownvote' => isset($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : FALSE,
353
      '#default_value' => isset($items[$delta]['rating']) ? $items[$delta]['rating'] : (isset($instance['default_value'][$delta]['rating']) ? $instance['default_value'][$delta]['rating'] : 0),
354
      '#widget' => $widget,
355
      '#settings' => $settings,
356
      '#values' => $values,
357
      '#description' => check_plain(isset($instance['description']) ? (($i18n) ? i18n_field_translate_property($instance, 'description') : t($instance['description'])) : FALSE),
358
      '#required' => isset($instance['required']) ? $instance['required'] : FALSE,
359
    );
360
  }
361

    
362
  return array($element);
363
}
364

    
365
/**
366
 * Implements hook_field_formatter_info().
367
 */
368
function fivestar_field_formatter_info() {
369
  return array(
370
    'fivestar_formatter_default' => array(
371
      'label' => t('As Stars'),
372
      'field types' => array('fivestar'),
373
      'settings' => array(
374
        // Note: Do not set to widget to 'default' by
375
        // default. "Stars (rated while editing)" should
376
        // default to whatever was selected as a widget
377
        // setting. Let hook_field_formatter_view() handle
378
        // defaults for instances that aren't set to anything.
379
        'widget' => array('fivestar_widget' => NULL),
380
        'style' => 'average',
381
        'text' => 'average',
382
        'expose' => TRUE,
383
      ),
384
    ),
385
    'fivestar_formatter_rating' => array(
386
      'label' => t('Rating (e.g. 4.2/5)'),
387
      'field types' => array('fivestar'),
388
    ),
389
    'fivestar_formatter_percentage' => array(
390
      'label' => t('Percentage (e.g. 92)'),
391
      'field types' => array('fivestar'),
392
    ),
393
  );
394
}
395

    
396
/**
397
 * Implements hook_field_formatter_settings_form().
398
 */
399
function fivestar_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
400
  $display = $instance['display'][$view_mode];
401
  $settings = $display['settings'];
402

    
403
  if ($display['type'] != 'fivestar_formatter_default') {
404
    return;
405
  }
406

    
407
  $element['widget'] = array(
408
    '#tree' => TRUE,
409
    '#type' => 'fieldset',
410
    '#title' => t('Star display options'),
411
    '#description' => t('Choose a style for your widget.'),
412
    '#weight' => -2,
413
    '#collapsible' => TRUE,
414
    '#collapsed' => TRUE,
415
  );
416

    
417
  $widgets = module_invoke_all('fivestar_widgets');
418

    
419
  $element['widget']['fivestar_widget'] = array(
420
    '#type' => 'radios',
421
    '#options' => array('default' => t('Default')) + $widgets,
422
    '#default_value' => isset($settings['widget']['fivestar_widget']) ? $settings['widget']['fivestar_widget'] : 'default',
423
    '#attributes' => array('class' => array('fivestar-widgets', 'clearfix')),
424
    '#pre_render' => array('fivestar_previews_expand'),
425
    '#attached' => array('css' => array(drupal_get_path('module', 'fivestar') . '/css/fivestar-admin.css')),
426
  );
427

    
428
  if ($instance['widget']['type'] == 'exposed') {
429
    $element['expose'] = array(
430
      '#type' => 'checkbox',
431
      '#title' => t('Allow voting on the entity.'),
432
      '#default_value' => $settings['expose'],
433
      '#return_value' => 1,
434
    );
435
  }
436

    
437
  $element['style'] = array(
438
    '#type' => 'select',
439
    '#title' => t('Value to display as stars'),
440
    '#default_value' => $settings['style'],
441
    '#options' => array(
442
      'average' => t('Average vote'),
443
      'user'    => t("User's vote"),
444
      'smart'   => t("User's vote if available, average otherwise"),
445
      'dual'    => t("Both user's and average vote"),
446
    ),
447
  );
448
  $element['text'] = array(
449
    '#type' => 'select',
450
    '#title' => t('Text to display under the stars'),
451
    '#default_value' => $settings['text'],
452
    '#options' => array(
453
      'none'    => t('No text'),
454
      'average' => t('Average vote'),
455
      'user'    => t("User's vote"),
456
      'smart'   => t("User's vote if available, average otherwise"),
457
      'dual'    => t("Both user's and average vote"),
458
    ),
459
  );
460

    
461
  return $element;
462
}
463

    
464
/**
465
 * Implements hook_field_formatter_settings_summary().
466
 */
467
function fivestar_field_formatter_settings_summary($field, $instance, $view_mode) {
468
  $display = $instance['display'][$view_mode];
469
  $settings = $display['settings'];
470

    
471
  if ($display['type'] != 'fivestar_formatter_default') {
472
    return;
473
  }
474

    
475
  $widgets = module_invoke_all('fivestar_widgets');
476

    
477
  if ($instance['widget']['type'] == 'exposed') {
478
    $summary = t("Style: @widget, Exposed: @expose, Stars display: @style, Text display: @text", array(
479
      '@widget' => isset($widgets[$settings['widget']['fivestar_widget']]) ? strtolower($widgets[$settings['widget']['fivestar_widget']]) : t('default'),
480
      '@expose' => ($settings['expose']) ? 'yes' : 'no',
481
      '@style' => strtolower($settings['style']),
482
      '@text' => strtolower($settings['text']),
483
    ));
484
    return $summary;
485
  }
486

    
487
  $summary = t("Style: @widget, Stars display: @style, Text display: @text", array(
488
    '@widget' => isset($widgets[$settings['widget']['fivestar_widget']]) ? $widgets[$settings['widget']['fivestar_widget']] : t('default'),
489
    '@style' => strtolower($settings['style']),
490
    '@text' => strtolower($settings['text']),
491
  ));
492

    
493
  return $summary;
494
}
495

    
496
/**
497
 * Implements hook_field_formatter_view().
498
 *
499
 * This function returns a renderable array for each fivestar field
500
 * to be displayed when viewing a node (in any view mode).
501
 * The render array will be either a form array created with
502
 * drupal_get_form() or a custom render array, to be sent to a
503
 * fivestar theme function.
504
 *
505
 * @param $items
506
 *   Array. Generated by fivestar_field_prepare_view(). This array contains
507
 *   processed voting info.
508
 *
509
 * @return element
510
 *   Renderable array. This array will always be $element[0], with only one
511
 *   top level item, because Fivestar does not offer multi-value fields.
512
 */
513
function fivestar_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
514
  $element = array();
515
  $settings = $display['settings'];
516
  $widgets = module_invoke_all('fivestar_widgets');
517
  $widget = _fivestar_get_widget($widgets, $display, $instance);
518
  $values = $items[0];
519

    
520
  // Determine if any set of stars to be displayed need to be
521
  // displayed in a form. (That is, can the user click the stars
522
  // to cast a vote?) If yes, hand off everything we know to the
523
  // fivestar_custom_widget form, and let it take care of the rest.
524
  // Note: Stars will only be displayed in a form in the following circumstance:
525
  // - Fivestar widget selected is "Stars (rated while viewing)"
526
  // - Fivestar display setting = "exposed".
527
  $is_form = ($instance['widget']['type'] == 'exposed'
528
              && user_access('rate content')
529
              && $display['type'] == 'fivestar_formatter_default'
530
              && $display['settings']['expose']) ? TRUE : FALSE;
531
  if ($is_form) {
532
    // @todo Get rid of voting categories setting, then change this so
533
    // axis = field name.
534
    $tag = (isset($field['settings']['axis'])) ? $field['settings']['axis'] : 'vote';
535
    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
536
    $settings = _fivestar_custom_widget_settings($entity_type, $instance, $display, $id, $tag, $widget);
537
    // Store entity and field data for later reuse.
538
    $settings += array(
539
      'entity_id' => $id,
540
      'entity_type' => $entity_type,
541
      'field_name' => $instance['field_name'],
542
      'langcode' => $langcode,
543
    );
544
    // If microdata module is enabled, attach the microdata attributes.
545
    $settings['microdata'] = module_exists('microdata') ? $entity->microdata[$field['field_name']] : array();
546

    
547
    $element[0] = drupal_get_form('fivestar_custom_widget', $values, $settings);
548
    // Our work here is done.
549
    return $element;
550
  }
551

    
552
  // No stars will be displayed in a form. Build a renderable array.
553
  $element[0] = array(
554
    // Add a container div around this field with the clearfix class on it.
555
    '#attributes' => array('class' => array('clearfix')),
556
    '#theme_wrappers' => array('container'),
557
  );
558

    
559
  // Determine if we are going to display stars, rating or percentage.
560
  $formatter = $display['type'];
561
  if ($formatter == 'fivestar_formatter_percentage' || $formatter == 'fivestar_formatter_rating') {
562
    $element[0]['user'] = array(
563
      '#theme' => $formatter,
564
      '#instance_settings' => $instance['settings'],
565
      '#display_settings' => $settings,
566
      '#item' => $values,
567
    );
568
    // No stars to display. Our work here is done.
569
    return $element;
570
  }
571

    
572
  // Determine which sets of stars are going to be displayed.
573
  // Options:
574
  // - Only show average of all votes.
575
  // - Only show the user his/her own vote.
576
  // - Show both the average and the user's own votes.
577
  $style = $display['settings']['style'];
578
  $show_average_stars = ($style == 'average' || $style == 'dual' || ($style == 'smart' && empty($values['user'])));
579
  $show_user_stars = ($style == 'user' || $style == 'dual' || ($style == 'smart' && !empty($values['user'])));
580
  if ($show_user_stars) {
581
    $element[0]['user'] = array(
582
      '#theme' => $display['type'],
583
      '#rating' => $values['user'],
584
      '#instance_settings' => $instance['settings'],
585
      '#display_settings' => $settings,
586
      '#widget' => $widget,
587
    );
588
    $element[0]['#attributes']['class'][] = 'fivestar-user-stars';
589
  }
590
  if ($show_average_stars) {
591
    $element[0]['average'] = array(
592
      '#theme' => $display['type'],
593
      '#rating' => $values['average'],
594
      '#instance_settings' => $instance['settings'],
595
      '#display_settings' => $settings,
596
      '#widget' => $widget,
597
    );
598
    $element[0]['#attributes']['class'][] = 'fivestar-average-stars';
599
  }
600
  if ($style === 'smart') {
601
    $element[0]['#attributes']['class'][] = 'fivestar-smart-stars';
602
  }
603
  elseif ($style === 'dual') {
604
    $element[0]['#attributes']['class'][] = 'fivestar-combo-stars';
605
  }
606

    
607
  // Determine which text is to be displayed.
608
  $text = $display['settings']['text'];
609
  $summary_options = array(
610
    'stars' => $instance['settings']['stars'],
611
    'votes' => NULL,
612
  );
613

    
614
  $summary_options['microdata'] = _fivestar_get_microdata_property_info($entity_type, $entity, $field, $instance);
615

    
616
  // If we're displaying both user and average ratings, add a description to
617
  // both the 'user' and 'average' elements.
618
  if ($style === 'dual') {
619
    $element[0]['user']['#description'] = theme('fivestar_summary', array(
620
      'user_rating' => $values['user'],
621
    ) + $summary_options);
622
    $element[0]['average']['#description'] = theme('fivestar_summary', array(
623
      'average_rating' => $values['average'],
624
      'votes' => $values['count'],
625
    ) + $summary_options);
626
  }
627
  // If we're only creating one element (either 'user' or 'average'), prepare
628
  // the correct description, and place it on that element.
629
  else {
630
    // Prepare the description.
631
    $show_average_text = ($text === 'average' || $text === 'dual' || ($text === 'smart' && empty($values['user'])));
632
    $show_user_text = ($text === 'user' || $text === 'dual' || ($text === 'smart' && !empty($values['user'])));
633
    if ($show_user_text) {
634
      $summary_options['user_rating'] = $values['user'];
635
      $element[0]['#attributes']['class'][] = 'fivestar-user-text';
636
    }
637
    if ($show_average_text) {
638
      $summary_options['average_rating'] = $values['average'];
639
      $summary_options['votes'] = $values['count'];
640
      $element[0]['#attributes']['class'][] = 'fivestar-average-text';
641
    }
642
    if ($text === 'smart') {
643
      $element[0]['#attributes']['class'][] = 'fivestar-smart-text';
644
    }
645
    elseif ($text === 'dual') {
646
      $element[0]['#attributes']['class'][] = 'fivestar-combo-text';
647
    }
648
    // Add the description to the set of stars. It might be named either 'user'
649
    // or 'average', so first figure out its name.
650
    $children = element_children($element[0]);
651
    $name = reset($children);
652
    $element[0][$name]['#description'] = theme('fivestar_summary', $summary_options);
653
  }
654

    
655
  return $element;
656
}
657

    
658
/**
659
 * Generate the $settings parameter to be passed to fivestar_custom_widget().
660
 *
661
 * @params
662
 *
663
 * @return settings
664
 *   Array. @see fivestar_custom_widget().
665
 */
666
function _fivestar_custom_widget_settings($entity_type, $instance, $display, $id, $tag, $widget) {
667
  $settings = $display['settings'];
668
  $settings = array(
669
    'stars' => (!empty($instance['settings']['stars'])) ? $instance['settings']['stars'] : 5,
670
    'allow_clear' => (!empty($instance['settings']['allow_clear'])) ? $instance['settings']['allow_clear'] : 0,
671
    'allow_revote' => (!empty($instance['settings']['allow_revote'])) ? $instance['settings']['allow_revote'] : 0,
672
    'allow_ownvote' => (!empty($instance['settings']['allow_ownvote'])) ? $instance['settings']['allow_ownvote'] : 0,
673
    'style' => $settings['style'],
674
    'text' => $settings['text'],
675
    'content_type' => $entity_type,
676
    'content_id' => $id,
677
    'tag' => $tag,
678
    'autosubmit' => TRUE,
679
    'title' => FALSE,
680
    'labels_enable' => FALSE,
681
    'labels' => array(),
682
    'widget' => $widget,
683
    'description' => $instance['description'],
684
  );
685

    
686
  return $settings;
687
}
688

    
689
/**
690
 * @param $widgets
691
 *   Array, $widgets = module_invoke_all('fivestar_widgets');
692
 *   $widgets = array('path/to/css' => 'Widget Name', 'path/to/more/css' => 'Widget 2');
693
 *
694
 * @param $display
695
 *   Array. This is the $display parameter passed to fivestar_field_formatter_view().
696
 *
697
 * @param $instance
698
 *   Array. This is the $instance parameter passed to fivestar_field_formatter_view().
699
 *
700
 * @return array
701
 *   $widget = array('name' => 'my widget', 'css' => 'sites/all/mymodule/mywidget.css');
702
 */
703
function _fivestar_get_widget($widgets, $display, $instance) {
704
  // If the type doesn't required widgets lets get out of here.
705
  // @todo Implement this WAY better.
706
  if (in_array($display['type'], array('fivestar_formatter_rating', 'fivestar_formatter_percentage'))) {
707
    return FALSE;
708
  }
709

    
710
  // Stars (rated while viewing) is $type = 'exposed'.
711
  // Stars (rated while editing) is $type = 'stars'.
712
  $type = $instance['widget']['type'];
713

    
714
  // Determine which widget to display.
715
  if (!$fivestar_widget = $display['settings']['widget']['fivestar_widget']) {
716
    // No display has been selected and saved by the user.
717
    if ($type == 'exposed') {
718
      // Stars rated while viewing, that is, $type = 'exposed', fall backs on 'default'
719
      // (which is the same as nothing).
720
      $fivestar_widget = 'default';
721
    }
722
    elseif ($type == 'stars') {
723
      // Stars rated while editing, that is, $type = stars,
724
      // falls back on whatever the user selected to be displayed on node/add and node/%/edit.
725
      $fivestar_widget = $instance['widget']['settings']['widget']['fivestar_widget'];
726
    }
727
  }
728

    
729
  $widget = array(
730
    'name' => isset($widgets[$fivestar_widget]) ? strtolower($widgets[$fivestar_widget]) : 'default',
731
    'css' => $fivestar_widget,
732
  );
733

    
734
  return $widget;
735
}
736

    
737
/**
738
 * Implements hook_field_prepare_view().
739
 */
740
function fivestar_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
741
  // @todo Clean this function up!
742
  $exposed_widgets = array();
743
  foreach ($entities as $id => $entity) {
744
    // Ensure the items aren't processed twice.
745
    if (!isset($items[$id][0]['count'])) {
746
      if ($instances[$id]['widget']['type'] == 'exposed') {
747
        $exposed_widgets[] = $id;
748
      }
749
      else {
750
        // If the widget type is not exposed, then the count is always 1 or 0.
751
        // The count is pointless to display.
752
        if (!empty($items[$id][0]['rating'])) {
753
          $values['count'] = 1;
754
          $values['user'] = $items[$id][0]['rating'];
755
          $values['average'] = $items[$id][0]['rating'];
756
        }
757
        else {
758
          $values['count'] = 0;
759
          $values['user'] = 0;
760
          $values['average'] = 0;
761
        }
762
        $items[$id] = array($values);
763
      }
764
    }
765
    if (!empty($exposed_widgets)) {
766
      $votes = fivestar_get_votes_multiple($entity_type, $exposed_widgets, $field['settings']['axis']);
767
      foreach ($votes[$entity_type] as $id => $vote) {
768
        // Populating the $items[$id] array even for items with no value forces
769
        // the render system to output a widget.
770
        $values['user'] = isset($vote['user']['value']) ? $vote['user']['value'] : 0;
771
        $values['average'] = isset($vote['average']['value']) ? $vote['average']['value'] : 0;
772
        $values['count'] = isset($vote['count']['value']) ? $vote['count']['value'] : 0;
773
        $items[$id] = array($values);
774
      }
775
    }
776
  }
777
}
778

    
779
/**
780
 * Implements hook_microdata_value_type_alter().
781
 */
782
function fivestar_microdata_value_types_alter(&$types) {
783
  $types['fivestar'] = 'item_option';
784
}
785

    
786
/**
787
 * Callback to alter the property info of fivestar fields.
788
 */
789
function fivestar_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
790
  $name = $field['field_name'];
791
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
792

    
793
  $property['getter callback'] = '_fivestar_field_property_values';
794
  $property['property info'] = array(
795
    'average_rating' => array(
796
      'label' => t('Average Rating'),
797
      'type' => 'text',
798
      'microdata' => TRUE,
799
    ),
800
    'user_rating' => array(
801
      'label' => t('User\'s Rating'),
802
      'type' => 'text',
803
      'microdata' => TRUE,
804
    ),
805
    'rating_count' => array(
806
      'label' => t('Rating count'),
807
      'type' => 'text',
808
      'microdata' => TRUE,
809
    ),
810
  );
811
}
812

    
813
/**
814
 * Callback for getting field property values.
815
 */
816
function _fivestar_field_property_values($entity, array $options, $name, $entity_type, $info) {
817
  $field = field_info_field($name);
818
  $langcode = field_language($entity_type, $entity, $name, isset($options['language']) ? $options['language']->language : NULL);
819
  $values = array();
820
  if (isset($entity->{$name}[$langcode])) {
821
    foreach ($entity->{$name}[$langcode] as $delta => $data) {
822
      $values[$delta]['user_rating'] = $data['rating'];
823
      $result = array();
824
      $result = votingapi_select_results(array(
825
        'entity_type' => $entity_type,
826
        'entity_id' => entity_id($entity_type, $entity),
827
        'value_type' => 'percent',
828
        'tag' => $field['settings']['axis'],
829
        'function' => 'average',
830
      ));
831
      $values[$delta]['average_rating'] = (isset($result[0]['value']) ? $result[0]['value'] : 0);
832
    }
833
  }
834
  // For an empty single-valued field, we have to return NULL.
835
  return $field['cardinality'] == 1 ? ($values ? reset($values) : NULL) : $values;
836
}
837

    
838
/**
839
 * Get microdata attributes for Fivestar field.
840
 */
841
function _fivestar_get_microdata_property_info($entity_type, $entity, $field, $instance) {
842
  // If microdata module is enabled, attach the microdata attributes the module
843
  // adds to the entity object.
844
  if (module_exists('microdata')) {
845
    $microdata = $entity->microdata[$field['field_name']];
846
  }
847
  // If microdata is not enabled, add empty arrays for each property so we
848
  // don't have to check later in the theme functions.
849
  else {
850
    $microdata = array(
851
      '#attributes' => array(),
852
    );
853
    // If Entity API is enabled, we can use that to get the properties.
854
    // Otherwise, replicate the Entity API logic for getting the properties.
855
    if (module_exists('entity')) {
856
      $entity_info = entity_get_all_property_info($entity_type);
857
    }
858
    else {
859
      $info = array();
860
      $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']] = array();
861
      fivestar_property_info_callback($info, $entity_type, $field, $instance, 'fivestar');
862
      $entity_info = $info[$entity_type]['bundles'][$instance['bundle']]['properties'];
863
    }
864
    foreach ($entity_info[$field['field_name']]['property info'] as $property_name => $property) {
865
      $microdata[$property_name]['#attributes'] = array();
866
    }
867
  }
868

    
869
  return $microdata;
870
}