Project

General

Profile

Paste
Download (30.6 KB) Statistics
| Branch: | Revision:

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

1
<?php
2

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

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

    
45
/**
46
 * Implementation of hook_field_settings_form().
47
 */
48
function fivestar_field_settings_form($field, $instance) {
49
  $form['axis'] = array(
50
    '#type' => 'select',
51
    '#required' => TRUE,
52
    '#title' => 'Voting Tag',
53
    '#options' => fivestar_get_tags(),
54
    '#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.'),
55
    '#default_value' => isset($field['settings']['axis']) ? $field['settings']['axis'] : '',
56
    '#disabled' => field_has_data($field),
57
  );
58

    
59
  return $form;
60
}
61

    
62
function fivestar_field_instance_settings_form($field, $instance) {
63
  $form = array();
64

    
65
  $widget_title = ($instance['widget']['type'] == 'select') ? t('Number of options') : t('Number of stars');
66
  $form['stars'] = array(
67
    '#type' => 'select',
68
    '#title' => check_plain($widget_title),
69
    '#options' => drupal_map_assoc(range(1, 10)),
70
    '#default_value' => isset($instance['settings']['stars']) ? $instance['settings']['stars'] : 5,
71
  );
72

    
73
  $form['allow_clear'] = array(
74
    '#type' => 'checkbox',
75
    '#title' => t('Allow users to cancel their ratings.'),
76
    '#default_value' => isset($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
77
    '#return_value' => 1,
78
  );
79

    
80
  $form['allow_revote'] = array(
81
    '#type' => 'checkbox',
82
    '#title' => t('Allow users to re-vote on already voted content.'),
83
    '#default_value' => isset($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : TRUE,
84
    '#return_value' => 1,
85
  );
86

    
87
  $form['allow_ownvote'] = array(
88
    '#type' => 'checkbox',
89
    '#title' => t('Allow users to vote on their own content.'),
90
    '#default_value' => isset($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : TRUE,
91
    '#return_value' => 1,
92
  );
93

    
94
  $options = fivestar_get_targets($field, $instance);
95
  $form['target'] = array(
96
    '#title' => t('Voting target'),
97
    '#type' => 'select',
98
    '#default_value' => (isset($instance['settings']['target']) && $instance['widget']['type'] != 'exposed') ? $instance['settings']['target'] : 'none',
99
    '#options' => $options,
100
    '#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>.'),
101
    '#access' => (count($options) > 1 && $instance['widget']['type'] != 'exposed'),
102
  );
103

    
104
  return $form;
105
}
106

    
107
/**
108
 * Implementation of hook_field_insert().
109
 */
110
function fivestar_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
111
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items);
112
}
113

    
114
/**
115
 * Implementation of hook_field_update().
116
 */
117
function fivestar_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
118
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items);
119
}
120

    
121
/**
122
 * Implementation of hook_field_delete().
123
 */
124
function fivestar_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
125
  _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, $items, 'delete');
126
}
127

    
128
function _fivestar_field_helper($entity_type, $entity, $field, $instance, $langcode, &$items, $op = '') {
129
  foreach ($items as $delta => $item) {
130
    if ((isset($entity->status) && !$entity->status) || $op == 'delete') {
131
      $rating = 0;
132
    }
133
    else {
134
      $rating = (isset($items[$delta]['rating'])) ? $items[$delta]['rating'] : 0;
135
    }
136
    $target = _fivestar_field_target($entity, $field, $instance, $item, $langcode);
137
    if (!empty($target)) {
138
      if ($entity_type == 'comment' && $op == 'delete') {
139
        $target['vote_source'] = $entity->hostname;
140
      }
141
      else {
142
        $target['vote_source'] = NULL;
143
      }
144
      _fivestar_cast_vote($target['entity_type'], $target['entity_id'], $rating, $field['settings']['axis'], $entity->uid, TRUE, $target['vote_source']);
145
      votingapi_recalculate_results($target['entity_type'], $target['entity_id']);
146
    }
147
    // The original callback is only called for a single updated field, but the Field API
148
    // then updates all fields of the entity. For an update, the Field API first deletes
149
    // the equivalent row in the database and then adds a new row based on the
150
    // information in $items here. If there are multiple Fivestar fields on an entity, the
151
    // one being updated is handled ok ('rating' having already been set to the new value),
152
    // but other Fivestar fields are set to NULL as 'rating' isn't set - not what an update
153
    // would be expected to do. So set 'rating' for all of the Fivestar fields from the
154
    // existing user data in $items. This preserves the user vote thru Field API's
155
    // delete/re-insert process.
156
    if (!isset($items[$delta]['rating'])) {
157
      $items[$delta]['rating'] = $items[$delta]['user'];
158
    }
159
  }
160
}
161

    
162
/**
163
 * Helper function to find the id that should be rated when a field is changed.
164
 */
165
function _fivestar_field_target($entity, $field, $instance, $item, $langcode) {
166
  if ($instance['widget']['type'] == 'exposed') {
167
    return NULL;
168
  }
169
  if (isset($instance['settings']['target'])) {
170
    $target = fivestar_get_targets($field, $instance, $instance['settings']['target'], $entity, $langcode);
171
  }
172
  else {
173
    // If all else fails, default to voting on the instance the field is attached to.
174
    list($id, $vid, $bundle) = entity_extract_ids($instance['entity_type'], $entity);
175
    $target = array(
176
      'entity_id' => $id,
177
      'entity_type' => $instance['entity_type'],
178
    );
179
  }
180
  return $target;
181
}
182

    
183
/**
184
 * Helper function to store a rating into the field storage.
185
 */
186
function _fivestar_update_field_value($entity_type, $entity, $field_name, $langcode, $value) {
187
  $entity->{$field_name}[$langcode][0]['rating'] = $value;
188
  $entity->original = isset($entity->original) ? $entity->original : NULL;
189
  field_attach_presave($entity_type, $entity);
190
  field_attach_update($entity_type, $entity);
191
}
192

    
193
/**
194
 * Implementation of hook_field_is_empty().
195
 */
196
function fivestar_field_is_empty($item, $field) {
197
  return empty($item['rating']) || $item['rating'] == '-';
198
}
199

    
200
/**
201
 * Implementation of hook_field_widget_info().
202
 */
203
function fivestar_field_widget_info() {
204
  return array(
205
    'exposed' => array(
206
      'label' => t('Stars (rated while viewing)'),
207
      'field types' => array('fivestar'),
208
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
209
    ),
210
    'stars' => array(
211
      'label' => t('Stars (rated while editing)'),
212
      'field types' => array('fivestar'),
213
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
214
      'settings' => array(
215
        'widget' => array(
216
          'fivestar_widget' => 'default',
217
        )
218
      )
219
    ),
220
    'fivestar_select' => array(
221
      'label' => t('Select list (rated while editing)'),
222
      'field types' => array('fivestar'),
223
      'behaviors' => array('multiple values' => FIELD_BEHAVIOR_NONE),
224
    ),
225
  );
226
}
227

    
228
/**
229
 * Implementation of hook_field_widget_settings_form().
230
 */
231
function fivestar_field_widget_settings_form($field, $instance) {
232
  $form = array();
233
  if ($instance['widget']['type'] == 'stars') {
234
    $form['widget'] = array(
235
      '#tree' => TRUE,
236
      '#type' => 'fieldset',
237
      '#title' => t('Star display options'),
238
      '#description' => t('Choose a style for your widget.'),
239
      '#weight' => -2,
240
      '#collapsible' => TRUE,
241
      '#collapsed' => TRUE,
242
    );
243

    
244
    $widgets = module_invoke_all('fivestar_widgets');
245

    
246
    $form['widget']['fivestar_widget'] = array(
247
      '#type' => 'radios',
248
      '#options' => array('default' => t('Default')) + $widgets,
249
      '#default_value' => isset($instance['widget']['settings']['widget']['fivestar_widget']) ? $instance['widget']['settings']['widget']['fivestar_widget'] : 'default',
250
      '#attributes' => array('class' => array('fivestar-widgets', 'clearfix')),
251
      '#pre_render' => array('fivestar_previews_expand'),
252
      '#attached' => array('css' => array(drupal_get_path('module', 'fivestar') . '/css/fivestar-admin.css')),
253
    );
254
  }
255

    
256
  return $form;
257
}
258

    
259
function fivestar_previews_expand($element) {
260

    
261
  foreach (element_children($element) as $css) {
262
    $vars = array(
263
      'css' => $css,
264
      'name' => strtolower($element[$css]['#title']),
265
    );
266
    $element[$css]['#description'] = theme('fivestar_preview_widget', $vars);
267
  }
268

    
269
  return $element;
270
}
271

    
272
/**
273
 * Implementation of hook_field_widget_form().
274
 */
275
function fivestar_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
276
  $element['#tree'] = TRUE;
277
  $i18n = function_exists('i18n_field_translate_property');
278
  if ($instance['widget']['type'] == 'fivestar_select' || ($instance['widget']['type'] == 'stars' && isset($form['#title']) && $form['#title'] == 'Default value')) {
279
    $options = array(0 => t('No stars'));
280
    if (empty($instance['settings']['stars'])) {
281
      $instance['settings']['stars'] = 5;
282
    }
283
    for ($i = 1; $i <= $instance['settings']['stars']; $i++) {
284
      $percentage = ceil($i * 100 / $instance['settings']['stars']);
285
      $options[$percentage] = format_plural($i, '1 star', '@count stars');
286
    }
287
    $element['rating'] = array(
288
      '#type' => 'select',
289
      '#title' => isset($instance['label']) ? (($i18n) ? i18n_field_translate_property($instance, 'label') : t($instance['label'])) : FALSE,
290
      '#options' => $options,
291
      '#default_value' => isset($items[$delta]['rating']) ? $items[$delta]['rating'] : NULL,
292
      '#description' => isset($instance['description']) ? (($i18n) ? i18n_field_translate_property($instance, 'description') : t($instance['description'])) : FALSE,
293
      '#required' => isset($instance['required']) ? $instance['required'] : FALSE,
294
    );
295
  }
296

    
297
  elseif ($instance['widget']['type'] == 'stars') {
298
    $widgets = module_invoke_all('fivestar_widgets');
299
    $active = isset($instance['widget']['settings']['widget']['fivestar_widget']) ? $instance['widget']['settings']['widget']['fivestar_widget'] : 'default';
300
    $widget = array(
301
      'name' => isset($widgets[$active]) ? strtolower($widgets[$active]) : 'default',
302
      'css' => $active,
303
    );
304

    
305
    $values = array(
306
      'user' => 0,
307
      'average' => 0,
308
      'count' => 0,
309
    );
310

    
311
    $settings = array(
312
      'stars' => $instance['settings']['stars'],
313
      'allow_clear' => !empty($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
314
      'allow_revote' => !empty($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : FALSE,
315
      'allow_ownvote' => !empty($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : FALSE,
316
      'style' => 'user',
317
      'text' => 'none',
318
      'widget' => $widget,
319
    );
320

    
321
    $element['rating'] = array(
322
      '#type'=> 'fivestar',
323
      '#title' => isset($instance['label']) ? (($i18n) ? i18n_field_translate_property($instance, 'label') : t($instance['label'])) : FALSE,
324
      '#stars' => isset($instance['settings']['stars']) ? $instance['settings']['stars'] : 5,
325
      '#allow_clear' => isset($instance['settings']['allow_clear']) ? $instance['settings']['allow_clear'] : FALSE,
326
      '#allow_revote' => isset($instance['settings']['allow_revote']) ? $instance['settings']['allow_revote'] : FALSE,
327
      '#allow_ownvote' => isset($instance['settings']['allow_ownvote']) ? $instance['settings']['allow_ownvote'] : FALSE,
328
      '#default_value' => isset($items[$delta]['rating']) ? $items[$delta]['rating'] : (isset($instance['default_value'][$delta]['rating']) ? $instance['default_value'][$delta]['rating'] : 0),
329
      '#widget' => $widget,
330
      '#settings' => $settings,
331
      '#values' => $values,
332
      '#description' => isset($instance['description']) ? (($i18n) ? i18n_field_translate_property($instance, 'description') : t($instance['description'])) : FALSE,
333
      '#required' => isset($instance['required']) ? $instance['required'] : FALSE,
334
    );
335
  }
336

    
337
  return array($element);
338
}
339

    
340
/**
341
 * Implementation of hook_field_formatter_info().
342
 */
343
function fivestar_field_formatter_info() {
344
  return array(
345
    'fivestar_formatter_default' => array(
346
      'label' => t('As Stars'),
347
      'field types' => array('fivestar'),
348
      'settings' =>  array(
349
        // Note: Do not set to widget to 'default' by
350
        // default. "Stars (rated while editing)" should
351
        // default to whatever was selected as a widget
352
        // setting. Let hook_field_formatter_view() handle
353
        // defaults for instances that aren't set to anything.
354
        'widget' => array('fivestar_widget' => NULL),
355
        'style' => 'average',
356
        'text' => 'average',
357
        'expose' => TRUE,
358
      ),
359
    ),
360
    'fivestar_formatter_rating' => array(
361
      'label' => t('Rating (e.g. 4.2/5)'),
362
      'field types' => array('fivestar'),
363
    ),
364
    'fivestar_formatter_percentage' => array(
365
      'label' => t('Percentage (e.g. 92)'),
366
      'field types' => array('fivestar'),
367
    ),
368
  );
369
}
370

    
371
/**
372
 * Implements hook_field_formatter_settings_form().
373
 */
374
function fivestar_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
375
  $display = $instance['display'][$view_mode];
376
  $settings = $display['settings'];
377

    
378
  if ($display['type'] != 'fivestar_formatter_default') {
379
    return;
380
  }
381

    
382
  $element['widget'] = array(
383
    '#tree' => TRUE,
384
    '#type' => 'fieldset',
385
    '#title' => t('Star display options'),
386
    '#description' => t('Choose a style for your widget.'),
387
    '#weight' => -2,
388
    '#collapsible' => TRUE,
389
    '#collapsed' => TRUE,
390
  );
391

    
392
  $widgets = module_invoke_all('fivestar_widgets');
393

    
394
  $element['widget']['fivestar_widget'] = array(
395
    '#type' => 'radios',
396
    '#options' => array('default' => t('Default')) + $widgets,
397
    '#default_value' => isset($settings['widget']['fivestar_widget']) ? $settings['widget']['fivestar_widget'] : 'default',
398
    '#attributes' => array('class' => array('fivestar-widgets', 'clearfix')),
399
    '#pre_render' => array('fivestar_previews_expand'),
400
    '#attached' => array('css' => array(drupal_get_path('module', 'fivestar') . '/css/fivestar-admin.css')),
401
  );
402

    
403
  if ($instance['widget']['type'] == 'exposed') {
404
    $element['expose'] = array(
405
      '#type' => 'checkbox',
406
      '#title' => t('Allow voting on the entity.'),
407
      '#default_value' => $settings['expose'],
408
      '#return_value' => 1
409
    );
410
  }
411

    
412
  $element['style'] = array(
413
    '#type' => 'select',
414
    '#title' => t('Value to display as stars'),
415
    '#default_value' => $settings['style'],
416
    '#options' => array(
417
      'average' => t('Average vote'),
418
      'user'    => t("User's vote"),
419
      'smart'   => t("User's vote if available, average otherwise"),
420
      'dual'    => t("Both user's and average vote"),
421
    ),
422
  );
423
  $element['text'] = array(
424
    '#type' => 'select',
425
    '#title' => t('Text to display under the stars'),
426
    '#default_value' => $settings['text'],
427
    '#options' => array(
428
      'none'    => t('No text'),
429
      'average' => t('Average vote'),
430
      'user'    => t("User's vote"),
431
      'smart'   => t("User's vote if available, average otherwise"),
432
      'dual'    => t("Both user's and average vote"),
433
    ),
434
  );
435

    
436
  return $element;
437
}
438

    
439
/**
440
 * Implements hook_field_formatter_settings_summary().
441
 */
442
function fivestar_field_formatter_settings_summary($field, $instance, $view_mode) {
443
  $display = $instance['display'][$view_mode];
444
  $settings = $display['settings'];
445

    
446
  if ($display['type'] != 'fivestar_formatter_default') {
447
    return;
448
  }
449

    
450
  $widgets = module_invoke_all('fivestar_widgets');
451

    
452
  if ($instance['widget']['type'] == 'exposed') {
453
    $summary = t("Style: @widget, Exposed: @expose, Stars display: @style, Text display: @text", array(
454
      '@widget' => isset($widgets[$settings['widget']['fivestar_widget']]) ? strtolower($widgets[$settings['widget']['fivestar_widget']]) : t('default'),
455
      '@expose' => ($settings['expose']) ? 'yes' : 'no',
456
      '@style' => strtolower($settings['style']),
457
      '@text' => strtolower($settings['text'])));
458
    return $summary;
459
  }
460

    
461
  $summary = t("Style: @widget, Stars display: @style, Text display: @text", array(
462
    '@widget' => isset($widgets[$settings['widget']['fivestar_widget']]) ? $widgets[$settings['widget']['fivestar_widget']] : t('default'),
463
    '@style' => strtolower($settings['style']),
464
    '@text' => strtolower($settings['text'])));
465

    
466
  return $summary;
467
}
468

    
469
/**
470
 * Implements hook_field_formatter_view().
471
 *
472
 * This function returns a renderable array for each fivestar field
473
 * to be displayed when viewing a node (in any view mode).
474
 * The render array will be either a form array created with
475
 * drupal_get_form() or a custom render array, to be sent to a
476
 * fivestar theme function.
477
 *
478
 * @param $items
479
 *  Array. Generated by fivestar_field_prepare_view(). This array contains
480
 *  processed voting info.
481
 *
482
 * @return $element
483
 *  Renderable array. This array will always be $element[0], with only one
484
 *  top level item, because Fivestar does not offer multi-value fields.
485
 */
486
function fivestar_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
487
  $element = array();
488
  $settings = $display['settings'];
489
  $widgets = module_invoke_all('fivestar_widgets');
490
  $widget = _fivestar_get_widget($widgets, $display, $instance);
491
  $values = $items[0];
492

    
493
  // Determine if any set of stars to be displayed need to be
494
  // displayed in a form. (That is, can the user click the stars
495
  // to cast a vote?) If yes, hand off everything we know to the
496
  // fivestar_custom_widget form, and let it take care of the rest.
497
  // Note: Stars will only be displayed in a form in the following circumstance:
498
  // - Fivestar widget selected is "Stars (rated while viewing)"
499
  // - Fivestar display setting = "exposed"
500
  $is_form = ($instance['widget']['type'] == 'exposed'
501
              && user_access('rate content')
502
              && $display['type'] == 'fivestar_formatter_default'
503
              && $display['settings']['expose']) ? TRUE : FALSE;
504
  if ($is_form) {
505
    // TODO. Get rid of voting categories setting, then change this so
506
    // axis = field name.
507
    $tag = (isset($field['settings']['axis'])) ? $field['settings']['axis'] : 'vote';
508
    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
509
    $settings = _fivestar_custom_widget_settings($entity_type, $instance, $display, $id, $tag, $widget);
510
    // Store entity and field data for later reuse.
511
    $settings += array(
512
      'entity_id' => $id,
513
      'entity_type' => $entity_type,
514
      'field_name' => $instance['field_name'],
515
      'langcode' => $langcode,
516
    );
517
    // If microdata module is enabled, attach the microdata attributes.
518
    $settings['microdata'] = module_exists('microdata') ? $entity->microdata[$field['field_name']] : array();
519

    
520
    $element[0] = drupal_get_form('fivestar_custom_widget', $values, $settings);
521
    // Our work here is done.
522
    return $element;
523
  }
524

    
525
  // No stars will be displayed in a form. Build a renderable array.
526
  $element[0] = array(
527
    // Add a container div around this field with the clearfix class on it.
528
    '#attributes' => array('class' => array('clearfix')),
529
    '#theme_wrappers' => array('container'),
530
  );
531

    
532
  // Determine if we are going to display stars, rating or percentage.
533
  $formatter = $display['type'];
534
  if ($formatter == 'fivestar_formatter_percentage' || $formatter == 'fivestar_formatter_rating') {
535
    $element[0]['user'] = array(
536
      '#theme' => $formatter,
537
      '#instance_settings' => $instance['settings'],
538
      '#display_settings' => $settings,
539
      '#item' => $values,
540
    );
541
    // No stars to display. Our work here is done.
542
    return $element;
543
  }
544

    
545
  // Determine which sets of stars are going to be displayed.
546
  // Options:
547
  // - Only show average of all votes.
548
  // - Only show the user his/her own vote.
549
  // - Show both the average and the user's own votes.
550
  $style = $display['settings']['style'];
551
  $show_average_stars = ($style == 'average' || $style == 'dual' || ($style == 'smart' && empty($values['user'])));
552
  $show_user_stars = ($style == 'user' || $style == 'dual' || ($style == 'smart' && !empty($values['user'])));
553
  if ($show_user_stars) {
554
    $element[0]['user'] = array(
555
      '#theme' => $display['type'],
556
      '#rating' => $values['user'],
557
      '#instance_settings' => $instance['settings'],
558
      '#display_settings' => $settings,
559
      '#widget' => $widget,
560
    );
561
    $element[0]['#attributes']['class'][] = 'fivestar-user-stars';
562
  }
563
  if ($show_average_stars) {
564
    $element[0]['average'] = array(
565
      '#theme' => $display['type'],
566
      '#rating' => $values['average'],
567
      '#instance_settings' => $instance['settings'],
568
      '#display_settings' => $settings,
569
      '#widget' => $widget,
570
    );
571
    $element[0]['#attributes']['class'][] = 'fivestar-average-stars';
572
  }
573
  if ($style === 'smart') {
574
    $element[0]['#attributes']['class'][] = 'fivestar-smart-stars';
575
  }
576
  elseif ($style === 'dual') {
577
    $element[0]['#attributes']['class'][] = 'fivestar-combo-stars';
578
  }
579

    
580
  // Determine which text is to be displayed.
581
  $text = $display['settings']['text'];
582
  $summary_options = array(
583
    'stars' => $instance['settings']['stars'],
584
    'votes' => NULL,
585
  );
586

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

    
589
  // If we're displaying both user and average ratings, add a description to
590
  // both the 'user' and 'average' elements.
591
  if ($style === 'dual') {
592
    $element[0]['user']['#description'] = theme('fivestar_summary', array(
593
      'user_rating' => $values['user'],
594
    ) + $summary_options);
595
    $element[0]['average']['#description'] = theme('fivestar_summary', array(
596
      'average_rating' => $values['average'],
597
      'votes' => $values['count'],
598
    ) + $summary_options);
599
  }
600
  // If we're only creating one element (either 'user' or 'average'), prepare
601
  // the correct description, and place it on that element.
602
  else {
603
    // Prepare the description.
604
    $show_average_text = ($text === 'average' || $text === 'dual' || ($text === 'smart' && empty($values['user'])));
605
    $show_user_text = ($text === 'user' || $text === 'dual' || ($text === 'smart' && !empty($values['user'])));
606
    if ($show_user_text) {
607
      $summary_options['user_rating'] = $values['user'];
608
      $element[0]['#attributes']['class'][] = 'fivestar-user-text';
609
    }
610
    if ($show_average_text) {
611
      $summary_options['average_rating'] = $values['average'];
612
      $summary_options['votes'] = $values['count'];
613
      $element[0]['#attributes']['class'][] = 'fivestar-average-text';
614
    }
615
    if ($text === 'smart') {
616
      $element[0]['#attributes']['class'][] = 'fivestar-smart-text';
617
    }
618
    elseif ($text === 'dual') {
619
      $element[0]['#attributes']['class'][] = 'fivestar-combo-text';
620
    }
621
    // Add the description to the set of stars. It might be named either 'user'
622
    // or 'average', so first figure out its name.
623
    $children = element_children($element[0]);
624
    $name = reset($children);
625
    $element[0][$name]['#description'] = theme('fivestar_summary', $summary_options);
626
  }
627

    
628
  return $element;
629
}
630

    
631
/**
632
 * Generate the $settings parameter to be passed to fivestar_custom_widget().
633
 *
634
 * @params
635
 *
636
 * @return $settings
637
 *  Array. @see fivestar_custom_widget().
638
 */
639
function _fivestar_custom_widget_settings($entity_type, $instance, $display, $id, $tag, $widget) {
640
  $settings = $display['settings'];
641
  $settings = array(
642
    'stars' => (!empty($instance['settings']['stars'])) ? $instance['settings']['stars'] : 5,
643
    'allow_clear' => (!empty($instance['settings']['allow_clear'])) ? $instance['settings']['allow_clear'] : 0,
644
    'allow_revote' => (!empty($instance['settings']['allow_revote'])) ? $instance['settings']['allow_revote'] : 0,
645
    'allow_ownvote' => (!empty($instance['settings']['allow_ownvote'])) ? $instance['settings']['allow_ownvote'] : 0,
646
    'style' => $settings['style'],
647
    'text' => $settings['text'],
648
    'content_type' => $entity_type,
649
    'content_id' => $id,
650
    'tag' => $tag,
651
    'autosubmit' => TRUE,
652
    'title' => FALSE,
653
    'labels_enable' => FALSE,
654
    'labels' => array(),
655
    'widget' => $widget,
656
    'description' => $instance['description'],
657
  );
658

    
659
  return $settings;
660
}
661

    
662
/**
663
 * @param $widgets
664
 *  Array, $widgets = module_invoke_all('fivestar_widgets');
665
 *  $widgets = array('path/to/css' => 'Widget Name', 'path/to/more/css' => 'Widget 2');
666
 *
667
 * @param $display
668
 *  Array. This is the $display parameter passed to fivestar_field_formatter_view().
669
 *
670
 * @param $instance
671
 *  Array. This is the $instance parameter passed to fivestar_field_formatter_view().
672
 *
673
 * @return $widget
674
 *  Array. $widget = array('name' => 'my widget', 'css' => 'sites/all/mymodule/mywidget.css');
675
 */
676
function _fivestar_get_widget($widgets, $display, $instance) {
677
  // If the type doesn't required widgets lets get out of here.
678
  // TODO: Implement this WAY better.
679
  if (in_array($display['type'], array('fivestar_formatter_rating', 'fivestar_formatter_percentage'))) {
680
    return FALSE;
681
  }
682

    
683
  // Stars (rated while viewing) is $type = 'exposed'.
684
  // Stars (rated while editing) is $type = 'stars'.
685
  $type = $instance['widget']['type'];
686

    
687
  // Determine which widget to display.
688
  if (!$fivestar_widget = $display['settings']['widget']['fivestar_widget']) {
689
    // No display has been selected and saved by the user.
690
    if ($type == 'exposed') {
691
      // Stars rated while viewing, that is, $type = 'exposed', fall backs on 'default'
692
      // (which is the same as nothing).
693
      $fivestar_widget = 'default';
694
    }
695
    elseif ($type == 'stars') {
696
      // Stars rated while editing, that is, $type = stars,
697
      // falls back on whatever the user selected to be displayed on node/add and node/%/edit
698
      $fivestar_widget = $instance['widget']['settings']['widget']['fivestar_widget'];
699
    }
700
  }
701

    
702
  $widget = array(
703
    'name' => isset($widgets[$fivestar_widget]) ? strtolower($widgets[$fivestar_widget]) : 'default',
704
    'css' => $fivestar_widget,
705
  );
706

    
707
  return $widget;
708
}
709

    
710
/**
711
 * Implements hook_field_prepare_view().
712
 */
713
function fivestar_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
714
  // TODO: Clean this function up!
715
  $exposed_widgets = array();
716
  foreach ($entities as $id => $entity) {
717
    // Ensure the items aren't processed twice.
718
    if (!isset($items[$id][0]['count'])) {
719
      if ($instances[$id]['widget']['type'] == 'exposed') {
720
        $exposed_widgets[] = $id;
721
      }
722
      else {
723
        // If the widget type is not exposed, then the count is always 1 or 0.
724
        // The count is pointless to display.
725
        if (!empty($items[$id][0]['rating'])) {
726
          $values['count'] = 1;
727
          $values['user'] = $items[$id][0]['rating'];
728
          $values['average'] = $items[$id][0]['rating'];
729
        }
730
        else {
731
          $values['count'] = 0;
732
          $values['user'] = 0;
733
          $values['average'] = 0;
734
        }
735
        $items[$id] = array($values);
736
      }
737
    }
738
    if(!empty($exposed_widgets)) {
739
      $votes = fivestar_get_votes_multiple($entity_type, $exposed_widgets, $field['settings']['axis']);
740
      foreach($votes[$entity_type] as $id => $vote) {
741
        // Populating the $items[$id] array even for items with no value forces
742
        // the render system to output a widget.
743
        $values['user'] = isset($vote['user']['value']) ? $vote['user']['value'] : 0;
744
        $values['average'] = isset($vote['average']['value']) ? $vote['average']['value'] : 0;
745
        $values['count'] = isset($vote['count']['value']) ? $vote['count']['value'] : 0;
746
        $items[$id] = array($values);
747
      }
748
    }
749
  }
750
}
751

    
752
/**
753
 * Implements hook_microdata_value_type_alter().
754
 */
755
function fivestar_microdata_value_types_alter(&$types) {
756
  $types['fivestar'] = 'item_option';
757
}
758

    
759
/**
760
 * Callback to alter the property info of fivestar fields.
761
 */
762
function fivestar_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
763
  $name = $field['field_name'];
764
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
765

    
766
  $property['property info'] = array(
767
    'average_rating' => array(
768
      'label' => t('Average Rating'),
769
      'type' => 'text',
770
      'microdata' => TRUE,
771
    ),
772
    'user_rating' => array(
773
      'label' => t('User\'s Rating'),
774
      'type' => 'text',
775
      'microdata' => TRUE,
776
    ),
777
    'rating_count' => array(
778
      'label' => t('Rating count'),
779
      'type' => 'text',
780
      'microdata' => TRUE,
781
    ),
782
  );
783
}
784

    
785
/**
786
 * Get microdata attributes for Fivestar field.
787
 */
788
function _fivestar_get_microdata_property_info($entity_type, $entity, $field, $instance) {
789
  // If microdata module is enabled, attach the microdata attributes the module
790
  // adds to the entity object.
791
  if (module_exists('microdata')) {
792
    $microdata = $entity->microdata[$field['field_name']];
793
  }
794
  // If microdata is not enabled, add empty arrays for each property so we
795
  // don't have to check later in the theme functions.
796
  else {
797
    $microdata = array(
798
      '#attributes' => array(),
799
    );
800
    // If Entity API is enabled, we can use that to get the properties.
801
    // Otherwise, replicate the Entity API logic for getting the properties.
802
    if (module_exists('entity')) {
803
      $entity_info = entity_get_all_property_info($entity_type);
804
    }
805
    else {
806
      $info = array();
807
      $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']] = array();
808
      fivestar_property_info_callback($info, $entity_type, $field, $instance, 'fivestar');
809
      $entity_info = $info[$entity_type]['bundles'][$instance['bundle']]['properties'];
810
    }
811
    foreach ($entity_info[$field['field_name']]['property info'] as $property_name => $property) {
812
      $microdata[$property_name]['#attributes'] = array();
813
    }
814
  }
815

    
816
  return $microdata;
817
}