Completed
Pull Request — release-2.1 (#4306)
by Jeremy
07:10
created

Subs-Admin.php ➔ getFileVersions()   F

Complexity

Conditions 31
Paths 5760

Size

Total Lines 157
Code Lines 84

Duplication

Lines 60
Ratio 38.22 %

Importance

Changes 0
Metric Value
cc 31
eloc 84
nc 5760
nop 1
dl 60
loc 157
rs 2
c 0
b 0
f 0

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 functions that are specifically done by administrators.
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
 * Get a list of versions that are currently installed on the server.
21
 * @param array $checkFor An array of what to check versions for - can contain one or more of 'gd', 'imagemagick', 'db_server', 'phpa', 'memcache', 'xcache', 'apc', 'php' or 'server'
22
 * @return array An array of versions (keys are same as what was in $checkFor, values are the versions)
23
 */
24
function getServerVersions($checkFor)
25
{
26
	global $txt, $db_connection, $_PHPA, $smcFunc, $cache_accelerator, $cache_memcached, $cacheAPI, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
27
28
	loadLanguage('Admin');
29
30
	$versions = array();
31
32
	// Is GD available?  If it is, we should show version information for it too.
33
	if (in_array('gd', $checkFor) && function_exists('gd_info'))
34
	{
35
		$temp = gd_info();
36
		$versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']);
37
	}
38
39
	// Why not have a look at ImageMagick? If it's installed, we should show version information for it too.
40
	if (in_array('imagemagick', $checkFor) && (class_exists('Imagick') || function_exists('MagickGetVersionString')))
41
	{
42
		if (class_exists('Imagick'))
43
		{
44
			$temp = New Imagick;
45
			$temp2 = $temp->getVersion();
46
			$im_version = $temp2['versionString'];
47
			$extension_version = 'Imagick ' . phpversion('Imagick');
48
		}
49
		else
50
		{
51
			$im_version = MagickGetVersionString();
52
			$extension_version = 'MagickWand ' . phpversion('MagickWand');
53
		}
54
55
		// We already know it's ImageMagick and the website isn't needed...
56
		$im_version = str_replace(array('ImageMagick ', ' https://www.imagemagick.org'), '', $im_version);
57
		$versions['imagemagick'] = array('title' => $txt['support_versions_imagemagick'], 'version' => $im_version . ' (' . $extension_version . ')');
58
	}
59
60
	// Now lets check for the Database.
61
	if (in_array('db_server', $checkFor))
62
	{
63
		db_extend();
64
		if (!isset($db_connection) || $db_connection === false)
65
			trigger_error('getServerVersions(): you need to be connected to the database in order to get its server version', E_USER_NOTICE);
66
		else
67
		{
68
			$versions['db_engine'] = array('title' => sprintf($txt['support_versions_db_engine'], $smcFunc['db_title']), 'version' => '');
69
			$versions['db_engine']['version'] = $smcFunc['db_get_engine']();
70
71
			$versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => '');
72
			$versions['db_server']['version'] = $smcFunc['db_get_version']();
73
		}
74
	}
75
76
	// If we're using memcache we need the server info.
77
	$memcache_version = '???';
78
	if (!empty($cache_accelerator) && ($cache_accelerator == 'memcached' || $cache_accelerator == 'memcache') && !empty($cache_memcached) && !empty($cacheAPI))
79
		$memcache_version = $cacheAPI->getVersion();
80
81
	// Check to see if we have any accelerators installed...
82
	if (in_array('phpa', $checkFor) && isset($_PHPA))
83
		$versions['phpa'] = array('title' => 'ionCube PHP-Accelerator', 'version' => $_PHPA['VERSION']);
84
	if (in_array('apc', $checkFor) && extension_loaded('apc'))
85
		$versions['apc'] = array('title' => 'Alternative PHP Cache', 'version' => phpversion('apc'));
86
	if (in_array('memcache', $checkFor) && function_exists('memcache_set'))
87
		$versions['memcache'] = array('title' => 'Memcached', 'version' => $memcache_version;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ';', expecting ')'
Loading history...
88
	if (in_array('xcache', $checkFor) && function_exists('xcache_set'))
89
		$versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION);
90
91
	if (in_array('php', $checkFor))
92
		$versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION, 'more' => '?action=admin;area=serversettings;sa=phpinfo');
93
94
	if (in_array('server', $checkFor))
95
		$versions['server'] = array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']);
96
97
	return $versions;
98
}
99
100
/**
101
 * Search through source, theme and language files to determine their version.
102
 * Get detailed version information about the physical SMF files on the server.
103
 *
104
 * - the input parameter allows to set whether to include SSI.php and whether
105
 *   the results should be sorted.
106
 * - returns an array containing information on source files, templates and
107
 *   language files found in the default theme directory (grouped by language).
108
 *
109
 * @param array &$versionOptions An array of options. Can contain one or more of 'include_ssi', 'include_subscriptions', 'include_tasks' and 'sort_results'
110
 * @return array An array of file version info.
111
 */
112
function getFileVersions(&$versionOptions)
113
{
114
	global $boarddir, $sourcedir, $settings, $tasksdir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
115
116
	// Default place to find the languages would be the default theme dir.
117
	$lang_dir = $settings['default_theme_dir'] . '/languages';
118
119
	$version_info = array(
120
		'file_versions' => array(),
121
		'default_template_versions' => array(),
122
		'template_versions' => array(),
123
		'default_language_versions' => array(),
124
		'tasks_versions' => array(),
125
	);
126
127
	// Find the version in SSI.php's file header.
128
	if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php'))
129
	{
130
		$fp = fopen($boarddir . '/SSI.php', 'rb');
131
		$header = fread($fp, 4096);
132
		fclose($fp);
133
134
		// The comment looks rougly like... that.
135
		if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
136
			$version_info['file_versions']['SSI.php'] = $match[1];
137
		// Not found!  This is bad.
138
		else
139
			$version_info['file_versions']['SSI.php'] = '??';
140
	}
141
142
	// Do the paid subscriptions handler?
143
	if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php'))
144
	{
145
		$fp = fopen($boarddir . '/subscriptions.php', 'rb');
146
		$header = fread($fp, 4096);
147
		fclose($fp);
148
149
		// Found it?
150
		if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
151
			$version_info['file_versions']['subscriptions.php'] = $match[1];
152
		// If we haven't how do we all get paid?
153
		else
154
			$version_info['file_versions']['subscriptions.php'] = '??';
155
	}
156
157
	// Load all the files in the Sources directory, except this file and the redirect.
158
	$sources_dir = dir($sourcedir);
159
	while ($entry = $sources_dir->read())
160
	{
161
		if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php')
162
		{
163
			// Read the first 4k from the file.... enough for the header.
164
			$fp = fopen($sourcedir . '/' . $entry, 'rb');
165
			$header = fread($fp, 4096);
166
			fclose($fp);
167
168
			// Look for the version comment in the file header.
169
			if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
170
				$version_info['file_versions'][$entry] = $match[1];
171
			// It wasn't found, but the file was... show a '??'.
172
			else
173
				$version_info['file_versions'][$entry] = '??';
174
		}
175
	}
176
	$sources_dir->close();
177
178
	// Load all the files in the tasks directory.
179
	if (!empty($versionOptions['include_tasks']))
180
	{
181
		$tasks_dir = dir($tasksdir);
182
		while ($entry = $tasks_dir->read())
183
		{
184
			if (substr($entry, -4) === '.php' && !is_dir($tasksdir . '/' . $entry) && $entry !== 'index.php')
185
			{
186
				// Read the first 4k from the file.... enough for the header.
187
				$fp = fopen($tasksdir . '/' . $entry, 'rb');
188
				$header = fread($fp, 4096);
189
				fclose($fp);
190
191
				// Look for the version comment in the file header.
192
				if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
193
					$version_info['tasks_versions'][$entry] = $match[1];
194
				// It wasn't found, but the file was... show a '??'.
195
				else
196
					$version_info['tasks_versions'][$entry] = '??';
197
			}
198
		}
199
		$tasks_dir->close();
200
	}
201
202
	// Load all the files in the default template directory - and the current theme if applicable.
203
	$directories = array('default_template_versions' => $settings['default_theme_dir']);
204
	if ($settings['theme_id'] != 1)
205
		$directories += array('template_versions' => $settings['theme_dir']);
206
207
	foreach ($directories as $type => $dirname)
208
	{
209
		$this_dir = dir($dirname);
210
		while ($entry = $this_dir->read())
211
		{
212
			if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry))
213
			{
214
				// Read the first 768 bytes from the file.... enough for the header.
215
				$fp = fopen($dirname . '/' . $entry, 'rb');
216
				$header = fread($fp, 768);
217
				fclose($fp);
218
219
				// Look for the version comment in the file header.
220
				if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
221
					$version_info[$type][$entry] = $match[1];
222
				// It wasn't found, but the file was... show a '??'.
223
				else
224
					$version_info[$type][$entry] = '??';
225
			}
226
		}
227
		$this_dir->close();
228
	}
229
230
	// Load up all the files in the default language directory and sort by language.
231
	$this_dir = dir($lang_dir);
232
	while ($entry = $this_dir->read())
233
	{
234
		if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry))
235
		{
236
			// Read the first 768 bytes from the file.... enough for the header.
237
			$fp = fopen($lang_dir . '/' . $entry, 'rb');
238
			$header = fread($fp, 768);
239
			fclose($fp);
240
241
			// Split the file name off into useful bits.
242
			list ($name, $language) = explode('.', $entry);
243
244
			// Look for the version comment in the file header.
245
			if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1)
246
				$version_info['default_language_versions'][$language][$name] = $match[1];
247
			// It wasn't found, but the file was... show a '??'.
248
			else
249
				$version_info['default_language_versions'][$language][$name] = '??';
250
		}
251
	}
252
	$this_dir->close();
253
254
	// Sort the file versions by filename.
255
	if (!empty($versionOptions['sort_results']))
256
	{
257
		ksort($version_info['file_versions']);
258
		ksort($version_info['default_template_versions']);
259
		ksort($version_info['template_versions']);
260
		ksort($version_info['default_language_versions']);
261
		ksort($version_info['tasks_versions']);
262
263
		// For languages sort each language too.
264
		foreach ($version_info['default_language_versions'] as $language => $dummy)
265
			ksort($version_info['default_language_versions'][$language]);
266
	}
267
	return $version_info;
268
}
269
270
/**
271
 * Update the Settings.php file.
272
 *
273
 * The most important function in this file for mod makers happens to be the
274
 * updateSettingsFile() function, but it shouldn't be used often anyway.
275
 *
276
 * - updates the Settings.php file with the changes supplied in config_vars.
277
 * - expects config_vars to be an associative array, with the keys as the
278
 *   variable names in Settings.php, and the values the variable values.
279
 * - does not escape or quote values.
280
 * - preserves case, formatting, and additional options in file.
281
 * - writes nothing if the resulting file would be less than 10 lines
282
 *   in length (sanity check for read lock.)
283
 * - check for changes to db_last_error and passes those off to a separate handler
284
 * - attempts to create a backup file and will use it should the writing of the
285
 *   new settings file fail
286
 *
287
 * @param array $config_vars An array of one or more variables to update
288
 */
289
function updateSettingsFile($config_vars)
290
{
291
	global $boarddir, $cachedir, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
292
293
	// Updating the db_last_error, then don't mess around with Settings.php
294
	if (count($config_vars) === 1 && isset($config_vars['db_last_error']))
295
	{
296
		updateDbLastError($config_vars['db_last_error']);
297
		return;
298
	}
299
300
	// When was Settings.php last changed?
301
	$last_settings_change = filemtime($boarddir . '/Settings.php');
302
303
	// Load the settings file.
304
	$settingsArray = trim(file_get_contents($boarddir . '/Settings.php'));
305
306
	// Break it up based on \r or \n, and then clean out extra characters.
307
	if (strpos($settingsArray, "\n") !== false)
308
		$settingsArray = explode("\n", $settingsArray);
309
	elseif (strpos($settingsArray, "\r") !== false)
310
		$settingsArray = explode("\r", $settingsArray);
311
	else
312
		return;
313
314
	// Presumably, the file has to have stuff in it for this function to be called :P.
315
	if (count($settingsArray) < 10)
316
		return;
317
318
	// remove any /r's that made there way in here
319
	foreach ($settingsArray as $k => $dummy)
320
		$settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n";
321
322
	// go line by line and see whats changing
323
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
324
	{
325
		// Don't trim or bother with it if it's not a variable.
326
		if (substr($settingsArray[$i], 0, 1) != '$')
327
			continue;
328
329
		$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
330
331
		// Look through the variables to set....
332
		foreach ($config_vars as $var => $val)
333
		{
334
			// be sure someone is not updating db_last_error this with a group
335
			if ($var === 'db_last_error')
336
			{
337
				updateDbLastError($val);
338
				unset($config_vars[$var]);
339
			}
340
			elseif (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
341
			{
342
				$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
343
				$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n";
344
345
				// This one's been 'used', so to speak.
346
				unset($config_vars[$var]);
347
			}
348
		}
349
350
		// End of the file ... maybe
351
		if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>')
352
			$end = $i;
353
	}
354
355
	// This should never happen, but apparently it is happening.
356
	if (empty($end) || $end < 10)
357
		$end = count($settingsArray) - 1;
358
359
	// Still more variables to go?  Then lets add them at the end.
360
	if (!empty($config_vars))
361
	{
362
		if (trim($settingsArray[$end]) == '?' . '>')
363
			$settingsArray[$end++] = '';
364
		else
365
			$end++;
366
367
		// Add in any newly defined vars that were passed
368
		foreach ($config_vars as $var => $val)
369
			$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
370
371
		$settingsArray[$end] = '?' . '>';
372
	}
373
	else
374
		$settingsArray[$end] = trim($settingsArray[$end]);
375
376
	// Sanity error checking: the file needs to be at least 12 lines.
377
	if (count($settingsArray) < 12)
378
		return;
379
380
	// Try to avoid a few pitfalls:
381
	//  - like a possible race condition,
382
	//  - or a failure to write at low diskspace
383
	//
384
	// Check before you act: if cache is enabled, we can do a simple write test
385
	// to validate that we even write things on this filesystem.
386
	if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
387
		$cachedir = $boarddir . '/cache';
388
389
	$test_fp = @fopen($cachedir . '/settings_update.tmp', "w+");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal w+ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
390
	if ($test_fp)
391
	{
392
		fclose($test_fp);
393
		$written_bytes = file_put_contents($cachedir . '/settings_update.tmp', 'test', LOCK_EX);
394
		@unlink($cachedir . '/settings_update.tmp');
395
396
		if ($written_bytes !== 4)
397
		{
398
			// Oops. Low disk space, perhaps. Don't mess with Settings.php then.
399
			// No means no. :P
400
			return;
401
		}
402
	}
403
404
	// Protect me from what I want! :P
405
	clearstatcache();
406
	if (filemtime($boarddir . '/Settings.php') === $last_settings_change)
407
	{
408
		// save the old before we do anything
409
		$settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
410
		$settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_bak.php') || filesize($boarddir . '/Settings_bak.php') === 0) : $settings_backup_fail;
411
412
		// write out the new
413
		$write_settings = implode('', $settingsArray);
414
		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
415
416
		// survey says ...
417
		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
418
		{
419
			// Well this is not good at all, lets see if we can save this
420
			$context['settings_message'] = 'settings_error';
421
422
			if (file_exists($boarddir . '/Settings_bak.php'))
423
				@copy($boarddir . '/Settings_bak.php', $boarddir . '/Settings.php');
424
		}
425
	}
426
427
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
428
	// it seems that there are times it might not. So let's MAKE it dump the cache.
429
	if (function_exists('opcache_invalidate'))
430
		opcache_invalidate($boarddir . '/Settings.php', true);
431
}
432
433
/**
434
 * Saves the time of the last db error for the error log
435
 * - Done separately from updateSettingsFile to avoid race conditions
436
 *   which can occur during a db error
437
 * - If it fails Settings.php will assume 0
438
 *
439
 * @param int $time The timestamp of the last DB error
440
 */
441
function updateDbLastError($time)
442
{
443
	global $boarddir, $cachedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
444
445
	// Write out the db_last_error file with the error timestamp
446
	file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = ' . $time . ';' . "\n" . '?' . '>', LOCK_EX);
447
	@touch($boarddir . '/' . 'Settings.php');
448
}
449
/**
450
 * Saves the admin's current preferences to the database.
451
 */
452
function updateAdminPreferences()
453
{
454
	global $options, $context, $smcFunc, $settings, $user_info;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
455
456
	// This must exist!
457
	if (!isset($context['admin_preferences']))
458
		return false;
459
460
	// This is what we'll be saving.
461
	$options['admin_preferences'] = $smcFunc['json_encode']($context['admin_preferences']);
462
463
	// Just check we haven't ended up with something theme exclusive somehow.
464
	$smcFunc['db_query']('', '
465
		DELETE FROM {db_prefix}themes
466
		WHERE id_theme != {int:default_theme}
467
		AND variable = {string:admin_preferences}',
468
		array(
469
			'default_theme' => 1,
470
			'admin_preferences' => 'admin_preferences',
471
		)
472
	);
473
474
	// Update the themes table.
475
	$smcFunc['db_insert']('replace',
476
		'{db_prefix}themes',
477
		array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
478
		array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']),
479
		array('id_member', 'id_theme', 'variable')
480
	);
481
482
	// Make sure we invalidate any cache.
483
	cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0);
484
}
485
486
/**
487
 * Send all the administrators a lovely email.
488
 * - loads all users who are admins or have the admin forum permission.
489
 * - uses the email template and replacements passed in the parameters.
490
 * - sends them an email.
491
 *
492
 * @param string $template Which email template to use
493
 * @param array $replacements An array of items to replace the variables in the template
494
 * @param array $additional_recipients An array of arrays of info for additional recipients. Should have 'id', 'email' and 'name' for each.
495
 */
496
function emailAdmins($template, $replacements = array(), $additional_recipients = array())
497
{
498
	global $smcFunc, $sourcedir, $language, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
499
500
	// We certainly want this.
501
	require_once($sourcedir . '/Subs-Post.php');
502
503
	// Load all members which are effectively admins.
504
	require_once($sourcedir . '/Subs-Members.php');
505
	$members = membersAllowedTo('admin_forum');
506
507
	// Load their alert preferences
508
	require_once($sourcedir . '/Subs-Notify.php');
509
	$prefs = getNotifyPrefs($members, 'announcements', true);
510
511
	$request = $smcFunc['db_query']('', '
512
		SELECT id_member, member_name, real_name, lngfile, email_address
513
		FROM {db_prefix}members
514
		WHERE id_member IN({array_int:members})',
515
		array(
516
			'members' => $members,
517
		)
518
	);
519
	$emails_sent = array();
520
	while ($row = $smcFunc['db_fetch_assoc']($request))
521
	{
522
		if (empty($prefs[$row['id_member']]['announcements']))
523
			continue;
524
525
		// Stick their particulars in the replacement data.
526
		$replacements['IDMEMBER'] = $row['id_member'];
527
		$replacements['REALNAME'] = $row['member_name'];
528
		$replacements['USERNAME'] = $row['real_name'];
529
530
		// Load the data from the template.
531
		$emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
532
533
		// Then send the actual email.
534
		sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1);
535
536
		// Track who we emailed so we don't do it twice.
537
		$emails_sent[] = $row['email_address'];
538
	}
539
	$smcFunc['db_free_result']($request);
540
541
	// Any additional users we must email this to?
542
	if (!empty($additional_recipients))
543
		foreach ($additional_recipients as $recipient)
544
		{
545
			if (in_array($recipient['email'], $emails_sent))
546
				continue;
547
548
			$replacements['IDMEMBER'] = $recipient['id'];
549
			$replacements['REALNAME'] = $recipient['name'];
550
			$replacements['USERNAME'] = $recipient['name'];
551
552
			// Load the template again.
553
			$emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']);
554
555
			// Send off the email.
556
			sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1);
557
		}
558
}
559
560
?>