Completed
Pull Request — development (#3089)
by John
09:06
created

Stats.subs.php ➔ topTopicReplies()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 64
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 37
nc 4
nop 1
dl 0
loc 64
ccs 0
cts 51
cp 0
crap 42
rs 8.6346
c 0
b 0
f 0

How to fix   Long Method   

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 is holds low-level database work used by the Stats.
5
 * Some functions/queries (or all :P) might be duplicate, along Elk.
6
 * They'll be here to avoid including many files in action_stats, and
7
 * perhaps for use of addons in a similar way they were using some
8
 * SSI functions.
9
 *
10
 * @name      ElkArte Forum
11
 * @copyright ElkArte Forum contributors
12
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
13
 *
14
 * This file contains code covered by:
15
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
16
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
17
 *
18
 * @version 2.0 dev
19
 *
20
 */
21
22
/**
23
 * Return the number of currently online members.
24
 *
25
 * @return double
26
 */
27 View Code Duplication
function onlineCount()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
28
{
29
	$db = database();
30
31
	$result = $db->query('', '
32
		SELECT COUNT(*)
33
		FROM {db_prefix}log_online',
34
		array(
35
		)
36
	);
37
	list ($users_online) = $db->fetch_row($result);
38
	$db->free_result($result);
39
40
	return $users_online;
41
}
42
43
/**
44
 * Gets totals for posts, topics, most online, new users, page views, emails
45
 *
46
 * - Can be used (and is) with days up value to generate averages.
47
 *
48
 * @return array
49
 */
50
function getAverages()
51
{
52
	$db = database();
53
54
	$result = $db->query('', '
55
		SELECT
56
			SUM(posts) AS posts, SUM(topics) AS topics, SUM(registers) AS registers,
57
			SUM(most_on) AS most_on, MIN(date) AS date, SUM(hits) AS hits, SUM(email) AS email
58
		FROM {db_prefix}log_activity',
59
		array(
60
		)
61
	);
62
	$row = $db->fetch_assoc($result);
63
	$db->free_result($result);
64
65
	return $row;
66
}
67
68
/**
69
 * Get the count of categories
70
 *
71
 * @return int
72
 */
73 View Code Duplication
function numCategories()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
74
{
75
	$db = database();
76
77
	$result = $db->query('', '
78
		SELECT COUNT(*)
79
		FROM {db_prefix}categories',
80
		array(
81
		)
82
	);
83
	list ($num_categories) = $db->fetch_row($result);
84
	$db->free_result($result);
85
86
	return $num_categories;
87
}
88
89
/**
90
 * Gets most online members for a specific date
91
 *
92
 * @param int $date
93
 * @return int
94
 */
95 View Code Duplication
function mostOnline($date)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
{
97
	$db = database();
98
99
	$result = $db->query('', '
100
		SELECT most_on
101
		FROM {db_prefix}log_activity
102
		WHERE date = {date:given_date}
103
		LIMIT 1',
104
		array(
105
			'given_date' => $date,
106
		)
107
	);
108
	list ($online) = $db->fetch_row($result);
109
	$db->free_result($result);
110
111
	return (int) $online;
112
}
113
114
/**
115
 * Loads a list of top x posters
116
 *
117
 * - x is configurable via $modSettings['stats_limit'].
118
 *
119
 * @param int|null $limit if empty defaults to 10
120
 * @return array
121
 */
122
function topPosters($limit = null)
123
{
124
	global $scripturl, $modSettings;
125
126
	$db = database();
127
128
	// If there is a default setting, let's not retrieve something bigger
129 View Code Duplication
	if (isset($modSettings['stats_limit']))
130
		$limit = empty($limit) ? $modSettings['stats_limit'] : ($limit < $modSettings['stats_limit'] ? $limit : $modSettings['stats_limit']);
131
	// Otherwise, fingers crossed and let's grab what is asked
132
	else
133
		$limit = empty($limit) ? 10 : $limit;
134
135
	// Make the query to the the x number of top posters
136
	$members_result = $db->query('', '
137
		SELECT id_member, real_name, posts
138
		FROM {db_prefix}members
139
		WHERE posts > {int:no_posts}
140
		ORDER BY posts DESC
141
		LIMIT {int:limit_posts}',
142
		array(
143
			'no_posts' => 0,
144
			'limit_posts' => $limit,
145
		)
146
	);
147
	$top_posters = array();
148
	$max_num_posts = 1;
149
	while ($row_members = $db->fetch_assoc($members_result))
150
	{
151
		// Build general member information for each top poster
152
		$top_posters[] = array(
153
			'name' => $row_members['real_name'],
154
			'id' => $row_members['id_member'],
155
			'num_posts' => $row_members['posts'],
156
			'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'],
157
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'
158
		);
159
		if ($max_num_posts < $row_members['posts'])
160
			$max_num_posts = $row_members['posts'];
161
	}
162
	$db->free_result($members_result);
163
164
	// Determine the percents and then format the num_posts
165 View Code Duplication
	foreach ($top_posters as $i => $poster)
166
	{
167
		$top_posters[$i]['post_percent'] = round(($poster['num_posts'] * 100) / $max_num_posts);
168
		$top_posters[$i]['num_posts'] = comma_format($top_posters[$i]['num_posts']);
169
	}
170
171
	return $top_posters;
172
}
173
174
/**
175
 * Loads a list of top x boards with number of board posts and board topics
176
 *
177
 * - x is configurable via $modSettings['stats_limit'].
178
 *
179
 * @param int|null $limit if not supplied, defaults to 10
180
 * @param boolean $read_status
181
 * @return array
182
 */
183
function topBoards($limit = null, $read_status = false)
184
{
185
	global $modSettings, $scripturl, $user_info;
186
187
	$db = database();
188
189
	// If there is a default setting, let's not retrieve something bigger
190 View Code Duplication
	if (isset($modSettings['stats_limit']))
191
		$limit = empty($limit) ? $modSettings['stats_limit'] : ($limit < $modSettings['stats_limit'] ? $limit : $modSettings['stats_limit']);
192
	// Otherwise, fingers crossed and let's grab what is asked
193
	else
194
		$limit = empty($limit) ? 10 : $limit;
195
196
	$boards_result = $db->query('', '
197
		SELECT b.id_board, b.name, b.num_posts, b.num_topics' . ($read_status ? ',' . (!$user_info['is_guest'] ? ' 1 AS is_read' : '
198
			(COALESCE(lb.id_msg, 0) >= b.id_last_msg) AS is_read') : '') . '
199
		FROM {db_prefix}boards AS b' . ($read_status ? '
200
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})' : '') . '
201
		WHERE {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
202
			AND b.id_board != {int:recycle_board}' : '') . '
203
			AND b.redirect = {string:blank_redirect}
204
		ORDER BY num_posts DESC
205
		LIMIT {int:limit_boards}',
206
		array(
207
			'recycle_board' => $modSettings['recycle_board'],
208
			'blank_redirect' => '',
209
			'limit_boards' => $limit,
210
			'current_member' => $user_info['id'],
211
		)
212
	);
213
	$top_boards = array();
214
	$max_num_posts = 1;
215
	while ($row_board = $db->fetch_assoc($boards_result))
216
	{
217
		// Load the boards info, number of posts, topics etc
218
		$top_boards[$row_board['id_board']] = array(
219
			'id' => $row_board['id_board'],
220
			'name' => $row_board['name'],
221
			'num_posts' => $row_board['num_posts'],
222
			'num_topics' => $row_board['num_topics'],
223
			'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0',
224
			'link' => '<a href="' . $scripturl . '?board=' . $row_board['id_board'] . '.0">' . $row_board['name'] . '</a>'
225
		);
226
		if ($read_status)
227
			$top_boards[$row_board['id_board']]['is_read'] = !empty($row_board['is_read']);
228
229
		if ($max_num_posts < $row_board['num_posts'])
230
			$max_num_posts = $row_board['num_posts'];
231
	}
232
	$db->free_result($boards_result);
233
234
	// Determine the post percentages for the boards, then format the numbers
235
	foreach ($top_boards as $i => $board)
236
	{
237
		$top_boards[$i]['post_percent'] = round(($board['num_posts'] * 100) / $max_num_posts);
238
		$top_boards[$i]['num_posts'] = comma_format($top_boards[$i]['num_posts']);
239
		$top_boards[$i]['num_topics'] = comma_format($top_boards[$i]['num_topics']);
240
	}
241
242
	return $top_boards;
243
}
244
245
/**
246
 * Loads a list of top x topics by replies
247
 *
248
 * - x is configurable via $modSettings['stats_limit'].
249
 *
250
 * @param int $limit if not supplied, defaults to 10
251
 * @return array
252
 */
253
function topTopicReplies($limit = 10)
254
{
255
	global $modSettings, $scripturl;
256
257
	$db = database();
258
259
	// If there is a default setting, let's not retrieve something bigger
260
	$limit = min($limit, $modSettings['stats_limit'] ?? 10);
261
262
	// Using the wrong alias here so that we can use {query_see_board}.
263
	$request = $db->query('', '
264
		SELECT id_topic, num_replies
265
		FROM {db_prefix}topics AS b
266
		WHERE {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
267
			AND b.id_board != {int:recycle_board}' : '') . '
268
			AND num_replies != {int:no_replies}' . ($modSettings['postmod_active'] ? '
269
			AND approved = {int:is_approved}' : ''),
270
		array(
271
			'no_replies' => 0,
272
			'is_approved' => 1,
273
			'recycle_board' => $modSettings['recycle_board'],
274
		)
275
	);
276
	$topic_ids = array();
277
	while ($row = $db->fetch_row($request))
278
		$topic_ids[$row[0]] = $row[1];
279
	$db->free_result($request);
280
	arsort($topic_ids);
281
	$topic_ids = array_slice($topic_ids, 0, $limit, true);
282
	$max_num_replies = max($topic_ids);
283
284
	// Find the top x topics by number of replies
285
	$topic_reply_result = $db->query('', '
286
		SELECT m.subject, t.num_replies, t.num_views, t.id_board, t.id_topic
287
		FROM {db_prefix}topics AS t
288
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
289
		WHERE t.id_topic IN ({array_int:topic_list})',
290
		array(
291
			'topic_list' => $topic_ids,
292
		)
293
	);
294
	$top_topics_replies = array();
295
	while ($row_topic_reply = $db->fetch_assoc($topic_reply_result))
296
	{
297
		// Build out this topics details for controller use
298
		$top_topics_replies[$row_topic_reply['id_topic']] = array(
299
			'id' => $row_topic_reply['id_topic'],
300
			'subject' => censor($row_topic_reply['subject']),
301
			'num_replies' => comma_format($row_topic_reply['num_replies']),
302
			'post_percent' => round(($row_topic_reply['num_replies'] * 100) / $max_num_replies),
303
			'num_views' => $row_topic_reply['num_views'],
304
			'href' => $scripturl . '?topic=' . $row_topic_reply['id_topic'] . '.0',
305
			'link' => '<a href="' . $scripturl . '?topic=' . $row_topic_reply['id_topic'] . '.0">' . $row_topic_reply['subject'] . '</a>'
306
		);
307
	}
308
	$db->free_result($topic_reply_result);
309
310
	// @todo dedupe this
311
	usort($top_topics_replies, function ($a, $b) {
312
		return $b['num_replies'] <=> $a['num_replies'];
313
	});
314
315
	return $top_topics_replies;
316
}
317
318
/**
319
 * Loads a list of top x topics by number of views
320
 *
321
 * - x is configurable via $modSettings['stats_limit'].
322
 *
323
 * @param int|null $limit if not supplied, defaults to 10
324
 * @return array
325
 */
326
function topTopicViews($limit = null)
327
{
328
	global $modSettings, $scripturl;
329
330
	$db = database();
331
332
	// If there is a default setting, let's not retrieve something bigger
333 View Code Duplication
	if (isset($modSettings['stats_limit']))
334
		$limit = empty($limit) ? $modSettings['stats_limit'] : ($limit < $modSettings['stats_limit'] ? $limit : $modSettings['stats_limit']);
335
	// Otherwise, fingers crossed and let's grab what is asked
336
	else
337
		$limit = empty($limit) ? 10 : $limit;
338
339
	// Large forums may need a bit more prodding...
340
	if ($modSettings['totalMessages'] > 100000)
341
	{
342
		$request = $db->query('', '
343
			SELECT id_topic
344
			FROM {db_prefix}topics
345
			WHERE num_views != {int:no_views}
346
			ORDER BY num_views DESC
347
			LIMIT 100',
348
			array(
349
				'no_views' => 0,
350
			)
351
		);
352
		$topic_ids = array();
353
		while ($row = $db->fetch_assoc($request))
354
			$topic_ids[] = $row['id_topic'];
355
		$db->free_result($request);
356
	}
357
	else
358
		$topic_ids = array();
359
360
	$topic_view_result = $db->query('', '
361
		SELECT m.subject, t.num_views, t.num_replies, t.id_board, t.id_topic, b.name
362
		FROM {db_prefix}topics AS t
363
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
364
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
365
			AND b.id_board != {int:recycle_board}' : '') . ')
366
		WHERE {query_see_board}' . (!empty($topic_ids) ? '
367
			AND t.id_topic IN ({array_int:topic_list})' : ($modSettings['postmod_active'] ? '
368
			AND t.approved = {int:is_approved}' : '')) . '
369
		ORDER BY t.num_views DESC
370
		LIMIT {int:topic_views}',
371
		array(
372
			'topic_list' => $topic_ids,
373
			'recycle_board' => $modSettings['recycle_board'],
374
			'is_approved' => 1,
375
			'topic_views' => $limit,
376
		)
377
	);
378
	$top_topics_views = array();
379
	$max_num_views = 1;
380
	while ($row_topic_views = $db->fetch_assoc($topic_view_result))
381
	{
382
		// Build the topic result array
383
		$row_topic_views['subject'] = censor($row_topic_views['subject']);
384
		$top_topics_views[$row_topic_views['id_topic']] = array(
385
			'id' => $row_topic_views['id_topic'],
386
			'board' => array(
387
				'id' => $row_topic_views['id_board'],
388
				'name' => $row_topic_views['name'],
389
				'href' => $scripturl . '?board=' . $row_topic_views['id_board'] . '.0',
390
				'link' => '<a href="' . $scripturl . '?board=' . $row_topic_views['id_board'] . '.0">' . $row_topic_views['name'] . '</a>'
391
			),
392
			'subject' => $row_topic_views['subject'],
393
			'num_replies' => $row_topic_views['num_replies'],
394
			'num_views' => $row_topic_views['num_views'],
395
			'href' => $scripturl . '?topic=' . $row_topic_views['id_topic'] . '.0',
396
			'link' => '<a href="' . $scripturl . '?topic=' . $row_topic_views['id_topic'] . '.0">' . $row_topic_views['subject'] . '</a>'
397
		);
398
399
		if ($max_num_views < $row_topic_views['num_views'])
400
			$max_num_views = $row_topic_views['num_views'];
401
	}
402
	$db->free_result($topic_view_result);
403
404
	// Percentages and final formatting
405 View Code Duplication
	foreach ($top_topics_views as $i => $topic)
406
	{
407
		$top_topics_views[$i]['post_percent'] = round(($topic['num_views'] * 100) / $max_num_views);
408
		$top_topics_views[$i]['num_views'] = comma_format($top_topics_views[$i]['num_views']);
409
	}
410
411
	return $top_topics_views;
412
}
413
414
/**
415
 * Loads a list of top x topic starters
416
 *
417
 * - x is configurable via $modSettings['stats_limit'].
418
 *
419
 * @return array
420
 */
421
function topTopicStarter()
422
{
423
	global $modSettings, $scripturl;
424
425
	$db = database();
426
	$members = array();
427
	$top_starters = array();
428
429
	// Try to cache this when possible, because it's a little unavoidably slow.
430
	if (!Cache::instance()->getVar($members, 'stats_top_starters', 360) || empty($members))
431
	{
432
		$request = $db->query('', '
433
			SELECT id_member_started, COUNT(*) AS hits
434
			FROM {db_prefix}topics' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
435
			WHERE id_board != {int:recycle_board}' : '') . '
436
			GROUP BY id_member_started',
437
			array(
438
				'recycle_board' => $modSettings['recycle_board'],
439
			)
440
		);
441
		while ($row = $db->fetch_row($request))
442
			$members[$row[0]] = $row[1];
443
		$db->free_result($request);
444
		arsort($members);
445
		$members = array_slice($members, 0, $modSettings['stats_limit'] ?? 10, true);
446
447
		Cache::instance()->put('stats_top_starters', $members, 360);
448
	}
449
	$max_num_topics = max($members);
450
451
	if (empty($members))
452
		$members = array(0 => 0);
453
454
	// Find the top starters of topics
455
	$members_result = $db->query('7', '
456
		SELECT id_member, real_name
457
		FROM {db_prefix}members
458
		WHERE id_member IN ({array_int:member_list})',
459
		array(
460
			'member_list' => array_keys($members),
461
		)
462
	);
463
	while ($row_members = $db->fetch_assoc($members_result))
464
	{
465
		// Our array of spammers, er topic starters !
466
		$top_starters[$row_members['id_member']] = array(
467
			'name' => $row_members['real_name'],
468
			'id' => $row_members['id_member'],
469
			'num_topics' => comma_format($members[$row_members['id_member']]),
470
			'post_percent' => round(($members[$row_members['id_member']] * 100) / $max_num_topics),
471
			'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'],
472
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'
473
		);
474
475
	}
476
	$db->free_result($members_result);
477
478
	// Even spammers must be orderly.
479
	uksort($top_starters, function ($a, $b) use ($members) {
480
		return $members[$b] <=> $members[$a];
481
	});
482
483
	return $top_starters;
484
}
485
486
/**
487
 * Loads a list of top users by online time
488
 *
489
 * - x is configurable via $modSettings['stats_limit'], defaults to 10
490
 *
491
 * @return array
492
 */
493
function topTimeOnline()
494
{
495
	global $modSettings, $scripturl, $txt;
496
497
	$db = database();
498
499
	$max_members = isset($modSettings['stats_limit']) ? $modSettings['stats_limit'] : 10;
500
501
	// Do we have something cached that will help speed this up?
502
	$temp = Cache::instance()->get('stats_total_time_members', 600);
503
504
	// Get the member data, sorted by total time logged in
505
	$members_result = $db->query('', '
506
		SELECT id_member, real_name, total_time_logged_in
507
		FROM {db_prefix}members' . (!empty($temp) ? '
508
		WHERE id_member IN ({array_int:member_list_cached})' : '') . '
509
		ORDER BY total_time_logged_in DESC
510
		LIMIT {int:top_online}',
511
		array(
512
			'member_list_cached' => $temp,
513
			'top_online' => isset($modSettings['stats_limit']) ? $modSettings['stats_limit'] : 20,
514
		)
515
	);
516
	$top_time_online = array();
517
	$temp2 = array();
518
	$max_time_online = 1;
519
	while ($row_members = $db->fetch_assoc($members_result))
520
	{
521
		$temp2[] = (int) $row_members['id_member'];
522
		if (count($top_time_online) >= $max_members)
523
			continue;
524
525
		// Figure out the days, hours and minutes.
526
		$timeDays = floor($row_members['total_time_logged_in'] / 86400);
527
		$timeHours = floor(($row_members['total_time_logged_in'] % 86400) / 3600);
528
529
		// Figure out which things to show... (days, hours, minutes, etc.)
530
		$timelogged = '';
531
		if ($timeDays > 0)
532
			$timelogged .= $timeDays . $txt['totalTimeLogged5'];
533
534
		if ($timeHours > 0)
535
			$timelogged .= $timeHours . $txt['totalTimeLogged6'];
536
537
		$timelogged .= floor(($row_members['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged7'];
538
539
		// Finally add it to the stats array
540
		$top_time_online[] = array(
541
			'id' => $row_members['id_member'],
542
			'name' => $row_members['real_name'],
543
			'time_online' => $timelogged,
544
			'seconds_online' => $row_members['total_time_logged_in'],
545
			'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'],
546
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'
547
		);
548
549
		if ($max_time_online < $row_members['total_time_logged_in'])
550
			$max_time_online = $row_members['total_time_logged_in'];
551
	}
552
	$db->free_result($members_result);
553
554
	// As always percentages are next
555
	foreach ($top_time_online as $i => $member)
556
		$top_time_online[$i]['time_percent'] = round(($member['seconds_online'] * 100) / $max_time_online);
557
558
	// Cache the ones we found for a bit, just so we don't have to look again.
559
	if ($temp !== $temp2)
560
		Cache::instance()->put('stats_total_time_members', $temp2, 600);
561
562
	return $top_time_online;
563
}
564
565
/**
566
 * Loads the monthly statistics and returns them in $context
567
 *
568
 * - page views, new registrations, topics posts, most on etc
569
 *
570
 */
571
function monthlyActivity()
572
{
573
	global $context, $scripturl, $txt;
574
575
	$db = database();
576
577
	$months_result = $db->query('', '
578
		SELECT
579
			YEAR(date) AS stats_year, MONTH(date) AS stats_month, SUM(hits) AS hits, SUM(registers) AS registers, SUM(topics) AS topics, SUM(posts) AS posts, MAX(most_on) AS most_on, COUNT(*) AS num_days
580
		FROM {db_prefix}log_activity
581
		GROUP BY stats_year, stats_month',
582
		array()
583
	);
584
	while ($row_months = $db->fetch_assoc($months_result))
585
	{
586
		$id_month = $row_months['stats_year'] . sprintf('%02d', $row_months['stats_month']);
587
		$expanded = !empty($_SESSION['expanded_stats'][$row_months['stats_year']]) && in_array($row_months['stats_month'], $_SESSION['expanded_stats'][$row_months['stats_year']]);
588
589
		if (!isset($context['yearly'][$row_months['stats_year']]))
590
			$context['yearly'][$row_months['stats_year']] = array(
591
				'year' => $row_months['stats_year'],
592
				'new_topics' => 0,
593
				'new_posts' => 0,
594
				'new_members' => 0,
595
				'most_members_online' => 0,
596
				'hits' => 0,
597
				'num_months' => 0,
598
				'months' => array(),
599
				'expanded' => false,
600
				'current_year' => $row_months['stats_year'] == date('Y'),
601
			);
602
603
		$context['yearly'][$row_months['stats_year']]['months'][(int) $row_months['stats_month']] = array(
604
			'id' => $id_month,
605
			'date' => array(
606
				'month' => sprintf('%02d', $row_months['stats_month']),
607
				'year' => $row_months['stats_year']
608
			),
609
			'href' => $scripturl . '?action=stats;' . ($expanded ? 'collapse' : 'expand') . '=' . $id_month . '#m' . $id_month,
610
			'link' => '<a href="' . $scripturl . '?action=stats;' . ($expanded ? 'collapse' : 'expand') . '=' . $id_month . '#m' . $id_month . '">' . $txt['months'][(int) $row_months['stats_month']] . ' ' . $row_months['stats_year'] . '</a>',
611
			'month' => $txt['months'][(int) $row_months['stats_month']],
612
			'year' => $row_months['stats_year'],
613
			'new_topics' => comma_format($row_months['topics']),
614
			'new_posts' => comma_format($row_months['posts']),
615
			'new_members' => comma_format($row_months['registers']),
616
			'most_members_online' => comma_format($row_months['most_on']),
617
			'hits' => comma_format($row_months['hits']),
618
			'num_days' => $row_months['num_days'],
619
			'days' => array(),
620
			'expanded' => $expanded
621
		);
622
623
		$context['yearly'][$row_months['stats_year']]['new_topics'] += $row_months['topics'];
624
		$context['yearly'][$row_months['stats_year']]['new_posts'] += $row_months['posts'];
625
		$context['yearly'][$row_months['stats_year']]['new_members'] += $row_months['registers'];
626
		$context['yearly'][$row_months['stats_year']]['hits'] += $row_months['hits'];
627
		$context['yearly'][$row_months['stats_year']]['num_months']++;
628
		$context['yearly'][$row_months['stats_year']]['expanded'] |= $expanded;
629
		$context['yearly'][$row_months['stats_year']]['most_members_online'] = max($context['yearly'][$row_months['stats_year']]['most_members_online'], $row_months['most_on']);
630
	}
631
632
	krsort($context['yearly']);
633
}
634
635
/**
636
 * Loads the statistics on a daily basis in $context.
637
 *
638
 * - called by action_stats().
639
 *
640
 * @param string $condition_string
641
 * @param mixed[] $condition_parameters = array()
642
 */
643
function getDailyStats($condition_string, $condition_parameters = array())
644
{
645
	global $context;
646
647
	$db = database();
648
649
	// Activity by day.
650
	$days_result = $db->query('', '
651
		SELECT YEAR(date) AS stats_year, MONTH(date) AS stats_month, DAYOFMONTH(date) AS stats_day, topics, posts, registers, most_on, hits
652
		FROM {db_prefix}log_activity
653
		WHERE ' . $condition_string . '
654
		ORDER BY stats_day DESC',
655
		$condition_parameters
656
	);
657
	while ($row_days = $db->fetch_assoc($days_result))
658
		$context['yearly'][$row_days['stats_year']]['months'][(int) $row_days['stats_month']]['days'][] = array(
659
			'day' => sprintf('%02d', $row_days['stats_day']),
660
			'month' => sprintf('%02d', $row_days['stats_month']),
661
			'year' => $row_days['stats_year'],
662
			'new_topics' => comma_format($row_days['topics']),
663
			'new_posts' => comma_format($row_days['posts']),
664
			'new_members' => comma_format($row_days['registers']),
665
			'most_members_online' => comma_format($row_days['most_on']),
666
			'hits' => comma_format($row_days['hits'])
667
		);
668
	$db->free_result($days_result);
669
}
670
671
/**
672
 * Returns the number of topics a user has started, including ones on boards
673
 * they may no longer have access on.
674
 *
675
 * - Does not count topics that are in the recycle board
676
 *
677
 * @param int $memID
678
 */
679 View Code Duplication
function UserStatsTopicsStarted($memID)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
680
{
681
	global $modSettings;
682
683
	$db = database();
684
685
	// Number of topics started.
686
	$result = $db->query('', '
687
		SELECT COUNT(*)
688
		FROM {db_prefix}topics
689
		WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
690
			AND id_board != {int:recycle_board}' : ''),
691
		array(
692
			'current_member' => $memID,
693
			'recycle_board' => $modSettings['recycle_board'],
694
		)
695
	);
696
	list ($num_topics) = $db->fetch_row($result);
697
	$db->free_result($result);
698
699
	return $num_topics;
700
}
701
702
/**
703
 * Returns the number of polls a user has started, including ones on boards
704
 * they may no longer have access on.
705
 *
706
 * - Does not count topics that are in the recycle board
707
 *
708
 * @param int $memID
709
 */
710
function UserStatsPollsStarted($memID)
711
{
712
	global $modSettings;
713
714
	$db = database();
715
716
	// Number polls started.
717
	$result = $db->query('', '
718
		SELECT COUNT(*)
719
		FROM {db_prefix}topics
720
		WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
721
			AND id_board != {int:recycle_board}' : '') . '
722
			AND id_poll != {int:no_poll}',
723
		array(
724
			'current_member' => $memID,
725
			'recycle_board' => $modSettings['recycle_board'],
726
			'no_poll' => 0,
727
		)
728
	);
729
	list ($num_polls) = $db->fetch_row($result);
730
	$db->free_result($result);
731
732
	return $num_polls;
733
}
734
735
/**
736
 * Returns the number of polls a user has voted in, including ones on boards
737
 * they may no longer have access on.
738
 *
739
 * @param int $memID
740
 */
741 View Code Duplication
function UserStatsPollsVoted($memID)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
742
{
743
	$db = database();
744
745
	// Number polls voted in.
746
	$result = $db->query('distinct_poll_votes', '
747
		SELECT COUNT(DISTINCT id_poll)
748
		FROM {db_prefix}log_polls
749
		WHERE id_member = {int:current_member}',
750
		array(
751
			'current_member' => $memID,
752
		)
753
	);
754
	list ($num_votes) = $db->fetch_row($result);
755
	$db->free_result($result);
756
757
	return $num_votes;
758
}
759
760
/**
761
 * Finds the 1-N list of boards that a user posts in most often
762
 *
763
 * - Returns array with some basic stats of post percent per board
764
 *
765
 * @param int $memID
766
 * @param int $limit
767
 */
768
function UserStatsMostPostedBoard($memID, $limit = 10)
769
{
770
	global $scripturl, $user_profile;
771
772
	$db = database();
773
774
	// Find the board this member spammed most often.
775
	$result = $db->query('', '
776
		SELECT
777
			b.id_board, MAX(b.name) AS name, MAX(b.num_posts) AS num_posts, COUNT(*) AS message_count
778
		FROM {db_prefix}messages AS m
779
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
780
		WHERE m.id_member = {int:current_member}
781
			AND b.count_posts = {int:count_enabled}
782
			AND {query_see_board}
783
		GROUP BY b.id_board
784
		ORDER BY message_count DESC
785
		LIMIT {int:limit}',
786
		array(
787
			'current_member' => $memID,
788
			'count_enabled' => 0,
789
			'limit' => (int) $limit,
790
		)
791
	);
792
	$popular_boards = array();
793
	while ($row = $db->fetch_assoc($result))
794
	{
795
		// Build the board details that this member is responsible for
796
		$popular_boards[$row['id_board']] = array(
797
			'id' => $row['id_board'],
798
			'posts' => $row['message_count'],
799
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
800
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
801
			'posts_percent' => $user_profile[$memID]['posts'] == 0 ? 0 : ($row['message_count'] * 100) / $user_profile[$memID]['posts'],
802
			'total_posts' => $row['num_posts'],
803
			'total_posts_member' => $user_profile[$memID]['posts'],
804
		);
805
	}
806
	$db->free_result($result);
807
808
	return $popular_boards;
809
}
810
811
/**
812
 * Finds the 1-N list of boards that a user participates in most often
813
 *
814
 * - Returns array with some basic stats of post percent per board as a percent of board activity
815
 *
816
 * @param int $memID
817
 * @param int $limit
818
 */
819
function UserStatsMostActiveBoard($memID, $limit = 10)
820
{
821
	global $scripturl;
822
823
	$db = database();
824
825
	// Find the board this member spammed most often.
826
	$result = $db->query('profile_board_stats', '
827
		SELECT
828
			b.id_board, MAX(b.name) AS name, b.num_posts, COUNT(*) AS message_count,
829
			CASE WHEN COUNT(*) > MAX(b.num_posts) THEN 1 ELSE COUNT(*) / MAX(b.num_posts) END * 100 AS percentage
830
		FROM {db_prefix}messages AS m
831
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
832
		WHERE m.id_member = {int:current_member}
833
			AND {query_see_board}
834
		GROUP BY b.id_board, b.num_posts
835
		ORDER BY percentage DESC
836
		LIMIT {int:limit}',
837
		array(
838
			'current_member' => $memID,
839
			'limit' => (int) $limit,
840
		)
841
	);
842
	$board_activity = array();
843
	while ($row = $db->fetch_assoc($result))
844
	{
845
		// What have they been doing in this board
846
		$board_activity[$row['id_board']] = array(
847
			'id' => $row['id_board'],
848
			'posts' => $row['message_count'],
849
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
850
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
851
			'percent' => comma_format((float) $row['percentage'], 2),
852
			'posts_percent' => (float) $row['percentage'],
853
			'total_posts' => $row['num_posts'],
854
		);
855
	}
856
	$db->free_result($result);
857
858
	return $board_activity;
859
}
860
861
/**
862
 * Finds the users posting activity by time of day
863
 *
864
 * - Returns array with some basic stats of post percent per hour
865
 *
866
 * @param int $memID
867
 */
868
function UserStatsPostingTime($memID)
869
{
870
	global $user_info, $modSettings;
871
872
	$posts_by_time = array();
873
	$hours = array();
874
	for ($hour = 0; $hour < 24; $hour++)
875
	{
876
		$posts_by_time[$hour] = array(
877
			'hour' => $hour,
878
			'hour_format' => stripos($user_info['time_format'], '%p') === false ? $hour : date('g a', mktime($hour)),
879
			'posts' => 0,
880
			'posts_percent' => 0,
881
			'relative_percent' => 0,
882
		);
883
	}
884
885
	$db = database();
886
887
	// Find the times when the users posts
888
	$result = $db->query('', '
889
		SELECT
890
			poster_time
891
		FROM {db_prefix}messages
892
		WHERE id_member = {int:current_member}' . ($modSettings['totalMessages'] > 100000 ? '
893
			AND id_topic > {int:top_ten_thousand_topics}' : ''),
894
		array(
895
			'current_member' => $memID,
896
			'top_ten_thousand_topics' => $modSettings['totalTopics'] - 10000,
897
		)
898
	);
899
	while (list ($poster_time) = $db->fetch_row($result))
900
	{
901
		// Cast as an integer to remove the leading 0.
902
		$hour = (int) standardTime($poster_time, '%H');
903
904
		if (!isset($hours[$hour]))
905
			$hours[$hour] = 0;
906
907
		$hours[$hour]++;
908
	}
909
	$db->free_result($result);
910
	$maxPosts = max($hours);
911
	$totalPosts = array_sum($hours);
912
913
	foreach ($hours as $hour => $num)
914
	{
915
		// When they post, hour by hour
916
		$posts_by_time[$hour] = array_merge($posts_by_time[$hour], array(
917
			'posts' => comma_format($num),
918
			'posts_percent' => round(($num * 100) / $totalPosts),
919
			'relative_percent' => round(($num * 100) / $maxPosts),
920
		));
921
	}
922
923
	return $posts_by_time;
924
}
925