votingapi.module

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

A generalized voting API for Drupal.

Maintains and provides an interface for a shared bin of vote and rating data. Modules can cast votes with arbitrary properties and VotingAPI will total them automatically. Support for basic anonymous voting by IP address, multi-criteria voting, arbitrary aggregation functions, etc.

Functions & methods

NameDescription
votingapi_add_resultsSave a bundle of vote results to the database.
votingapi_add_votesSave a collection of votes to the database.
votingapi_cronImplementation of hook_cron().
votingapi_current_user_identifierGenerate a proper identifier for the current user: if they have an account, return their UID. Otherwise, return their IP address.
votingapi_delete_resultsDelete cached vote results from the database.
votingapi_delete_votesDelete votes from the database.
votingapi_entity_deleteImplements hook_entity_delete().
votingapi_menuImplementation of hook_menu().
votingapi_metadataReturns metadata about tags, value_types, and results defined by vote modules.
votingapi_permissionImplements hook_permission().
votingapi_recalculate_resultsRecalculates the aggregate results of all votes for a piece of content.
votingapi_select_resultsSelect cached vote results from the database.
votingapi_select_single_result_valueRetrieve the value of the first result matching the criteria passed in.
votingapi_select_single_vote_valueRetrieve the value of the first vote matching the criteria passed in.
votingapi_select_votesSelect individual votes from the database.
votingapi_set_votesCast a vote on a particular piece of content.
votingapi_views_apiImplementation of hook_views_api().
votingapi_votingapi_storage_add_voteImplementation of hook_votingapi_storage_add_vote().
votingapi_votingapi_storage_delete_votesImplementation of hook_votingapi_storage_delete_votes().
votingapi_votingapi_storage_select_votesImplementation of hook_votingapi_storage_select_votes().
votingapi_votingapi_storage_standard_resultsBuilds the default VotingAPI results for the three supported voting styles.
_votingapi_cron_delete_orphanedDelete votes and cache entries for a number of entities in the queue.
_votingapi_delete_cache_by_entityHelper function to delete all cache entries on given entities.
_votingapi_delete_votes_by_entityHelper function to delete all votes on given entities.
_votingapi_prep_votePopulate the value of any unset vote properties.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * A generalized voting API for Drupal.
  5. *
  6. * Maintains and provides an interface for a shared bin of vote and rating
  7. * data. Modules can cast votes with arbitrary properties and VotingAPI will
  8. * total them automatically. Support for basic anonymous voting by IP address,
  9. * multi-criteria voting, arbitrary aggregation functions, etc.
  10. */
  11. /**
  12. * Implementation of hook_menu().
  13. */
  14. function votingapi_menu() {
  15. $items = array();
  16. $items['admin/config/search/votingapi'] = array(
  17. 'title' => 'Voting API',
  18. 'description' => 'Configure sitewide settings for user-generated ratings and votes.',
  19. 'page callback' => 'drupal_get_form',
  20. 'page arguments' => array('votingapi_settings_form'),
  21. 'access callback' => 'user_access',
  22. 'access arguments' => array('administer voting api'),
  23. 'file' => 'votingapi.admin.inc',
  24. 'type' => MENU_NORMAL_ITEM
  25. );
  26. if (module_exists('devel_generate')) {
  27. $items['admin/config/development/generate/votingapi'] = array(
  28. 'title' => 'Generate votes',
  29. 'description' => 'Generate a given number of votes on site content. Optionally delete existing votes.',
  30. 'page callback' => 'drupal_get_form',
  31. 'page arguments' => array('votingapi_generate_votes_form'),
  32. 'access arguments' => array('administer voting api'),
  33. 'file' => 'votingapi.admin.inc',
  34. );
  35. }
  36. return $items;
  37. }
  38. /**
  39. * Implements hook_permission().
  40. */
  41. function votingapi_permission() {
  42. return array(
  43. 'administer voting api' => array(
  44. 'title' => t('Administer Voting API'),
  45. ),
  46. );
  47. }
  48. /**
  49. * Implementation of hook_views_api().
  50. */
  51. function votingapi_views_api() {
  52. return array(
  53. 'api' => 3,
  54. 'path' => drupal_get_path('module', 'votingapi') . '/views',
  55. );
  56. }
  57. /**
  58. * Implementation of hook_cron().
  59. *
  60. * Allows db-intensive recalculations to be deferred until cron-time.
  61. */
  62. function votingapi_cron() {
  63. if (variable_get('votingapi_calculation_schedule', 'immediate') == 'cron') {
  64. $time = REQUEST_TIME;
  65. $last_cron = variable_get('votingapi_last_cron', 0);
  66. $result = db_query('SELECT DISTINCT entity_type, entity_id FROM {votingapi_vote} WHERE timestamp > :timestamp', array(':timestamp' => $last_cron));
  67. foreach ($result as $content) {
  68. votingapi_recalculate_results($content->entity_type, $content->entity_id, TRUE);
  69. }
  70. variable_set('votingapi_last_cron', $time);
  71. }
  72. _votingapi_cron_delete_orphaned();
  73. }
  74. /**
  75. * Cast a vote on a particular piece of content.
  76. *
  77. * This function does most of the heavy lifting needed by third-party modules
  78. * based on VotingAPI. Handles clearing out old votes for a given piece of
  79. * content, saving the incoming votes, and re-tallying the results given the
  80. * new data.
  81. *
  82. * Modules that need more explicit control can call votingapi_add_votes() and
  83. * manage the deletion/recalculation tasks manually.
  84. *
  85. * @param $votes
  86. * An array of votes, each with the following structure:
  87. * $vote['entity_type'] (Optional, defaults to 'node')
  88. * $vote['entity_id'] (Required)
  89. * $vote['value_type'] (Optional, defaults to 'percent')
  90. * $vote['value'] (Required)
  91. * $vote['tag'] (Optional, defaults to 'vote')
  92. * $vote['uid'] (Optional, defaults to current user)
  93. * $vote['vote_source'] (Optional, defaults to current IP)
  94. * $vote['timestamp'] (Optional, defaults to REQUEST_TIME)
  95. * @param $criteria
  96. * A keyed array used to determine what votes will be deleted when the current
  97. * vote is cast. If no value is specified, all votes for the current content
  98. * by the current user will be reset. If an empty array is passed in, no votes
  99. * will be reset and all incoming votes will be saved IN ADDITION to existing
  100. * ones.
  101. * $criteria['vote_id'] (If this is set, all other keys are skipped)
  102. * $criteria['entity_type']
  103. * $criteria['entity_type']
  104. * $criteria['value_type']
  105. * $criteria['tag']
  106. * $criteria['uid']
  107. * $criteria['vote_source']
  108. * $criteria['timestamp'] (If this is set, records with timestamps
  109. * GREATER THAN the set value will be selected.)
  110. * @return
  111. * An array of vote result records affected by the vote. The values are
  112. * contained in a nested array keyed thusly:
  113. * $value = $results[$entity_type][$entity_id][$tag][$value_type][$function]
  114. *
  115. * @see votingapi_add_votes()
  116. * @see votingapi_recalculate_results()
  117. */
  118. function votingapi_set_votes(&$votes, $criteria = NULL) {
  119. $touched = array();
  120. if (!empty($votes['entity_id'])) {
  121. $votes = array($votes);
  122. }
  123. // Handle clearing out old votes if they exist.
  124. if (!isset($criteria)) {
  125. // If the calling function didn't explicitly set criteria for vote deletion,
  126. // build up the delete queries here.
  127. foreach ($votes as $vote) {
  128. $tmp = votingapi_current_user_identifier();
  129. $tmp += $vote;
  130. if (isset($tmp['value'])) {
  131. unset($tmp['value']);
  132. }
  133. votingapi_delete_votes(votingapi_select_votes($tmp));
  134. }
  135. }
  136. elseif (is_array($criteria)) {
  137. // The calling function passed in an explicit set of delete filters.
  138. if (!empty($criteria['entity_id'])) {
  139. $criteria = array($criteria);
  140. }
  141. foreach ($criteria as $c) {
  142. votingapi_delete_votes(votingapi_select_votes($c));
  143. }
  144. }
  145. foreach ($votes as $key => $vote) {
  146. _votingapi_prep_vote($vote);
  147. $votes[$key] = $vote; // Is this needed? Check to see how PHP4 handles refs.
  148. }
  149. // Cast the actual votes, inserting them into the table.
  150. votingapi_add_votes($votes);
  151. foreach ($votes as $vote) {
  152. $touched[$vote['entity_type']][$vote['entity_id']] = TRUE;
  153. }
  154. if (variable_get('votingapi_calculation_schedule', 'immediate') != 'cron') {
  155. foreach ($touched as $type => $ids) {
  156. foreach ($ids as $id => $bool) {
  157. $touched[$type][$id] = votingapi_recalculate_results($type, $id);
  158. }
  159. }
  160. }
  161. return $touched;
  162. }
  163. /**
  164. * Generate a proper identifier for the current user: if they have an account,
  165. * return their UID. Otherwise, return their IP address.
  166. */
  167. function votingapi_current_user_identifier() {
  168. global $user;
  169. $criteria = array('uid' => $user->uid);
  170. if (!$user->uid) {
  171. $criteria['vote_source'] = ip_address();
  172. }
  173. return $criteria;
  174. }
  175. /**
  176. * Implementation of hook_votingapi_storage_add_vote().
  177. */
  178. function votingapi_votingapi_storage_add_vote(&$vote) {
  179. drupal_write_record('votingapi_vote', $vote);
  180. }
  181. /**
  182. * Implementation of hook_votingapi_storage_delete_votes().
  183. */
  184. function votingapi_votingapi_storage_delete_votes($votes, $vids) {
  185. db_delete('votingapi_vote')->condition('vote_id', $vids, 'IN')->execute();
  186. }
  187. /**
  188. * Implementation of hook_votingapi_storage_select_votes().
  189. */
  190. function votingapi_votingapi_storage_select_votes($criteria, $limit) {
  191. $query = db_select('votingapi_vote')->fields('votingapi_vote');
  192. foreach ($criteria as $key => $value) {
  193. if ($key == 'timestamp') {
  194. $query->condition($key, $value, '>');
  195. }
  196. else {
  197. $query->condition($key, $value, is_array($value) ? 'IN' : '=');
  198. }
  199. }
  200. if (!empty($limit)) {
  201. $query->range(0, $limit);
  202. }
  203. return $query->execute()->fetchAll(PDO::FETCH_ASSOC);
  204. }
  205. /**
  206. * Save a collection of votes to the database.
  207. *
  208. * This function does most of the heavy lifting for VotingAPI the main
  209. * votingapi_set_votes() function, but does NOT automatically triger re-tallying
  210. * of results. As such, it's useful for modules that must insert their votes in
  211. * separate batches without triggering unecessary recalculation.
  212. *
  213. * Remember that any module calling this function implicitly assumes responsibility
  214. * for calling votingapi_recalculate_results() when all votes have been inserted.
  215. *
  216. * @param $votes
  217. * A vote or array of votes, each with the following structure:
  218. * $vote['entity_type'] (Optional, defaults to 'node')
  219. * $vote['entity_id'] (Required)
  220. * $vote['value_type'] (Optional, defaults to 'percent')
  221. * $vote['value'] (Required)
  222. * $vote['tag'] (Optional, defaults to 'vote')
  223. * $vote['uid'] (Optional, defaults to current user)
  224. * $vote['vote_source'] (Optional, defaults to current IP)
  225. * $vote['timestamp'] (Optional, defaults to REQUEST_TIME)
  226. * @return
  227. * The same votes, with vote_id keys populated.
  228. *
  229. * @see votingapi_set_votes()
  230. * @see votingapi_recalculate_results()
  231. */
  232. function votingapi_add_votes(&$votes) {
  233. if (!empty($votes['entity_id'])) {
  234. $votes = array($votes);
  235. }
  236. $function = variable_get('votingapi_storage_module', 'votingapi') . '_votingapi_storage_add_vote';
  237. foreach ($votes as $key => $vote) {
  238. _votingapi_prep_vote($vote);
  239. $function($vote);
  240. $votes[$key] = $vote;
  241. }
  242. module_invoke_all('votingapi_insert', $votes);
  243. return $votes;
  244. }
  245. /**
  246. * Save a bundle of vote results to the database.
  247. *
  248. * This function is called by votingapi_recalculate_results() after tallying
  249. * the values of all cast votes on a piece of content. This function will be of
  250. * little use for most third-party modules, unless they manually insert their
  251. * own result data.
  252. *
  253. * @param vote_results
  254. * An array of vote results, each with the following properties:
  255. * $vote_result['entity_type']
  256. * $vote_result['entity_id']
  257. * $vote_result['value_type']
  258. * $vote_result['value']
  259. * $vote_result['tag']
  260. * $vote_result['function']
  261. * $vote_result['timestamp'] (Optional, defaults to REQUEST_TIME)
  262. */
  263. function votingapi_add_results($vote_results = array()) {
  264. if (!empty($vote_results['entity_id'])) {
  265. $vote_results = array($vote_results);
  266. }
  267. foreach ($vote_results as $vote_result) {
  268. $vote_result['timestamp'] = REQUEST_TIME;
  269. drupal_write_record('votingapi_cache', $vote_result);
  270. }
  271. }
  272. /**
  273. * Delete votes from the database.
  274. *
  275. * @param $votes
  276. * An array of votes to delete. Minimally, each vote must have the 'vote_id'
  277. * key set.
  278. */
  279. function votingapi_delete_votes($votes = array()) {
  280. if (!empty($votes)) {
  281. module_invoke_all('votingapi_delete', $votes);
  282. $vids = array();
  283. foreach ($votes as $vote) {
  284. $vids[] = $vote['vote_id'];
  285. }
  286. $function = variable_get('votingapi_storage_module', 'votingapi') . '_votingapi_storage_delete_votes';
  287. $function($votes, $vids);
  288. }
  289. }
  290. /**
  291. * Delete cached vote results from the database.
  292. *
  293. * @param $vote_results
  294. * An array of vote results to delete. Minimally, each vote result must have
  295. * the 'vote_cache_id' key set.
  296. */
  297. function votingapi_delete_results($vote_results = array()) {
  298. if (!empty($vote_results)) {
  299. $vids = array();
  300. foreach ($vote_results as $vote) {
  301. $vids[] = $vote['vote_cache_id'];
  302. }
  303. db_delete('votingapi_cache')->condition('vote_cache_id', $vids, 'IN')->execute();
  304. }
  305. }
  306. /**
  307. * Select individual votes from the database.
  308. *
  309. * @param $criteria
  310. * A keyed array used to build the select query. Keys can contain
  311. * a single value or an array of values to be matched.
  312. * $criteria['vote_id'] (If this is set, all other keys are skipped)
  313. * $criteria['entity_id']
  314. * $criteria['entity_type']
  315. * $criteria['value_type']
  316. * $criteria['tag']
  317. * $criteria['uid']
  318. * $criteria['vote_source']
  319. * $criteria['timestamp'] (If this is set, records with timestamps
  320. * GREATER THAN the set value will be selected.)
  321. * @param $limit
  322. * An optional integer specifying the maximum number of votes to return.
  323. * @return
  324. * An array of votes matching the criteria.
  325. */
  326. function votingapi_select_votes($criteria = array(), $limit = 0) {
  327. $anon_window = variable_get('votingapi_anonymous_window', 86400);
  328. if (!empty($criteria['vote_source']) && $anon_window >= 0) {
  329. $criteria['timestamp'] = REQUEST_TIME - $anon_window;
  330. }
  331. $function = variable_get('votingapi_storage_module', 'votingapi') . '_votingapi_storage_select_votes';
  332. return $function($criteria, $limit);
  333. }
  334. /**
  335. * Select cached vote results from the database.
  336. *
  337. * @param $criteria
  338. * A keyed array used to build the select query. Keys can contain
  339. * a single value or an array of values to be matched.
  340. * $criteria['vote_cache_id'] (If this is set, all other keys are skipped)
  341. * $criteria['entity_id']
  342. * $criteria['entity_type']
  343. * $criteria['value_type']
  344. * $criteria['tag']
  345. * $criteria['function']
  346. * $criteria['timestamp'] (If this is set, records with timestamps
  347. * GREATER THAN the set value will be selected.)
  348. * @param $limit
  349. * An optional integer specifying the maximum number of votes to return.
  350. * @return
  351. * An array of vote results matching the criteria.
  352. */
  353. function votingapi_select_results($criteria = array(), $limit = 0) {
  354. $query = db_select('votingapi_cache')->fields('votingapi_cache');
  355. foreach ($criteria as $key => $value) {
  356. $query->condition($key, $value, is_array($value) ? 'IN' : '=');
  357. }
  358. if (!empty($limit)) {
  359. $query->range(0, $limit);
  360. }
  361. return $query->execute()->fetchAll(PDO::FETCH_ASSOC);
  362. }
  363. /**
  364. * Recalculates the aggregate results of all votes for a piece of content.
  365. *
  366. * Loads all votes for a given piece of content, then calculates and caches the
  367. * aggregate vote results. This is only intended for modules that have assumed
  368. * responsibility for the full voting cycle: the votingapi_set_vote() function
  369. * recalculates automatically.
  370. *
  371. * @param $entity_type
  372. * A string identifying the type of content being rated. Node, comment,
  373. * aggregator item, etc.
  374. * @param $entity_id
  375. * The key ID of the content being rated.
  376. * @return
  377. * An array of the resulting votingapi_cache records, structured thusly:
  378. * $value = $results[$ag][$value_type][$function]
  379. *
  380. * @see votingapi_set_votes()
  381. */
  382. function votingapi_recalculate_results($entity_type, $entity_id, $force_calculation = FALSE) {
  383. // if we're operating in cron mode, and the 'force recalculation' flag is NOT set,
  384. // bail out. The cron run will pick up the results.
  385. if (variable_get('votingapi_calculation_schedule', 'immediate') != 'cron' || $force_calculation == TRUE) {
  386. $query = db_delete('votingapi_cache')
  387. ->condition('entity_type', $entity_type)
  388. ->condition('entity_id', $entity_id)
  389. ->execute();
  390. $function = variable_get('votingapi_storage_module', 'votingapi') . '_votingapi_storage_standard_results';
  391. // Bulk query to pull the majority of the results we care about.
  392. $cache = $function($entity_type, $entity_id);
  393. // Give other modules a chance to alter the collection of votes.
  394. drupal_alter('votingapi_results', $cache, $entity_type, $entity_id);
  395. // Now, do the caching. Woo.
  396. $cached = array();
  397. foreach ($cache as $tag => $types) {
  398. foreach ($types as $type => $functions) {
  399. foreach ($functions as $function => $value) {
  400. $cached[] = array(
  401. 'entity_type' => $entity_type,
  402. 'entity_id' => $entity_id,
  403. 'value_type' => $type,
  404. 'value' => $value,
  405. 'tag' => $tag,
  406. 'function' => $function,
  407. );
  408. }
  409. }
  410. }
  411. votingapi_add_results($cached);
  412. // Give other modules a chance to act on the results of the vote totaling.
  413. module_invoke_all('votingapi_results', $cached, $entity_type, $entity_id);
  414. return $cached;
  415. }
  416. }
  417. /**
  418. * Returns metadata about tags, value_types, and results defined by vote modules.
  419. *
  420. * If your module needs to determine what existing tags, value_types, etc., are
  421. * being supplied by other modules, call this function. Querying the votingapi
  422. * tables for this information directly is discouraged, as values may not appear
  423. * consistently. (For example, 'average' does not appear in the cache table until
  424. * votes have actually been cast in the cache table.)
  425. *
  426. * Three major bins of data are stored: tags, value_types, and functions. Each
  427. * entry in these bins is keyed by the value stored in the actual VotingAPI
  428. * tables, and contains an array with (minimally) 'name' and 'description' keys.
  429. * Modules can add extra keys to their entries if desired.
  430. *
  431. * This metadata can be modified or expanded using hook_votingapi_metadata_alter().
  432. *
  433. * @return
  434. * An array of metadata defined by VotingAPI and altered by vote modules.
  435. *
  436. * @see hook_votingapi_metadata_alter()
  437. */
  438. function votingapi_metadata($reset = FALSE) {
  439. static $data;
  440. if ($reset || !isset($data)) {
  441. $data = array(
  442. 'tags' => array(
  443. 'vote' => array(
  444. 'name' => t('Normal vote'),
  445. 'description' => t('The default tag for votes on content. If multiple votes with different tags are being cast on a piece of content, consider casting a "summary" vote with this tag as well.'),
  446. ),
  447. ),
  448. 'value_types' => array(
  449. 'percent' => array(
  450. 'name' => t('Percent'),
  451. 'description' => t('Votes in a specific range. Values are stored in a 1-100 range, but can be represented as any scale when shown to the user.'),
  452. ),
  453. 'points' => array(
  454. 'name' => t('Points'),
  455. 'description' => t('Votes that contribute points/tokens/karma towards a total. May be positive or negative.'),
  456. ),
  457. ),
  458. 'functions' => array(
  459. 'count' => array(
  460. 'name' => t('Number of votes'),
  461. 'description' => t('The number of votes cast for a given piece of content.'),
  462. ),
  463. 'average' => array(
  464. 'name' => t('Average vote'),
  465. 'description' => t('The average vote cast on a given piece of content.'),
  466. ),
  467. 'sum' => array(
  468. 'name' => t('Total score'),
  469. 'description' => t('The sum of all votes for a given piece of content.'),
  470. 'value_types' => array('points'),
  471. ),
  472. ),
  473. );
  474. drupal_alter('votingapi_metadata', $data);
  475. }
  476. return $data;
  477. }
  478. /**
  479. * Builds the default VotingAPI results for the three supported voting styles.
  480. */
  481. function votingapi_votingapi_storage_standard_results($entity_type, $entity_id) {
  482. $cache = array();
  483. $sql = "SELECT v.value_type, v.tag, ";
  484. $sql .= "COUNT(v.value) as value_count, SUM(v.value) as value_sum ";
  485. $sql .= "FROM {votingapi_vote} v ";
  486. $sql .= "WHERE v.entity_type = :type AND v.entity_id = :id AND v.value_type IN ('points', 'percent') ";
  487. $sql .= "GROUP BY v.value_type, v.tag";
  488. $results = db_query($sql, array(':type' => $entity_type, ':id' => $entity_id));
  489. foreach ($results as $result) {
  490. $cache[$result->tag][$result->value_type]['count'] = $result->value_count;
  491. $cache[$result->tag][$result->value_type]['average'] = $result->value_sum / $result->value_count;
  492. if ($result->value_type == 'points') {
  493. $cache[$result->tag][$result->value_type]['sum'] = $result->value_sum;
  494. }
  495. }
  496. $sql = "SELECT v.tag, v.value, v.value_type, COUNT(1) AS score ";
  497. $sql .= "FROM {votingapi_vote} v ";
  498. $sql .= "WHERE v.entity_type = :type AND v.entity_id = :id AND v.value_type = 'option' ";
  499. $sql .= "GROUP BY v.value, v.tag, v.value_type";
  500. $results = db_query($sql, array(':type' => $entity_type, ':id' => $entity_id));
  501. foreach ($results as $result) {
  502. $cache[$result->tag][$result->value_type]['option-' . $result->value] = $result->score;
  503. }
  504. return $cache;
  505. }
  506. /**
  507. * Retrieve the value of the first vote matching the criteria passed in.
  508. */
  509. function votingapi_select_single_vote_value($criteria = array()) {
  510. if ($results = votingapi_select_votes($criteria, 1)) {
  511. return $results[0]['value'];
  512. }
  513. }
  514. /**
  515. * Retrieve the value of the first result matching the criteria passed in.
  516. */
  517. function votingapi_select_single_result_value($criteria = array()) {
  518. if ($results = votingapi_select_results($criteria, 1)) {
  519. return $results[0]['value'];
  520. }
  521. }
  522. /**
  523. * Populate the value of any unset vote properties.
  524. *
  525. * @param $vote
  526. * A single vote.
  527. * @return
  528. * A vote object with all required properties filled in with
  529. * their default values.
  530. */
  531. function _votingapi_prep_vote(&$vote) {
  532. global $user;
  533. if (empty($vote['prepped'])) {
  534. $vote += array(
  535. 'entity_type' => 'node',
  536. 'value_type' => 'percent',
  537. 'tag' => 'vote',
  538. 'uid' => $user->uid,
  539. 'timestamp' => REQUEST_TIME,
  540. 'vote_source' => ip_address(),
  541. 'prepped' => TRUE
  542. );
  543. }
  544. }
  545. /**
  546. * Implements hook_entity_delete().
  547. *
  548. * Delete all votes and cache entries for the deleted entities
  549. */
  550. function votingapi_entity_delete($entity, $type) {
  551. $ids = entity_extract_ids($type, $entity);
  552. $id = array($ids[0]);
  553. _votingapi_delete_cache_by_entity($id, $type);
  554. _votingapi_delete_votes_by_entity($id, $type);
  555. }
  556. /**
  557. * Helper function to delete all cache entries on given entities.
  558. *
  559. * @param array entity ids
  560. * @param string entity type
  561. */
  562. function _votingapi_delete_cache_by_entity($entity_ids, $type) {
  563. $result = db_select('votingapi_cache', 'v')
  564. ->fields('v', array('vote_cache_id'))
  565. ->condition('entity_type', $type)
  566. ->condition('entity_id', $entity_ids)
  567. ->execute();
  568. $votes = array();
  569. foreach ($result as $row) {
  570. $votes[]['vote_cache_id'] = $row->vote_cache_id;
  571. }
  572. votingapi_delete_results($votes);
  573. }
  574. /**
  575. * Helper function to delete all votes on given entities.
  576. *
  577. * @param array entity ids
  578. * @param string entity type
  579. */
  580. function _votingapi_delete_votes_by_entity($entity_ids, $type) {
  581. $result = db_select('votingapi_vote', 'v')
  582. ->fields('v', array('vote_id', 'entity_type', 'entity_id'))
  583. ->condition('entity_type', $type)
  584. ->condition('entity_id', $entity_ids)
  585. ->execute();
  586. $votes = array();
  587. foreach ($result as $row) {
  588. $votes[] = (array) $row;
  589. }
  590. votingapi_delete_votes($votes);
  591. }
  592. /**
  593. * Delete votes and cache entries for a number of entities in the queue.
  594. */
  595. function _votingapi_cron_delete_orphaned() {
  596. $queue = DrupalQueue::get('VotingAPIOrphaned');
  597. $limit = variable_get('votingapi_cron_orphaned_max', 50);
  598. $done = 0;
  599. while (($item = $queue->claimItem()) && $done++ < $limit) {
  600. _votingapi_delete_cache_by_entity(array($item->data['entity_id']), $item->data['entity_type']);
  601. _votingapi_delete_votes_by_entity(array($item->data['entity_id']), $item->data['entity_type']);
  602. $queue->deleteItem($item);
  603. }
  604. }