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