votingapi.admin.inc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. /**
  3. * @file
  4. * Configuration forms and helper functions for VotingAPI module.
  5. */
  6. /**
  7. * Administrative settings for VotingAPI.
  8. */
  9. function votingapi_settings_form($form_state) {
  10. $period = array(0 => t('Immediately')) + drupal_map_assoc(array(
  11. 300,
  12. 900,
  13. 1800,
  14. 3600,
  15. 10800,
  16. 21600,
  17. 32400,
  18. 43200,
  19. 86400,
  20. 172800,
  21. 345600,
  22. 604800,
  23. ), 'format_interval') + array(-1 => t('Never'));
  24. $form['votingapi_anonymous_window'] = array(
  25. '#type' => 'select',
  26. '#title' => t('Anonymous vote rollover'),
  27. '#description' => t('The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to \'never\' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes.'),
  28. '#default_value' => variable_get('votingapi_anonymous_window', 86400),
  29. '#options' => $period,
  30. );
  31. $form['votingapi_user_window'] = array(
  32. '#type' => 'select',
  33. '#title' => t('Registered user vote rollover'),
  34. '#description' => t('The amount of time that must pass before two registered user votes from the same user ID are considered unique. Setting this to \'never\' will eliminate most double-voting for registered users.'),
  35. '#default_value' => variable_get('votingapi_user_window', -1),
  36. '#options' => $period,
  37. );
  38. $form['votingapi_calculation_schedule'] = array(
  39. '#type' => 'radios',
  40. '#title' => t('Vote tallying'),
  41. '#description' => t('On high-traffic sites, administrators can use this setting to postpone the calculation of vote results.'),
  42. '#default_value' => variable_get('votingapi_calculation_schedule', 'immediate'),
  43. '#options' => array(
  44. 'immediate' => t('Tally results whenever a vote is cast'),
  45. 'cron' => t('Tally results at cron-time'),
  46. 'manual' => t('Do not tally results automatically: I am using a module that manages its own vote results.'),
  47. ),
  48. );
  49. return system_settings_form($form);
  50. }
  51. /**
  52. * Developer tool to generate dummy votes.
  53. */
  54. function votingapi_generate_votes_form() {
  55. $form['node_types'] = array(
  56. '#type' => 'checkboxes',
  57. '#title' => t('Which node types should receive votes?'),
  58. '#options' => node_type_get_names(),
  59. '#default_value' => array_keys(node_type_get_names()),
  60. );
  61. $form['vote_type'] = array(
  62. '#type' => 'select',
  63. '#title' => t('What type of votes should be generated?'),
  64. '#options' => array(
  65. 'percent' => t('Percentage (Fivestar style)'),
  66. 'points' => t('Point-based (Digg style)'),
  67. ),
  68. '#default_value' => 'percent',
  69. );
  70. $form['kill_votes'] = array(
  71. '#type' => 'checkbox',
  72. '#title' => t('Delete existing votes before generating new ones.'),
  73. '#default_value' => FALSE,
  74. );
  75. $form['submit'] = array(
  76. '#type' => 'submit',
  77. '#value' => t('Do it!'),
  78. );
  79. return $form;
  80. }
  81. /**
  82. * Submit handler for votingapi_generate_votes_form.
  83. */
  84. function votingapi_generate_votes_form_submit($form, &$form_state) {
  85. $options = array(
  86. 'types' => $form_state['values']['node_types'],
  87. 'kill' => $form_state['values']['kill_votes'],
  88. );
  89. require_once(drupal_get_path('module', 'votingapi') . '/votingapi.devel.inc');
  90. votingapi_generate_votes('node', $form_state['values']['vote_type'], $options);
  91. }
  92. /**
  93. * Rebuild the voting results and clear the voting cache.
  94. */
  95. function votingapi_rebuild_form($form, $form_state) {
  96. $query = db_select('votingapi_vote')
  97. ->fields('votingapi_vote', array('tag'))
  98. ->distinct(TRUE);
  99. $results = $query
  100. ->execute()
  101. ->fetchAll(PDO::FETCH_ASSOC);
  102. $tags = array('All Tags');
  103. foreach ($results as $tag_type) {
  104. $tags[] = $tag_type['tag'];
  105. }
  106. $form['results'] = array(
  107. '#type' => 'fieldset',
  108. '#title' => t('Rebuild voting results'),
  109. );
  110. $form['results']['votingapi_rebuild_content_selected_tag'] = array(
  111. '#type' => 'select',
  112. '#title' => t('Content tags'),
  113. '#options' => $tags,
  114. );
  115. $form['results']['votingapi_rebuild_content_tags'] = array(
  116. '#type' => 'hidden',
  117. '#value' => $tags,
  118. );
  119. $form['results']['votingapi_recalculate_all'] = array(
  120. '#type' => 'submit',
  121. '#value' => t('Rebuild all Voting Results'),
  122. );
  123. $form['results']['help'] = array(
  124. '#prefix' => '<div class="description">',
  125. '#markup' => t('This action rebuilds all voting results, and may be a lengthy process.')
  126. . '<br>' . t("In regular site confirurations, this process does not re-determine <em>validity</em> of votes, so if you change 'rollover' settings on the General Settings page, this does not mean that e.g. Fivestar votes attached to comments will be re-evaluated."),
  127. '#suffix' => '</div>',
  128. );
  129. return $form;
  130. }
  131. /**
  132. * Submit handler for votingapi_rebuild_form.
  133. *
  134. * Recalculates voting results for all content, using the batch API.
  135. */
  136. function votingapi_rebuild_form_submit($form, &$form_state) {
  137. $tags = $form_state['values']['votingapi_rebuild_content_tags'];
  138. $selected_tag_index = $form_state['values']['votingapi_rebuild_content_selected_tag'];
  139. // Change the list of tags to remove accordingly.
  140. if ($selected_tag_index > 0) {
  141. $tags = array($tags[$selected_tag_index]);
  142. }
  143. else {
  144. unset($tags[0]);
  145. }
  146. $operations = array();
  147. foreach ($tags as $tag) {
  148. $operations[] = array('_votingapi_results_rebuild', array($tag));
  149. }
  150. $batch = array(
  151. 'title' => t('Recalculating votes'),
  152. 'operations' => $operations,
  153. 'init_message' => 'Preparing to recalculate',
  154. 'finished' => '_votingapi_results_rebuild_finished',
  155. 'file' => drupal_get_path('module', 'votingapi') . '/votingapi.admin.inc',
  156. );
  157. // Set the recalculation batch so it can be processed.
  158. batch_set($batch);
  159. // Redirect the user to the votingapi rebuild page.
  160. $form_state['redirect'] = 'admin/config/search/votingapi/rebuild';
  161. }
  162. /**
  163. * Batch API callback for the vote rebuilding operation.
  164. */
  165. function _votingapi_results_rebuild($tag, &$context) {
  166. if (empty($context['sandbox']['max_entity_id'])) {
  167. // Set up the sandbox to work within.
  168. $context['sandbox']['current_entity_id'] = 0;
  169. $context['sandbox']['current_entity_types'] = array();
  170. // Get the lowest/highest entity_id for this context, for later comparisons.
  171. $res = db_query('SELECT MIN(entity_id) as min_id, MAX(entity_id) as max_id FROM {votingapi_vote} WHERE tag = :tag', array(':tag' => $tag))->fetchAssoc();
  172. $context['sandbox']['max_entity_id'] = $res['max_id'];
  173. $context['sandbox']['min_entity_id'] = $res['min_id'] - 1;
  174. $context['sandbox']['max_entity_id_type'] = db_query('SELECT max(entity_type) FROM {votingapi_vote} WHERE entity_id = :id AND tag = :tag',
  175. array(
  176. ':id' => $context['sandbox']['max_entity_id'],
  177. ':tag' => $tag
  178. ))->fetchField();
  179. $context['results']['count'] = 0;
  180. }
  181. // Grab all entity_ids from the votingapi_vote table.
  182. // Theoretically, there can be an endless loop if there are $limit entities
  183. // (of different types) with the same entity_id... We'll take that chance.
  184. $limit = 100;
  185. // $result = db_query_range('SELECT DISTINCT entity_type, entity_id FROM {votingapi_vote} WHERE tag = :tag AND entity_id >= :cid ORDER BY entity_id ASC',
  186. $result = db_query_range('SELECT DISTINCT entity_type, entity_id FROM {votingapi_vote}
  187. WHERE tag = :tag AND (entity_id > :current_entity_id OR (entity_id = :current_entity_id AND entity_type > :current_entity_type))
  188. ORDER BY entity_id, entity_type', 0, $limit, array(
  189. ':tag' => $tag,
  190. ':current_entity_id' => $context['sandbox']['current_entity_id'],
  191. ':current_entity_type' => $context['sandbox']['current_entity_type']
  192. ));
  193. foreach ($result as $row) {
  194. // Force vote recalculation for this entity.
  195. votingapi_recalculate_results($row->entity_type, $row->entity_id, TRUE);
  196. $context['results']['count']++;
  197. $context['sandbox']['current_entity_id'] = $row->entity_id;
  198. $context['sandbox']['current_entity_type'] = $row->entity_type;
  199. }
  200. // Check if the process has not finished yet.
  201. if ($context['sandbox']['current_entity_id'] != $context['sandbox']['max_entity_id']
  202. || $context['sandbox']['current_entity_type'] != $context['sandbox']['max_entity_id_type']
  203. ) {
  204. // Make sure 'finished' < 1. Using entity IDs here may cause the progress
  205. // bar to behave eratically, but on the other hand, we make sure we actually
  206. // process all items instead of the number of items we had at the start.
  207. // (If we compared the items processed against the number of items at the
  208. // start of the process, we would not process the highest item if an extra
  209. // (older) entity got a vote, during processing.)
  210. // Using min_entity_id as 'base' should help smoothness at the start a bit.
  211. $context['finished'] =
  212. ($context['sandbox']['current_entity_id'] - $context['sandbox']['min_entity_id'])
  213. / ($context['sandbox']['max_entity_id'] + 1 - $context['sandbox']['min_entity_id']);
  214. }
  215. }
  216. /**
  217. * Batch API callback for finished recalculation batch operations.
  218. */
  219. function _votingapi_results_rebuild_finished($success, $results, $operations) {
  220. if ($success) {
  221. $message = format_plural($results['count'], 'One content item was recalculated.', '@count content items were recalculated');
  222. }
  223. else {
  224. $message = t('The voting results rebuild process encountered an error.');
  225. }
  226. drupal_set_message($message);
  227. }