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