View source
<?php
module_load_include('inc', 'advpoll', 'includes/advpoll_voteapi');
module_load_include('inc', 'advpoll', 'includes/advpoll_helper');
function advpoll_menu() {
$menu['admin/config/search/advpoll'] = array(
'title' => 'Advanced Poll',
'description' => 'Configure sitewide Advanced Poll settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'advpoll_settings_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer polls',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'advpoll.admin.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/votes'] = array(
'title' => 'Votes',
'page callback' => 'advpoll_votes_page',
'page arguments' => array(
1,
),
'access callback' => '_advpoll_votes_access',
'access arguments' => array(
1,
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/electoral-list'] = array(
'title' => 'Electoral list',
'page callback' => 'advpoll_electoral_list_page',
'page arguments' => array(
1,
),
'access callback' => '_advpoll_electoral_list_access',
'access arguments' => array(
1,
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/remove'] = array(
'page callback' => 'advpoll_remove_voter',
'page arguments' => array(
1,
),
'access arguments' => array(
'administer polls',
),
'weight' => 3,
'type' => MENU_CALLBACK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/votes/clear'] = array(
'page callback' => 'advpoll_clear_votes_page',
'page arguments' => array(
1,
),
'access callback' => '_advpoll_clear_votes_access',
'access arguments' => array(
1,
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/write-ins'] = array(
'title' => 'Write-ins',
'page callback' => 'advpoll_writeins_page',
'page arguments' => array(
1,
),
'access callback' => '_advpoll_writeins_access',
'access arguments' => array(
1,
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
$menu['node/%node/advpoll/results'] = array(
'title' => 'Results',
'page callback' => 'advpoll_results_page',
'page arguments' => array(
1,
),
'access callback' => '_advpoll_results_access',
'access arguments' => array(
1,
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
'file' => 'advpoll.pages.inc',
'file path' => drupal_get_path('module', 'advpoll') . '/includes',
);
return $menu;
}
function advpoll_permission() {
return array(
'vote on polls' => array(
'title' => t('Vote on polls'),
'description' => t('User may vote on polls.'),
),
'cancel own vote' => array(
'title' => t('Cancel own vote'),
'description' => t('User may cancel their vote in cases where individual user votes are being tracked.'),
),
'administer polls' => array(
'title' => t('Administer polls'),
'description' => t('User may use poll administration pages.'),
'restrict access' => TRUE,
),
'inspect all votes' => array(
'title' => t('Inspect all votes'),
'description' => t('User may use votes administration page.'),
'restrict access' => TRUE,
),
'show vote results' => array(
'title' => t('Show vote results'),
'description' => t('User may view poll results in cases where access to results is restricted by node settings.'),
),
'access electoral list' => array(
'title' => t('Access electoral list'),
'description' => t('User may see electoral lists associated with each poll.'),
),
'add write-ins' => array(
'title' => t('Add write-in votes'),
'description' => t('User may add write-ins for polls that allow them.'),
'restrict access' => TRUE,
),
'show write-ins' => array(
'title' => t('Show write-in votes in results'),
'description' => t('User may see write-in items in results. Allows for moderation of write-ins.'),
'restrict access' => TRUE,
),
);
}
function advpoll_node_view($node, $view_mode) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
if ($data->behavior == 'approval' || $data->behavior == 'pool') {
drupal_add_css(drupal_get_path('module', 'advpoll') . '/css/advpoll.css', array(
'group' => CSS_DEFAULT,
'every_page' => TRUE,
));
$weight = 1;
if (!empty($node->content['advpoll_choice']['#weight'])) {
$weight = $node->content['advpoll_choice']['#weight'];
}
unset($node->content['advpoll_choice']);
if (advpoll_user_eligibility($node)) {
$voteform = drupal_get_form('advpoll_choice_form', $node);
$node->content['advpoll_choice'] = array(
0 => $voteform,
'#weight' => $weight,
);
}
else {
$results = advpoll_display_results($node->nid, $data);
$node->content['advpoll_choice'] = array(
'#markup' => $results,
'#weight' => $weight,
);
}
}
}
}
function advpoll_block_info() {
$blocks['advpoll_recent'] = array(
'info' => t('Advanced Poll: Most Recent'),
'cache' => DRUPAL_NO_CACHE,
);
$blocks = advpoll_get_poll_info_blocks($blocks);
return $blocks;
}
function advpoll_block_view($delta = '') {
$block = array();
switch ($delta) {
case 'advpoll_recent':
$recent = advpoll_get_recent();
$block['subject'] = t('Most Recent Poll');
if ($recent) {
$block['content'] = array(
'#markup' => $recent,
);
}
break;
default:
$block = advpoll_generate_block_view($block, $delta);
break;
}
return $block;
}
function advpoll_theme($existing, $type, $theme, $path) {
return array(
'advpoll_bar' => array(
'variables' => array(
'percentage' => 0,
'votes' => 0,
'voted' => 0,
),
'path' => drupal_get_path('module', 'advpoll') . '/templates',
'template' => 'advpoll-bar',
),
'advpoll_closed' => array(
'variables' => array(
'data' => NULL,
),
'path' => drupal_get_path('module', 'advpoll') . '/templates',
'template' => 'advpoll-closed',
),
'advpoll_noresults' => array(
'variables' => array(
'available_date' => NULL,
'votes' => NULL,
'nid' => NULL,
'cancel_form' => NULL,
),
'path' => drupal_get_path('module', 'advpoll') . '/templates',
'template' => 'advpoll-noresults',
),
'advpoll_results' => array(
'variables' => array(
'bars' => NULL,
'total' => 0,
'voted' => NULL,
'nid' => NULL,
'cancel_form' => NULL,
),
'path' => drupal_get_path('module', 'advpoll') . '/templates',
'template' => 'advpoll-results',
),
'advpoll_ineligible' => array(
'variables' => array(
'data' => NULL,
),
'path' => drupal_get_path('module', 'advpoll') . '/templates',
'template' => 'advpoll-ineligible',
),
);
}
function advpoll_node_presave($node) {
if ($node->type === 'advpoll') {
$lang = entity_language('node', $node);
if (isset($node->advpoll_choice[$lang])) {
$choices = $node->advpoll_choice[$lang];
}
else {
$choices = $node->advpoll_choice[LANGUAGE_NONE];
}
$count = count($choices);
if (isset($node->devel_generate)) {
$node->advpoll_dates[$lang][0]['value'] = date('Y-m-d 00:00:00', time());
$node->advpoll_dates[$lang][0]['value2'] = date('Y-m-d 00:00:00', time() + 60 * 60 * 24 * 30);
$node->advpoll_max_choices[$lang][0]['value'] = $count;
$node->advpoll_closed[$lang][0]['value'] = 'open';
$node->advpoll_mode[$lang][0]['value'] = 'normal';
$node->advpoll_cookie_duration[$lang][0]['value'] = 60;
$node->advpoll_results[$lang][0]['value'] = 'aftervote';
unset($node->advpoll_options);
}
$ids = array();
for ($i = 0; $i < $count; $i++) {
$id = $choices[$i]['choice_id'];
if (!in_array($id, $ids)) {
$ids[] = $id;
}
else {
$new_id = dechex(time() * rand(5, 50));
$node->advpoll_choice[$lang][$i]['choice_id'] = $new_id;
$ids[] = $new_id;
}
}
if (isset($node->nid)) {
$criteria = array();
$criteria['entity_id'] = $node->nid;
$criteria['entity_type'] = 'node';
$results = votingapi_select_votes($criteria);
if ($results) {
$no_match = array();
foreach ($results as $vote) {
if (!in_array($vote['tag'], $ids)) {
$no_match[] = $vote;
}
}
votingapi_delete_votes($no_match);
}
}
}
}
function advpoll_node_delete($node) {
db_delete('votingapi_vote')
->condition('entity_id', $node->nid)
->condition('entity_type', 'node')
->execute();
}
function advpoll_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'advpoll_node_form') {
$lang = $form['language']['#value'];
if (!isset($form['advpoll_max_choices'][$lang])) {
$lang = LANGUAGE_NONE;
}
$form['advpoll_max_choices'][$lang][0]['value']['#element_validate'][] = 'advpoll_validate_max_choices';
}
}
function advpoll_validate_max_choices($element, &$form_state, $form) {
$input_choices = array();
$lang = entity_language('node', $form_state['node']);
if (!isset($form_state['input']['advpoll_choice'][$lang])) {
$lang = LANGUAGE_NONE;
}
$choices = $form_state['values']['advpoll_choice'][$lang];
if (array_key_exists('add_more', $choices)) {
unset($choices['add_more']);
}
foreach ($choices as $choice) {
if ($choice['choice']) {
$input_choices[] = $choice['choice'];
}
}
$max = (int) $element['#value'];
if ($max > count($input_choices)) {
form_error($element, t('The max number of choices is @max.', array(
'@max' => $max,
)));
}
if (count($input_choices) < 2) {
form_set_error('advpoll_choice', t('Please enter at least two choices.'));
}
}
function advpoll_display_results($nid, $data, $page = 0) {
$output = '';
$form = NULL;
$expired = FALSE;
$not_open_yet = FALSE;
if ($data->state == 'close') {
$expired = TRUE;
}
if ($data->start_date && $data->start_date > REQUEST_TIME) {
$not_open_yet = TRUE;
}
if ($data->end_date && $data->end_date < REQUEST_TIME) {
$expired = TRUE;
}
$votes = array();
if ($data->mode == 'normal') {
$votes = advpoll_get_user_votes($nid);
}
if (user_access('cancel own vote') && $votes && !$expired) {
$form = drupal_get_form('advpoll_cancel_vote_form', $nid);
}
$rendered_form = drupal_render($form);
if (!$page && $not_open_yet) {
$output .= theme('advpoll_closed', array(
'data' => $data,
));
}
elseif (!$page && !$votes && $data->electoral && ($data->show_results == 'afterclose' || $data->show_results == 'never') && !$expired) {
$output .= theme('advpoll_ineligible', array(
'data' => $data,
));
}
elseif (!$page && ($data->show_results == 'never' || $data->show_results == 'afterclose' && !$expired)) {
$output .= theme('advpoll_noresults', array(
'data' => $data,
'votes' => $votes,
'nid' => $nid,
'cancel_form' => $rendered_form,
));
}
else {
$results = advpoll_get_votes($nid, $data);
$bars = '';
$final = advpoll_update_choices($data->choices, $results['choices']);
foreach ($final as $item) {
$voted = FALSE;
if (in_array($item['tag'], $votes)) {
$voted = TRUE;
}
$title = $item['title'];
$show_bar = TRUE;
if ($item['write_in']) {
$title .= ' ' . t('(Write in)');
$show_bar = _advpoll_show_writeins_access();
}
if ($show_bar) {
$bars .= theme('advpoll_bar', array(
'title' => filter_xss($title),
'percentage' => $item['percentage'],
'votes' => $item['votes'],
'voted' => $voted,
));
}
}
$output .= theme('advpoll_results', array(
'bars' => $bars,
'total' => $results['total'],
'voted' => $votes,
'nid' => $nid,
'cancel_form' => $rendered_form,
));
}
return $output;
}
function advpoll_update_choices($choices, $results) {
$choice_set = array();
$write_in = array();
foreach ($choices as $choice) {
$choice_set[$choice['choice_id']] = $choice['choice'];
$write_in[$choice['choice_id']] = $choice['write_in'];
}
$final = array();
foreach ($results as $result) {
$final[] = array(
'title' => $choice_set[$result['index']],
'percentage' => $result['percentage'],
'votes' => $result['votes'],
'tag' => $result['index'],
'write_in' => $write_in[$result['index']],
);
unset($choice_set[$result['index']]);
}
if (count($choice_set) > 0) {
foreach ($choice_set as $key => $choice) {
$final[] = array(
'title' => $choice,
'percentage' => 0,
'votes' => 0,
'tag' => $key,
'write_in' => $write_in[$key],
);
}
}
return $final;
}
function advpoll_choice_form($form, &$form_state, $values) {
$data = advpoll_get_data($values);
$count = count($data->choices);
$options = array();
$form['#id'] = 'advpoll-form-' . $values->nid;
for ($i = 0; $i < $count; $i++) {
if (!$data->choices[$i]['write_in']) {
$options[$data->choices[$i]['choice_id']] = strip_tags($data->choices[$i]['choice']);
}
}
$input_type = 'radios';
if ($data->max_choices > 1) {
$input_type = 'checkboxes';
}
if ($data->write_in && $data->mode != 'unlimited') {
$options['write-in'] = t('Other (Write-in)');
$form['choice_' . $count] = array(
'#type' => $input_type,
'#title' => '',
'#options' => $options,
'#ajax' => array(
'callback' => 'advpoll_writein_callback',
'wrapper' => 'advpoll-form-' . $values->nid,
'effect' => 'fade',
),
);
if (isset($form_state['values'])) {
foreach ($form_state['values'] as $key => $item) {
if ($key == 'choice_' . $count) {
break;
}
}
$selected = FALSE;
if ($input_type == 'radios') {
if ($item === 'write-in') {
$selected = TRUE;
}
}
else {
if ($item['write-in']) {
$selected = TRUE;
}
}
if ($selected) {
$form['write_in'] = array(
'#type' => 'textfield',
'#element_validate' => array(
'advpoll_writein_validate',
),
'#prefix' => '<div class="advpoll-write-in">',
'#suffix' => '</div>',
'#size' => '30',
);
}
}
}
else {
$form['choice_' . $count] = array(
'#type' => $input_type,
'#title' => '',
'#options' => $options,
);
}
$form['submit'] = array(
'#type' => 'submit',
'#ajax' => array(
'callback' => 'advpoll_form_submit',
'wrapper' => 'advpoll-form-' . $values->nid,
'name' => 'submit1',
),
'#id' => 'edit-submit-advpoll-' . $values->nid,
'#value' => t('Vote'),
);
return $form;
}
function advpoll_writein_validate($element, &$form_state, $form) {
if (empty($element['#value'])) {
form_error($element, t('Please type in your write-in choice or select a different option.'));
}
}
function advpoll_writein_callback($form, $form_state) {
return $form;
}
function advpoll_form_submit($form, &$form_state) {
drupal_validate_form('advpoll_choice_form', $form, $form_state);
if (form_get_errors()) {
$form_state['rebuild'] = TRUE;
return $form;
}
$data = advpoll_get_form_data($form_state);
$count = count($data->choices);
$votes = $form['choice_' . $count]['#value'];
$node = $form_state['build_info']['args'][0];
$nid = $form_state['build_info']['args'][0]->nid;
$writein = '';
$message = advpoll_form_submit_check($data, $nid);
if ($message) {
$form['message'] = array(
'#type' => 'markup',
'#prefix' => '<div id="message">',
'#suffix' => '</div>',
'#markup' => $message,
);
return $form;
}
if ($data->write_in) {
if (isset($form_state['values']['write_in'])) {
$writein = trim($form_state['values']['write_in']);
$writein = filter_xss($writein);
$writein = check_plain($writein);
if ($writein) {
$processed_writein = advpoll_process_writein($nid, $writein, $data);
$writein_id = $processed_writein['choice_id'];
$data->choices[] = $processed_writein;
}
else {
$form['message'] = array(
'#type' => 'markup',
'#prefix' => '<div id="message">',
'#suffix' => '</div>',
'#markup' => t('Please type in a valid write-in choice or select a different option.'),
);
return $form;
}
}
}
if ($data->max_choices > 1) {
if ($writein) {
unset($votes['write-in']);
$votes[$writein_id] = $writein_id;
}
$selected = advpoll_checkbox_selected($data->choices, $votes);
}
else {
if ($writein) {
$votes = $writein_id;
}
$selected = advpoll_radio_selected($data->choices, $votes);
}
if (count($selected) > 0 && count($selected) <= $data->max_choices) {
foreach ($selected as $item) {
$vote = array();
$vote['type'] = 'node';
$vote['nodetype'] = $node->type;
$vote['tag'] = $item;
$vote['nid'] = $nid;
$vote['value'] = 1;
$vote['mode'] = $data->mode;
$vote['duration'] = $data->cookie_duration;
advpoll_add_votes($vote);
}
$element['#markup'] = advpoll_display_results($nid, $data);
return $element;
}
else {
$form['message'] = array(
'#type' => 'markup',
'#prefix' => '<div id="message">',
'#suffix' => '</div>',
'#markup' => t('Select up to @quantity @votes.', array(
'@quantity' => $data->max_choices,
'@votes' => format_plural($data->max_choices, 'vote', 'votes'),
)),
);
return $form;
}
}
function advpoll_form_submit_check($data, $nid) {
if ($data->mode === 'cookie' && isset($_COOKIE['advpoll' . $nid])) {
return t('You have already voted on this poll.');
}
if ($data->mode === 'normal') {
global $user;
$criteria = array();
$criteria['entity_id'] = $nid;
$criteria['entity_type'] = 'node';
$criteria['value_type'] = 'percent';
$criteria['uid'] = $user->uid;
if (!$user->uid) {
$criteria['vote_source'] = ip_address();
}
$results = votingapi_select_votes($criteria);
if ($results) {
return t('You have already voted on this poll.');
}
}
}
function advpoll_cancel_vote_form($form, &$form_state, $nid) {
$form['#nid'] = $nid;
$form['submit'] = array(
'#type' => 'submit',
'#ajax' => array(
'callback' => 'advpoll_cancel_vote_submit',
'wrapper' => 'advpoll-' . $nid,
'name' => 'submit1',
),
'#value' => t('Cancel your vote'),
);
return $form;
}
function advpoll_cancel_vote_submit($form, &$form_state) {
global $user;
$nid = $form['#nid'];
$criteria = array();
$criteria['entity_id'] = $nid;
$criteria['entity_type'] = 'node';
$criteria['uid'] = $user->uid;
if (!$user->uid) {
$criteria['vote_source'] = ip_address();
}
votingapi_delete_votes(votingapi_select_votes($criteria));
$node = node_load($nid);
if (advpoll_user_eligibility($node)) {
$update_form = drupal_get_form('advpoll_choice_form', $node);
return drupal_render($update_form);
}
}
function advpoll_process_writein($nid, $writein, $data) {
$node = node_load($nid);
$id = dechex(time() * rand(5, 50));
$writein_choice = array();
if ($node) {
$lang = entity_language('node', $node);
$node_choices = $node->advpoll_choice[$lang];
$writein_choice = array(
'choice' => $writein,
'write_in' => 1,
'choice_id' => $id,
);
$node_choices[] = $writein_choice;
$node->advpoll_choice[$lang] = $node_choices;
node_save($node);
}
return $writein_choice;
}
function advpoll_get_recent() {
$loaded_node = menu_get_object();
$node = '';
$result = db_query_range("\n SELECT n.nid FROM {node} n\n LEFT JOIN {field_data_advpoll_dates} d\n ON d.revision_id = n.vid\n LEFT JOIN {field_data_advpoll_closed} c\n ON c.revision_id = n.vid\n LEFT JOIN {field_data_advpoll_options} o\n ON o.revision_id = n.vid\n LEFT JOIN {field_data_advpoll_results} r\n ON r.revision_id = n.vid\n WHERE\n n.nid NOT IN(\n SELECT entity_id\n FROM {field_data_advpoll_options}\n WHERE advpoll_options_value = 'electoral'\n ) AND\n n.type = 'advpoll' AND\n n.status = 1 AND\n (\n c.advpoll_closed_value = 'open' \n OR (c.advpoll_closed_value = 'close' AND r.advpoll_results_value = 'afterclose')\n )\n AND\n d.advpoll_dates_value < NOW() AND\n (d.advpoll_dates_value2 > NOW()\n OR r.advpoll_results_value = 'afterclose')\n ORDER BY n.created DESC", 0, 1);
if ($result) {
foreach ($result as $record) {
$node = node_load($record->nid);
break;
}
if ($node) {
if ($loaded_node) {
if ($loaded_node->nid == $record->nid) {
return '';
}
}
$rendered_node = node_view($node);
return drupal_render($rendered_node);
}
}
return $node;
}
function advpoll_get_poll_info_blocks($blocks) {
$result = db_query("\n SELECT n.title, n.nid FROM {node} n\n LEFT JOIN {field_data_advpoll_dates} d\n ON d.revision_id = n.vid\n LEFT JOIN {field_data_advpoll_closed} c\n ON c.revision_id = n.vid\n LEFT JOIN {field_data_advpoll_options} o\n ON o.revision_id = n.vid\n WHERE\n n.type = 'advpoll' AND\n o.advpoll_options_value = 'block' AND\n n.status = 1 AND\n c.advpoll_closed_value = 'open' AND\n d.advpoll_dates_value < NOW()\n AND d.advpoll_dates_value2 > NOW()\n ORDER BY n.created DESC\n ");
if ($result) {
foreach ($result as $record) {
$blocks['advpoll_block_' . $record->nid] = array(
'info' => t('Advanced Poll: @title', array(
'@title' => $record->title,
)),
'cache' => DRUPAL_NO_CACHE,
);
}
}
return $blocks;
}
function advpoll_generate_block_view($block, $delta) {
$pattern = '/(?i)(advpoll)/';
if (@preg_match($pattern, $delta)) {
$loaded_node = menu_get_object();
$parts = explode('_', $delta);
$nid = $parts[count($parts) - 1];
$is_block = FALSE;
if ($nid) {
$node = node_load($nid);
if (gettype($node) == 'object') {
isset($node->advpoll_options[$node->language]) ? $options = $node->advpoll_options[$node->language] : ($options = $node->advpoll_options[LANGUAGE_NONE]);
foreach ($options as $option) {
if ($option['value'] == 'block') {
$is_block = TRUE;
}
}
if ($is_block) {
if ($loaded_node) {
if ($loaded_node->nid == $node->nid) {
return $block;
}
}
$block['subject'] = t('Poll');
$block['content'] = node_view($node);
}
}
}
}
return $block;
}
function _advpoll_results_access($node) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
$votes = advpoll_get_votes($node->nid, $data);
return $votes['total'] > 0 && (user_access("show vote results") || user_access('administer polls'));
}
}
function _advpoll_electoral_list_access($node) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
return (user_access('access electoral list') || user_access('administer polls')) && $data->electoral;
}
}
function _advpoll_votes_access($node) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
$votes = advpoll_get_votes($node->nid, $data);
return $votes['total'] > 0 && (user_access('inspect all votes') || user_access('administer polls'));
}
}
function _advpoll_clear_votes_access($node) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
$votes = advpoll_get_votes($node->nid, $data);
return $votes['total'] > 0 && user_access('administer polls');
}
}
function _advpoll_writeins_access($node) {
if ($node->type == 'advpoll') {
$data = advpoll_get_data($node);
return user_access('administer polls') && $data->write_in;
}
}
function _advpoll_show_writeins_access() {
return user_access('show write-ins');
}
function advpoll_votingapi_relationships() {
$relationships[] = array(
'description' => t('Advanced Poll'),
'entity_type' => 'advpoll',
'base_table' => 'node',
'entity_id_column' => 'nid',
);
return $relationships;
}
function advpoll_round($percentage, $precision = 0) {
$rounding_method = variable_get('advpoll_percentage_rounding_method', 'round');
if (is_callable($rounding_method)) {
$result = $rounding_method($percentage, $precision);
}
else {
$result = $percentage;
}
return $result;
}