admin/content/types and edit the type you would like to rate.', array('!types' => url('admin/content/types'))), t('On the settings page for the content type, a set of options is available for fivestar, where you can enabled rating for that type and set rating options.'), ); $output .= theme('item_list', $steps, NULL, 'ol'); } return $output; } /** * Implementation of hook_menu. * Provides a callback url where votes can be submitted by the client-side * javascript. */ function fivestar_menu() { $items = array(); $items['admin/settings/votingapi'] = array( 'title' => 'Voting API', 'description' => 'Global settings for the Voting API.', 'page callback' => 'drupal_get_form', 'page arguments' => array('votingapi_settings_form'), 'access callback' => 'user_access', 'access arguments' => array('administer voting api'), 'type' => MENU_NORMAL_ITEM ); $items['admin/settings/fivestar'] = array( 'title' => 'Fivestar', 'page callback' => 'drupal_get_form', 'page arguments' => array('fivestar_settings'), 'access callback' => 'user_access', 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); $items['fivestar/preview/widget'] = array( 'page callback' => 'fivestar_preview_page', 'access callback' => 'user_access', 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); $items['fivestar/preview/node'] = array( 'page callback' => 'fivestar_preview', 'access callback' => 'user_access', 'access arguments' => array('administer content types'), 'type' => MENU_CALLBACK, ); $items['fivestar/vote'] = array( 'page callback' => 'fivestar_vote', 'access callback' => 'user_access', 'access arguments' => array('rate content'), 'type' => MENU_CALLBACK, ); return $items; } /** * Implementation of hook_init(). * * These includes do not need to be loaded for cached pages. */ function fivestar_init() { if (module_exists('content')) { module_load_include('inc', 'fivestar', 'fivestar_field'); } } /** * Implementation of hook_perm (permissions). * Exposes permissions for rating content, viewing aggregate ratings, and using PHP * snippets when configuring fivestar CCK fields. */ function fivestar_perm() { return array('rate content', 'use PHP for fivestar target'); } /** * Implementation of hook_theme(). */ function fivestar_theme() { return array( // Fivestar theme functions. 'fivestar' => array( 'arguments' => array('element' => NULL), ), 'fivestar_node_type_form' => array( 'arguments' => array('form' => NULL), ), 'fivestar_preview' => array( 'arguments' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL, 'labels_enable' => TRUE, 'labels' => array()), ), 'fivestar_preview_page' => array( 'arguments' => array(), ), 'fivestar_preview_wrapper' => array( 'arguments' => array('content' => NULL, 'type' => 'direct'), ), 'fivestar_select' => array( 'arguments' => array('element' => NULL), ), 'fivestar_settings' => array( 'arguments' => array('form' => NULL), ), 'fivestar_static' => array( 'arguments' => array('rating' => NULL, 'stars' => 5), ), 'fivestar_static_element' => array( 'arguments' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL), ), 'fivestar_summary' => array( 'arguments' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5), ), 'fivestar_widget' => array( 'arguments' => array('form' => NULL), ), ); } /** * Implementation of hook_form_alter * Adds fivestar enaable and position to the node-type configuration form. * */ function fivestar_form_alter(&$form, &$form_state, $form_id) { if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { // Goofy hack to get the buttons at the end of the array. $form['workflow']['#weight'] += 1; $form['submit']['#weight'] += 1; $form['delete']['#weight'] += 1; $form['fivestar'] = array( '#type' => 'fieldset', '#title' => t('Fivestar ratings'), '#collapsible' => TRUE, '#collapsed' => !variable_get('fivestar_'. $form['#node_type']->type, 0), '#description' => t('To rate this content, enable Fivestar rating below. These settings will be used for both comments (if available) and direct rating.'), '#theme' => 'fivestar_node_type_form', '#attributes' => array('id' => 'fivestar-node-type-form'), ); $form['fivestar']['fivestar'] = array( '#type' => 'checkbox', '#title' => t('Enable Fivestar rating'), '#default_value' => variable_get('fivestar_'. $form['#node_type']->type, 0), '#return_value' => 1, '#weight' => -5, ); $form['fivestar']['fivestar_stars'] = array( '#type' => 'select', '#title' => t('Number of stars'), '#options' => drupal_map_assoc(range(1, 10)), '#default_value' => variable_get('fivestar_stars_'. $form['#node_type']->type, 5), '#weight' => -4, ); $form['fivestar']['labels'] = array( '#type' => 'fieldset', '#title' => t('Star Labels'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#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.'), ); $form['fivestar']['labels']['fivestar_labels_enable'] = array( '#type' => 'checkbox', '#title' => t('Display labels on mouse over'), '#default_value' => variable_get('fivestar_labels_enable_'. $form['#node_type']->type, 1), '#return_value' => 1, '#weight' => -5, '#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.'), ); // Create the Mouseover text forms for each of the rating options // This form depends on the number of stars, and these extra textfields will be hidden with javascript $star_count = variable_get('fivestar_stars_'. $form['#node_type']->type, 5); $labels = variable_get('fivestar_labels_'. $form['#node_type']->type, array()); for ($n = 0; $n <= 10; $n++) { if ($star_count == 5 && $n <= 5) { // If the default 5 stars are chosen, then use these five default label values. $default_labels = array('Cancel rating', 'Poor', 'Okay', 'Good', 'Great', 'Awesome'); } elseif ($n == 0) { $default_labels[$n] = 'Cancel rating'; } else { $default_labels[$n] = 'Give it @star/@count'; } $form['fivestar']['labels']['fivestar_label_'. $n] = array( '#type' => 'textfield', '#title' => $n > 0 ? t('Star @star label', array('@star' => $n)) : t('Cancel label'), '#default_value' => isset($labels[$n]) ? $labels[$n] : $default_labels[$n], '#prefix' => '
', '#suffix' => '
', '#size' => 30, ); } $form['fivestar']['direct'] = array( '#type' => 'fieldset', '#title' => t('Direct rating widget'), '#collapsible' => FALSE, '#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.'), '#weight' => 2, ); $form['fivestar']['direct']['fivestar_style'] = array( '#type' => 'select', '#title' => t('Star display style'), '#default_value' => variable_get('fivestar_style_'. $form['#node_type']->type, 'average'), '#options' => array( 'average' => t('Display average vote value'), 'user' => t('Display user vote value'), 'smart' => t('User vote if available, average otherwise'), 'dual' => t('Both user and average vote'), ), ); $form['fivestar']['direct']['fivestar_text'] = array( '#type' => 'select', '#title' => t('Text display style'), '#default_value' => variable_get('fivestar_text_'. $form['#node_type']->type, 'dual'), '#options' => array( 'none' => t('Display no text beneath stars'), 'average' => t('Current average in text'), 'user' => t('User current vote in text'), 'smart' => t('User vote if available, average otherwise'), 'dual' => t('Both user and average vote'), ), ); $form['fivestar']['direct']['fivestar_title'] = array( '#type' => 'checkbox', '#title' => t('Show widget title'), '#default_value' => variable_get('fivestar_title_'. $form['#node_type']->type, 1), '#return_value' => 1, ); $form['fivestar']['direct']['fivestar_unvote'] = array( '#type' => 'checkbox', '#title' => t('Allow users to undo their votes'), '#default_value' => variable_get('fivestar_unvote_'. $form['#node_type']->type, 0), '#return_value' => 1, ); $form['fivestar']['direct']['fivestar_position_teaser'] = array( '#type' => 'select', '#title' => t('Teaser display'), '#default_value' => variable_get('fivestar_position_teaser_'. $form['#node_type']->type, 'hidden'), '#options' => array( 'above' => t('Rating widget above the teaser'), 'below' => t('Rating widget below the teaser'), 'above_static' => t('Static display above the teaser'), 'below_static' => t('Static display below the teaser'), 'hidden' => t(''), ), ); $form['fivestar']['direct']['fivestar_position'] = array( '#type' => 'select', '#title' => t('Full node display'), '#default_value' => variable_get('fivestar_position_'. $form['#node_type']->type, 'below'), '#options' => array( 'above' => t('Above the node body'), 'below' => t('Below the node body'), 'above_static' => t('Static display above the node body'), 'below_static' => t('Static display below the node body'), 'hidden' => t(''), ), ); $form['fivestar']['direct']['fivestar_direct_preview'] = array( '#type' => 'item', '#title' => t('Direct rating widget preview'), '#value' => theme( 'fivestar_preview', $form['fivestar']['direct']['fivestar_style']['#default_value'], $form['fivestar']['direct']['fivestar_text']['#default_value'], $form['fivestar']['fivestar_stars']['#default_value'], $form['fivestar']['direct']['fivestar_unvote']['#default_value'], $form['fivestar']['direct']['fivestar_title']['#default_value'] ? NULL : FALSE, $form['fivestar']['fivestar_labels_enable']['#default_value'], variable_get('fivestar_labels_'. $form['#node_type']->type, array()) ), ); if (!$form['fivestar']['fivestar']['#default_value']) { $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', ''); } else { $form['fivestar']['direct']['fivestar_direct_preview']['#value'] = theme('fivestar_preview_wrapper', $form['fivestar']['direct']['fivestar_direct_preview']['#value']); } $form['#submit'][] = 'fivestar_node_type_form_submit'; } // Add comment form modifications. if (module_exists('comment')) { module_load_include('inc', 'fivestar', 'fivestar_comment'); fivestar_comment_form_alter($form, $form_state, $form_id); } } /** * Additional submit handler for the node type form. */ function fivestar_node_type_form_submit($form, &$form_state) { // Do not save any fivestar variables if fivestar is disabled. if (isset($form_state['values']['fivestar']) && $form_state['values']['fivestar'] === 0) { foreach ($form_state['values'] as $key => $value) { if (strpos($key, 'fivestar') === 0) { variable_del($key .'_'. $form_state['values']['type']); } } } // Merge labels into a single variable. $labels = array(); for ($n = 0; $n <= 10; $n++) { $labels[] = $form_state['values']['fivestar_label_'. $n]; variable_del('fivestar_label_'. $n .'_'. $form_state['values']['type']); } variable_del('fivestar_labels_'. $form_state['values']['type']); if ($form_state['values']['fivestar_labels_enable']) { variable_set('fivestar_labels_'. $form_state['values']['type'], $labels); } } /** * Theme function to add the Fivestar preview to the node type form. */ function theme_fivestar_node_type_form($form) { drupal_add_js(drupal_get_path('module', 'fivestar') .'/fivestar-admin.js'); drupal_add_js(array('fivestar' => array('preview_url' => url('fivestar/preview/node'))), 'setting'); $output = ''; $output .= drupal_render($form['fivestar']); $output .= drupal_render($form['fivestar_stars']); // Star labels. $output .= drupal_render($form['labels']); // Direct rating settings form. $direct = ''; $direct .= '
'; $direct .= drupal_render($form['direct']['fivestar_style']); $direct .= drupal_render($form['direct']['fivestar_text']); $direct .= drupal_render($form['direct']['fivestar_title']); $direct .= drupal_render($form['direct']['fivestar_unvote']); $direct .= drupal_render($form['direct']['fivestar_position_teaser']); $direct .= drupal_render($form['direct']['fivestar_position']); $direct .= '
'; $direct .= '
'; $direct .= drupal_render($form['direct']['fivestar_direct_preview']); $direct .= '
'; $form['direct']['#children'] = $direct; $output .= drupal_render($form['direct']); // Comment settings form. if (module_exists('comment')) { $comment = ''; $comment .= '
'; $comment .= drupal_render($form['comment']['fivestar_comment']); $comment .= '
'; $comment .= '
'; $comment .= drupal_render($form['comment']['fivestar_comment_preview']); $comment .= '
'; $form['comment']['#children'] = $comment; $output .= drupal_render($form['comment']); } // Any remaining cruft (should be empty). $output .= drupal_render($form); return $output; } /** * Implementation of hook_node_types(). */ function fivestar_node_type($op, $info) { $type = $info->type; $variables = array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser'); // Be responsible and cleanup unneeded variables. if ($op == 'delete') { foreach ($variables as $variable) { variable_del($variable .'_'. $type); } } // When changing the type name, update the variables. elseif ($op == 'update' && !empty($info->old_type) && $info->old_type != $info->type) { foreach ($variables as $variable) { $value = variable_get($variable .'_'. $type, -1); if ($value != -1) { variable_del($variable .'_'. $type); variable_set($variable .'_'. $type, $value); } } } } /** * Callback function for admin/settings/fivestar. Display the settings form. */ function fivestar_settings() { $form = array(); $form['fivestar_widget'] = array( '#type' => 'radios', '#title' => t('Widget display'), '#options' => array('default' => t('Default')) + module_invoke_all('fivestar_widgets'), '#default_value' => variable_get('fivestar_widget', 'default'), '#description' => t('Choose a widget set to be used on your site.'), '#attributes' => array('class' => 'fivestar-widgets clear-block'), ); return system_settings_form($form); } function theme_fivestar_settings($form) { fivestar_add_css(); drupal_set_title(t('Fivestar Settings')); // Default preview. $form['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':
'; // Preview for each widget. $widget_number = 0; foreach (element_children($form['fivestar_widget']) as $widget_key) { if ($widget_key != 'default') { $form['fivestar_widget'][$widget_key]['#description'] = $form['fivestar_widget'][$widget_key]['#title'] .' '. t('Preview') .':
'; $widget_number++; } } return drupal_render($form); } /** * Callback function for fivestar/preview/widget. Outputs an entire page * containing a preview of the passed in fivestar widget format. */ function fivestar_preview_page($widget_number = NULL) { $widgets = module_invoke_all('fivestar_widgets'); $css_files = array_keys($widgets); if (isset($css_files[$widget_number])) { fivestar_add_css($css_files[$widget_number]); } else { fivestar_add_css('default'); } fivestar_add_js(); print theme('fivestar_preview_page'); exit; } function theme_fivestar_preview_page() { $form = array(); $form['vote'] = array( '#type' => 'fivestar', '#stars' => 5, '#auto_submit' => FALSE, '#allow_clear' => TRUE, ); $form = form_builder('fivestar_preview', $form); $output = "\n"; $output .= ''; $output .= ''; $output .= ' '. t('Fivestar Preview') .''; $output .= drupal_get_html_head(); $output .= drupal_get_css(); $output .= drupal_get_js(); $output .= ' '; $output .= ' '; $output .= drupal_render($form); $output .= ''; return $output; } /** * Callback function for fivestar/preview/node. Outputs a JSON page containing * a Fivestar preview of a node rating widget. */ function fivestar_preview() { // Perform a few basic security checks. $style = check_plain($_POST['style']); $text = check_plain($_POST['text']); $stars = (int)$_POST['stars']; $unvote = (boolean)$_POST['unvote']; $title = (boolean)$_POST['title']; $labels_enable = (boolean)$_POST['labels_enable']; $labels = (array)$_POST['labels']; foreach ($labels as $key => $label) { $labels[$key] = filter_xss_admin($label); } $output = theme('fivestar_preview', $style, $text, $stars, $unvote, $title ? NULL : FALSE, $labels_enable, $labels); drupal_set_header('Content-Type: text/javascript; charset=utf-8'); print drupal_to_js(array('status' => TRUE, 'data' => $output)); } function theme_fivestar_preview($style = NULL, $text = NULL, $stars = NULL, $unvote = NULL, $title = NULL, $labels_enable = TRUE, $labels = array()) { $values = array( 'average' => 50, 'user' => 80, 'count' => 20, ); $settings = array( 'stars' => $stars, 'allow_clear' => $unvote, 'style' => $style, 'text' => $text, 'title' => $title, 'autosubmit' => FALSE, 'labels_enable' => $labels_enable, 'labels' => $labels, ); $form = drupal_get_form('fivestar_custom_widget', $values, $settings); // This regex is sadly necessary because having duplicate form_tokens or // form_id elements can cause the content type form to choke. Forms inside of // forms is also frowned upon, so this removes the wrapping form tag as well. $form = str_replace(array(''), array(''), $form); $form = preg_replace('/( method=".*?")|( action=".*?")|()/', '', $form); return $form; } function theme_fivestar_preview_wrapper($content, $type = 'direct') { return '
'. $content .'
'; } /** * Callback function for fivestar/vote path * @param type * A content-type to log the vote to. 'node' is the most common. * @param cid * A content id to log the vote to. This would be a node ID, a comment ID, etc. * @param value * A value from 1-100, representing the vote cast for the content. * @return * An XML chunk containing the results of the vote, for use by the client-side * javascript code. */ function fivestar_vote($type, $cid, $value, $tag = 'vote') { $result = _fivestar_cast_vote($type, $cid, $value, $tag); if ($type == 'node') { $node = node_load($cid); } $stars = variable_get('fivestar_stars_'. (!isset($node) ? 'default' : $node->type), 5); $output = ''; $output .= ''; $output .= ''; if (count($result)) { foreach ($result as $data) { if ($data['tag'] == $tag) { $output .= '<'. $data['function'] .'>'. $data['value'] .''; $summary[$data['tag']][$data['function']] = $data['value']; } } } $output .= ''; $output .= ''. theme('fivestar_summary', NULL, $summary[$tag]['average'], $summary[$tag]['count'], $stars) .''; $output .= ''. theme('fivestar_summary', $value, NULL, $summary[$tag]['count'], $stars) .''; $output .= ''. theme('fivestar_summary', $value, $summary[$tag]['average'], $summary[$tag]['count'], $stars) .''; $output .= ''; $output .= ''; $output .= ''; $output .= ''. $value .''; $output .= ''. $type .''; $output .= ''. $cid .''; $output .= ''; drupal_set_header("Content-Type: text/xml"); exit($output); } /** * Internal function to handle vote casting, flood control, XSS, IP based * voting, etc... */ function _fivestar_cast_vote($type, $cid, $value, $tag = NULL, $uid = NULL) { global $user; $tag = empty($tag) ? 'vote' : $tag; $uid = empty($uid) ? $user->uid : $uid; // Bail out if the user's trying to vote on an invalid object. if (!_fivestar_validate_target($type, $cid)) { return array(); } // Sanity-check the incoming values. if (is_numeric($cid) && is_numeric($value)) { if ($value > 100) { $value = 100; } // Get the user's current vote. $criteria = array('content_type' => $type, 'content_id' => $cid, 'tag' => $tag); $user_votes = votingapi_select_votes($criteria += array('uid' => $uid)); if ($value == 0) { votingapi_delete_votes($user_votes); } else { votingapi_set_votes($criteria += array('value' => $value, 'uid' => $uid), $user_votes); } return votingapi_recalculate_results($type, $cid); } else { return array(); } } function fivestar_get_votes($type, $cid, $tag = 'vote', $uid = NULL) { global $user; if (empty($uid)) { $uid = $user->uid; } $criteria = array( 'content_type' => $type, 'content_id' => $cid, 'value_type' => 'percent', 'tag' => $tag, ); $votes = array( 'average' => array(), 'count' => array(), 'user' => array(), ); $results = votingapi_select_results($criteria); foreach ($results as $result) { if ($result['function'] == 'average') { $votes['average'] = $result; } if ($result['function'] == 'count') { $votes['count'] = $result; } } if ($user->uid) { $user_vote = votingapi_select_votes($criteria += array('uid' => $user->uid)); $votes['user'] = $user_vote[0]; } else { // If the user is anonymous, we never bother loading their existing votes. // Not only would it be hit-or-miss, it would break page caching. Safer to always // show the 'fresh' version to anon users. $votes['user'] = array('value' => 0); } return $votes; } /** * Internal function to check that node-type accepts votes * * @param text $type type of target (currently only node is supported) * @param text $id identifier within the type (in this case node-type) * * @return boolean */ function _fivestar_validate_target($type, $id) { switch ($type) { case 'node': if ($node = node_load($id)) { if (variable_get('fivestar_'. $node->type, 0)) { return TRUE; } else { return FALSE; } } else { return FALSE; } default: return FALSE; } } /** * Implementation of hook_fivestar_widgets. * * This hook allows other modules to create additional custom widgets for * the fivestar module. * * @return array * An array of key => value pairs suitable for inclusion as the #options in a * select or radios form element. Each key must be the location of a css * file for a fivestar widget. Each value should be the name of the widget. */ function fivestar_fivestar_widgets() { $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets'; $files = file_scan_directory($widgets_directory, '\.css$'); $widgets = array(); foreach ($files as $file) { $widgets[$file->filename] = drupal_ucfirst($file->name); } return $widgets; } /** * Implementation of hook_nodeapi() * * Adds the fievestar widget to the node view. */ function fivestar_nodeapi(&$node, $op, $teaser, $page) { switch ($op) { case 'view': if ($node->in_preview == FALSE && variable_get('fivestar_'. $node->type, 0)) { if ($teaser) { $position = variable_get('fivestar_position_teaser_'. $node->type, 'above'); } else { $position = variable_get('fivestar_position_'. $node->type, 'above'); } switch ($position) { case 'above': case 'below': $node->content['fivestar_widget'] = array( '#value' => fivestar_widget_form($node), '#weight' => $position == 'above' ? -10 : 50, ); break; case 'above_static': case 'below_static': $stars = variable_get('fivestar_stars_'. $node->type, 5); $node->content['fivestar_widget'] = array( '#value' => fivestar_static('node', $node->nid, $node->type), '#weight' => $position == 'above_static' ? -10 : 50, ); break; default: // We'll do nothing. break; } } break; } } function fivestar_block($op = 'list', $delta = 0, $edit = array()) { global $user; switch ($op) { case 'list': $blocks[0]['info'] = t('Fivestar: Rate this node'); return $blocks; case 'view': if (user_access('access content') && user_access('rate content')) { if (arg(0) == 'node' && is_numeric(arg(1)) && (arg(2) == '' || arg(2) == 'view')) { $node = node_load(arg(1)); if (_fivestar_validate_target('node', $node->nid)) { $block['subject'] = t('Rate This'); $block['content'] = fivestar_widget_form($node); } return $block; } } break; } } function fivestar_widget_form($node) { return drupal_get_form('fivestar_form_node_'. $node->nid, 'node', $node->nid); } /** * Implementation of hook_forms. This is necessary when multiple fivestar * forms appear on the same page, each requiring a separate form_id, but all * using the same underlying callbacks. */ function fivestar_forms($form_id, $args) { if (strpos($form_id, 'fivestar_form') !== FALSE) { if ($form_id == 'fivestar_form_'. $args[0] .'_'. $args[1]) { $forms[$form_id] = array('callback' => 'fivestar_form'); return $forms; } } } /** * Create 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. */ function fivestar_form(&$form_state, $content_type, $content_id) { global $user; if ($content_type == 'node') { if (is_numeric($content_id)) { $node = node_load($content_id); } else { return array(); } } $votes = fivestar_get_votes($content_type, $content_id); $values = array( 'user' => $votes['user']['value'], 'average' => $votes['average']['value'], 'count' => $votes['count']['value'], ); $settings = array( 'stars' => variable_get('fivestar_stars_'. $node->type, 5), 'allow_clear' => variable_get('fivestar_unvote_'. $node->type, FALSE), 'style' => variable_get('fivestar_style_'. $node->type, 'average'), 'text' => variable_get('fivestar_text_'. $node->type, 'combo'), 'content_type' => $content_type, 'content_id' => $content_id, 'autosubmit' => TRUE, 'title' => variable_get('fivestar_title_'. $node->type, 1) ? NULL : FALSE, 'labels_enable' => variable_get('fivestar_labels_enable_'. $node->type, 1), 'labels' => variable_get('fivestar_labels_'. $node->type, array()), ); return fivestar_custom_widget(&$form_state, $values, $settings); } function fivestar_static($content_type, $content_id, $node_type = NULL) { global $user; $criteria = array( 'content_type' => $content_type, 'content_id' => $content_id, 'value_type' => 'percent', 'tag' => 'vote', ); $votes = fivestar_get_votes($content_type, $content_id); if ($content_type == 'node') { $star_display = variable_get('fivestar_style_'. $node_type, 'average'); $text_display = variable_get('fivestar_text_'. $node_type, 'dual'); $title_display = variable_get('fivestar_title_'. $node_type, 1); $stars = variable_get('fivestar_stars_'. $node_type, 5); switch ($star_display) { case 'average': case 'dual': $star_value = $votes['average']['value']; $title = $title_display ? t('Average') : NULL; break; case 'user': $star_value = $votes['user']['value']; $title = $title_display ? t('Your rating') : NULL; break; case 'smart': $star_value = $votes['user']['value'] ? $votes['user']['value'] : $votes['average']['value']; $title = $title_display ? $votes['user']['value'] ? t('Your rating') : t('Average') : NULL; break; } // Set all text values, then unset the unnecessary ones. $user_value = $votes['user']['value']; $average_value = $votes['average']['value']; $count_value = $votes['count']['value']; switch ($text_display) { case 'average': $user_value = NULL; break; case 'user': $average_value = NULL; break; case 'smart': if ($votes['user']['value']) { $average_value = NULL; } else { $user_value = NULL; } break; case 'none': $count_value = NULL; } } // Possibly add other content types here (comment, user, etc). else { $stars = 5; $star_value = $votes['average']['value']; $user_value = $votes['user']['value']; $average_value = $votes['average']['value']; $count_value = $votes['count']['value']; } $star_display = theme('fivestar_static', $star_value, $stars); $text_display = theme('fivestar_summary', $user_value, $average_value, $count_value, $stars); return theme('fivestar_static_element', $star_display, $title, $text_display); } function fivestar_custom_widget(&$form_state, $values, $settings) { $form = array( '#attributes' => array('class' => 'fivestar-widget'), '#redirect' => FALSE, '#theme' => 'fivestar_widget', ); $form['#submit'][] = 'fivestar_form_submit'; if (isset($settings['content_type'])) { $form['content_type'] = array( '#type' => 'hidden', '#value' => $settings['content_type'], '#id' => $settings['content_id'] ? 'edit-content-type-'. $settings['content_id'] : NULL, ); } if (isset($settings['content_id'])) { $form['content_id'] = array( '#type' => 'hidden', '#value' => $settings['content_id'], '#id' => $settings['content_id'] ? 'edit-content-id-'. $settings['content_id'] : NULL, ); } $form['vote'] = array( '#type' => 'fivestar', '#stars' => $settings['stars'], '#vote_count' => $values['count'], '#vote_average' => $values['average'], '#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE, '#auto_submit_path' => 'fivestar/vote/'. $settings['content_type'] .'/'. $settings['content_id'], '#allow_clear' => $settings['allow_clear'], '#content_id' => $settings['content_id'], '#required' => isset($settings['required']) ? $settings['required'] : FALSE, '#labels_enable' => isset($settings['labels_enable']) ? $settings['labels_enable'] : TRUE, '#labels' => isset($settings['labels']) ? $settings['labels'] : NULL, ); $form['fivestar_submit'] = array( '#type' => 'submit', '#value' => t('Rate'), '#attributes' => array('class' => 'fivestar-submit'), '#id' => $settings['content_id'] ? 'edit-fivestar-submit-'. $settings['content_id'] : NULL, ); switch ($settings['text']) { case 'user': $form['vote']['#description'] = theme('fivestar_summary', $values['user'], NULL, $values['count'], $settings['stars']); $form['vote']['#attributes']['class'] .= ' fivestar-user-text'; break; case 'average': $form['vote']['#description'] = theme('fivestar_summary', NULL, $values['average'], $values['count'], $settings['stars']); $form['vote']['#attributes']['class'] .= ' fivestar-average-text'; break; case 'smart': $form['vote']['#description'] = theme('fivestar_summary', $values['user'], $values['user'] ? NULL : $values['average'], $values['count'], $settings['stars']); $form['vote']['#attributes']['class'] .= ' fivestar-smart-text '. ($values['user'] ? 'fivestar-user-text' : 'fivestar-average-text'); break; case 'dual': $form['vote']['#description'] = theme('fivestar_summary', $values['user'], $values['average'], $values['count'], $settings['stars']); $form['vote']['#attributes']['class'] .= ' fivestar-combo-text'; break; } switch ($settings['style']) { case 'average': $form['vote']['#title'] = t('Average'); $form['vote']['#default_value'] = $values['average']; $form['vote']['#attributes']['class'] .= ' fivestar-average-stars'; break; case 'user': $form['vote']['#title'] = t('Your rating'); $form['vote']['#default_value'] = $values['user']; $form['vote']['#attributes']['class'] .= ' fivestar-user-stars'; break; case 'smart': $form['vote']['#title'] = $values['user'] ? t('Your rating') : t('Average'); $form['vote']['#default_value'] = $values['user'] ? $values['user'] : $values['average']; $form['vote']['#attributes']['class'] .= ' fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars'); break; case 'dual': $form['vote']['#title'] = t('Your rating'); $form['vote']['#default_value'] = $values['user']; $form['vote']['#attributes']['class'] .= ' fivestar-combo-stars'; $form['#attributes']['class'] .= ' fivestar-combo-stars'; $static_average = theme('fivestar_static', $values['average'], $settings['stars']); $form['average'] = array( '#type' => 'markup', '#value' => theme('fivestar_static_element', $static_average, $settings['title'] !== FALSE ? t('Average') : NULL), '#weight' => -1, ); break; } // Set an over-ridding title if passed in. // An empty title won't change the default, a string will set a new title, // and title === FALSE will unset the title entirely. if (isset($settings['title'])) { if ($settings['title'] !== FALSE) { $form['vote']['#title'] = $settings['title']; } else { unset($form['vote']['#title']); unset($form['average']['#title']); } } return $form; } /** * Submit handler for the above form (non-javascript version). */ function fivestar_form_submit($form, &$form_state) { if ($form_state['values']['form_id'] == 'fivestar_form_'. $form_state['values']['content_type'] .'_'. $form_state['values']['content_id']) { // Cast the vote. $result = _fivestar_cast_vote($form_state['values']['content_type'], $form_state['values']['content_id'], $form_state['values']['vote']); // Set a message that the vote was received. if ($form_state['values']['vote'] === '0') { drupal_set_message(t('Your vote has been cleared.')); } elseif (is_numeric($form_state['values']['vote'])) { drupal_set_message(t('Thank you for your vote.')); } // Regenerate the page with a drupal_goto() to update the current values. drupal_goto(); } } /** * Implementation of hook_elements * * Defines 'fivestar' form element type */ function fivestar_elements() { $type['fivestar'] = array( '#input' => TRUE, '#stars' => 5, '#widget' => 'stars', '#allow_clear' => FALSE, '#auto_submit' => FALSE, '#auto_submit_path' => '', '#labels_enable' => TRUE, '#process' => array('fivestar_expand'), ); return $type; } /** * Theme the fivestar form element by adding necessary css and javascript. */ function theme_fivestar($element) { // Add necessary CSS. fivestar_add_css(); // Add necessary javascript. if ($element['#widget'] == 'stars' ) { fivestar_add_js(); } return theme('form_element', $element, $element['#children']); } /** * Theme the straight HTML version of the fivestar select list. This is used * to remove the wrapping 'form-item' div from the select list. */ function theme_fivestar_select($element) { $select = ''; $size = $element['#size'] ? ' size="'. $element['#size'] .'"' : ''; _form_set_class($element, array('form-select')); $multiple = isset($element['#multiple']) && $element['#multiple']; return ''; } /** * Theme an entire fivestar widget, including the submit button and the normal * fivestar widget themed in the theme_fivestar() function. */ function theme_fivestar_widget($form) { // Move the description outside of the vote element. $description = empty($form['vote']['#description']) ? ' ' : $form['vote']['#description']; unset($form['vote']['#description']); $output = ''; $output .= '
'; $output .= drupal_render($form); $output .= '
'. $description .'
'; $output .= '
'; return $output; } /** * Display a plain HTML VIEW ONLY version of the widget * with the specified rating * * @param $rating * The desired rating to display out of 100 (i.e. 80 is 4 out of 5 stars) * @param $stars * The total number of stars this rating is out of * @return * A themed HTML string representing the star widget * */ function theme_fivestar_static($rating, $stars = 5) { // Add necessary CSS. fivestar_add_css(); $output = ''; $output .= '
'; $numeric_rating = $rating/(100/$stars); for ($n=1; $n <= $stars; $n++) { $star_value = ceil((100/$stars) * $n); $prev_star_value = ceil((100/$stars) * ($n-1)); $zebra = ($n % 2 == 0) ? 'even' : 'odd'; $first = $n == 1 ? ' star-first' : ''; $last = $n == $stars ? ' star-last' : ''; $output .= '
'; if ($rating < $star_value && $rating > $prev_star_value) { $percent = (($rating - $prev_star_value) / ($star_value - $prev_star_value)) * 100; $output .= ''; } elseif ($rating >= $star_value) { $output .= ''; } else { $output .= ''; } if ($n == 1)$output .= $numeric_rating; $output .= '
'; } $output .= '
'; return $output; } function theme_fivestar_summary($user_rating, $average_rating, $votes = 0, $stars = 5) { if ($votes == 0) { return '
'. t('No votes yet') .'
'; } $output = ''; if ($user_rating) { $div_class = 'user'; $user_stars = round(($user_rating * $stars) / 100, 1); $output .= ''. t('Your rating: !stars', array('!stars' => $user_rating ? $user_stars : t('None'))) .''; } if ($user_rating && $average_rating) { $output .= ' '; } if ($average_rating) { $div_class = 'average'; $average_stars = round(($average_rating * $stars) / 100, 1); $output .= ''. t('Average: !stars', array('!stars' => $average_stars)) .''; } if ($user_rating && $average_rating) { $div_class = 'combo'; } $vote_text = format_plural($votes, 'vote', 'votes'); $output .= ' '. t('(!votes !vote_text)', array('!votes' => $votes, '!vote_text' => $vote_text)) .''; $output = '
'. $output .'
'; return $output; } /** * Display a static fivestar value as stars with a title and description. */ function theme_fivestar_static_element($value, $title = NULL, $description = NULL) { $output .= '
'; $element = array( '#type' => 'item', '#title' => $title, ); $output .= theme('form_element', $element, $value); $output .= isset($description) ? '
'. $description .'
' : ''; $output .= '
'; return $output; } /** * Fetch the necessary CSS files to render the fivestar widget. */ function fivestar_add_css($widget_css = NULL) { // Add fivestar CSS. drupal_add_css(drupal_get_path('module', 'fivestar') .'/fivestar.css'); // Add widget specific CSS. if (!isset($widget_css)) { $widget_css = variable_get('fivestar_widget', 'default'); } if ($widget_css != 'default') { drupal_add_css($widget_css, 'module'); } } /** * Add necessary JS files and settings to render the fivestar widget. */ function fivestar_add_js() { static $js_added = FALSE; // Add necessary javascript only once per page. if (!$js_added) { drupal_add_js(drupal_get_path('module', 'fivestar') .'/jquery.rating.js'); drupal_add_js(array('fivestar' => array('titleUser' => t('Your rating') .':', 'titleAverage' => t('Average') .':')), 'setting'); $js_added = TRUE; } } /** * Process callback for fivestar_element -- see fivestar_element() */ function fivestar_expand($element) { static $fivestar_id = 0; if (isset($element['#vote_count'])) { $element['vote_count'] = array( '#type' => 'hidden', '#value' => $element['#vote_count'], '#id' => 'edit-vote-count-'. $fivestar_id, ); } if (isset($element['#vote_average'])) { $element['vote_average'] = array( '#type' => 'hidden', '#value' => $element['#vote_average'], '#id' => 'edit-vote-average-'. $fivestar_id, ); } if ($element['#auto_submit'] && !empty($element['#auto_submit_path'])) { $element['auto_submit_path'] = array( '#type' => 'hidden', '#value' => url($element['#auto_submit_path']), '#attributes' => array('class' => 'fivestar-path'), '#id' => 'edit-auto-submit-path-'. $fivestar_id, ); } $options = array('-' => t('Select rating')); $default_value = $element['#default_value']; for ($i = 0; $i <= $element['#stars']; $i++) { $this_value = ceil($i * 100/$element['#stars']); $next_value = ceil(($i+1) * 100/$element['#stars']); // Display clear button only if enabled. if ($element['#allow_clear'] == TRUE && $i == 0) { $options[$this_value] = isset($element['#labels'][$i]) ? t(filter_xss_admin($element['#labels'][$i])) : t('Cancel rating'); } // Display a normal star value. if ($i > 0) { if (isset($element['#labels'][$i])) { $options[$this_value] = $element['#labels'][$i] == '' ? $i : t(filter_xss_admin($element['#labels'][$i]), array('@star' => $i, '@count' => $element['#stars'])); } else { $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars'])); } } // Round up the default value to the next exact star value if needed. if ($this_value < $element['#default_value'] && $next_value > $element['#default_value']) { $default_value = $next_value; } } $element['vote'] = array( '#type' => 'select', '#options' => $options, '#required' => $element['#required'], '#default_value' => $default_value, '#parents' => $element['#parents'], '#id' => 'edit-vote-'. $fivestar_id, '#theme' => 'fivestar_select', '#weight' => $element['#weight'], ); // If a default value is not exactly on a radio value, round up to the next one if ($element['#default_value'] > $this_value && $element['#default_value'] <= $next_value) { $element['vote']['#default_value'] = $next_value; } // Set a class for the display of label text on hover. $label_class = $element['#labels_enable'] ? ' fivestar-labels-hover' : ''; $element['#prefix'] = '
'; $element['#suffix'] = '
'; // Add validation function that considers a 0 value as empty. $element['#validate']['fivestar_validate'] = array(); $fivestar_id++; return $element; } function fivestar_validate($form, &$form_state) { if ($form['#required'] && (empty($form_state['values']['vote']) || $form_state['values']['vote'] == '-')) { form_error($form, t('!name field is required.', array('!name' => $form['#title']))); } } function fivestar_votingapi_views_formatters($details = array()) { if ($details['value_type'] == 'percent') { return array('fivestar_views_value_display_handler' => t('Fivestar rating')); } } function fivestar_views_value_display_handler($op, $filter, $value, &$query) { if ($value === NULL) { return $value; } else { // Determine number of stars to display if (is_numeric($query->options)) { $stars = $query->options; } elseif (isset($query->node_type)) { $stars = variable_get('fivestar_stars_'. $query->node_type, 5); } else { $type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $query->nid)); $stars = variable_get('fivestar_stars_'. (!isset($type) ? 'default' : $type), 5); } return theme('fivestar_static', $value, $stars); } }