fivestar.module

Tracking 5.x-1.x branch
  1. drupal
    1. 5 contributions/fivestar/fivestar.module
    2. 6 contributions/fivestar/fivestar.module
    3. 7 contributions/fivestar/fivestar.module

A simple n-star voting widget, usable in other forms.

Functions & methods

NameDescription
fivestar_add_cssFetch the necessary CSS files to render the fivestar widget.
fivestar_add_inline_cssAdd Inline CSS to the page, only used on admin/settings/fivestar page.
fivestar_add_jsAdd necessary JS files and settings to render the fivestar widget.
fivestar_block
fivestar_check_tokenCheck to see if a token value matches the specified node.
fivestar_custom_widget
fivestar_elementsImplementation of hook_elements().
fivestar_expandProcess callback for fivestar_element -- see fivestar_element()
fivestar_fivestar_accessImplementation of hook_fivestar_access().
fivestar_fivestar_widgetsImplementation of hook_fivestar_widgets().
fivestar_formCreate the fivestar form for the current item. Note that this is not an implementation of hook_form(). We should probably change the function to reflect that.
fivestar_formsImplementation of hook_forms().
fivestar_form_alterImplementation of hook_form_alter().
fivestar_form_submitSubmit handler for the above form (non-javascript version).
fivestar_get_inline_cssRetrieve a list of all inline CSS to be added to the page.
fivestar_get_tokenGet a private token used to protect links from CSRF attacks.
fivestar_help
fivestar_initImplementation of hook_init(). Not that this will cause Drupal to post a warning on the admin screen when agressive caching is activated. Like CCK, Fivestar's use of hook_init IS compatible with agressive caching, we just need a way to annouce that.
fivestar_linkImplementation of hook_link().
fivestar_menuImplementation of hook_menu().
fivestar_nodeapiImplementation of hook_nodeapi().
fivestar_node_typeImplementation of hook_node_types().
fivestar_node_type_form_submitAdditional submit handler for the node type form.
fivestar_permImplementation of hook_perm().
fivestar_previewCallback function for fivestar/preview/node. Outputs a JSON page containing a Fivestar preview of a node rating widget.
fivestar_preview_colorCallback function for fivestar/preview/color.
fivestar_settingsCallback function for admin/settings/fivestar. Display the settings form.
fivestar_settings_submit
fivestar_static
fivestar_validate
fivestar_validate_targetCheck that an item being voted upon is a valid vote.
fivestar_views_value_display_handler
fivestar_views_widget_compact_handler
fivestar_views_widget_handler
fivestar_views_widget_normal_handler
fivestar_voteCallback function for fivestar/vote.
fivestar_votingapi_views_formatters
fivestar_widget_form
theme_fivestarTheme the fivestar form element by adding necessary css and javascript.
theme_fivestar_node_type_formTheme function to add the Fivestar preview to the node type form.
theme_fivestar_preview
theme_fivestar_preview_widget
theme_fivestar_preview_wrapper
theme_fivestar_selectTheme the straight HTML version of the fivestar select list. This is used to remove the wrapping 'form-item' div from the select list.
theme_fivestar_settings
theme_fivestar_staticDisplay a plain HTML VIEW ONLY version of the widget with the specified rating
theme_fivestar_static_elementDisplay a static fivestar value as stars with a title and description.
theme_fivestar_summary
theme_fivestar_widgetTheme an entire fivestar widget, including the submit button and the normal fivestar widget themed in the theme_fivestar() function.
_fivestar_cast_voteInternal function to handle vote casting, flood control, XSS, IP based voting, etc...

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * A simple n-star voting widget, usable in other forms.
  5. */
  6. function fivestar_help($section) {
  7. switch ($section) {
  8. case 'admin/settings/fivestar':
  9. $output = t('This page is used to configure site-wide features of the fivestar module. To setup fivestar to rate content:');
  10. $steps = array(
  11. t('Configure site-wide settings for fivestar below.'),
  12. t('Go to <a href="!types">admin/content/types</a> and edit the type you would like to rate.', array('!types' => url('admin/content/types'))),
  13. t('On the settings page for the content type, a set of options is available for fivestar, where you can enable rating for that type and set rating options.'),
  14. );
  15. $output .= theme('item_list', $steps, NULL, 'ol');
  16. }
  17. return $output;
  18. }
  19. /**
  20. * Implementation of hook_menu().
  21. *
  22. * Provides a callback url where votes can be submitted by the client-side
  23. * javascript.
  24. */
  25. function fivestar_menu($may_cache) {
  26. $items = array();
  27. if ($may_cache) {
  28. $items[] = array(
  29. 'path' => 'admin/settings/fivestar',
  30. 'title' => t('Fivestar'),
  31. 'description' => t('Configure site-wide widgets used for Fivestar rating.'),
  32. 'callback' => 'drupal_get_form',
  33. 'callback arguments' => array('fivestar_settings'),
  34. 'type' => MENU_NORMAL_ITEM,
  35. 'access' => user_access('administer site configuration'),
  36. );
  37. $items[] = array(
  38. 'path' => 'fivestar/preview/color',
  39. 'callback' => 'fivestar_preview_color',
  40. 'type' => MENU_CALLBACK,
  41. 'access' => user_access('administer site configuration'),
  42. );
  43. $items[] = array(
  44. 'path' => 'fivestar/preview/node',
  45. 'callback' => 'fivestar_preview',
  46. 'type' => MENU_CALLBACK,
  47. 'access' => user_access('administer content types'),
  48. );
  49. $items[] = array(
  50. 'path' => 'fivestar/vote',
  51. 'callback' => 'fivestar_vote',
  52. 'type' => MENU_CALLBACK,
  53. 'access' => user_access('rate content'),
  54. );
  55. }
  56. return $items;
  57. }
  58. /**
  59. * Implementation of hook_init().
  60. * Not that this will cause Drupal to post a warning on the admin screen
  61. * when agressive caching is activated. Like CCK, Fivestar's use of hook_init
  62. * IS compatible with agressive caching, we just need a way to annouce that.
  63. */
  64. function fivestar_init() {
  65. // Ensure we are not serving a cached page.
  66. if (function_exists('drupal_set_content')) {
  67. if (module_exists('content')) {
  68. include_once(drupal_get_path('module', 'fivestar') .'/fivestar_field.inc');
  69. }
  70. // Add necessary CSS and JS.
  71. // TODO: These shouldn't be loaded on every page, but block caching omits
  72. // CSS and JS files that would be otherwise added.
  73. fivestar_add_js();
  74. fivestar_add_css();
  75. }
  76. }
  77. /**
  78. * Implementation of hook_perm().
  79. *
  80. * Exposes permissions for rating content, viewing aggregate ratings, and using PHP
  81. * snippets when configuring fivestar CCK fields.
  82. */
  83. function fivestar_perm() {
  84. return array('rate content', 'use PHP for fivestar target');
  85. }
  86. /**
  87. * Implementation of hook_form_alter().
  88. *
  89. * Adds fivestar enaable and position to the node-type configuration form.
  90. */
  91. function fivestar_form_alter($form_id, &$form) {
  92. if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
  93. // Goofy hack to get the buttons at the end of the array.
  94. $form['workflow']['#weight'] = isset($form['workflow']['#weight']) ? $form['workflow']['#weight'] + 1 : 1;
  95. $form['submit']['#weight'] = isset($form['submit']['#weight']) ? $form['submit']['#weight'] + 1 : 1;
  96. $form['delete']['#weight'] = isset($form['delete']['#weight']) ? $form['delete']['#weight'] + 1 : 1;
  97. $form['fivestar'] = array(
  98. '#type' => 'fieldset',
  99. '#title' => t('Fivestar ratings'),
  100. '#collapsible' => TRUE,
  101. '#collapsed' => !variable_get('fivestar_'. $form['#node_type']->type, 0),
  102. '#description' => t('To rate this content, enable Fivestar rating below. These settings will be used for both comments (if available) and direct rating.'),
  103. '#theme' => 'fivestar_node_type_form',
  104. '#attributes' => array('id' => 'fivestar-node-type-form'),
  105. );
  106. $form['fivestar']['fivestar'] = array(
  107. '#type' => 'checkbox',
  108. '#title' => t('Enable Fivestar rating'),
  109. '#default_value' => variable_get('fivestar_'. $form['#node_type']->type, 0),
  110. '#return_value' => 1,
  111. '#weight' => -5,
  112. );
  113. $form['fivestar']['fivestar_stars'] = array(
  114. '#type' => 'select',
  115. '#title' => t('Number of stars'),
  116. '#options' => drupal_map_assoc(range(1, 10)),
  117. '#default_value' => variable_get('fivestar_stars_'. $form['#node_type']->type, 5),
  118. '#weight' => -4,
  119. );
  120. $form['fivestar']['labels'] = array(
  121. '#type' => 'fieldset',
  122. '#title' => t('Star Labels'),
  123. '#collapsible' => TRUE,
  124. '#collapsed' => TRUE,
  125. '#description' => t('These star labels appear as the link title when javascript is enabled as well as the select list options when javascript is disabled.'),
  126. );
  127. $form['fivestar']['labels']['fivestar_labels_enable'] = array(
  128. '#type' => 'checkbox',
  129. '#title' => t('Display labels on mouse over'),
  130. '#default_value' => variable_get('fivestar_labels_enable_'. $form['#node_type']->type, 1),
  131. '#return_value' => 1,
  132. '#weight' => -5,
  133. '#description' => t('When enabled, the star labels will dynamically appear underneath the stars as the user hovers over each star to provide a more descriptive qualitative meaning for each star value.'),
  134. );
  135. // Create the Mouseover text forms for each of the rating options
  136. // This form depends on the number of stars, and these extra textfields will be hidden with javascript
  137. $star_count = variable_get('fivestar_stars_'. $form['#node_type']->type, 5);
  138. $labels = variable_get('fivestar_labels_'. $form['#node_type']->type, array());
  139. for ($n = 0; $n <= 10; $n++) {
  140. if ($star_count == 5 && $n <= 5) {
  141. // If the default 5 stars are chosen, then use these five default label values.
  142. $default_labels = array(t('Cancel rating'), t('Poor'), t('Okay'), t('Good'), t('Great'), t('Awesome'));
  143. }
  144. elseif ($n == 0) {
  145. $default_labels[$n] = t('Cancel rating');
  146. }
  147. else {
  148. $default_labels[$n] = t('Give it @star/@count');
  149. }
  150. $form['fivestar']['labels']['fivestar_label_'. $n] = array(
  151. '#type' => 'textfield',
  152. '#title' => $n > 0 ? t('Star @star label', array('@star' => $n)) : t('Cancel label'),
  153. '#default_value' => isset($labels[$n]) ? $labels[$n] : $default_labels[$n],
  154. '#prefix' => '<div id="fivestar-label-'. $n .'" class="fivestar-label">',
  155. '#suffix' => '</div>',
  156. '#size' => 30,
  157. );
  158. }
  159. $form['fivestar']['direct'] = array(
  160. '#type' => 'fieldset',
  161. '#title' => t('Direct rating widget'),
  162. '#collapsible' => FALSE,
  163. '#description' => t('These settings allow you to display a rating widget to your users while they are viewing content of this type. Rating will immediately register a vote for that piece of content.'),
  164. '#weight' => 2,
  165. );
  166. $form['fivestar']['direct']['fivestar_style'] = array(
  167. '#type' => 'select',
  168. '#title' => t('Star display style'),
  169. '#default_value' => variable_get('fivestar_style_'. $form['#node_type']->type, 'average'),
  170. '#options' => array(
  171. 'average' => t('Display average vote value'),
  172. 'user' => t('Display user vote value'),
  173. 'smart' => t('User vote if available, average otherwise'),
  174. 'dual' => t('Both user and average vote'),
  175. ),
  176. );
  177. $form['fivestar']['direct']['fivestar_text'] = array(
  178. '#type' => 'select',
  179. '#title' => t('Text display style'),
  180. '#default_value' => variable_get('fivestar_text_'. $form['#node_type']->type, 'dual'),
  181. '#options' => array(
  182. 'none' => t('Display no text beneath stars'),
  183. 'average' => t('Current average in text'),
  184. 'user' => t('User current vote in text'),
  185. 'smart' => t('User vote if available, average otherwise'),
  186. 'dual' => t('Both user and average vote'),
  187. ),
  188. );
  189. $form['fivestar']['direct']['fivestar_title'] = array(
  190. '#type' => 'checkbox',
  191. '#title' => t('Show widget title'),
  192. '#default_value' => variable_get('fivestar_title_'. $form['#node_type']->type, 1),
  193. '#return_value' => 1,
  194. );
  195. $form['fivestar']['direct']['fivestar_feedback'] = array(
  196. '#type' => 'checkbox',
  197. '#title' => t('Enable confirmations to inform a vote was saved or deleted.'),
  198. '#default_value' => variable_get('fivestar_feedback_'. $form['#node_type']->type, 1),
  199. '#return_value' => 1
  200. );
  201. $form['fivestar']['direct']['fivestar_unvote'] = array(
  202. '#type' => 'checkbox',
  203. '#title' => t('Allow users to undo their votes'),
  204. '#default_value' => variable_get('fivestar_unvote_'. $form['#node_type']->type, 0),
  205. '#return_value' => 1,
  206. );
  207. $form['fivestar']['direct']['fivestar_position_teaser'] = array(
  208. '#type' => 'select',
  209. '#title' => t('Teaser display'),
  210. '#default_value' => variable_get('fivestar_position_teaser_'. $form['#node_type']->type, 'hidden'),
  211. '#options' => array(
  212. 'above' => t('Clickable widget above teaser'),
  213. 'below' => t('Clickable widget below teaser'),
  214. 'above_static' => t('Static display above teaser'),
  215. 'below_static' => t('Static display below teaser'),
  216. 'link' => t('Teaser link to full node widget'),
  217. 'hidden' => '<'. t('hidden') .'>',
  218. ),
  219. );
  220. $form['fivestar']['direct']['fivestar_position'] = array(
  221. '#type' => 'select',
  222. '#title' => t('Full node display'),
  223. '#default_value' => variable_get('fivestar_position_'. $form['#node_type']->type, 'below'),
  224. '#options' => array(
  225. 'above' => t('Clickable widget above node body'),
  226. 'below' => t('Clickable widget below node body'),
  227. 'above_static' => t('Static display above node body'),
  228. 'below_static' => t('Static display below node body'),
  229. 'hidden' => '<'. t('hidden') .'>',
  230. ),
  231. );
  232. $form['fivestar']['direct']['fivestar_direct_preview'] = array(
  233. '#type' => 'item',
  234. '#title' => t('Direct rating widget preview'),
  235. '#value' => theme(
  236. 'fivestar_preview',
  237. $form['fivestar']['direct']['fivestar_style']['#default_value'],
  238. $form['fivestar']['direct']['fivestar_text']['#default_value'],
  239. $form['fivestar']['fivestar_stars']['#default_value'],
  240. $form['fivestar']['direct']['fivestar_unvote']['#default_value'],
  241. $form['fivestar']['direct']['fivestar_title']['#default_value'] ? NULL : FALSE,
  242. $form['fivestar']['labels']['fivestar_labels_enable']['#default_value'],
  243. variable_get('fivestar_labels_'. $form['#node_type']->type, array())
  244. ),
  245. );
  246. if (!$form['fivestar']['fivestar']['#default_value']) {
  247. $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', '');
  248. }
  249. else {
  250. $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', $form['fivestar']['direct']['fivestar_direct_preview']['#value']);
  251. }
  252. $form['#submit']['fivestar_node_type_form_submit'] = array();
  253. }
  254. }
  255. /**
  256. * Additional submit handler for the node type form.
  257. */
  258. function fivestar_node_type_form_submit($form_id, &$form_values) {
  259. // Do not save any fivestar variables if fivestar is disabled.
  260. if (isset($form_values['fivestar']) && $form_values['fivestar'] === 0) {
  261. foreach ($form_values as $key => $value) {
  262. if (strpos($key, 'fivestar') === 0) {
  263. variable_del($key .'_'. $form_values['type']);
  264. }
  265. }
  266. }
  267. // Merge labels into a single variable.
  268. $labels = array();
  269. for ($n = 0; $n <= 10; $n++) {
  270. $labels[] = $form_values['fivestar_label_'. $n];
  271. variable_del('fivestar_label_'. $n .'_'. $form_values['type']);
  272. }
  273. variable_del('fivestar_labels_'. $form_values['type']);
  274. if ($form_values['fivestar_labels_enable']) {
  275. variable_set('fivestar_labels_'. $form_values['type'], $labels);
  276. }
  277. }
  278. /**
  279. * Theme function to add the Fivestar preview to the node type form.
  280. */
  281. function theme_fivestar_node_type_form($form) {
  282. drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar-admin.js');
  283. drupal_add_js(array('fivestar' => array('preview_url' => url('fivestar/preview/node'))), 'setting');
  284. drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar-admin.css', 'module', 'all', FALSE);
  285. $output = '';
  286. $output .= drupal_render($form['fivestar']);
  287. $output .= drupal_render($form['fivestar_stars']);
  288. // Star labels.
  289. $output .= drupal_render($form['labels']);
  290. // Direct rating settings form.
  291. $direct = '';
  292. $direct .= '<div id="fivestar-direct-form">';
  293. $direct .= drupal_render($form['direct']['fivestar_style']);
  294. $direct .= drupal_render($form['direct']['fivestar_text']);
  295. $direct .= drupal_render($form['direct']['fivestar_title']);
  296. $direct .= drupal_render($form['direct']['fivestar_unvote']);
  297. $direct .= drupal_render($form['direct']['fivestar_feedback']);
  298. $direct .= drupal_render($form['direct']['fivestar_position_teaser']);
  299. $direct .= drupal_render($form['direct']['fivestar_position']);
  300. $direct .= '</div>';
  301. $direct .= '<div id="fivestar-direct-preview">';
  302. $direct .= drupal_render($form['direct']['fivestar_direct_preview']);
  303. $direct .= '</div>';
  304. $form['direct']['#children'] = $direct;
  305. $output .= drupal_render($form['direct']);
  306. // Comment settings form.
  307. if (module_exists('fivestar_comment')) {
  308. $comment = '';
  309. $comment .= '<div id="fivestar-comment-form">';
  310. $comment .= drupal_render($form['comment']['fivestar_comment']);
  311. $comment .= '</div>';
  312. $comment .= '<div id="fivestar-comment-preview">';
  313. $comment .= drupal_render($form['comment']['fivestar_comment_preview']);
  314. $comment .= '</div>';
  315. $form['comment']['#children'] = $comment;
  316. $output .= drupal_render($form['comment']);
  317. }
  318. // Any remaining cruft (should be empty).
  319. $output .= drupal_render($form);
  320. return $output;
  321. }
  322. /**
  323. * Implementation of hook_node_types().
  324. */
  325. function fivestar_node_type($op, $info) {
  326. $type = $info->type;
  327. $variables = array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser');
  328. // Be responsible and cleanup unneeded variables.
  329. if ($op == 'delete') {
  330. foreach ($variables as $variable) {
  331. variable_del($variable .'_'. $type);
  332. }
  333. }
  334. // When changing the type name, update the variables.
  335. elseif ($op == 'update' && !empty($info->old_type) && $info->old_type != $info->type) {
  336. foreach ($variables as $variable) {
  337. $value = variable_get($variable .'_'. $type, -1);
  338. if ($value != -1) {
  339. variable_del($variable .'_'. $type);
  340. variable_set($variable .'_'. $type, $value);
  341. }
  342. }
  343. }
  344. }
  345. /**
  346. * Callback function for admin/settings/fivestar. Display the settings form.
  347. */
  348. function fivestar_settings() {
  349. $form = array();
  350. $form['widget'] = array(
  351. '#tree' => FALSE,
  352. '#type' => 'fieldset',
  353. '#title' => t('Widget display'),
  354. '#description' => t('Choose a widget set to be used on your site. Widgets supporting custom colors can be further customized by adjusting the color scheme.'),
  355. '#weight' => -2,
  356. );
  357. $widgets = module_invoke_all('fivestar_widgets');
  358. $classic_widgets = array();
  359. $color_widgets = array();
  360. foreach ($widgets as $path => $name) {
  361. $directory = dirname($path);
  362. $matches = file_scan_directory($directory, '-template.');
  363. if (empty($matches)) {
  364. $classic_widgets[$path] = $name;
  365. }
  366. else {
  367. $color_widgets[$path] = $name;
  368. }
  369. }
  370. // If using a color widget, set the default value to the original path.
  371. $default_value = variable_get('fivestar_widget', 'default');
  372. foreach ($color_widgets as $path => $name) {
  373. if (basename($path) == basename($default_value)) {
  374. $default_value = $path;
  375. }
  376. }
  377. $form['widget']['fivestar_widget'] = array(
  378. '#type' => 'radios',
  379. '#options' => array('default' => t('Default')) + $classic_widgets + $color_widgets,
  380. '#default_value' => $default_value,
  381. '#attributes' => array('class' => 'fivestar-widgets'),
  382. );
  383. $form['widget']['fivestar_color_widget'] = array(
  384. '#type' => 'radios',
  385. '#title' => t('Custom color widgets'),
  386. '#options' => $color_widgets,
  387. '#attributes' => array('class' => 'fivestar-widgets'),
  388. );
  389. include_once(drupal_get_path('module', 'fivestar') .'/fivestar_color.inc');
  390. $form['color'] = fivestar_color_form();
  391. $form['#validate']['fivestar_color_form_validate'] = array();
  392. $form['#submit']['fivestar_color_form_submit'] = array();
  393. $form['#submit']['fivestar_settings_submit'] = array();
  394. $form['fivestar_anonymous_vote_interval'] = array(
  395. '#type' => 'select',
  396. '#title' => t('Anonymous vote interval'),
  397. '#options' => array(0 => t('Immediately')) + drupal_map_assoc(array(300, 900, 1800, 3600, 10800, 21600, 32400, 43200, 86400, 172800, 345600, 604800), 'format_interval') + array(-1 => t('Never')),
  398. '#default_value' => variable_get('fivestar_anonymous_vote_interval', 86400),
  399. '#description' => t('Anonymous users may add another vote after this interval. Because the same IP addresses may be used by different people, allowing the same IP to vote again several days later may yield more votes.'),
  400. );
  401. $form['submit'] = array(
  402. '#type' => 'submit',
  403. '#value' => t('Save configuration'),
  404. '#weight' => 45,
  405. );
  406. return $form;
  407. }
  408. function fivestar_settings_submit($form_id, $form_values) {
  409. variable_set('fivestar_widget', $form_values['fivestar_widget']);
  410. variable_set('fivestar_anonymous_vote_interval', $form_values['fivestar_anonymous_vote_interval']);
  411. }
  412. function theme_fivestar_settings($form) {
  413. drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar-admin.css', 'module', 'all', FALSE);
  414. drupal_set_title(t('Fivestar Settings'));
  415. // Default preview.
  416. $form['widget']['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':<br />'. theme('fivestar_preview_widget', 'default');
  417. // Preview for each classic widget.
  418. foreach (element_children($form['widget']['fivestar_widget']) as $widget_key) {
  419. if ($widget_key != 'default') {
  420. $form['widget']['fivestar_widget'][$widget_key]['#description'] = $form['widget']['fivestar_widget'][$widget_key]['#title'] .' '. t('Preview') .':<br />'. theme('fivestar_preview_widget', $widget_key);
  421. }
  422. }
  423. // Preview for each color-enabled widget.
  424. foreach (element_children($form['widget']['fivestar_color_widget']) as $widget_key) {
  425. $form['widget']['fivestar_color_widget'][$widget_key] = $form['widget']['fivestar_widget'][$widget_key];
  426. $form['widget']['fivestar_color_widget'][$widget_key]['#description'] = $form['widget']['fivestar_color_widget'][$widget_key]['#title'] .' '. t('Preview') .':<br />'. theme('fivestar_preview_widget', $widget_key);
  427. unset($form['widget']['fivestar_widget'][$widget_key]);
  428. }
  429. // Add the new styles to the page.
  430. drupal_set_html_head("<style type=\"text/css\" media=\"all\">\n". fivestar_get_inline_css() ."</style>");
  431. $form['widget']['fivestar_widget']['#attributes']['class'] .= ' clear-block';
  432. $form['widget']['fivestar_color_widget']['#attributes']['class'] .= ' fivestar-color-widgets clear-block';
  433. return drupal_render($form);
  434. }
  435. function theme_fivestar_preview_widget($css_file) {
  436. static $default_css_added = FALSE;
  437. // Add the default CSS to the page to ensure the defaults take precedence.
  438. if (!$default_css_added) {
  439. $css = file_get_contents(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');
  440. // Prepend the classes with the unique widget div.
  441. $css = preg_replace('/((div)?\.fivestar-widget)/', 'div.fivestar-widgets $1', $css);
  442. // Update relative URLs with absolute locations.
  443. $css = preg_replace('/url\(\.\.\/(.*?)\)/', 'url('. base_path() . drupal_get_path('module', 'fivestar') .'/$1)', $css);
  444. fivestar_add_inline_css('default', $css);
  445. $default_css_added = TRUE;
  446. }
  447. // Add widget specific CSS to the page.
  448. $widget_name = str_replace('.css', '', basename($css_file));
  449. $widget_path = dirname($css_file);
  450. if ($widget_name != 'default') {
  451. $css = file_get_contents($css_file);
  452. // Prepend the classes with the unique widget div.
  453. $css = preg_replace('/((div)?\.fivestar-widget)/', 'div#fivestar-preview-'. $widget_name .' $1', $css);
  454. // Update relative URLs with absolute locations.
  455. $css = preg_replace('/url\((.*?)\)/', 'url('. base_path() . $widget_path .'/$1)', $css);
  456. fivestar_add_inline_css($widget_name, $css);
  457. }
  458. $form = array();
  459. $form['vote'] = array(
  460. '#type' => 'fivestar',
  461. '#stars' => 5,
  462. '#auto_submit' => FALSE,
  463. '#allow_clear' => TRUE,
  464. );
  465. $form = form_builder('fivestar_preview', $form);
  466. $output = '<div class="fivestar-star-preview" id="fivestar-preview-'. $widget_name .'">';
  467. $output .= drupal_render($form);
  468. $output .= '</div>';
  469. return $output;
  470. }
  471. /**
  472. * Callback function for fivestar/preview/color.
  473. *
  474. * Outputs a dynamically generated star or cancel png.
  475. */
  476. function fivestar_preview_color() {
  477. include_once(drupal_get_path('module', 'fivestar') .'/fivestar_color.inc');
  478. $args = func_get_args();
  479. // Remove query string if it gets passed in as argument.
  480. $filename = preg_replace('/\?.*$/', '', array_pop($args));
  481. $type = array_pop($args);
  482. $widget = array_pop($args);
  483. // Convert args to our color scheme.
  484. $color_scheme = array(
  485. 'on1' => $args[0],
  486. 'on2' => $args[1],
  487. 'hover1' => $args[2],
  488. 'hover2' => $args[3],
  489. 'off1' => $args[4],
  490. 'off2' => $args[5],
  491. 'matte' => $args[6],
  492. );
  493. // Find the source location of the desired widget.
  494. $widgets = module_invoke_all('fivestar_widgets');
  495. foreach ($widgets as $key => $name) {
  496. if (drupal_strtolower($name) == $widget) {
  497. $source_directory = str_replace($widget .'.css', '', $key);
  498. break;
  499. }
  500. }
  501. // Generate the requested image and exit.
  502. $image = _fivestar_color_render($source_directory . str_replace('.png', '-template.png', $filename), $color_scheme, $type);
  503. drupal_set_header('Content-type: image/png');
  504. drupal_set_header("Expires: ". gmdate("D, d M Y H:i:s", time() + 300) ." GMT");
  505. drupal_set_header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
  506. drupal_set_header("Cache-Control: max-age=300");
  507. imagepng($image);
  508. exit();
  509. }
  510. /**
  511. * Callback function for fivestar/preview/node. Outputs a JSON page containing
  512. * a Fivestar preview of a node rating widget.
  513. */
  514. function fivestar_preview() {
  515. // Perform a few basic security checks.
  516. $style = check_plain($_POST['style']);
  517. $text = check_plain($_POST['text']);
  518. $stars = (int)$_POST['stars'];
  519. $unvote = (boolean)$_POST['unvote'];
  520. $title = (boolean)$_POST['title'];
  521. $feedback_enable = (boolean)$_POST['feedback'];
  522. $labels_enable = (boolean)$_POST['labels_enable'];
  523. $labels = (array)$_POST['labels'];
  524. foreach ($labels as $key => $label) {
  525. $labels[$key] = filter_xss_admin($label);
  526. }
  527. $output = theme('fivestar_preview', $style, $text, $stars, $unvote, $title ? NULL : FALSE, $feedback_enable, $labels_enable, $labels);
  528. drupal_set_header('Content-Type: text/javascript; charset=utf-8');
  529. print drupal_to_js(array('status' => TRUE, 'data' => $output));
  530. }
  531. function theme_fivestar_preview($style = NULL, $text = NULL, $stars = NULL, $unvote = NULL, $title = NULL, $feedback_enable = TRUE, $labels_enable = TRUE, $labels = array()) {
  532. $values = array(
  533. 'average' => 50,
  534. 'user' => 80,
  535. 'count' => 20,
  536. );
  537. $settings = array(
  538. 'stars' => $stars,
  539. 'allow_clear' => $unvote,
  540. 'style' => $style,
  541. 'text' => $text,
  542. 'title' => $title,
  543. 'autosubmit' => FALSE,
  544. 'feedback_enable' => $feedback_enable,
  545. 'labels_enable' => $labels_enable,
  546. 'labels' => $labels,
  547. );
  548. $form = drupal_get_form('fivestar_custom_widget', $values, $settings);
  549. // This regex is sadly necessary because having duplicate form_tokens or
  550. // form_id elements can cause the content type form to choke. Forms inside of
  551. // forms is also frowned upon, so this removes the wrapping form tag as well.
  552. $form = str_replace(array('<form', '</form>'), array('<div', '</div>'), $form);
  553. $form = preg_replace('/( method=".*?")|( action=".*?")|(<input.*?name="(form_token|form_id|destination)".*?\/>)/', '', $form);
  554. return $form;
  555. }
  556. function theme_fivestar_preview_wrapper($content, $type = 'direct') {
  557. return '<div class="fivestar-preview fivestar-preview-'. $type .'">'. $content .'</div>';
  558. }
  559. /**
  560. * Callback function for fivestar/vote.
  561. *
  562. * @param type
  563. * A content-type to log the vote to. 'node' is the most common.
  564. * @param cid
  565. * A content id to log the vote to. This would be a node ID, a comment ID, etc.
  566. * @param tag
  567. * Multi-axis tag to allow multiple votes per node. 'vote' is the most common.
  568. * @param value
  569. * A value from 1-100, representing the vote cast for the content.
  570. * @return
  571. * An XML chunk containing the results of the vote, for use by the client-side
  572. * javascript code.
  573. */
  574. function fivestar_vote($type, $cid, $tag, $value) {
  575. drupal_set_header("Content-Type: text/xml");
  576. $output = '';
  577. $output .= '<?xml version="1.0" encoding="UTF-8"?>';
  578. // Rebuild the #auto_submit_path that was used as the token seed.
  579. $path = preg_replace('/\/'. $value .'$/', '', $_GET['q']);
  580. if (!isset($_GET['token']) || !fivestar_check_token($_GET['token'], $path)) {
  581. $output .= '<xml><error>'. t('Invalid token') .'</error></xml>';
  582. exit($output);
  583. }
  584. _fivestar_cast_vote($type, $cid, $value, $tag);
  585. $result = votingapi_recalculate_results($type, $cid);
  586. // Retrieve cached result if recalculation is not set to immediate.
  587. if (!is_array($result)) {
  588. $result = votingapi_get_voting_results($type, $cid);
  589. }
  590. if ($type == 'node') {
  591. $node = node_load($cid);
  592. }
  593. $stars = variable_get('fivestar_stars_'. (!isset($node) ? 'default' : $node->type), 5);
  594. $feedback_enable = variable_get('fivestar_feedback_'. (!isset($node) ? 'default' : $node->type), 1);
  595. $output .= '<xml><result>';
  596. if (count($result)) {
  597. foreach ($result as $data) {
  598. if ($data->tag == $tag) {
  599. $output .= '<'. $data->function .'>'. $data->value .'</'. $data->function .'>';
  600. $summary[$data->tag][$data->function] = $data->value;
  601. }
  602. }
  603. }
  604. $output .= '<summary>';
  605. $output .= '<average><![CDATA['. theme('fivestar_summary', NULL, $summary[$tag]['average'], NULL, $stars, $feedback_enable) .']]></average>';
  606. $output .= '<average_count><![CDATA['. theme('fivestar_summary', NULL, $summary[$tag]['average'], $summary[$tag]['count'], $stars, $feedback_enable) .']]></average_count>';
  607. $output .= '<user><![CDATA['. theme('fivestar_summary', $value, NULL, NULL, $stars, $feedback_enable) .']]></user>';
  608. $output .= '<user_count><![CDATA['. theme('fivestar_summary', $value, NULL, $summary[$tag]['count'], $stars, $feedback_enable) .']]></user_count>';
  609. $output .= '<combo><![CDATA['. theme('fivestar_summary', $value, $summary[$tag]['average'], $summary[$tag]['count'], $stars, $feedback_enable) .']]></combo>';
  610. $output .= '<count><![CDATA['. theme('fivestar_summary', NULL, NULL, $summary[$tag]['count'], $stars, $feedback_enable) .']]></count>';
  611. $output .= '</summary>';
  612. $output .= '</result>';
  613. $output .= '<vote>';
  614. $output .= '<value>'. $value .'</value>';
  615. $output .= '<type>'. $type .'</type>';
  616. $output .= '<id>'. $cid .'</id>';
  617. $output .= '<tag>'. $tag .'</tag>';
  618. $output .= '</vote></xml>';
  619. drupal_set_header("Content-Type: text/xml");
  620. exit($output);
  621. }
  622. /**
  623. * Internal function to handle vote casting, flood control, XSS, IP based
  624. * voting, etc...
  625. */
  626. function _fivestar_cast_vote($type, $cid, $value, $tag = NULL, $uid = NULL, $skip_validation = FALSE) {
  627. global $user;
  628. $tag = empty($tag) ? 'vote' : $tag;
  629. // Bail out if the user's trying to vote on an invalid object.
  630. if (!$skip_validation && !fivestar_validate_target($type, $cid, $uid)) {
  631. return array();
  632. }
  633. // Prep variables for anonymous vs. registered voting.
  634. if (!isset($uid)) {
  635. $uid = $user->uid;
  636. }
  637. $anon_interval = variable_get('fivestar_anonymous_vote_interval', 86400);
  638. // Sanity-check the incoming values.
  639. if (is_numeric($cid) && is_numeric($value)) {
  640. if ($value > 100) {
  641. $value = 100;
  642. }
  643. if ($uid) {
  644. // If the user is logged in, we'll look for votes from that uid.
  645. $sql = "SELECT vote_id FROM {votingapi_vote} WHERE content_type='%s' AND content_id=%d AND value_type='percent' AND uid=%d AND tag = '%s'";
  646. $result = db_query($sql, $type, $cid, $uid, $tag);
  647. }
  648. else {
  649. // Otherwise, we'll look for votes from the same IP address within the anonymous interval.
  650. $hostname = $_SERVER['REMOTE_ADDR'];
  651. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  652. $hostname .= '-'. $_SERVER['HTTP_X_FORWARDED_FOR'];
  653. }
  654. $sql = "SELECT vote_id FROM {votingapi_vote} WHERE content_type='%s' AND content_id=%d AND value_type='percent' AND uid=%d AND tag = '%s' AND hostname='%s'";
  655. $sql .= $anon_interval != -1 ? " AND timestamp > %d" : '';
  656. $result = db_query($sql, $type, $cid, $uid, $tag, $hostname, time() - $anon_interval);
  657. }
  658. // If the old vote exists, either delete it (if the new one is zero)
  659. // or change it. If it doesn't exist and the vote is non-zero, cast
  660. // it and recalculate.
  661. if ($old_vote = db_fetch_object($result)) {
  662. if ($value == 0){
  663. votingapi_delete_vote($old_vote);
  664. }
  665. else {
  666. $vote = votingapi_change_vote($old_vote, $value);
  667. }
  668. }
  669. elseif ($value != 0) {
  670. $vote = votingapi_add_vote($type, $cid, $value, 'percent', $tag, $uid);
  671. }
  672. return $vote;
  673. }
  674. else {
  675. return array();
  676. }
  677. }
  678. /**
  679. * Check that an item being voted upon is a valid vote.
  680. *
  681. * @param $type
  682. * Type of target (currently only node is supported).
  683. * @param $id
  684. * Identifier within the type (in this case nid).
  685. * @param $uid
  686. * The user trying to cast the vote.
  687. *
  688. * @return boolean
  689. */
  690. function fivestar_validate_target($type, $id, $uid = NULL) {
  691. if (!isset($uid)) {
  692. $uid = $GLOBALS['user']->uid;
  693. }
  694. $access = module_invoke_all('fivestar_access', $type, $id, $uid);
  695. foreach ($access as $result) {
  696. if ($result == TRUE) {
  697. return TRUE;
  698. }
  699. if ($result === FALSE) {
  700. return FALSE;
  701. }
  702. }
  703. }
  704. /**
  705. * Implementation of hook_fivestar_access().
  706. *
  707. * This hook is called before every vote is cast through Fivestar. It allows
  708. * modules to allow voting on any type of content, such as nodes, users, or
  709. * comments, even though only nodes are supported by Fivestar directly.
  710. *
  711. * @param $type
  712. * Type of target (currently only node is supported).
  713. * @param $id
  714. * Identifier within the type (in this case nid).
  715. * @param $uid
  716. * The user ID trying to cast the vote.
  717. *
  718. * @return boolean or NULL
  719. * Returns TRUE if voting is supported on this object.
  720. * Returns NULL if voting is not supported on this object by this module.
  721. * If needing to absolutely deny all voting on this object, regardless
  722. * of permissions defined in other modules, return FALSE. Note if all
  723. * modules return NULL, stating no preference, then access will be denied.
  724. */
  725. function fivestar_fivestar_access($type, $id, $uid) {
  726. if ($type == 'node' && $node = node_load($id)) {
  727. if (variable_get('fivestar_'. $node->type, 0)) {
  728. return TRUE;
  729. }
  730. }
  731. }
  732. /**
  733. * Implementation of hook_fivestar_widgets().
  734. *
  735. * This hook allows other modules to create additional custom widgets for
  736. * the fivestar module.
  737. *
  738. * @return array
  739. * An array of key => value pairs suitable for inclusion as the #options in a
  740. * select or radios form element. Each key must be the location of a css
  741. * file for a fivestar widget. Each value should be the name of the widget.
  742. */
  743. function fivestar_fivestar_widgets() {
  744. $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
  745. $files = file_scan_directory($widgets_directory, '\.css$');
  746. $widgets = array();
  747. foreach ($files as $file) {
  748. if (strpos($file->filename, '-rtl.css') === FALSE) {
  749. $widgets[$file->filename] = drupal_ucfirst(str_replace('-color', '', $file->name));
  750. }
  751. }
  752. return $widgets;
  753. }
  754. /**
  755. * Implementation of hook_nodeapi().
  756. *
  757. * Adds the fievestar widget to the node view.
  758. */
  759. function fivestar_nodeapi(&$node, $op, $teaser, $page) {
  760. switch ($op) {
  761. case 'view':
  762. if ($node->in_preview == FALSE && !isset($node->modr8_form_teaser) && variable_get('fivestar_'. $node->type, 0)) {
  763. if ($teaser) {
  764. $position = variable_get('fivestar_position_teaser_'. $node->type, 'above');
  765. }
  766. else {
  767. $position = variable_get('fivestar_position_'. $node->type, 'above');
  768. }
  769. switch ($position) {
  770. case 'above':
  771. case 'below':
  772. if (user_access('rate content') && fivestar_validate_target('node', $node->nid)) {
  773. $node->content['fivestar_widget'] = array(
  774. '#value' => fivestar_widget_form($node),
  775. '#weight' => $position == 'above' ? -10 : 50,
  776. );
  777. break;
  778. } // Fall through to static if not allowed to rate.
  779. $position .= '_static';
  780. case 'above_static':
  781. case 'below_static':
  782. $stars = variable_get('fivestar_stars_'. $node->type, 5);
  783. $node->content['fivestar_widget'] = array(
  784. '#value' => fivestar_static('node', $node->nid, 'vote', $node->type),
  785. '#weight' => strpos($position, 'above') === 0 ? -10 : 50,
  786. );
  787. break;
  788. default:
  789. // We'll do nothing.
  790. break;
  791. }
  792. }
  793. break;
  794. }
  795. }
  796. /**
  797. * Implementation of hook_link().
  798. *
  799. * Add a "rate" link to node teaser.
  800. */
  801. function fivestar_link($type, $node = NULL, $teaser = FALSE) {
  802. $links = array();
  803. if ($type == "node" && $teaser) {
  804. if (variable_get('fivestar_position_teaser_'. $node->type, 'above') == "link") {
  805. $links['rate'] = array(
  806. 'title' => t('Rate'),
  807. 'href' => 'node/'. $node->nid,
  808. 'fragment' => 'fivestar-form-node-'. $node->nid,
  809. 'attributes' => array('title' => t('Rate this @type', array('@type' => node_get_types('name', $node->type)))),
  810. );
  811. }
  812. }
  813. return $links;
  814. }
  815. function fivestar_block($op = 'list', $delta = 0, $edit = array()) {
  816. switch ($op) {
  817. case 'list':
  818. $blocks[0]['info'] = t('Fivestar: Rate this node');
  819. return $blocks;
  820. case 'view':
  821. if (user_access('access content') && user_access('rate content')) {
  822. if (arg(0) == 'node' && is_numeric(arg(1)) && (arg(2) == '' || arg(2) == 'view')) {
  823. $node = node_load(arg(1));
  824. if (fivestar_validate_target('node', $node->nid)) {
  825. $block['subject'] = t('Rate This');
  826. $block['content'] = fivestar_widget_form($node);
  827. return $block;
  828. }
  829. }
  830. }
  831. break;
  832. }
  833. }
  834. function fivestar_widget_form($node) {
  835. return drupal_get_form('fivestar_form_node_'. $node->nid, 'node', $node->nid);
  836. }
  837. /**
  838. * Get a private token used to protect links from CSRF attacks.
  839. */
  840. function fivestar_get_token($value) {
  841. global $user;
  842. // Anonymous users don't get a session ID, which breaks page caching.
  843. $session_id = $user->uid ? session_id() : '';
  844. $private_key = drupal_get_private_key();
  845. return md5($session_id . $value . $private_key);
  846. }
  847. /**
  848. * Check to see if a token value matches the specified node.
  849. */
  850. function fivestar_check_token($token, $value) {
  851. return fivestar_get_token($value) == $token;
  852. }
  853. /**
  854. * Implementation of hook_forms().
  855. *
  856. * This is necessary when multiple fivestar forms appear on the same page, each
  857. * requiring a separate form_id, but all using the same underlying callbacks.
  858. */
  859. function fivestar_forms() {
  860. $args = func_get_args();
  861. if (strpos($args[0][0], 'fivestar_form') !== FALSE) {
  862. if ($args[0][0] == 'fivestar_form_'. $args[0][1] .'_'. $args[0][2]) {
  863. $forms[$args[0][0]] = array('callback' => 'fivestar_form');
  864. return $forms;
  865. }
  866. }
  867. }
  868. /**
  869. * Create the fivestar form for the current item.
  870. * Note that this is not an implementation of hook_form(). We should probably
  871. * change the function to reflect that.
  872. */
  873. function fivestar_form($content_type, $content_id) {
  874. global $user;
  875. if ($content_type == 'node') {
  876. if (is_numeric($content_id)) {
  877. $node = node_load($content_id);
  878. }
  879. else {
  880. return array();
  881. }
  882. }
  883. $current_avg = votingapi_get_voting_result($content_type, $content_id, 'percent', 'vote', 'average');
  884. $current_count = votingapi_get_voting_result($content_type, $content_id, 'percent', 'vote', 'count');
  885. if ($user->uid) {
  886. $user_vote = votingapi_get_vote($content_type, $content_id, 'percent', 'vote', $user->uid);
  887. }
  888. else {
  889. // If the user is anonymous, we never bother loading their existing votes.
  890. // Not only would it be hit-or-miss, it would break page caching. Safer to always
  891. // show the 'fresh' version to anon users.
  892. $user_vote->value = 0;
  893. }
  894. $values = array(
  895. 'average' => (int)$current_avg->value,
  896. 'user' => (int)$user_vote->value,
  897. 'count' => (int)$current_count->value,
  898. );
  899. $settings = array(
  900. 'stars' => variable_get('fivestar_stars_'. $node->type, 5),
  901. 'allow_clear' => variable_get('fivestar_unvote_'. $node->type, FALSE),
  902. 'style' => variable_get('fivestar_style_'. $node->type, 'average'),
  903. 'text' => variable_get('fivestar_text_'. $node->type, 'dual'),
  904. 'content_type' => $content_type,
  905. 'content_id' => $content_id,
  906. 'tag' => 'vote',
  907. 'autosubmit' => TRUE,
  908. 'title' => variable_get('fivestar_title_'. $node->type, 1) ? NULL : FALSE,
  909. 'feedback_enable' => variable_get('fivestar_feedback_'. $node->type, 1),
  910. 'labels_enable' => variable_get('fivestar_labels_enable_'. $node->type, 1),
  911. 'labels' => variable_get('fivestar_labels_'. $node->type, array()),
  912. );
  913. return fivestar_custom_widget($values, $settings);
  914. }
  915. function fivestar_static($content_type, $content_id, $tag = 'vote', $node_type = NULL) {
  916. global $user;
  917. $current_avg = votingapi_get_voting_result($content_type, $content_id, 'percent', $tag, 'average');
  918. $current_count = votingapi_get_voting_result($content_type, $content_id, 'percent', $tag, 'count');
  919. $user_vote = $user->uid ? votingapi_get_vote($content_type, $content_id, 'percent', $tag, $user->uid) : 0;
  920. if ($content_type == 'node') {
  921. // Content type should always be passed to avoid this node load.
  922. if (!isset($node_type)) {
  923. $node = node_load($content_id);
  924. $node_type = $node->type;
  925. }
  926. $star_display = variable_get('fivestar_style_'. $node_type, 'average');
  927. $text_display = variable_get('fivestar_text_'. $node_type, 'dual');
  928. $title_display = variable_get('fivestar_title_'. $node_type, 1);
  929. $stars = variable_get('fivestar_stars_'. $node_type, 5);
  930. switch ($star_display) {
  931. case 'user':
  932. if (user_access('rate content')) {
  933. $star_value = $user_vote->value;
  934. $title = $title_display ? t('Your rating') : NULL;
  935. break;
  936. } // Fall through to average if not allowed to rate.
  937. case 'smart':
  938. if (user_access('rate content')) {
  939. $star_value = $user_vote->value ? $user_vote->value : $current_avg->value;
  940. $title = $title_display ? $user_vote->value ? t('Your rating') : t('Average') : NULL;
  941. break;
  942. } // Fall through to average if not allowed to rate.
  943. case 'average':
  944. case 'dual':
  945. $star_value = $current_avg->value;
  946. $title = $title_display ? t('Average') : NULL;
  947. break;
  948. }
  949. // Set all text values, then unset the unnecessary ones.
  950. $user_value = $user_vote->value;
  951. $average_value = $current_avg->value;
  952. $count_value = $current_count->value;
  953. switch ($text_display) {
  954. case 'average':
  955. $user_value = NULL;
  956. break;
  957. case 'user':
  958. $average_value = NULL;
  959. break;
  960. case 'smart':
  961. if ($user_vote->value) {
  962. $average_value = NULL;
  963. }
  964. else {
  965. $user_value = NULL;
  966. }
  967. break;
  968. }
  969. }
  970. // Possibly add other content types here (comment, user, etc).
  971. else {
  972. $stars = 5;
  973. $star_value = $current_avg->value;
  974. $user_value = $user_vote->value;
  975. $average_value = $current_avg->value;
  976. $count_value = $current_count->value;
  977. }
  978. $star_display = theme('fivestar_static', $star_value, $stars);
  979. $text_display = $text_display == 'none' ? NULL : theme('fivestar_summary', $user_value, $average_value, $count_value, $stars, FALSE);
  980. return theme('fivestar_static_element', $star_display, $title, $text_display);
  981. }
  982. function fivestar_custom_widget($values, $settings) {
  983. $form = array(
  984. '#attributes' => array('class' => 'fivestar-widget'),
  985. '#base' => 'fivestar_form',
  986. '#redirect' => FALSE,
  987. '#theme' => 'fivestar_widget',
  988. );
  989. if (isset($settings['content_type'])) {
  990. $form['content_type'] = array(
  991. '#type' => 'hidden',
  992. '#value' => $settings['content_type'],
  993. '#id' => $settings['content_id'] ? 'edit-content-type-'. $settings['content_id'] : NULL,
  994. );
  995. }
  996. if (isset($settings['content_id'])) {
  997. $form['content_id'] = array(
  998. '#type' => 'hidden',
  999. '#value' => $settings['content_id'],
  1000. '#id' => $settings['content_id'] ? 'edit-content-id-'. $settings['content_id'] : NULL,
  1001. );
  1002. }
  1003. $form['vote'] = array(
  1004. '#type' => 'fivestar',
  1005. '#stars' => $settings['stars'],
  1006. '#vote_count' => $values['count'],
  1007. '#vote_average' => $values['average'],
  1008. '#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE,
  1009. '#auto_submit_path' => (!isset($settings['autosubmit']) || $settings['autosubmit']) ? 'fivestar/vote/'. $settings['content_type'] .'/'. $settings['content_id'] .'/'. $settings['tag'] : NULL,
  1010. '#allow_clear' => $settings['allow_clear'],
  1011. '#content_id' => isset($settings['content_id']) ? $settings['content_id'] : NULL,
  1012. '#required' => isset($settings['required']) ? $settings['required'] : FALSE,
  1013. '#feedback_enable' => isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE,
  1014. '#labels_enable' => isset($settings['labels_enable']) ? $settings['labels_enable'] : TRUE,
  1015. '#labels' => isset($settings['labels']) ? $settings['labels'] : NULL,
  1016. '#tag' => isset($settings['tag']) ? $settings['tag'] : 'vote',
  1017. );
  1018. $form['destination'] = array(
  1019. '#type' => 'hidden',
  1020. '#value' => $_GET['q'],
  1021. '#id' => isset($settings['content_id']) ? 'edit-destination-'. $settings['content_id'] : NULL,
  1022. );
  1023. $form['fivestar_submit'] = array(
  1024. '#type' => 'submit',
  1025. '#value' => t('Rate'),
  1026. '#attributes' => array('class' => 'fivestar-submit'),
  1027. '#id' => isset($settings['content_id']) ? 'edit-fivestar-submit-'. $settings['content_id'] : NULL,
  1028. );
  1029. $form['vote']['#attributes']['class'] = isset($form['vote']['#attributes']['class']) ? $form['vote']['#attributes']['class'] : '';
  1030. $settings['feedback_enable'] = isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE;
  1031. switch ($settings['text']) {
  1032. case 'user':
  1033. $form['vote']['#description'] = theme('fivestar_summary', $values['user'], NULL, $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
  1034. $form['vote']['#attributes']['class'] .= ' fivestar-user-text';
  1035. break;
  1036. case 'average':
  1037. $form['vote']['#description'] = $settings['style'] == 'dual' ? NULL : theme('fivestar_summary', NULL, $values['average'], $values['count'], $settings['stars'], $settings['feedback_enable']);
  1038. $form['vote']['#attributes']['class'] .= ' fivestar-average-text';
  1039. break;
  1040. case 'smart':
  1041. $form['vote']['#description'] = ($settings['style'] == 'dual' && !$values['user']) ? NULL : theme('fivestar_summary', $values['user'], $values['user'] ? NULL : $values['average'], $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
  1042. $form['vote']['#attributes']['class'] .= ' fivestar-smart-text '. ($values['user'] ? 'fivestar-user-text' : 'fivestar-average-text');
  1043. break;
  1044. case 'dual':
  1045. $form['vote']['#description'] = theme('fivestar_summary', $values['user'], $settings['style'] == 'dual' ? NULL : $values['average'], $settings['style'] == 'dual' ? NULL : $values['count'], $settings['stars'], $settings['feedback_enable']);
  1046. $form['vote']['#attributes']['class'] .= ' fivestar-combo-text';
  1047. break;
  1048. }
  1049. switch ($settings['style']) {
  1050. case 'average':
  1051. $form['vote']['#title'] = t('Average');
  1052. $form['vote']['#default_value'] = $values['average'];
  1053. $form['vote']['#attributes']['class'] .= ' fivestar-average-stars';
  1054. break;
  1055. case 'user':
  1056. $form['vote']['#title'] = t('Your rating');
  1057. $form['vote']['#default_value'] = $values['user'];
  1058. $form['vote']['#attributes']['class'] .= ' fivestar-user-stars';
  1059. break;
  1060. case 'smart':
  1061. $form['vote']['#title'] = $values['user'] ? t('Your rating') : t('Average');
  1062. $form['vote']['#default_value'] = $values['user'] ? $values['user'] : $values['average'];
  1063. $form['vote']['#attributes']['class'] .= ' fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars');
  1064. break;
  1065. case 'dual':
  1066. $form['vote']['#title'] = t('Your rating');
  1067. $form['vote']['#default_value'] = $values['user'];
  1068. $form['vote']['#attributes']['class'] .= ' fivestar-combo-stars';
  1069. $form['#attributes']['class'] .= ' fivestar-combo-stars';
  1070. $static_average = theme('fivestar_static', $values['average'], $settings['stars'], $settings['tag']);
  1071. if ($settings['text'] == 'none' && !$settings['labels_enable'] && !$settings['feedback_enable']) {
  1072. $static_description = NULL;
  1073. }
  1074. elseif ($settings['text'] != 'none') {
  1075. $static_description = theme('fivestar_summary', NULL, $settings['text'] == 'user' ? NULL : (isset($values['average']) ? $values['average'] : 0), isset($values['count']) ? $values['count'] : 0, $settings['stars'], FALSE);
  1076. }
  1077. else {
  1078. $static_description = '&nbsp;';
  1079. }
  1080. $form['average'] = array(
  1081. '#type' => 'markup',
  1082. '#value' => theme('fivestar_static_element', $static_average, $settings['title'] !== FALSE ? t('Average') : NULL, $static_description),
  1083. '#weight' => -1,
  1084. );
  1085. break;
  1086. }
  1087. // Set an over-ridding title if passed in.
  1088. // An empty title won't change the default, a string will set a new title,
  1089. // and title === FALSE will unset the title entirely.
  1090. if (isset($settings['title'])) {
  1091. if ($settings['title'] !== FALSE) {
  1092. $form['vote']['#title'] = $settings['title'];
  1093. }
  1094. else {
  1095. unset($form['vote']['#title']);
  1096. unset($form['average']['#title']);
  1097. }
  1098. }
  1099. return $form;
  1100. }
  1101. /**
  1102. * Submit handler for the above form (non-javascript version).
  1103. */
  1104. function fivestar_form_submit($form_id, $form_values) {
  1105. if ($form_id == 'fivestar_form_'. $form_values['content_type'] .'_'. $form_values['content_id']) {
  1106. // Cast the vote.
  1107. _fivestar_cast_vote($form_values['content_type'], $form_values['content_id'], $form_values['vote']);
  1108. votingapi_recalculate_results($form_values['content_type'], $form_values['content_id']);
  1109. // Set a message that the vote was received.
  1110. if ($form_values['vote'] === '0') {
  1111. drupal_set_message(t('Your vote has been cleared.'));
  1112. }
  1113. elseif (is_numeric($form_values['vote'])) {
  1114. drupal_set_message(t('Thank you for your vote.'));
  1115. }
  1116. // Regenerate the page with a drupal_goto() to update the current values.
  1117. drupal_goto();
  1118. }
  1119. }
  1120. /**
  1121. * Implementation of hook_elements().
  1122. *
  1123. * Defines 'fivestar' form element type
  1124. */
  1125. function fivestar_elements() {
  1126. $type['fivestar'] = array(
  1127. '#input' => TRUE,
  1128. '#stars' => 5,
  1129. '#widget' => 'stars',
  1130. '#allow_clear' => FALSE,
  1131. '#auto_submit' => FALSE,
  1132. '#auto_submit_path' => '',
  1133. '#labels_enable' => TRUE,
  1134. '#feedback_enable' => TRUE,
  1135. '#process' => array('fivestar_expand' => array()),
  1136. );
  1137. return $type;
  1138. }
  1139. /**
  1140. * Theme the fivestar form element by adding necessary css and javascript.
  1141. */
  1142. function theme_fivestar($element) {
  1143. if (empty($element['#description'])) {
  1144. if ($element['#feedback_enable']) {
  1145. $element['#description'] = '<div class="fivestar-summary fivestar-feedback-enabled">&nbsp;</div>';
  1146. }
  1147. elseif ($element['#labels_enable']) {
  1148. $element['#description'] = '<div class="fivestar-summary">&nbsp;</div>';
  1149. }
  1150. }
  1151. return theme('form_element', $element, $element['#children']);
  1152. }
  1153. /**
  1154. * Theme the straight HTML version of the fivestar select list. This is used
  1155. * to remove the wrapping 'form-item' div from the select list.
  1156. */
  1157. function theme_fivestar_select($element) {
  1158. $select = '';
  1159. $size = $element['#size'] ? ' size="'. $element['#size'] .'"' : '';
  1160. _form_set_class($element, array('form-select'));
  1161. $multiple = isset($element['#multiple']) && $element['#multiple'];
  1162. return '<select name="'. $element['#name'] .''. ($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="'. $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>';
  1163. }
  1164. /**
  1165. * Theme an entire fivestar widget, including the submit button and the normal
  1166. * fivestar widget themed in the theme_fivestar() function.
  1167. */
  1168. function theme_fivestar_widget($form) {
  1169. // Only print out the summary if text is being displayed or using rollover text.
  1170. if (empty($form['vote']['#description']) && strpos($form['vote']['#prefix'], 'fivestar-labels-hover') === FALSE) {
  1171. unset($form['vote']['#description']);
  1172. }
  1173. $class = 'fivestar-form';
  1174. $class .= '-'. (isset($form['vote']['#tag']) ? $form['tag']['#tag'] : 'vote');
  1175. $class .= '-'. (isset($form['content_id']['#value']) ? $form['content_id']['#value'] : 0);
  1176. $output = '';
  1177. $output .= '<div class="'. $class .' clear-block">';
  1178. $output .= drupal_render($form);
  1179. $output .= '</div>';
  1180. return $output;
  1181. }
  1182. /**
  1183. * Display a plain HTML VIEW ONLY version of the widget
  1184. * with the specified rating
  1185. *
  1186. * @param $rating
  1187. * The desired rating to display out of 100 (i.e. 80 is 4 out of 5 stars)
  1188. * @param $stars
  1189. * The total number of stars this rating is out of
  1190. * @param $tag
  1191. * Allows multiple ratings per node
  1192. * @return
  1193. * A themed HTML string representing the star widget
  1194. *
  1195. */
  1196. function theme_fivestar_static($rating, $stars = 5, $tag = 'vote') {
  1197. $output = '';
  1198. $output .= '<div class="fivestar-widget-static fivestar-widget-static-'. $tag .' fivestar-widget-static-'. $stars .' clear-block">';
  1199. $numeric_rating = $rating/(100/$stars);
  1200. for ($n=1; $n <= $stars; $n++) {
  1201. $star_value = ceil((100/$stars) * $n);
  1202. $prev_star_value = ceil((100/$stars) * ($n-1));
  1203. $zebra = ($n % 2 == 0) ? 'even' : 'odd';
  1204. $first = $n == 1 ? ' star-first' : '';
  1205. $last = $n == $stars ? ' star-last' : '';
  1206. $output .= '<div class="star star-'. $n .' star-'. $zebra . $first . $last .'">';
  1207. if ($rating < $star_value && $rating > $prev_star_value) {
  1208. $percent = (($rating - $prev_star_value) / ($star_value - $prev_star_value)) * 100;
  1209. $output .= '<span class="on" style="width: '. $percent .'%">';
  1210. }
  1211. elseif ($rating >= $star_value) {
  1212. $output .= '<span class="on">';
  1213. }
  1214. else {
  1215. $output .= '<span class="off">';
  1216. }
  1217. if ($n == 1)$output .= $numeric_rating;
  1218. $output .= '</span></div>';
  1219. }
  1220. $output .= '</div>';
  1221. return $output;
  1222. }
  1223. function theme_fivestar_summary($user_rating, $average_rating, $votes, $stars = 5, $feedback = TRUE) {
  1224. $output = '';
  1225. $div_class = '';
  1226. if (isset($user_rating)) {
  1227. $div_class = isset($votes) ? 'user-count' : 'user';
  1228. $user_stars = round(($user_rating * $stars) / 100, 1);
  1229. $output .= '<span class="user-rating">'. t('Your rating: <span>!stars</span>', array('!stars' => $user_rating ? $user_stars : t('None'))) .'</span>';
  1230. }
  1231. if (isset($user_rating) && isset($average_rating)) {
  1232. $output .= ' ';
  1233. }
  1234. if (isset($average_rating)) {
  1235. $div_class = isset($votes) ? 'average-count' : 'average';
  1236. $average_stars = round(($average_rating * $stars) / 100, 1);
  1237. $output .= '<span class="average-rating">'. t('Average: <span>!stars</span>', array('!stars' => $average_stars)) .'</span>';
  1238. }
  1239. if (isset($user_rating) && isset($average_rating)) {
  1240. $div_class = 'combo';
  1241. }
  1242. if (isset($votes) && !(isset($user_rating) || isset($average_rating))) {
  1243. $output .= ' <span class="total-votes">'. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .'</span>';
  1244. $div_class = 'count';
  1245. }
  1246. elseif (isset($votes)) {
  1247. $output .= ' <span class="total-votes">('. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .')</span>';
  1248. }
  1249. if ($votes === 0) {
  1250. $output = '<span class="empty">'. t('No votes yet') .'</span>';
  1251. }
  1252. $output = '<div class="fivestar-summary fivestar-summary-'. $div_class . ($feedback ? ' fivestar-feedback-enabled' : '') .'">'. $output .'</div>';
  1253. return $output;
  1254. }
  1255. /**
  1256. * Display a static fivestar value as stars with a title and description.
  1257. */
  1258. function theme_fivestar_static_element($value, $title = NULL, $description = NULL) {
  1259. $output = '';
  1260. $output .= '<div class="fivestar-static-form-item">';
  1261. $element = array(
  1262. '#type' => 'item',
  1263. '#title' => $title,
  1264. '#description' => $description,
  1265. );
  1266. $output .= theme('form_element', $element, $value);
  1267. $output .= '</div>';
  1268. return $output;
  1269. }
  1270. /**
  1271. * Fetch the necessary CSS files to render the fivestar widget.
  1272. */
  1273. function fivestar_add_css($widget_css = NULL) {
  1274. // Add fivestar CSS.
  1275. drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');
  1276. // Add widget specific CSS.
  1277. if (!isset($widget_css)) {
  1278. $widget_css = variable_get('fivestar_widget', 'default');
  1279. }
  1280. if ($widget_css != 'default') {
  1281. drupal_add_css($widget_css);
  1282. }
  1283. }
  1284. /**
  1285. * Add necessary JS files and settings to render the fivestar widget.
  1286. */
  1287. function fivestar_add_js() {
  1288. static $js_added = FALSE;
  1289. // Add necessary javascript only once per page.
  1290. if (!$js_added) {
  1291. $settings = array(
  1292. 'titleUser' => t('Your rating') .': ',
  1293. 'titleAverage' => t('Average') .': ',
  1294. 'feedbackSavingVote' => t('Saving your vote...'),
  1295. 'feedbackVoteSaved' => t('Your vote has been saved.'),
  1296. 'feedbackDeletingVote' => t('Deleting your vote...'),
  1297. 'feedbackVoteDeleted' => t('Your vote has been deleted.'),
  1298. );
  1299. drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar.js');
  1300. drupal_add_js(array('fivestar' => $settings), 'setting');
  1301. $js_added = TRUE;
  1302. }
  1303. }
  1304. /**
  1305. * Add Inline CSS to the page, only used on admin/settings/fivestar page.
  1306. */
  1307. function fivestar_add_inline_css($widget_key = NULL, $css = NULL, $reset = FALSE) {
  1308. static $inline_css;
  1309. if (!isset($inline_css) || $reset) {
  1310. $inline_css = array();
  1311. }
  1312. if (isset($widget_key) && isset($inline_css)) {
  1313. $inline_css[$widget_key] = $css;
  1314. }
  1315. return $inline_css;
  1316. }
  1317. /**
  1318. * Retrieve a list of all inline CSS to be added to the page.
  1319. */
  1320. function fivestar_get_inline_css() {
  1321. $inline_css = fivestar_add_inline_css();
  1322. return implode("\n", $inline_css);
  1323. }
  1324. /**
  1325. * Process callback for fivestar_element -- see fivestar_element()
  1326. */
  1327. function fivestar_expand($element) {
  1328. static $fivestar_id = 0;
  1329. if (isset($element['#vote_count'])) {
  1330. $element['vote_count'] = array(
  1331. '#type' => 'hidden',
  1332. '#value' => $element['#vote_count'],
  1333. '#id' => 'edit-vote-count-'. $fivestar_id,
  1334. );
  1335. }
  1336. if (isset($element['#vote_average'])) {
  1337. $element['vote_average'] = array(
  1338. '#type' => 'hidden',
  1339. '#value' => $element['#vote_average'],
  1340. '#id' => 'edit-vote-average-'. $fivestar_id,
  1341. );
  1342. }
  1343. if ($element['#auto_submit'] && !empty($element['#auto_submit_path'])) {
  1344. $element['auto_submit_path'] = array(
  1345. '#type' => 'hidden',
  1346. '#value' => url($element['#auto_submit_path']),
  1347. '#attributes' => array('class' => 'fivestar-path'),
  1348. '#id' => 'edit-auto-submit-path-'. $fivestar_id,
  1349. );
  1350. $element['auto_submit_token'] = array(
  1351. '#type' => 'hidden',
  1352. '#value' => fivestar_get_token($element['#auto_submit_path']),
  1353. '#attributes' => array('class' => 'fivestar-token'),
  1354. '#id' => 'edit-auto-submit-token-'. $fivestar_id,
  1355. );
  1356. }
  1357. if (!isset($element['#default_value'])) {
  1358. $element['#default_value'] = 0;
  1359. }
  1360. $options = array('-' => t('Select rating'));
  1361. $default_value = $element['#default_value'];
  1362. for ($i = 0; $i <= $element['#stars']; $i++) {
  1363. $this_value = ceil($i * 100/$element['#stars']);
  1364. $next_value = ceil(($i+1) * 100/$element['#stars']);
  1365. // Display clear button only if enabled.
  1366. if ($element['#allow_clear'] == TRUE && $i == 0) {
  1367. $options[$this_value] = isset($element['#labels'][$i]) ? t(filter_xss_admin($element['#labels'][$i])) : t('Cancel rating');
  1368. }
  1369. // Display a normal star value.
  1370. if ($i > 0) {
  1371. if (isset($element['#labels'][$i])) {
  1372. $options[$this_value] = $element['#labels'][$i] == '' ? $i : t(filter_xss_admin($element['#labels'][$i]), array('@star' => $i, '@count' => $element['#stars']));
  1373. }
  1374. else {
  1375. $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
  1376. }
  1377. }
  1378. // Round up the default value to the next exact star value if needed.
  1379. if ($this_value < $element['#default_value'] && $next_value > $element['#default_value']) {
  1380. $default_value = $next_value;
  1381. }
  1382. }
  1383. $element['vote'] = array(
  1384. '#type' => 'select',
  1385. '#options' => $options,
  1386. '#required' => $element['#required'],
  1387. '#default_value' => $default_value,
  1388. '#parents' => $element['#parents'],
  1389. '#id' => 'edit-vote-'. $fivestar_id,
  1390. '#theme' => 'fivestar_select',
  1391. '#weight' => $element['#weight'],
  1392. );
  1393. // If a default value is not exactly on a radio value, round up to the next one
  1394. if ($element['#default_value'] > $this_value && $element['#default_value'] <= $next_value) {
  1395. $element['vote']['#default_value'] = $next_value;
  1396. }
  1397. // Set a class for the display of label text on hover.
  1398. $label_class = $element['#labels_enable'] ? ' fivestar-labels-hover' : '';
  1399. $element['#id'] = 'edit-vote-'. $fivestar_id;
  1400. $element['#prefix'] = '<div class="fivestar-form-item '. (isset($element['#attributes']['class']) ? $element['#attributes']['class'] : '') . $label_class .'">';
  1401. $element['#suffix'] = '</div>';
  1402. // Add validation function that considers a 0 value as empty.
  1403. $element['#validate']['fivestar_validate'] = array();
  1404. $fivestar_id++;
  1405. return $element;
  1406. }
  1407. function fivestar_validate($form) {
  1408. if ($form['#required'] && (empty($form['vote']['#value']) || $form['vote']['#value'] == '-')) {
  1409. form_error($form, t('!name field is required.', array('!name' => $form['#title'])));
  1410. }
  1411. }
  1412. function fivestar_votingapi_views_formatters($details = array()) {
  1413. if ($details['value_type'] == 'percent') {
  1414. return array(
  1415. 'fivestar_views_value_display_handler' => t('Fivestar Stars (display only)'),
  1416. 'fivestar_views_widget_compact_handler' => t('Fivestar Stars (clickable, no text)'),
  1417. 'fivestar_views_widget_normal_handler' => t('Fivestar Stars (clickable, with text)'),
  1418. );
  1419. }
  1420. }
  1421. function fivestar_views_value_display_handler($op, $filter, $value, &$query) {
  1422. // Determine number of stars to display
  1423. if (is_numeric($query->options)) {
  1424. $stars = $query->options;
  1425. }
  1426. elseif (isset($query->node_type)) {
  1427. $stars = variable_get('fivestar_stars_'. $query->node_type, 5);
  1428. }
  1429. else {
  1430. $type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $query->nid));
  1431. $stars = variable_get('fivestar_stars_'. (!isset($type) ? 'default' : $type), 5);
  1432. }
  1433. // TODO: Find a reliable way of determining the tag.
  1434. return theme('fivestar_static', $value, $stars, 'vote');
  1435. }
  1436. function fivestar_views_widget_compact_handler($op, $filter, $value, &$query) {
  1437. return fivestar_views_widget_handler($op, $filter, $value, $query, FALSE);
  1438. }
  1439. function fivestar_views_widget_normal_handler($op, $filter, $value, &$query) {
  1440. return fivestar_views_widget_handler($op, $filter, $value, $query, TRUE);
  1441. }
  1442. function fivestar_views_widget_handler($op, $filter, $value, &$query, $summary) {
  1443. global $user;
  1444. // If the user can't rate, use the display handler.
  1445. if (!user_access('rate content')) {
  1446. return fivestar_views_value_display_handler($op, $filter, $value, $query);
  1447. }
  1448. $content_type = 'node';
  1449. $content_id = $query->nid;
  1450. $node_type = isset($query->node_type) ? $query->node_type : db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $query->nid));
  1451. $current_count = votingapi_get_voting_result($content_type, $content_id, 'percent', 'vote', 'count');
  1452. if ($user->uid) {
  1453. $user_vote = votingapi_get_vote($content_type, $content_id, 'percent', 'vote', $user->uid);
  1454. }
  1455. else {
  1456. $user_vote->value = 0;
  1457. }
  1458. $values = array(
  1459. 'average' => (int)$value,
  1460. 'user' => (int)$user_vote->value,
  1461. 'count' => (int)$current_count->value,
  1462. );
  1463. $settings = array(
  1464. 'stars' => variable_get('fivestar_stars_'. $node_type, 5),
  1465. 'allow_clear' => variable_get('fivestar_unvote_'. $node_type, FALSE),
  1466. // If the user has setup this content type to use smart stars, display
  1467. // the smart version instead of just the average.
  1468. 'style' => variable_get('fivestar_style_'. $node_type, 'average') != 'smart' ? 'average' : 'smart',
  1469. 'text' => $summary ? variable_get('fivestar_text_'. $node_type, 'dual') : 'none',
  1470. 'content_type' => $content_type,
  1471. 'content_id' => $content_id,
  1472. 'autosubmit' => TRUE,
  1473. 'title' => FALSE,
  1474. 'tag' => 'vote',
  1475. 'feedback_enable' => $summary ? variable_get('fivestar_feedback_'. $node_type, 1) : FALSE,
  1476. 'labels_enable' => $summary ? variable_get('fivestar_labels_enable_'. $node_type, 1) : FALSE,
  1477. 'labels' => $summary ? variable_get('fivestar_labels_'. $node_type, array()) : array(),
  1478. );
  1479. return drupal_get_form('fivestar_custom_widget', $values, $settings);
  1480. }