loadPollContext()   F
last analyzed

Complexity

Conditions 49
Paths > 20000

Size

Total Lines 151
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2450

Importance

Changes 0
Metric Value
cc 49
eloc 71
dl 0
loc 151
rs 0
c 0
b 0
f 0
nc 1302884352
nop 1
ccs 0
cts 59
cp 0
crap 2450

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains those functions pertaining to polls, including removing
5
 * resetting votes, editing, adding, and more
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * @version 2.0 dev
12
 *
13
 */
14
15
use BBC\ParserWrapper;
16
use ElkArte\User;
17
18
/**
19
 * This function deals with the poll ID associated to a topic.
20
 * It allows to retrieve or update the poll ID associated with this topic ID.
21
 *
22
 * If $pollID is not passed, the current poll ID of the topic, if any, is returned.
23
 * If $pollID is passed, the topic is updated to point to the new poll.
24
 *
25
 * @param int $topicID the ID of the topic
26
 * @param int|null $pollID = null the ID of the poll, if any. If null is passed, it retrieves the current ID.
27
 * @return int
28
 */
29
function associatedPoll($topicID, $pollID = null)
30
{
31
	// Retrieve the poll ID.
32 8
	if ($pollID === null)
33
	{
34 6
		require_once(SUBSDIR . '/Topic.subs.php');
35 6
		$pollID = topicAttribute($topicID, array('id_poll'));
36
37 6
		return $pollID['id_poll'];
38
	}
39
40 8
	setTopicAttribute($topicID, array('id_poll' => $pollID));
41
42 8
	return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
43
}
44
45
/**
46
 * Remove a poll.
47
 *
48
 * @param int[]|int $pollID The id of the poll to remove
49
 */
50
function removePoll($pollID)
51
{
52
	$db = database();
53 2
54
	$pollID = is_array($pollID) ? $pollID : array($pollID);
55 2
56
	// Remove votes.
57
	$db->query('', '
58 2
		DELETE FROM {db_prefix}log_polls
59
		WHERE id_poll IN ({array_int:id_polls})',
60
		array(
61
			'id_polls' => $pollID,
62 2
		)
63
	);
64
65
	// Remove the choices associated with this poll.
66
	$db->query('', '
67 2
		DELETE FROM {db_prefix}poll_choices
68
		WHERE id_poll IN ({array_int:id_polls})',
69
		array(
70
			'id_polls' => $pollID,
71 2
		)
72
	);
73
74
	// Remove the poll.
75
	$db->query('', '
76 2
		DELETE FROM {db_prefix}polls
77
		WHERE id_poll IN ({array_int:id_polls})',
78
		array(
79
			'id_polls' => $pollID,
80 2
		)
81
	);
82
}
83 2
84
/**
85
 * Reset votes for the poll.
86
 *
87
 * @param int $pollID The ID of the poll to reset the votes on
88
 */
89
function resetVotes($pollID)
90
{
91
	$db = database();
92
93
	$db->query('', '
94
		UPDATE {db_prefix}polls
95
		SET 
96
			num_guest_voters = {int:no_votes}, reset_poll = {int:time}
97
		WHERE id_poll = {int:id_poll}',
98
		array(
99
			'no_votes' => 0,
100
			'id_poll' => $pollID,
101
			'time' => time(),
102
		)
103
	);
104
105
	$db->query('', '
106
		UPDATE {db_prefix}poll_choices
107
		SET 
108
			votes = {int:no_votes}
109
		WHERE id_poll = {int:id_poll}',
110
		array(
111
			'no_votes' => 0,
112
			'id_poll' => $pollID,
113
		)
114
	);
115
116
	$db->query('', '
117
		DELETE FROM {db_prefix}log_polls
118
		WHERE id_poll = {int:id_poll}',
119
		array(
120
			'id_poll' => $pollID,
121
		)
122
	);
123
}
124
125
/**
126
 * Get all poll information you wanted to know.
127
 *
128
 * - Only returns info on the poll, not its options.
129
 *
130
 * @param int $id_poll The id of the poll to load
131
 * @param bool $ignore_permissions if true permissions are not checked.
132
 *             If false, {query_see_board} boardsAllowedTo('poll_view') and
133
 *             $modSettings['postmod_active'] will be considered in the query.
134
 *             This param is currently used only in SSI, it may be useful in any
135
 *             kind of integration
136
 * @return array|false array of poll information, or false if no poll is found
137
 */
138
function pollInfo($id_poll, $ignore_permissions = true)
139
{
140
	global $modSettings;
141
142
	$db = database();
143 2
144
	$boardsAllowed = array();
145 2
	if ($ignore_permissions === false)
146
	{
147 2
		$boardsAllowed = boardsAllowedTo('poll_view');
148 2
149
		if (empty($boardsAllowed))
150
		{
151
			return false;
152
		}
153
	}
154
155
	// Read info from the db
156
	$request = $db->query('', '
157
		SELECT
158
			p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
159 2
			p.guest_vote, p.id_member, COALESCE(mem.real_name, p.poster_name) AS poster_name,
160
			p.num_guest_voters, p.reset_poll' . ($ignore_permissions ? '' : ',
161
			b.id_board') . '
162
		FROM {db_prefix}polls AS p
163 2
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)' . ($ignore_permissions ? '' : '
164 2
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)') . '
165
		WHERE p.id_poll = {int:id_poll}' . ($ignore_permissions ? '' : ((in_array(0, $boardsAllowed) ? '' : '
166 2
			AND b.id_board IN ({array_int:boards_allowed_see})') . (!$modSettings['postmod_active'] ? '' : '
167 2
			AND t.approved = {int:is_approved}'))) . '
168 2
		LIMIT 1',
169 2
		array(
170 2
			'id_poll' => $id_poll,
171
			'boards_allowed_see' => $boardsAllowed,
172
			'is_approved' => 1,
173 2
		)
174 2
	);
175 2
	$poll_info = $request->fetch_assoc();
176
	$request->free_result();
177
178 2
	if (empty($poll_info))
179 2
	{
180
		return false;
181 2
	}
182
183 2
	$request = $db->query('', '
184
		SELECT COUNT(DISTINCT id_member) AS total
185
		FROM {db_prefix}log_polls
186
		WHERE id_poll = {int:id_poll}
187
			AND id_member != {int:not_guest}',
188
		array(
189
			'id_poll' => $id_poll,
190
			'not_guest' => 0,
191
		)
192
	);
193
	list ($poll_info['total']) = $request->fetch_row();
194
	$request->free_result();
195
196
	// Total voters needs to include guest voters
197
	$poll_info['total'] += $poll_info['num_guest_voters'];
198
199
	return $poll_info;
200
}
201
202
/**
203
 * Retrieve poll information, for the poll associated
204
 * to topic $topicID.
205
 *
206
 * @param int $topicID the topic with an associated poll.
207
 *
208
 * @return string[]|bool
209
 */
210
function pollInfoForTopic($topicID)
211
{
212
	$db = database();
213
214
	// Check if a poll currently exists on this topic, and get the id, question and starter.
215
	$request = $db->query('', '
216 2
		SELECT
217
			t.id_member_started, p.id_poll, p.voting_locked, p.question,
218
			p.hide_results, p.expire_time, p.max_votes, p.change_vote,
219 2
			m.subject, p.guest_vote, p.id_member AS poll_starter
220
		FROM {db_prefix}topics AS t
221
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
222
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
223
		WHERE t.id_topic = {int:current_topic}
224
		LIMIT 1',
225
		array(
226
			'current_topic' => $topicID,
227
		)
228
	);
229
230 2
	// The topic must exist
231
	if ($request->num_rows() === 0)
232
	{
233
		return false;
234
	}
235 2
236
	// Get the poll information.
237
	$pollinfo = $request->fetch_assoc();
238
	$request->free_result();
239
240
	return $pollinfo;
241 2
}
242 2
243
/**
244 2
 * Retrieve the id of the topic associated to a poll
245
 *
246
 * @param int $pollID the topic with an associated poll.
247
 * @return array the topic id and the board id, false if no topics found
248
 */
249
function topicFromPoll($pollID)
250
{
251
	$db = database();
252
253
	// Check if a poll currently exists on this topic, and get the id, question and starter.
254
	$request = $db->query('', '
255
		SELECT
256
			t.id_topic, b.id_board
257
		FROM {db_prefix}topics AS t
258
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
259
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
260
		WHERE p.id_poll = {int:current_poll}
261
		LIMIT 1',
262
		array(
263
			'current_poll' => $pollID,
264
		)
265
	);
266
267
	// The topic must exist
268
	if ($request->num_rows() === 0)
269
	{
270
		$topicID = false;
271
	}
272
	// Get the poll information.
273
	else
274
	{
275
		list ($topicID, $boardID) = $request->fetch_row();
276
	}
277
278
	$request->free_result();
279
280
	return array($topicID, $boardID);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $boardID does not seem to be defined for all execution paths leading up to this point.
Loading history...
281
}
282
283
/**
284
 * Return poll options, customized for a given member.
285
 *
286
 * What it does:
287
 *
288
 * - The function adds to poll options the information if the user
289
 * has voted in this poll.
290
 * - It censors the label in the result array.
291
 *
292
 * @param int $id_poll The id of the poll to query
293
 * @param int $id_member The id of the member
294
 *
295
 * @return array
296
 */
297
function pollOptionsForMember($id_poll, $id_member)
298
{
299
	$db = database();
300
301
	// Get the choices
302
	$pollOptions = array();
303
	$db->fetchQuery('
304
		SELECT 
305
			pc.id_choice, pc.label, pc.votes, COALESCE(lp.id_choice, -1) AS voted_this
306
		FROM {db_prefix}poll_choices AS pc
307
			LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
308
		WHERE pc.id_poll = {int:id_poll}
309
		ORDER by pc.id_choice',
310
		array(
311
			'current_member' => $id_member,
312
			'id_poll' => $id_poll,
313
			'not_guest' => 0,
314
		)
315
	)->fetch_callback(
316
		function ($row) use (&$pollOptions) {
317
			$row['label'] = censor($row['label']);
318
			$pollOptions[$row['id_choice']] = $row;
319
		}
320
	);
321
322
	return $pollOptions;
323
}
324
325
/**
326
 * Returns poll options.
327
 * It censors the label in the result array.
328
 *
329
 * @param int $id_poll The id of the poll to load its options
330
 *
331
 * @return array
332
 */
333
function pollOptions($id_poll)
334
{
335
	$db = database();
336
337
	$pollOptions = array();
338
	$db->fetchQuery('
339
		SELECT 
340
			label, votes, id_choice
341 2
		FROM {db_prefix}poll_choices
342
		WHERE id_poll = {int:id_poll}',
343 2
		array(
344 2
			'id_poll' => $id_poll,
345
		)
346
	)->fetch_callback(
347
		function ($row) use (&$pollOptions) {
348
			$row['label'] = censor($row['label']);
349
			$pollOptions[$row['id_choice']] = $row;
350 2
		}
351
	);
352 2
353
	return $pollOptions;
354 2
}
355 2
356 2
/**
357
 * Create a poll
358
 *
359 2
 * @param string $question The title/question of the poll
360
 * @param int $id_member = false The id of the creator
361
 * @param string $poster_name The name of the poll creator
362
 * @param int $max_votes = 1 The maximum number of votes you can do
363
 * @param int $hide_results = 1 If the results should be hidden
364
 * @param int $expire = 0 The time in days that this poll will expire
365
 * @param int $can_change_vote = 0 If you can change your vote
366
 * @param int $can_guest_vote = 0 If guests can vote
367
 * @param mixed[] $options = array() The poll options
368
 * @return int the id of the created poll
369
 */
370
function createPoll($question, $id_member, $poster_name, $max_votes = 1, $hide_results = 1, $expire = 0, $can_change_vote = 0, $can_guest_vote = 0, array $options = array())
371
{
372
	$expire = empty($expire) ? 0 : time() + $expire * 3600 * 24;
373
374
	$db = database();
375
	$db->insert('',
376
		'{db_prefix}polls',
377
		array(
378
			'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
379 8
			'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
380
		),
381 8
		array(
382 8
			$question, $hide_results, $max_votes, $expire, $id_member,
383 8
			$poster_name, $can_change_vote, $can_guest_vote,
384
		),
385 8
		array('id_poll')
386
	);
387
388
	$id_poll = $db->insert_id('{db_prefix}polls');
389 8
390 8
	if (!empty($options))
391
	{
392 8
		addPollOptions($id_poll, $options);
0 ignored issues
show
Bug introduced by
It seems like $id_poll can also be of type boolean; however, parameter $id_poll of addPollOptions() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

392
		addPollOptions(/** @scrutinizer ignore-type */ $id_poll, $options);
Loading history...
393
	}
394
395 8
	call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
396
397 8
	return $id_poll;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $id_poll also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
398
}
399
400
/**
401
 * Modify an existing poll
402 8
 *
403
 * @param int $id_poll The id of the poll that should be updated
404 8
 * @param string $question The title/question of the poll
405
 * @param int $max_votes = 1 The maximum number of votes you can do
406
 * @param int $hide_results = 1 If the results should be hidden
407
 * @param int $expire = 0 The time in days that this poll will expire
408
 * @param int $can_change_vote = 0 If you can change your vote
409
 * @param int $can_guest_vote = 0 If guests can vote
410
 */
411
function modifyPoll($id_poll, $question, $max_votes = 1, $hide_results = 1, $expire = 0, $can_change_vote = 0, $can_guest_vote = 0)
412
{
413
	$db = database();
414
415
	$db->query('', '
416
		UPDATE {db_prefix}polls
417
		SET 
418
			question = {string:question}, change_vote = {int:change_vote},' . (allowedTo('moderate_board') ? '
419
			hide_results = {int:hide_results}, expire_time = {int:expire_time}, max_votes = {int:max_votes},
420
			guest_vote = {int:guest_vote}' : '
421
			hide_results = CASE WHEN expire_time = {int:expire_time_zero} AND {int:hide_results} = 2 THEN 1 ELSE {int:hide_results} END') . '
422
		WHERE id_poll = {int:id_poll}',
423
		array(
424
			'id_poll' => $id_poll,
425
			'question' => $question,
426
			'max_votes' => $max_votes,
427
			'hide_results' => $hide_results,
428
			'expire_time' => $expire,
429
			'change_vote' => $can_change_vote,
430
			'guest_vote' => $can_guest_vote,
431
			'expire_time_zero' => 0,
432
		)
433
	);
434
435
	call_integration_hook('integrate_poll_add_edit', array($id_poll, true));
436
}
437
438
/**
439
 * Add options to an already created poll
440
 *
441
 * @param int $id_poll The id of the poll you're adding the options to
442
 * @param mixed[] $options The options to choose from
443
 */
444
function addPollOptions($id_poll, array $options)
445
{
446
	$db = database();
447
448
	$pollOptions = array();
449
	foreach ($options as $i => $option)
450
	{
451
		$pollOptions[] = array($id_poll, $i, $option);
452
	}
453
454
	$db->insert('insert',
455 2
		'{db_prefix}poll_choices',
456
		array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'),
457 2
		$pollOptions,
458 2
		array('id_poll', 'id_choice')
459
	);
460 2
}
461
462
/**
463 2
 * Insert some options to an already created poll
464 2
 *
465 2
 * @param mixed[] $options An array holding the poll choices
466 1
 */
467 2
function insertPollOptions($options)
468
{
469 2
	$db = database();
470
471
	$db->insert('',
472
		'{db_prefix}poll_choices',
473
		array(
474
			'id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255', 'votes' => 'int',
475
		),
476
		$options,
477
		array()
478
	);
479
}
480
481
/**
482
 * Add a single option to an already created poll
483
 *
484
 * @param mixed[] $options An array holding the poll choices
485
 */
486
function modifyPollOption($options)
487
{
488
	$db = database();
489
490
	foreach ($options as $option)
491
	{
492
		$db->query('', '
493
			UPDATE {db_prefix}poll_choices
494
			SET 
495
				label = {string:option_name}
496
			WHERE id_poll = {int:id_poll}
497
				AND id_choice = {int:id_choice}',
498
			array(
499
				'id_poll' => $option[0],
500
				'id_choice' => $option[1],
501
				'option_name' => $option[2],
502
			)
503
		);
504
	}
505
}
506
507
/**
508
 * Delete a bunch of options from a poll
509
 *
510
 * @param int $id_poll The id of the poll you're deleting the options from
511
 * @param int[] $id_options An array holding the choice id
512
 */
513
function deletePollOptions($id_poll, $id_options)
514
{
515
	$db = database();
516
517
	$db->query('', '
518
		DELETE FROM {db_prefix}log_polls
519
		WHERE id_poll = {int:id_poll}
520
			AND id_choice IN ({array_int:delete_options})',
521
		array(
522
			'delete_options' => $id_options,
523
			'id_poll' => $id_poll,
524
		)
525
	);
526
527
	$db->query('', '
528
		DELETE FROM {db_prefix}poll_choices
529
		WHERE id_poll = {int:id_poll}
530
			AND id_choice IN ({array_int:delete_options})',
531
		array(
532
			'delete_options' => $id_options,
533
			'id_poll' => $id_poll,
534
		)
535
	);
536
}
537
538
/**
539
 * Retrieves the topic and, if different, poll starter
540
 * for the poll associated with the $id_topic.
541
 *
542
 * @param int $id_topic The id of the topic
543
 *
544
 * @return array
545
 */
546
function pollStarters($id_topic)
547
{
548
	$db = database();
549
550
	$pollStarters = array();
551
	$request = $db->query('', '
552
		SELECT 
553
			t.id_member_started, p.id_member AS poll_starter
554
		FROM {db_prefix}topics AS t
555
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
556
		WHERE t.id_topic = {int:current_topic}
557
		LIMIT 1',
558
		array(
559
			'current_topic' => $id_topic,
560
		)
561 2
	);
562
563 2
	if ($request->num_rows() !== 0)
564 2
	{
565
		$pollStarters = $request->fetch_row();
566
	}
567
568
	$request->free_result();
569
570
	return $pollStarters;
571
}
572 2
573
/**
574
 * Check if they have already voted, or voting is locked.
575
 *
576 2
 * @param int $topic the topic with an associated poll
577
 * @return mixed[]
578 2
 */
579
function checkVote($topic)
580
{
581 2
	$db = database();
582
583 2
	return $db->fetchQuery('
584
		SELECT 
585
			COALESCE(lp.id_choice, -1) AS selected, p.voting_locked, p.id_poll, p.expire_time, p.max_votes, p.change_vote,
586
			p.guest_vote, p.reset_poll, p.num_guest_voters
587
		FROM {db_prefix}topics AS t
588
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
589
			LEFT JOIN {db_prefix}log_polls AS lp ON (p.id_poll = lp.id_poll AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
590
		WHERE t.id_topic = {int:current_topic}
591
		LIMIT 1',
592
		array(
593
			'current_member' => User::$info->id,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
594
			'current_topic' => $topic,
595
			'not_guest' => 0,
596
		)
597
	)->fetch_assoc();
598
}
599
600
/**
601
 * Removes the member's vote from a poll.
602
 *
603
 * @param int $id_member The id of the member
604
 * @param int $id_poll The topic with an associated poll.
605
 */
606
function removeVote($id_member, $id_poll)
607
{
608
	$db = database();
609
610
	$db->query('', '
611
		DELETE FROM {db_prefix}log_polls
612
		WHERE id_member = {int:current_member}
613
			AND id_poll = {int:id_poll}',
614
		array(
615
			'current_member' => $id_member,
616
			'id_poll' => $id_poll,
617
		)
618
	);
619
}
620
621
/**
622
 * Used to decrease the vote counter for the given poll.
623
 *
624
 * @param int $id_poll The id of the poll to lower the vote count
625
 * @param int[] $options The available poll options
626
 */
627
function decreaseVoteCounter($id_poll, $options)
628
{
629
	$db = database();
630
631
	$db->query('', '
632
		UPDATE {db_prefix}poll_choices
633
		SET 
634
		 	votes = votes - 1
635
		WHERE id_poll = {int:id_poll}
636
			AND id_choice IN ({array_int:poll_options})
637
			AND votes > {int:votes}',
638
		array(
639
			'poll_options' => $options,
640
			'id_poll' => $id_poll,
641
			'votes' => 0,
642
		)
643
	);
644
}
645
646
/**
647
 * Increase the vote counter for the given poll.
648
 *
649
 * @param int $id_poll The id of the poll to increase the vote count
650
 * @param int[] $options The available poll options
651
 */
652
function increaseVoteCounter($id_poll, $options)
653
{
654
	$db = database();
655
656
	$db->query('', '
657
		UPDATE {db_prefix}poll_choices
658
		SET 
659
			votes = votes + 1
660
		WHERE id_poll = {int:id_poll}
661
			AND id_choice IN ({array_int:poll_options})',
662
		array(
663
			'poll_options' => $options,
664
			'id_poll' => $id_poll,
665
		)
666
	);
667
}
668
669
/**
670
 * Add a vote to a poll.
671
 *
672
 * @param mixed[] $insert array of vote details, includes member and their choice
673
 */
674
function addVote($insert)
675
{
676
	$db = database();
677
678
	$db->insert('insert',
679
		'{db_prefix}log_polls',
680
		array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'),
681
		$insert,
682
		array('id_poll', 'id_member', 'id_choice')
683
	);
684
}
685
686
/**
687
 * Increase the vote counter for guest votes.
688
 *
689
 * @param int $id_poll The id of the poll to increase
690
 */
691
function increaseGuestVote($id_poll)
692
{
693
	$db = database();
694
695
	$db->query('', '
696
		UPDATE {db_prefix}polls
697
		SET 
698
			num_guest_voters = num_guest_voters + 1
699
		WHERE id_poll = {int:id_poll}',
700
		array(
701
			'id_poll' => $id_poll,
702
		)
703
	);
704
}
705
706
/**
707
 * Determines who voted what.
708
 *
709
 * @param int $id_member id of the member who's vote choice we want
710
 * @param int $id_poll id fo the poll the member voted in
711
 *
712
 * @return int[]
713
 */
714
function determineVote($id_member, $id_poll)
715
{
716
	$db = database();
717
	$pollOptions = [];
718
719
	$db->fetchQuery('
720
		SELECT 
721
			id_choice
722
		FROM {db_prefix}log_polls
723
		WHERE id_member = {int:current_member}
724
			AND id_poll = {int:id_poll}',
725
		array(
726
			'current_member' => $id_member,
727
			'id_poll' => $id_poll,
728
		)
729
	)->fetch_callback(
730
		function ($row) use (&$pollOptions) {
731
			if (isset($row['id_choice']))
732
			{
733
				$pollOptions[] = $row['id_choice'];
734
			}
735
		}
736
	);
737
738
	return $pollOptions;
739
}
740
741
/**
742
 * Get some basic details from a poll
743
 *
744
 * @param int $id_topic
745
 * @return string[]|bool
746
 * @deprecated since 2.0 - use pollInfoForTopic instead
747
 */
748
function pollStatus($id_topic)
749
{
750
	\ElkArte\Errors\Errors::instance()->log_deprecated('pollStatus()', 'pollInfoForTopic()');
0 ignored issues
show
Bug introduced by
The type ElkArte\Errors\Errors was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
751
752
	return pollInfoForTopic($id_topic);
753
}
754
755
/**
756
 * Update the locked status from a given poll.
757
 *
758
 * @param int $id_poll The id of the poll to check
759
 * @param int $locked the value to set in voting_locked
760
 */
761
function lockPoll($id_poll, $locked)
762
{
763
	$db = database();
764
765
	$db->query('', '
766
		UPDATE {db_prefix}polls
767
		SET 
768
			voting_locked = {int:voting_locked}
769
		WHERE id_poll = {int:id_poll}',
770
		array(
771
			'voting_locked' => $locked,
772
			'id_poll' => $id_poll,
773
		)
774
	);
775
}
776
777
/**
778
 * Gets poll choices from a given poll.
779
 *
780
 * @param int $id_poll The id of the poll
781
 * @return array
782
 */
783
function getPollChoices($id_poll)
784
{
785
	$db = database();
786
787
	$choices = array();
788
	$number = 1;
789
	$db->fetchQuery('
790
		SELECT 
791
			label, votes, id_choice
792
		FROM {db_prefix}poll_choices
793
		WHERE id_poll = {int:id_poll}',
794
		array(
795
			'id_poll' => $id_poll,
796
		)
797
	)->fetch_callback(
798
		function ($row) use (&$choices, &$number) {
799
			$row['label'] = censor($row['label']);
800
			$choices[$row['id_choice']] = array(
801
				'id' => $row['id_choice'],
802
				'number' => $number++,
803
				'votes' => $row['votes'],
804
				'label' => $row['label'],
805
				'is_last' => false
806
			);
807
		}
808
	);
809
810
	return $choices;
811
}
812
813
/**
814
 * Get the poll starter from a given poll.
815
 *
816
 * @param int $id_topic The id of the topic that has an associated poll
817
 *
818
 * @return array
819
 * @throws \ElkArte\Exceptions\Exception no_board
820
 */
821
function getPollStarter($id_topic)
822
{
823
	$db = database();
824
825
	$request = $db->query('', '
826
		SELECT 
827
			t.id_member_started, t.id_poll, p.id_member AS poll_starter, p.expire_time
828
		FROM {db_prefix}topics AS t
829
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
830
		WHERE t.id_topic = {int:current_topic}
831
		LIMIT 1',
832
		array(
833
			'current_topic' => $id_topic,
834
		)
835
	);
836
	if ($request->num_rows() === 0)
837
	{
838
		throw new \ElkArte\Exceptions\Exception('no_board');
839
	}
840
	$bcinfo = $request->fetch_assoc();
841
	$request->free_result();
842
843
	return $bcinfo;
844
}
845
846
/**
847
 * Loads in $context whatever is needed to show a poll
848
 *
849
 * @param int $poll_id simply a poll id...
850
 */
851
function loadPollContext($poll_id)
852
{
853
	global $context, $txt;
854
855
	// Get the question and if it's locked.
856
	$pollinfo = pollInfo($poll_id);
857
858
	// Get all the options, and calculate the total votes.
859
	$pollOptions = pollOptionsForMember($poll_id, User::$info->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
860
861
	// Compute total votes.
862
	$realtotal = 0;
863
	$pollinfo['has_voted'] = false;
864
	foreach ($pollOptions as $choice)
865
	{
866
		$realtotal += $choice['votes'];
867
		$pollinfo['has_voted'] |= $choice['voted_this'] != -1;
868
	}
869
870
	// If this is a guest we need to do our best to work out if they have voted, and what they voted for.
871
	if (User::$info->is_guest && $pollinfo['guest_vote'] && allowedTo('poll_vote'))
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
872
	{
873
		if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $poll_id . ',') !== false)
874
		{
875
			// ;id,timestamp,[vote,vote...]; etc
876
			$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
877
878
			// Find the poll we're after.
879
			foreach ($guestinfo as $i => $guestvoted)
880
			{
881
				$guestvoted = explode(',', $guestvoted);
882
				if ($guestvoted[0] == $poll_id)
883
				{
884
					break;
885
				}
886
			}
887
888
			// Has the poll been reset since guest voted?
889
			if ($pollinfo['reset_poll'] > $guestvoted[1])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $guestvoted seems to be defined by a foreach iteration on line 879. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
890
			{
891
				// Remove the poll info from the cookie to allow guest to vote again
892
				unset($guestinfo[$i]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $i seems to be defined by a foreach iteration on line 879. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
893
				if (!empty($guestinfo))
894
				{
895
					$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
896
				}
897
				else
898
				{
899
					unset($_COOKIE['guest_poll_vote']);
900
				}
901
			}
902
			else
903
			{
904
				// What did they vote for?
905
				unset($guestvoted[0], $guestvoted[1]);
906
				foreach ($pollOptions as $choice => $details)
907
				{
908
					$pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
909
					$pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
910
				}
911
				unset($choice, $details, $guestvoted);
912
			}
913
			unset($guestinfo, $guestvoted, $i);
914
		}
915
	}
916
917
	$bbc_parser = ParserWrapper::instance();
918
919
	// Set up the basic poll information.
920
	$starter_href = getUrl('profile', ['action' => 'profile', 'u' => $pollinfo['id_member'], 'name' => $pollinfo['poster_name']]);
921
	$context['poll'] = array(
922
		'id' => $poll_id,
923
		'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'),
924
		'question' => $bbc_parser->parsePoll($pollinfo['question']),
925
		'total_votes' => $pollinfo['total'],
926
		'change_vote' => !empty($pollinfo['change_vote']),
927
		'is_locked' => !empty($pollinfo['voting_locked']),
928
		'options' => array(),
929
		'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
930
		'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
931
		'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '',
932
		'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(),
933
		'expire_time' => !empty($pollinfo['expire_time']) ? standardTime($pollinfo['expire_time']) : 0,
934
		'has_voted' => !empty($pollinfo['has_voted']),
935
		'starter' => array(
936
			'id' => $pollinfo['id_member'],
937
			'name' => $pollinfo['poster_name'],
938
			'href' => $pollinfo['id_member'] == 0 ? '' : $starter_href,
939
			'link' => $pollinfo['id_member'] == 0 ? $pollinfo['poster_name'] : '<a href="' . $starter_href . '">' . $pollinfo['poster_name'] . '</a>'
940
		)
941
	);
942
943
	// Make the lock and edit permissions defined above more directly accessible.
944
	$context['allow_lock_poll'] = $context['poll']['lock'];
945
	$context['allow_edit_poll'] = $context['poll']['edit'];
946
947
	// You're allowed to vote if:
948
	// 1. the poll did not expire, and
949
	// 2. you're either not a guest OR guest voting is enabled... and
950
	// 3. you're not trying to view the results, and
951
	// 4. the poll is not locked, and
952
	// 5. you have the proper permissions, and
953
	// 6. you haven't already voted before.
954
	$context['allow_vote'] = !$context['poll']['is_expired'] && (User::$info->is_guest === false || ($pollinfo['guest_vote'] && allowedTo('poll_vote'))) && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && !$context['poll']['has_voted'];
955
956
	// You're allowed to view the results if:
957
	// 1. you're just a super-nice-guy, or
958
	// 2. anyone can see them (hide_results == 0), or
959
	// 3. you can see them after you voted (hide_results == 1), or
960
	// 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
961
	$context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
962
	$context['poll']['show_results'] = $context['allow_poll_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
963
964
	// You're allowed to change your vote if:
965
	// 1. the poll did not expire, and
966
	// 2. you're not a guest... and
967
	// 3. the poll is not locked, and
968
	// 4. you have the proper permissions, and
969
	// 5. you have already voted, and
970
	// 6. the poll creator has said you can!
971
	$context['allow_change_vote'] = !$context['poll']['is_expired'] && User::$info->is_guest === false && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
972
973
	// You're allowed to return to voting options if:
974
	// 1. you are (still) allowed to vote.
975
	// 2. you are currently seeing the results.
976
	$context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
977
978
	// Calculate the percentages and bar lengths...
979
	$divisor = $realtotal == 0 ? 1 : $realtotal;
0 ignored issues
show
introduced by
The condition $realtotal == 0 is always true.
Loading history...
980
981
	// Determine if a decimal point is needed in order for the options to add to 100%.
982
	$precision = $realtotal == 100 ? 0 : 1;
0 ignored issues
show
introduced by
The condition $realtotal == 100 is always false.
Loading history...
983
984
	// Now look through each option, and...
985
	foreach ($pollOptions as $i => $option)
986
	{
987
		// First calculate the percentage, and then the width of the bar...
988
		$bar = round(($option['votes'] * 100) / $divisor, $precision);
989
		$barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3);
990
991
		// Now add it to the poll's contextual theme data.
992
		$context['poll']['options'][$i] = array(
993
			'id' => 'options-' . $i,
994
			'percent' => $bar,
995
			'votes' => $option['votes'],
996
			'voted_this' => $option['voted_this'] != -1, /* Todo: I notice 'bar' here is not used in the theme any longer - only in SSI. */
997
			'bar' => '<div class="poll_gradient" style="width: ' . $barWide . 'px;"></div>',
998
			'bar_ndt' => $bar > 0 ? '<div class="bar poll-bar" style="width: ' . $bar . '%;"></div>' : '<div class="bar poll-bar"></div>',
999
			'bar_width' => $barWide,
1000
			'option' => $bbc_parser->parsePoll($option['label']),
1001
			'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($pollinfo['max_votes'] > 1 ? 'check' : 'radio') . '" />'
1002
		);
1003
	}
1004
}
1005