Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

ManageSearchEngines.php ➔ logSpider()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 58
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 31
nc 5
nop 0
dl 0
loc 58
rs 8.7274
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 contains all the screens that relate to search engines.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Entry point for this section.
21
 */
22
function SearchEngines()
23
{
24
	global $context, $txt, $modSettings;
25
26
	isAllowedTo('admin_forum');
27
28
	loadLanguage('Search');
29
	loadTemplate('ManageSearch');
30
31
	if (!empty($modSettings['spider_mode']))
32
	{
33
		$subActions = array(
34
			'editspiders' => 'EditSpider',
35
			'logs' => 'SpiderLogs',
36
			'settings' => 'ManageSearchEngineSettings',
37
			'spiders' => 'ViewSpiders',
38
			'stats' => 'SpiderStats',
39
		);
40
		$default = 'stats';
41
	}
42
	else
43
	{
44
		$subActions = array(
45
			'settings' => 'ManageSearchEngineSettings',
46
		);
47
		$default = 'settings';
48
	}
49
50
	// Ensure we have a valid subaction.
51
	$context['sub_action'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : $default;
52
53
	$context['page_title'] = $txt['search_engines'];
54
55
	// Some more tab data.
56
	$context[$context['admin_menu_name']]['tab_data'] = array(
57
		'title' => $txt['search_engines'],
58
		'description' => $txt['search_engines_description'],
59
	);
60
61
	call_integration_hook('integrate_manage_search_engines', array(&$subActions));
62
63
	// Call the function!
64
	call_helper($subActions[$context['sub_action']]);
65
}
66
67
/**
68
 * This is really just the settings page.
69
 *
70
 * @param bool $return_config Whether to return the config_vars array (used for admin search)
71
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
72
 */
73
function ManageSearchEngineSettings($return_config = false)
74
{
75
	global $context, $txt, $scripturl, $sourcedir, $smcFunc;
76
77
	$config_vars = array(
78
		// How much detail?
79
		array('select', 'spider_mode', 'subtext' => $txt['spider_mode_note'], array($txt['spider_mode_off'], $txt['spider_mode_standard'], $txt['spider_mode_high'], $txt['spider_mode_vhigh']), 'onchange' => 'disableFields();'),
80
		'spider_group' => array('select', 'spider_group', 'subtext' => $txt['spider_group_note'], array($txt['spider_group_none'], $txt['membergroups_members'])),
81
		array('select', 'show_spider_online', array($txt['show_spider_online_no'], $txt['show_spider_online_summary'], $txt['show_spider_online_detail'], $txt['show_spider_online_detail_admin'])),
82
	);
83
84
	// Set up a message.
85
	$context['settings_message'] = '<span class="smalltext">' . sprintf($txt['spider_settings_desc'], $scripturl . '?action=admin;area=logs;sa=settings;' . $context['session_var'] . '=' . $context['session_id']) . '</span>';
86
87
	// Do some javascript.
88
	$javascript_function = '
89
		function disableFields()
90
		{
91
			disabledState = document.getElementById(\'spider_mode\').value == 0;';
92
93
	foreach ($config_vars as $variable)
94
		if ($variable[1] != 'spider_mode')
95
			$javascript_function .= '
96
			if (document.getElementById(\'' . $variable[1] . '\'))
97
				document.getElementById(\'' . $variable[1] . '\').disabled = disabledState;';
98
99
	$javascript_function .= '
100
		}
101
		disableFields();';
102
103
	call_integration_hook('integrate_modify_search_engine_settings', array(&$config_vars));
104
105
	if ($return_config)
106
		return $config_vars;
107
108
	// We need to load the groups for the spider group thingy.
109
	$request = $smcFunc['db_query']('', '
110
		SELECT id_group, group_name
111
		FROM {db_prefix}membergroups
112
		WHERE id_group != {int:admin_group}
113
			AND id_group != {int:moderator_group}',
114
		array(
115
			'admin_group' => 1,
116
			'moderator_group' => 3,
117
		)
118
	);
119 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
120
		$config_vars['spider_group'][2][$row['id_group']] = $row['group_name'];
121
	$smcFunc['db_free_result']($request);
122
123
	// Make sure it's valid - note that regular members are given id_group = 1 which is reversed in Load.php - no admins here!
124
	if (isset($_POST['spider_group']) && !isset($config_vars['spider_group'][2][$_POST['spider_group']]))
125
		$_POST['spider_group'] = 0;
126
127
	// We'll want this for our easy save.
128
	require_once($sourcedir . '/ManageServer.php');
129
130
	// Setup the template.
131
	$context['page_title'] = $txt['settings'];
132
	$context['sub_template'] = 'show_settings';
133
134
	// Are we saving them - are we??
135 View Code Duplication
	if (isset($_GET['save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
136
	{
137
		checkSession();
138
139
		call_integration_hook('integrate_save_search_engine_settings');
140
		saveDBSettings($config_vars);
141
		recacheSpiderNames();
142
		$_SESSION['adm-save'] = true;
143
		redirectexit('action=admin;area=sengines;sa=settings');
144
	}
145
146
	// Final settings...
147
	$context['post_url'] = $scripturl . '?action=admin;area=sengines;save;sa=settings';
148
	$context['settings_title'] = $txt['settings'];
149
	addInlineJavaScript($javascript_function, true);
150
151
	// Prepare the settings...
152
	prepareDBSettingContext($config_vars);
153
}
154
155
/**
156
 * View a list of all the spiders we know about.
157
 */
158
function ViewSpiders()
159
{
160
	global $context, $txt, $sourcedir, $scripturl, $smcFunc, $modSettings;
161
162 View Code Duplication
	if (!isset($_SESSION['spider_stat']) || $_SESSION['spider_stat'] < time() - 60)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
163
	{
164
		consolidateSpiderStats();
165
		$_SESSION['spider_stat'] = time();
166
	}
167
168
	// Are we adding a new one?
169
	if (!empty($_POST['addSpider']))
170
		return EditSpider();
171
	// User pressed the 'remove selection button'.
172
	elseif (!empty($_POST['removeSpiders']) && !empty($_POST['remove']) && is_array($_POST['remove']))
173
	{
174
		checkSession();
175
		validateToken('admin-ser');
176
177
		// Make sure every entry is a proper integer.
178
		foreach ($_POST['remove'] as $index => $spider_id)
179
			$_POST['remove'][(int) $index] = (int) $spider_id;
180
181
		// Delete them all!
182
		$smcFunc['db_query']('', '
183
			DELETE FROM {db_prefix}spiders
184
			WHERE id_spider IN ({array_int:remove_list})',
185
			array(
186
				'remove_list' => $_POST['remove'],
187
			)
188
		);
189
		$smcFunc['db_query']('', '
190
			DELETE FROM {db_prefix}log_spider_hits
191
			WHERE id_spider IN ({array_int:remove_list})',
192
			array(
193
				'remove_list' => $_POST['remove'],
194
			)
195
		);
196
		$smcFunc['db_query']('', '
197
			DELETE FROM {db_prefix}log_spider_stats
198
			WHERE id_spider IN ({array_int:remove_list})',
199
			array(
200
				'remove_list' => $_POST['remove'],
201
			)
202
		);
203
204
		cache_put_data('spider_search', null, 300);
205
		recacheSpiderNames();
206
	}
207
208
	// Get the last seens.
209
	$request = $smcFunc['db_query']('', '
210
		SELECT id_spider, MAX(last_seen) AS last_seen_time
211
		FROM {db_prefix}log_spider_stats
212
		GROUP BY id_spider',
213
		array(
214
		)
215
	);
216
217
	$context['spider_last_seen'] = array();
218
	while ($row = $smcFunc['db_fetch_assoc']($request))
219
		$context['spider_last_seen'][$row['id_spider']] = $row['last_seen_time'];
220
	$smcFunc['db_free_result']($request);
221
222
	createToken('admin-ser');
223
	$listOptions = array(
224
		'id' => 'spider_list',
225
		'title' => $txt['spiders'],
226
		'items_per_page' => $modSettings['defaultMaxListItems'],
227
		'base_href' => $scripturl . '?action=admin;area=sengines;sa=spiders',
228
		'default_sort_col' => 'name',
229
		'get_items' => array(
230
			'function' => 'list_getSpiders',
231
		),
232
		'get_count' => array(
233
			'function' => 'list_getNumSpiders',
234
		),
235
		'no_items_label' => $txt['spiders_no_entries'],
236
		'columns' => array(
237
			'name' => array(
238
				'header' => array(
239
					'value' => $txt['spider_name'],
240
				),
241
				'data' => array(
242
					'function' => function($rowData) use ($smcFunc, $scripturl)
243
					{
244
						return sprintf('<a href="%1$s?action=admin;area=sengines;sa=editspiders;sid=%2$d">%3$s</a>', $scripturl, $rowData['id_spider'], $smcFunc['htmlspecialchars']($rowData['spider_name']));
245
					},
246
				),
247
				'sort' => array(
248
					'default' => 'spider_name',
249
					'reverse' => 'spider_name DESC',
250
				),
251
			),
252
			'last_seen' => array(
253
				'header' => array(
254
					'value' => $txt['spider_last_seen'],
255
				),
256
				'data' => array(
257
					'function' => function($rowData) use ($context, $txt)
258
					{
259
						return isset($context['spider_last_seen'][$rowData['id_spider']]) ? timeformat($context['spider_last_seen'][$rowData['id_spider']]) : $txt['spider_last_never'];
260
					},
261
				),
262
			),
263
			'user_agent' => array(
264
				'header' => array(
265
					'value' => $txt['spider_agent'],
266
				),
267
				'data' => array(
268
					'db_htmlsafe' => 'user_agent',
269
				),
270
				'sort' => array(
271
					'default' => 'user_agent',
272
					'reverse' => 'user_agent DESC',
273
				),
274
			),
275
			'ip_info' => array(
276
				'header' => array(
277
					'value' => $txt['spider_ip_info'],
278
				),
279
				'data' => array(
280
					'db_htmlsafe' => 'ip_info',
281
					'class' => 'smalltext',
282
				),
283
				'sort' => array(
284
					'default' => 'ip_info',
285
					'reverse' => 'ip_info DESC',
286
				),
287
			),
288
			'check' => array(
289
				'header' => array(
290
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);">',
291
					'class' => 'centercol',
292
				),
293
				'data' => array(
294
					'sprintf' => array(
295
						'format' => '<input type="checkbox" name="remove[]" value="%1$d">',
296
						'params' => array(
297
							'id_spider' => false,
298
						),
299
					),
300
					'class' => 'centercol',
301
				),
302
			),
303
		),
304
		'form' => array(
305
			'href' => $scripturl . '?action=admin;area=sengines;sa=spiders',
306
			'token' => 'admin-ser',
307
		),
308
		'additional_rows' => array(
309
			array(
310
				'position' => 'bottom_of_list',
311
				'value' => '
312
					<input type="submit" name="removeSpiders" value="' . $txt['spiders_remove_selected'] . '" data-confirm="' . $txt['spider_remove_selected_confirm'] . '" class="button_submit you_sure">
313
					<input type="submit" name="addSpider" value="' . $txt['spiders_add'] . '" class="button_submit">
314
				',
315
			),
316
		),
317
	);
318
319
	require_once($sourcedir . '/Subs-List.php');
320
	createList($listOptions);
321
322
	$context['sub_template'] = 'show_list';
323
	$context['default_list'] = 'spider_list';
324
}
325
326
/**
327
 * Callback function for createList()
328
 * @param int $start The item to start with (for pagination purposes)
329
 * @param int $items_per_page The number of items to show per page
330
 * @param string $sort A string indicating how to sort the results
331
 * @return array An array of information about known spiders
332
 */
333 View Code Duplication
function list_getSpiders($start, $items_per_page, $sort)
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...
334
{
335
	global $smcFunc;
336
337
	$request = $smcFunc['db_query']('', '
338
		SELECT id_spider, spider_name, user_agent, ip_info
339
		FROM {db_prefix}spiders
340
		ORDER BY {raw:sort}
341
		LIMIT {int:start}, {int:items}',
342
		array(
343
			'sort' => $sort,
344
			'start' => $start,
345
			'items' => $items_per_page,
346
		)
347
	);
348
	$spiders = array();
349
	while ($row = $smcFunc['db_fetch_assoc']($request))
350
		$spiders[$row['id_spider']] = $row;
351
	$smcFunc['db_free_result']($request);
352
353
	return $spiders;
354
}
355
356
/**
357
 * Callback function for createList()
358
 * @return int The number of known spiders
359
 */
360
function list_getNumSpiders()
361
{
362
	global $smcFunc;
363
364
	$request = $smcFunc['db_query']('', '
365
		SELECT COUNT(*) AS num_spiders
366
		FROM {db_prefix}spiders',
367
		array(
368
		)
369
	);
370
	list ($numSpiders) = $smcFunc['db_fetch_row']($request);
371
	$smcFunc['db_free_result']($request);
372
373
	return $numSpiders;
374
}
375
376
/**
377
 * Here we can add, and edit, spider info!
378
 */
379
function EditSpider()
380
{
381
	global $context, $smcFunc, $txt;
382
383
	// Some standard stuff.
384
	$context['id_spider'] = !empty($_GET['sid']) ? (int) $_GET['sid'] : 0;
385
	$context['page_title'] = $context['id_spider'] ? $txt['spiders_edit'] : $txt['spiders_add'];
386
	$context['sub_template'] = 'spider_edit';
387
388
	// Are we saving?
389
	if (!empty($_POST['save']))
390
	{
391
		checkSession();
392
		validateToken('admin-ses');
393
394
		$ips = array();
395
		// Check the IP range is valid.
396
		$ip_sets = explode(',', $_POST['spider_ip']);
397
		foreach ($ip_sets as $set)
398
		{
399
			$test = ip2range(trim($set));
400
			if (!empty($test))
401
				$ips[] = $set;
402
		}
403
		$ips = implode(',', $ips);
404
405
		// Goes in as it is...
406
		if ($context['id_spider'])
407
			$smcFunc['db_query']('', '
408
				UPDATE {db_prefix}spiders
409
				SET spider_name = {string:spider_name}, user_agent = {string:spider_agent},
410
					ip_info = {string:ip_info}
411
				WHERE id_spider = {int:current_spider}',
412
				array(
413
					'current_spider' => $context['id_spider'],
414
					'spider_name' => $_POST['spider_name'],
415
					'spider_agent' => $_POST['spider_agent'],
416
					'ip_info' => $ips,
417
				)
418
			);
419
		else
420
			$smcFunc['db_insert']('insert',
421
				'{db_prefix}spiders',
422
				array(
423
					'spider_name' => 'string', 'user_agent' => 'string', 'ip_info' => 'string',
424
				),
425
				array(
426
					$_POST['spider_name'], $_POST['spider_agent'], $ips,
427
				),
428
				array('id_spider')
429
			);
430
431
432
		cache_put_data('spider_search', null);
433
		recacheSpiderNames();
434
435
		redirectexit('action=admin;area=sengines;sa=spiders');
436
	}
437
438
	// The default is new.
439
	$context['spider'] = array(
440
		'id' => 0,
441
		'name' => '',
442
		'agent' => '',
443
		'ip_info' => '',
444
	);
445
446
	// An edit?
447
	if ($context['id_spider'])
448
	{
449
		$request = $smcFunc['db_query']('', '
450
			SELECT id_spider, spider_name, user_agent, ip_info
451
			FROM {db_prefix}spiders
452
			WHERE id_spider = {int:current_spider}',
453
			array(
454
				'current_spider' => $context['id_spider'],
455
			)
456
		);
457
		if ($row = $smcFunc['db_fetch_assoc']($request))
458
			$context['spider'] = array(
459
				'id' => $row['id_spider'],
460
				'name' => $row['spider_name'],
461
				'agent' => $row['user_agent'],
462
				'ip_info' => $row['ip_info'],
463
			);
464
		$smcFunc['db_free_result']($request);
465
	}
466
467
	createToken('admin-ses');
468
}
469
470
/**
471
 * Do we think the current user is a spider?
472
 *
473
 * @todo Should this not be... you know... in a different file?
474
 * @return int The ID of the spider if it's known or 0 if it isn't known/isn't a spider
475
 */
476
function SpiderCheck()
477
{
478
	global $modSettings, $smcFunc;
479
480
	if (isset($_SESSION['id_robot']))
481
		unset($_SESSION['id_robot']);
482
	$_SESSION['robot_check'] = time();
483
484
	// We cache the spider data for ten minutes if we can.
485
	if (($spider_data = cache_get_data('spider_search', 600)) === null)
486
	{
487
		$request = $smcFunc['db_query']('', '
488
			SELECT id_spider, user_agent, ip_info
489
			FROM {db_prefix}spiders
490
			ORDER BY LENGTH(user_agent) DESC',
491
			array(
492
			)
493
		);
494
		$spider_data = array();
495
		while ($row = $smcFunc['db_fetch_assoc']($request))
496
			$spider_data[] = $row;
497
		$smcFunc['db_free_result']($request);
498
499
		cache_put_data('spider_search', $spider_data, 600);
500
	}
501
502
	if (empty($spider_data))
503
		return false;
504
505
	// Only do these bits once.
506
	$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
507
508
	foreach ($spider_data as $spider)
0 ignored issues
show
Bug introduced by
The expression $spider_data of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
509
	{
510
		// User agent is easy.
511
		if (!empty($spider['user_agent']) && strpos($ci_user_agent, strtolower($spider['user_agent'])) !== false)
512
			$_SESSION['id_robot'] = $spider['id_spider'];
513
		// IP stuff is harder.
514
		elseif ($_SERVER['REMOTE_ADDR'])
515
		{
516
			$ips = explode(',', $spider['ip_info']);
517
			foreach ($ips as $ip)
518
			{
519
				if ($ip === '')
520
					continue;
521
522
				$ip = ip2range($ip);
523
				if (!empty($ip))
524
				{
525
					if (inet_ptod($ip['low']) <= inet_ptod($_SERVER['REMOTE_ADDR']) && inet_ptod($ip['high']) >= inet_ptod($_SERVER['REMOTE_ADDR']))
526
						$_SESSION['id_robot'] = $spider['id_spider'];
527
				}
528
			}
529
		}
530
531
		if (isset($_SESSION['id_robot']))
532
			break;
533
	}
534
535
	// If this is low server tracking then log the spider here as opposed to the main logging function.
536
	if (!empty($modSettings['spider_mode']) && $modSettings['spider_mode'] == 1 && !empty($_SESSION['id_robot']))
537
		logSpider();
538
539
	return !empty($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
540
}
541
542
/**
543
 * Log the spider presence online.
544
 *
545
 * @todo Different file?
546
 */
547
function logSpider()
548
{
549
	global $smcFunc, $modSettings, $context;
550
551
	if (empty($modSettings['spider_mode']) || empty($_SESSION['id_robot']))
552
		return;
553
554
	// Attempt to update today's entry.
555
	if ($modSettings['spider_mode'] == 1)
556
	{
557
		$date = strftime('%Y-%m-%d', forum_time(false));
558
		$smcFunc['db_query']('', '
559
			UPDATE {db_prefix}log_spider_stats
560
			SET last_seen = {int:current_time}, page_hits = page_hits + 1
561
			WHERE id_spider = {int:current_spider}
562
				AND stat_date = {date:current_date}',
563
			array(
564
				'current_date' => $date,
565
				'current_time' => time(),
566
				'current_spider' => $_SESSION['id_robot'],
567
			)
568
		);
569
570
		// Nothing updated?
571
		if ($smcFunc['db_affected_rows']() == 0)
572
		{
573
			$smcFunc['db_insert']('ignore',
574
				'{db_prefix}log_spider_stats',
575
				array(
576
					'id_spider' => 'int', 'last_seen' => 'int', 'stat_date' => 'date', 'page_hits' => 'int',
577
				),
578
				array(
579
					$_SESSION['id_robot'], time(), $date, 1,
580
				),
581
				array('id_spider', 'stat_date')
582
			);
583
		}
584
	}
585
	// If we're tracking better stats than track, better stats - we sort out the today thing later.
586
	else
587
	{
588
		if ($modSettings['spider_mode'] > 2)
589
		{
590
			$url = $_GET + array('USER_AGENT' => $_SERVER['HTTP_USER_AGENT']);
591
			unset($url['sesc'], $url[$context['session_var']]);
592
			$url = $smcFunc['json_encode']($url);
593
		}
594
		else
595
			$url = '';
596
597
		$smcFunc['db_insert']('insert',
598
			'{db_prefix}log_spider_hits',
599
			array('id_spider' => 'int', 'log_time' => 'int', 'url' => 'string'),
600
			array($_SESSION['id_robot'], time(), $url),
601
			array()
602
		);
603
	}
604
}
605
606
/**
607
 * This function takes any unprocessed hits and turns them into stats.
608
 */
609
function consolidateSpiderStats()
610
{
611
	global $smcFunc;
612
613
	$request = $smcFunc['db_query']('consolidate_spider_stats', '
614
		SELECT id_spider, MAX(log_time) AS last_seen, COUNT(*) AS num_hits
615
		FROM {db_prefix}log_spider_hits
616
		WHERE processed = {int:not_processed}
617
		GROUP BY id_spider, MONTH(log_time), DAYOFMONTH(log_time)',
618
		array(
619
			'not_processed' => 0,
620
		)
621
	);
622
	$spider_hits = array();
623
	while ($row = $smcFunc['db_fetch_assoc']($request))
624
		$spider_hits[] = $row;
625
	$smcFunc['db_free_result']($request);
626
627
	if (empty($spider_hits))
628
		return;
629
630
	// Attempt to update the master data.
631
	$stat_inserts = array();
632
	foreach ($spider_hits as $stat)
633
	{
634
		// We assume the max date is within the right day.
635
		$date = strftime('%Y-%m-%d', $stat['last_seen']);
636
		$smcFunc['db_query']('', '
637
			UPDATE {db_prefix}log_spider_stats
638
			SET page_hits = page_hits + {int:hits},
639
				last_seen = CASE WHEN last_seen > {int:last_seen} THEN last_seen ELSE {int:last_seen} END
640
			WHERE id_spider = {int:current_spider}
641
				AND stat_date = {date:last_seen_date}',
642
			array(
643
				'last_seen_date' => $date,
644
				'last_seen' => $stat['last_seen'],
645
				'current_spider' => $stat['id_spider'],
646
				'hits' => $stat['num_hits'],
647
			)
648
		);
649
		if ($smcFunc['db_affected_rows']() == 0)
650
			$stat_inserts[] = array($date, $stat['id_spider'], $stat['num_hits'], $stat['last_seen']);
651
	}
652
653
	// New stats?
654
	if (!empty($stat_inserts))
655
		$smcFunc['db_insert']('ignore',
656
			'{db_prefix}log_spider_stats',
657
			array('stat_date' => 'date', 'id_spider' => 'int', 'page_hits' => 'int', 'last_seen' => 'int'),
658
			$stat_inserts,
659
			array('stat_date', 'id_spider')
660
		);
661
662
	// All processed.
663
	$smcFunc['db_query']('', '
664
		UPDATE {db_prefix}log_spider_hits
665
		SET processed = {int:is_processed}
666
		WHERE processed = {int:not_processed}',
667
		array(
668
			'is_processed' => 1,
669
			'not_processed' => 0,
670
		)
671
	);
672
}
673
674
/**
675
 * See what spiders have been up to.
676
 */
677
function SpiderLogs()
678
{
679
	global $context, $txt, $sourcedir, $scripturl, $smcFunc, $modSettings;
680
681
	// Load the template and language just incase.
682
	loadLanguage('Search');
683
	loadTemplate('ManageSearch');
684
685
	// Did they want to delete some entries?
686
	if ((!empty($_POST['delete_entries']) && isset($_POST['older'])) || !empty($_POST['removeAll']))
687
	{
688
		checkSession();
689
		validateToken('admin-sl');
690
691
		if (!empty($_POST['delete_entries']) && isset($_POST['older']))
692
		{
693
			$deleteTime = time() - (((int) $_POST['older']) * 24 * 60 * 60);
694
695
			// Delete the entires.
696
			$smcFunc['db_query']('', '
697
			DELETE FROM {db_prefix}log_spider_hits
698
			WHERE log_time < {int:delete_period}',
699
				array(
700
					'delete_period' => $deleteTime,
701
				)
702
			);
703
		}
704
		else
705
		{
706
			// Deleting all of them
707
			$smcFunc['db_query']('', '
708
			TRUNCATE TABLE {db_prefix}log_spider_hits',
709
				array()
710
			);
711
		}
712
	}
713
714
	$listOptions = array(
715
		'id' => 'spider_logs',
716
		'items_per_page' => $modSettings['defaultMaxListItems'],
717
		'title' => $txt['spider_logs'],
718
		'no_items_label' => $txt['spider_logs_empty'],
719
		'base_href' => $context['admin_area'] == 'sengines' ? $scripturl . '?action=admin;area=sengines;sa=logs' : $scripturl . '?action=admin;area=logs;sa=spiderlog',
720
		'default_sort_col' => 'log_time',
721
		'get_items' => array(
722
			'function' => 'list_getSpiderLogs',
723
		),
724
		'get_count' => array(
725
			'function' => 'list_getNumSpiderLogs',
726
		),
727
		'columns' => array(
728
			'name' => array(
729
				'header' => array(
730
					'value' => $txt['spider'],
731
				),
732
				'data' => array(
733
					'db' => 'spider_name',
734
				),
735
				'sort' => array(
736
					'default' => 's.spider_name',
737
					'reverse' => 's.spider_name DESC',
738
				),
739
			),
740
			'log_time' => array(
741
				'header' => array(
742
					'value' => $txt['spider_time'],
743
				),
744
				'data' => array(
745
					'function' => function($rowData)
746
					{
747
						return timeformat($rowData['log_time']);
748
					},
749
				),
750
				'sort' => array(
751
					'default' => 'sl.id_hit DESC',
752
					'reverse' => 'sl.id_hit',
753
				),
754
			),
755
			'viewing' => array(
756
				'header' => array(
757
					'value' => $txt['spider_viewing'],
758
				),
759
				'data' => array(
760
					'db' => 'url',
761
				),
762
			),
763
		),
764
		'form' => array(
765
			'token' => 'admin-sl',
766
			'href' => $scripturl . '?action=admin;area=sengines;sa=logs',
767
		),
768
		'additional_rows' => array(
769
			array(
770
				'position' => 'after_title',
771
				'value' => $txt['spider_logs_info'],
772
			),
773
			array(
774
				'position' => 'below_table_data',
775
				'value' => '<input type="submit" name="removeAll" value="' . $txt['spider_log_empty_log'] . '" data-confirm="' . $txt['spider_log_empty_log_confirm'] . '" class="button_submit you_sure">',
776
			),
777
		),
778
	);
779
780
	createToken('admin-sl');
781
782
	require_once($sourcedir . '/Subs-List.php');
783
	createList($listOptions);
784
785
	// Now determine the actions of the URLs.
786
	if (!empty($context['spider_logs']['rows']))
787
	{
788
		$urls = array();
789
790
		// Grab the current /url.
791
		foreach ($context['spider_logs']['rows'] as $k => $row)
792
		{
793
			// Feature disabled?
794
			if (empty($row['data']['viewing']['value']) && isset($modSettings['spider_mode']) && $modSettings['spider_mode'] < 3)
795
				$context['spider_logs']['rows'][$k]['viewing']['value'] = '<em>' . $txt['spider_disabled'] . '</em>';
796
			else
797
				$urls[$k] = array($row['data']['viewing']['value'], -1);
798
		}
799
800
		// Now stick in the new URLs.
801
		require_once($sourcedir . '/Who.php');
802
		$urls = determineActions($urls, 'whospider_');
803
		foreach ($urls as $k => $new_url)
804
		{
805
			$context['spider_logs']['rows'][$k]['data']['viewing']['value'] = $new_url;
806
		}
807
	}
808
809
	$context['page_title'] = $txt['spider_logs'];
810
	$context['sub_template'] = 'show_spider_logs';
811
	$context['default_list'] = 'spider_logs';
812
}
813
814
/**
815
 * Callback function for createList()
816
 *
817
 * @param int $start The item to start with (for pagination purposes)
818
 * @param int $items_per_page How many items to show per page
819
 * @param string $sort A string indicating how to sort the results
820
 * @return array An array of spider log data
821
 */
822 View Code Duplication
function list_getSpiderLogs($start, $items_per_page, $sort)
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...
823
{
824
	global $smcFunc;
825
826
	$request = $smcFunc['db_query']('', '
827
		SELECT sl.id_spider, sl.url, sl.log_time, s.spider_name
828
		FROM {db_prefix}log_spider_hits AS sl
829
			INNER JOIN {db_prefix}spiders AS s ON (s.id_spider = sl.id_spider)
830
		ORDER BY {raw:sort}
831
		LIMIT {int:start}, {int:items}',
832
		array(
833
			'sort' => $sort,
834
			'start' => $start,
835
			'items' => $items_per_page,
836
		)
837
	);
838
	$spider_logs = array();
839
	while ($row = $smcFunc['db_fetch_assoc']($request))
840
		$spider_logs[] = $row;
841
	$smcFunc['db_free_result']($request);
842
843
	return $spider_logs;
844
}
845
846
/**
847
 * Callback function for createList()
848
 * @return int The number of spider log entries
849
 */
850
function list_getNumSpiderLogs()
851
{
852
	global $smcFunc;
853
854
	$request = $smcFunc['db_query']('', '
855
		SELECT COUNT(*) AS num_logs
856
		FROM {db_prefix}log_spider_hits',
857
		array(
858
		)
859
	);
860
	list ($numLogs) = $smcFunc['db_fetch_row']($request);
861
	$smcFunc['db_free_result']($request);
862
863
	return $numLogs;
864
}
865
866
/**
867
 * Show the spider statistics.
868
 */
869
function SpiderStats()
870
{
871
	global $context, $txt, $sourcedir, $scripturl, $smcFunc, $modSettings;
872
873
	// Force an update of the stats every 60 seconds.
874 View Code Duplication
	if (!isset($_SESSION['spider_stat']) || $_SESSION['spider_stat'] < time() - 60)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
875
	{
876
		consolidateSpiderStats();
877
		$_SESSION['spider_stat'] = time();
878
	}
879
880
	// Are we cleaning up some old stats?
881
	if (!empty($_POST['delete_entries']) && isset($_POST['older']))
882
	{
883
		checkSession();
884
		validateToken('admin-ss');
885
886
		$deleteTime = time() - (((int) $_POST['older']) * 24 * 60 * 60);
887
888
		// Delete the entires.
889
		$smcFunc['db_query']('', '
890
			DELETE FROM {db_prefix}log_spider_stats
891
			WHERE last_seen < {int:delete_period}',
892
			array(
893
				'delete_period' => $deleteTime,
894
			)
895
		);
896
	}
897
898
	// Get the earliest and latest dates.
899
	$request = $smcFunc['db_query']('', '
900
		SELECT MIN(stat_date) AS first_date, MAX(stat_date) AS last_date
901
		FROM {db_prefix}log_spider_stats',
902
		array(
903
		)
904
	);
905
906
	list ($min_date, $max_date) = $smcFunc['db_fetch_row']($request);
907
	$smcFunc['db_free_result']($request);
908
909
	$min_year = (int) substr($min_date, 0, 4);
910
	$max_year = (int) substr($max_date, 0, 4);
911
	$min_month = (int) substr($min_date, 5, 2);
912
	$max_month = (int) substr($max_date, 5, 2);
913
914
	// Prepare the dates for the drop down.
915
	$date_choices = array();
916
	for ($y = $min_year; $y <= $max_year; $y++)
917
		for ($m = 1; $m <= 12; $m++)
918
		{
919
			// This doesn't count?
920
			if ($y == $min_year && $m < $min_month)
921
				continue;
922
			if ($y == $max_year && $m > $max_month)
923
				break;
924
925
			$date_choices[$y . $m] = $txt['months_short'][$m] . ' ' . $y;
926
		}
927
928
	// What are we currently viewing?
929
	$current_date = isset($_REQUEST['new_date']) && isset($date_choices[$_REQUEST['new_date']]) ? $_REQUEST['new_date'] : $max_date;
930
931
	// Prepare the HTML.
932
	$date_select = '
933
		' . $txt['spider_stats_select_month'] . ':
934
		<select name="new_date" onchange="document.spider_stat_list.submit();">';
935
936
	if (empty($date_choices))
937
		$date_select .= '
938
			<option></option>';
939
	else
940
		foreach ($date_choices as $id => $text)
941
			$date_select .= '
942
			<option value="' . $id . '"' . ($current_date == $id ? ' selected' : '') . '>' . $text . '</option>';
943
944
	$date_select .= '
945
		</select>
946
		<noscript>
947
			<input type="submit" name="go" value="' . $txt['go'] . '" class="button_submit">
948
		</noscript>';
949
950
	// If we manually jumped to a date work out the offset.
951
	if (isset($_REQUEST['new_date']))
952
	{
953
		$date_query = sprintf('%04d-%02d-01', substr($current_date, 0, 4), substr($current_date, 4));
954
955
		$request = $smcFunc['db_query']('', '
956
			SELECT COUNT(*) AS offset
957
			FROM {db_prefix}log_spider_stats
958
			WHERE stat_date < {date:date_being_viewed}',
959
			array(
960
				'date_being_viewed' => $date_query,
961
			)
962
		);
963
		list ($_REQUEST['start']) = $smcFunc['db_fetch_row']($request);
964
		$smcFunc['db_free_result']($request);
965
	}
966
967
	$listOptions = array(
968
		'id' => 'spider_stat_list',
969
		'title' => $txt['spider'] . ' ' . $txt['spider_stats'],
970
		'items_per_page' => $modSettings['defaultMaxListItems'],
971
		'base_href' => $scripturl . '?action=admin;area=sengines;sa=stats',
972
		'default_sort_col' => 'stat_date',
973
		'get_items' => array(
974
			'function' => 'list_getSpiderStats',
975
		),
976
		'get_count' => array(
977
			'function' => 'list_getNumSpiderStats',
978
		),
979
		'no_items_label' => $txt['spider_stats_no_entries'],
980
		'columns' => array(
981
			'stat_date' => array(
982
				'header' => array(
983
					'value' => $txt['date'],
984
				),
985
				'data' => array(
986
					'db' => 'stat_date',
987
				),
988
				'sort' => array(
989
					'default' => 'stat_date',
990
					'reverse' => 'stat_date DESC',
991
				),
992
			),
993
			'name' => array(
994
				'header' => array(
995
					'value' => $txt['spider_name'],
996
				),
997
				'data' => array(
998
					'db' => 'spider_name',
999
				),
1000
				'sort' => array(
1001
					'default' => 's.spider_name',
1002
					'reverse' => 's.spider_name DESC',
1003
				),
1004
			),
1005
			'page_hits' => array(
1006
				'header' => array(
1007
					'value' => $txt['spider_stats_page_hits'],
1008
				),
1009
				'data' => array(
1010
					'db' => 'page_hits',
1011
				),
1012
				'sort' => array(
1013
					'default' => 'ss.page_hits',
1014
					'reverse' => 'ss.page_hits DESC',
1015
				),
1016
			),
1017
		),
1018
		'form' => array(
1019
			'href' => $scripturl . '?action=admin;area=sengines;sa=stats',
1020
			'name' => 'spider_stat_list',
1021
		),
1022
		'additional_rows' => array(
1023
			array(
1024
				'position' => 'below_table_data',
1025
				'value' => $date_select,
1026
				'style' => 'text-align: right;',
1027
			),
1028
		),
1029
	);
1030
1031
	createToken('admin-ss');
1032
1033
	require_once($sourcedir . '/Subs-List.php');
1034
	createList($listOptions);
1035
1036
	$context['sub_template'] = 'show_spider_stats';
1037
	$context['default_list'] = 'spider_stat_list';
1038
}
1039
1040
/**
1041
 * Callback function for createList()
1042
 * Get a list of spider stats from the log_spider table
1043
 *
1044
 * @param int $start The item to start with (for pagination purposes)
1045
 * @param int $items_per_page The number of items to show per page
1046
 * @param string $sort A string indicating how to sort the results
1047
 * @return array An array of spider statistics info
1048
 */
1049 View Code Duplication
function list_getSpiderStats($start, $items_per_page, $sort)
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...
1050
{
1051
	global $smcFunc;
1052
1053
	$request = $smcFunc['db_query']('', '
1054
		SELECT ss.id_spider, ss.stat_date, ss.page_hits, s.spider_name
1055
		FROM {db_prefix}log_spider_stats AS ss
1056
			INNER JOIN {db_prefix}spiders AS s ON (s.id_spider = ss.id_spider)
1057
		ORDER BY {raw:sort}
1058
		LIMIT {int:start}, {int:items}',
1059
		array(
1060
			'sort' => $sort,
1061
			'start' => $start,
1062
			'items' => $items_per_page,
1063
		)
1064
	);
1065
	$spider_stats = array();
1066
	while ($row = $smcFunc['db_fetch_assoc']($request))
1067
		$spider_stats[] = $row;
1068
	$smcFunc['db_free_result']($request);
1069
1070
	return $spider_stats;
1071
}
1072
1073
/**
1074
 * Callback function for createList()
1075
 * Get the number of spider stat rows from the log spider stats table
1076
 *
1077
 * @return int The number of rows in the log_spider_stats table
1078
 */
1079
function list_getNumSpiderStats()
1080
{
1081
	global $smcFunc;
1082
1083
	$request = $smcFunc['db_query']('', '
1084
		SELECT COUNT(*) AS num_stats
1085
		FROM {db_prefix}log_spider_stats',
1086
		array(
1087
		)
1088
	);
1089
	list ($numStats) = $smcFunc['db_fetch_row']($request);
1090
	$smcFunc['db_free_result']($request);
1091
1092
	return $numStats;
1093
}
1094
1095
/**
1096
 * Recache spider names?
1097
 */
1098
function recacheSpiderNames()
1099
{
1100
	global $smcFunc;
1101
1102
	$request = $smcFunc['db_query']('', '
1103
		SELECT id_spider, spider_name
1104
		FROM {db_prefix}spiders',
1105
		array()
1106
	);
1107
	$spiders = array();
1108
	while ($row = $smcFunc['db_fetch_assoc']($request))
1109
		$spiders[$row['id_spider']] = $row['spider_name'];
1110
	$smcFunc['db_free_result']($request);
1111
1112
	updateSettings(array('spider_name_cache' => $smcFunc['json_encode']($spiders)));
1113
}
1114
1115
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...