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 2019 Simple Machines and individual contributors |
||||||
11 | * @license http://www.simplemachines.org/about/smf/license.php BSD |
||||||
12 | * |
||||||
13 | * @version 2.1 RC2 |
||||||
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 | * |
||||||
22 | * @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' |
||||||
23 | * @return array An array of versions (keys are same as what was in $checkFor, values are the versions) |
||||||
24 | */ |
||||||
25 | function getServerVersions($checkFor) |
||||||
26 | { |
||||||
27 | global $txt, $db_connection, $_PHPA, $smcFunc, $cache_accelerator, $cache_memcached, $cacheAPI, $modSettings; |
||||||
28 | |||||||
29 | loadLanguage('Admin'); |
||||||
30 | |||||||
31 | $versions = array(); |
||||||
32 | |||||||
33 | // Is GD available? If it is, we should show version information for it too. |
||||||
34 | if (in_array('gd', $checkFor) && function_exists('gd_info')) |
||||||
35 | { |
||||||
36 | $temp = gd_info(); |
||||||
37 | $versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']); |
||||||
38 | } |
||||||
39 | |||||||
40 | // Why not have a look at ImageMagick? If it's installed, we should show version information for it too. |
||||||
41 | if (in_array('imagemagick', $checkFor) && (class_exists('Imagick') || function_exists('MagickGetVersionString'))) |
||||||
42 | { |
||||||
43 | if (class_exists('Imagick')) |
||||||
44 | { |
||||||
45 | $temp = New Imagick; |
||||||
46 | $temp2 = $temp->getVersion(); |
||||||
47 | $im_version = $temp2['versionString']; |
||||||
48 | $extension_version = 'Imagick ' . phpversion('Imagick'); |
||||||
49 | } |
||||||
50 | else |
||||||
51 | { |
||||||
52 | $im_version = MagickGetVersionString(); |
||||||
53 | $extension_version = 'MagickWand ' . phpversion('MagickWand'); |
||||||
54 | } |
||||||
55 | |||||||
56 | // We already know it's ImageMagick and the website isn't needed... |
||||||
57 | $im_version = str_replace(array('ImageMagick ', ' https://www.imagemagick.org'), '', $im_version); |
||||||
58 | $versions['imagemagick'] = array('title' => $txt['support_versions_imagemagick'], 'version' => $im_version . ' (' . $extension_version . ')'); |
||||||
59 | } |
||||||
60 | |||||||
61 | // Now lets check for the Database. |
||||||
62 | if (in_array('db_server', $checkFor)) |
||||||
63 | { |
||||||
64 | db_extend(); |
||||||
65 | if (!isset($db_connection) || $db_connection === false) |
||||||
66 | trigger_error('getServerVersions(): you need to be connected to the database in order to get its server version', E_USER_NOTICE); |
||||||
67 | else |
||||||
68 | { |
||||||
69 | $versions['db_engine'] = array('title' => sprintf($txt['support_versions_db_engine'], $smcFunc['db_title']), 'version' => ''); |
||||||
70 | $versions['db_engine']['version'] = $smcFunc['db_get_vendor'](); |
||||||
71 | |||||||
72 | $versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => ''); |
||||||
73 | $versions['db_server']['version'] = $smcFunc['db_get_version'](); |
||||||
74 | } |
||||||
75 | } |
||||||
76 | |||||||
77 | // If we're using memcache we need the server info. |
||||||
78 | $memcache_version = '???'; |
||||||
79 | if (!empty($cache_accelerator) && ($cache_accelerator == 'memcached' || $cache_accelerator == 'memcache') && !empty($cache_memcached) && !empty($cacheAPI)) |
||||||
80 | $memcache_version = $cacheAPI->getVersion(); |
||||||
81 | |||||||
82 | // Check to see if we have any accelerators installed... |
||||||
83 | if (in_array('phpa', $checkFor) && isset($_PHPA)) |
||||||
84 | $versions['phpa'] = array('title' => 'ionCube PHP-Accelerator', 'version' => $_PHPA['VERSION']); |
||||||
85 | if (in_array('apc', $checkFor) && extension_loaded('apc')) |
||||||
86 | $versions['apc'] = array('title' => 'Alternative PHP Cache', 'version' => phpversion('apc')); |
||||||
87 | if (in_array('memcache', $checkFor) && function_exists('memcache_set')) |
||||||
88 | $versions['memcache'] = array('title' => 'Memcached', 'version' => $memcache_version); |
||||||
89 | if (in_array('xcache', $checkFor) && function_exists('xcache_set')) |
||||||
90 | $versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
91 | |||||||
92 | if (in_array('php', $checkFor)) |
||||||
93 | $versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION, 'more' => '?action=admin;area=serversettings;sa=phpinfo'); |
||||||
94 | |||||||
95 | if (in_array('server', $checkFor)) |
||||||
96 | $versions['server'] = array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']); |
||||||
97 | |||||||
98 | return $versions; |
||||||
99 | } |
||||||
100 | |||||||
101 | /** |
||||||
102 | * Search through source, theme and language files to determine their version. |
||||||
103 | * Get detailed version information about the physical SMF files on the server. |
||||||
104 | * |
||||||
105 | * - the input parameter allows to set whether to include SSI.php and whether |
||||||
106 | * the results should be sorted. |
||||||
107 | * - returns an array containing information on source files, templates and |
||||||
108 | * language files found in the default theme directory (grouped by language). |
||||||
109 | * |
||||||
110 | * @param array &$versionOptions An array of options. Can contain one or more of 'include_ssi', 'include_subscriptions', 'include_tasks' and 'sort_results' |
||||||
111 | * @return array An array of file version info. |
||||||
112 | */ |
||||||
113 | function getFileVersions(&$versionOptions) |
||||||
114 | { |
||||||
115 | global $boarddir, $sourcedir, $settings, $tasksdir; |
||||||
116 | |||||||
117 | // Default place to find the languages would be the default theme dir. |
||||||
118 | $lang_dir = $settings['default_theme_dir'] . '/languages'; |
||||||
119 | |||||||
120 | $version_info = array( |
||||||
121 | 'file_versions' => array(), |
||||||
122 | 'default_template_versions' => array(), |
||||||
123 | 'template_versions' => array(), |
||||||
124 | 'default_language_versions' => array(), |
||||||
125 | 'tasks_versions' => array(), |
||||||
126 | ); |
||||||
127 | |||||||
128 | // Find the version in SSI.php's file header. |
||||||
129 | if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php')) |
||||||
130 | { |
||||||
131 | $fp = fopen($boarddir . '/SSI.php', 'rb'); |
||||||
132 | $header = fread($fp, 4096); |
||||||
0 ignored issues
–
show
It seems like
$fp can also be of type false ; however, parameter $handle of fread() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
133 | fclose($fp); |
||||||
0 ignored issues
–
show
It seems like
$fp can also be of type false ; however, parameter $handle of fclose() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
134 | |||||||
135 | // The comment looks rougly like... that. |
||||||
136 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||||
137 | $version_info['file_versions']['SSI.php'] = $match[1]; |
||||||
138 | // Not found! This is bad. |
||||||
139 | else |
||||||
140 | $version_info['file_versions']['SSI.php'] = '??'; |
||||||
141 | } |
||||||
142 | |||||||
143 | // Do the paid subscriptions handler? |
||||||
144 | if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php')) |
||||||
145 | { |
||||||
146 | $fp = fopen($boarddir . '/subscriptions.php', 'rb'); |
||||||
147 | $header = fread($fp, 4096); |
||||||
148 | fclose($fp); |
||||||
149 | |||||||
150 | // Found it? |
||||||
151 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||||
152 | $version_info['file_versions']['subscriptions.php'] = $match[1]; |
||||||
153 | // If we haven't how do we all get paid? |
||||||
154 | else |
||||||
155 | $version_info['file_versions']['subscriptions.php'] = '??'; |
||||||
156 | } |
||||||
157 | |||||||
158 | // Load all the files in the Sources directory, except this file and the redirect. |
||||||
159 | $sources_dir = dir($sourcedir); |
||||||
160 | while ($entry = $sources_dir->read()) |
||||||
161 | { |
||||||
162 | if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php') |
||||||
163 | { |
||||||
164 | // Read the first 4k from the file.... enough for the header. |
||||||
165 | $fp = fopen($sourcedir . '/' . $entry, 'rb'); |
||||||
166 | $header = fread($fp, 4096); |
||||||
167 | fclose($fp); |
||||||
168 | |||||||
169 | // Look for the version comment in the file header. |
||||||
170 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||||
171 | $version_info['file_versions'][$entry] = $match[1]; |
||||||
172 | // It wasn't found, but the file was... show a '??'. |
||||||
173 | else |
||||||
174 | $version_info['file_versions'][$entry] = '??'; |
||||||
175 | } |
||||||
176 | } |
||||||
177 | $sources_dir->close(); |
||||||
178 | |||||||
179 | // Load all the files in the tasks directory. |
||||||
180 | if (!empty($versionOptions['include_tasks'])) |
||||||
181 | { |
||||||
182 | $tasks_dir = dir($tasksdir); |
||||||
183 | while ($entry = $tasks_dir->read()) |
||||||
184 | { |
||||||
185 | if (substr($entry, -4) === '.php' && !is_dir($tasksdir . '/' . $entry) && $entry !== 'index.php') |
||||||
186 | { |
||||||
187 | // Read the first 4k from the file.... enough for the header. |
||||||
188 | $fp = fopen($tasksdir . '/' . $entry, 'rb'); |
||||||
189 | $header = fread($fp, 4096); |
||||||
190 | fclose($fp); |
||||||
191 | |||||||
192 | // Look for the version comment in the file header. |
||||||
193 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||||
194 | $version_info['tasks_versions'][$entry] = $match[1]; |
||||||
195 | // It wasn't found, but the file was... show a '??'. |
||||||
196 | else |
||||||
197 | $version_info['tasks_versions'][$entry] = '??'; |
||||||
198 | } |
||||||
199 | } |
||||||
200 | $tasks_dir->close(); |
||||||
201 | } |
||||||
202 | |||||||
203 | // Load all the files in the default template directory - and the current theme if applicable. |
||||||
204 | $directories = array('default_template_versions' => $settings['default_theme_dir']); |
||||||
205 | if ($settings['theme_id'] != 1) |
||||||
206 | $directories += array('template_versions' => $settings['theme_dir']); |
||||||
207 | |||||||
208 | foreach ($directories as $type => $dirname) |
||||||
209 | { |
||||||
210 | $this_dir = dir($dirname); |
||||||
211 | while ($entry = $this_dir->read()) |
||||||
212 | { |
||||||
213 | if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry)) |
||||||
214 | { |
||||||
215 | // Read the first 768 bytes from the file.... enough for the header. |
||||||
216 | $fp = fopen($dirname . '/' . $entry, 'rb'); |
||||||
217 | $header = fread($fp, 768); |
||||||
218 | fclose($fp); |
||||||
219 | |||||||
220 | // Look for the version comment in the file header. |
||||||
221 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||||
222 | $version_info[$type][$entry] = $match[1]; |
||||||
223 | // It wasn't found, but the file was... show a '??'. |
||||||
224 | else |
||||||
225 | $version_info[$type][$entry] = '??'; |
||||||
226 | } |
||||||
227 | } |
||||||
228 | $this_dir->close(); |
||||||
229 | } |
||||||
230 | |||||||
231 | // Load up all the files in the default language directory and sort by language. |
||||||
232 | $this_dir = dir($lang_dir); |
||||||
233 | while ($entry = $this_dir->read()) |
||||||
234 | { |
||||||
235 | if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry)) |
||||||
236 | { |
||||||
237 | // Read the first 768 bytes from the file.... enough for the header. |
||||||
238 | $fp = fopen($lang_dir . '/' . $entry, 'rb'); |
||||||
239 | $header = fread($fp, 768); |
||||||
240 | fclose($fp); |
||||||
241 | |||||||
242 | // Split the file name off into useful bits. |
||||||
243 | list ($name, $language) = explode('.', $entry); |
||||||
244 | |||||||
245 | // Look for the version comment in the file header. |
||||||
246 | if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) |
||||||
247 | $version_info['default_language_versions'][$language][$name] = $match[1]; |
||||||
248 | // It wasn't found, but the file was... show a '??'. |
||||||
249 | else |
||||||
250 | $version_info['default_language_versions'][$language][$name] = '??'; |
||||||
251 | } |
||||||
252 | } |
||||||
253 | $this_dir->close(); |
||||||
254 | |||||||
255 | // Sort the file versions by filename. |
||||||
256 | if (!empty($versionOptions['sort_results'])) |
||||||
257 | { |
||||||
258 | ksort($version_info['file_versions']); |
||||||
259 | ksort($version_info['default_template_versions']); |
||||||
260 | ksort($version_info['template_versions']); |
||||||
261 | ksort($version_info['default_language_versions']); |
||||||
262 | ksort($version_info['tasks_versions']); |
||||||
263 | |||||||
264 | // For languages sort each language too. |
||||||
265 | foreach ($version_info['default_language_versions'] as $language => $dummy) |
||||||
266 | ksort($version_info['default_language_versions'][$language]); |
||||||
267 | } |
||||||
268 | return $version_info; |
||||||
269 | } |
||||||
270 | |||||||
271 | /** |
||||||
272 | * Update the Settings.php file. |
||||||
273 | * |
||||||
274 | * The most important function in this file for mod makers happens to be the |
||||||
275 | * updateSettingsFile() function, but it shouldn't be used often anyway. |
||||||
276 | * |
||||||
277 | * - updates the Settings.php file with the changes supplied in config_vars. |
||||||
278 | * - expects config_vars to be an associative array, with the keys as the |
||||||
279 | * variable names in Settings.php, and the values the variable values. |
||||||
280 | * - does not escape or quote values. |
||||||
281 | * - preserves case, formatting, and additional options in file. |
||||||
282 | * - writes nothing if the resulting file would be less than 10 lines |
||||||
283 | * in length (sanity check for read lock.) |
||||||
284 | * - check for changes to db_last_error and passes those off to a separate handler |
||||||
285 | * - attempts to create a backup file and will use it should the writing of the |
||||||
286 | * new settings file fail |
||||||
287 | * |
||||||
288 | * @param array $config_vars An array of one or more variables to update |
||||||
289 | */ |
||||||
290 | function updateSettingsFile($config_vars) |
||||||
291 | { |
||||||
292 | global $boarddir, $cachedir, $context; |
||||||
293 | |||||||
294 | // Updating the db_last_error, then don't mess around with Settings.php |
||||||
295 | if (count($config_vars) === 1 && isset($config_vars['db_last_error'])) |
||||||
296 | { |
||||||
297 | updateDbLastError($config_vars['db_last_error']); |
||||||
0 ignored issues
–
show
The call to
updateDbLastError() has too many arguments starting with $config_vars['db_last_error'] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
298 | return; |
||||||
299 | } |
||||||
300 | |||||||
301 | // When was Settings.php last changed? |
||||||
302 | $last_settings_change = filemtime($boarddir . '/Settings.php'); |
||||||
303 | |||||||
304 | // Load the settings file. |
||||||
305 | $settingsArray = trim(file_get_contents($boarddir . '/Settings.php')); |
||||||
306 | |||||||
307 | // Break it up based on \r or \n, and then clean out extra characters. |
||||||
308 | if (strpos($settingsArray, "\n") !== false) |
||||||
309 | $settingsArray = explode("\n", $settingsArray); |
||||||
310 | elseif (strpos($settingsArray, "\r") !== false) |
||||||
311 | $settingsArray = explode("\r", $settingsArray); |
||||||
312 | else |
||||||
313 | return; |
||||||
314 | |||||||
315 | // Presumably, the file has to have stuff in it for this function to be called :P. |
||||||
316 | if (count($settingsArray) < 10) |
||||||
317 | return; |
||||||
318 | |||||||
319 | // remove any /r's that made there way in here |
||||||
320 | foreach ($settingsArray as $k => $dummy) |
||||||
321 | $settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n"; |
||||||
322 | |||||||
323 | // go line by line and see whats changing |
||||||
324 | for ($i = 0, $n = count($settingsArray); $i < $n; $i++) |
||||||
325 | { |
||||||
326 | // Don't trim or bother with it if it's not a variable. |
||||||
327 | if (substr($settingsArray[$i], 0, 1) != '$') |
||||||
328 | continue; |
||||||
329 | |||||||
330 | $settingsArray[$i] = trim($settingsArray[$i]) . "\n"; |
||||||
331 | |||||||
332 | // Look through the variables to set.... |
||||||
333 | foreach ($config_vars as $var => $val) |
||||||
334 | { |
||||||
335 | // be sure someone is not updating db_last_error this with a group |
||||||
336 | if ($var === 'db_last_error') |
||||||
337 | { |
||||||
338 | updateDbLastError($val); |
||||||
339 | unset($config_vars[$var]); |
||||||
340 | } |
||||||
341 | elseif (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0) |
||||||
342 | { |
||||||
343 | $comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#'); |
||||||
344 | $settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n"; |
||||||
345 | |||||||
346 | // This one's been 'used', so to speak. |
||||||
347 | unset($config_vars[$var]); |
||||||
348 | } |
||||||
349 | } |
||||||
350 | |||||||
351 | // End of the file ... maybe |
||||||
352 | if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>') |
||||||
353 | $end = $i; |
||||||
354 | } |
||||||
355 | |||||||
356 | // This should never happen, but apparently it is happening. |
||||||
357 | if (empty($end) || $end < 10) |
||||||
358 | $end = count($settingsArray) - 1; |
||||||
359 | |||||||
360 | // Still more variables to go? Then lets add them at the end. |
||||||
361 | if (!empty($config_vars)) |
||||||
362 | { |
||||||
363 | if (trim($settingsArray[$end]) == '?' . '>') |
||||||
364 | $settingsArray[$end++] = ''; |
||||||
365 | else |
||||||
366 | $end++; |
||||||
367 | |||||||
368 | // Add in any newly defined vars that were passed |
||||||
369 | foreach ($config_vars as $var => $val) |
||||||
370 | $settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n"; |
||||||
371 | |||||||
372 | $settingsArray[$end] = '?' . '>'; |
||||||
373 | } |
||||||
374 | else |
||||||
375 | $settingsArray[$end] = trim($settingsArray[$end]); |
||||||
376 | |||||||
377 | // Sanity error checking: the file needs to be at least 12 lines. |
||||||
378 | if (count($settingsArray) < 12) |
||||||
379 | return; |
||||||
380 | |||||||
381 | // Try to avoid a few pitfalls: |
||||||
382 | // - like a possible race condition, |
||||||
383 | // - or a failure to write at low diskspace |
||||||
384 | // |
||||||
385 | // Check before you act: if cache is enabled, we can do a simple write test |
||||||
386 | // to validate that we even write things on this filesystem. |
||||||
387 | if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache')) |
||||||
388 | $cachedir = $boarddir . '/cache'; |
||||||
389 | |||||||
390 | $test_fp = @fopen($cachedir . '/settings_update.tmp', "w+"); |
||||||
391 | if ($test_fp) |
||||||
0 ignored issues
–
show
|
|||||||
392 | { |
||||||
393 | fclose($test_fp); |
||||||
394 | $written_bytes = file_put_contents($cachedir . '/settings_update.tmp', 'test', LOCK_EX); |
||||||
395 | @unlink($cachedir . '/settings_update.tmp'); |
||||||
396 | |||||||
397 | if ($written_bytes !== 4) |
||||||
398 | { |
||||||
399 | // Oops. Low disk space, perhaps. Don't mess with Settings.php then. |
||||||
400 | // No means no. :P |
||||||
401 | return; |
||||||
402 | } |
||||||
403 | } |
||||||
404 | |||||||
405 | // Protect me from what I want! :P |
||||||
406 | clearstatcache(); |
||||||
407 | if (filemtime($boarddir . '/Settings.php') === $last_settings_change) |
||||||
408 | { |
||||||
409 | // save the old before we do anything |
||||||
410 | $settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php'); |
||||||
411 | $settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_bak.php') || filesize($boarddir . '/Settings_bak.php') === 0) : $settings_backup_fail; |
||||||
412 | |||||||
413 | // write out the new |
||||||
414 | $write_settings = implode('', $settingsArray); |
||||||
415 | $written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX); |
||||||
416 | |||||||
417 | // survey says ... |
||||||
418 | if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail) |
||||||
419 | { |
||||||
420 | // Well this is not good at all, lets see if we can save this |
||||||
421 | $context['settings_message'] = 'settings_error'; |
||||||
422 | |||||||
423 | if (file_exists($boarddir . '/Settings_bak.php')) |
||||||
424 | @copy($boarddir . '/Settings_bak.php', $boarddir . '/Settings.php'); |
||||||
425 | } |
||||||
426 | } |
||||||
427 | |||||||
428 | // Even though on normal installations the filemtime should prevent this being used by the installer incorrectly |
||||||
429 | // it seems that there are times it might not. So let's MAKE it dump the cache. |
||||||
430 | if (function_exists('opcache_invalidate')) |
||||||
431 | opcache_invalidate($boarddir . '/Settings.php', true); |
||||||
432 | } |
||||||
433 | |||||||
434 | /** |
||||||
435 | * Saves the time of the last db error for the error log |
||||||
436 | * - Done separately from updateSettingsFile to avoid race conditions |
||||||
437 | * which can occur during a db error |
||||||
438 | * - If it fails Settings.php will assume 0 |
||||||
439 | * |
||||||
440 | * @param int $time The timestamp of the last DB error |
||||||
441 | */ |
||||||
442 | function updateDbLastError($time) |
||||||
443 | { |
||||||
444 | global $boarddir, $cachedir; |
||||||
445 | |||||||
446 | // Write out the db_last_error file with the error timestamp |
||||||
447 | file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = ' . $time . ';' . "\n" . '?' . '>', LOCK_EX); |
||||||
448 | @touch($boarddir . '/' . 'Settings.php'); |
||||||
449 | } |
||||||
450 | |||||||
451 | /** |
||||||
452 | * Saves the admin's current preferences to the database. |
||||||
453 | */ |
||||||
454 | function updateAdminPreferences() |
||||||
455 | { |
||||||
456 | global $options, $context, $smcFunc, $settings, $user_info; |
||||||
457 | |||||||
458 | // This must exist! |
||||||
459 | if (!isset($context['admin_preferences'])) |
||||||
460 | return false; |
||||||
461 | |||||||
462 | // This is what we'll be saving. |
||||||
463 | $options['admin_preferences'] = $smcFunc['json_encode']($context['admin_preferences']); |
||||||
464 | |||||||
465 | // Just check we haven't ended up with something theme exclusive somehow. |
||||||
466 | $smcFunc['db_query']('', ' |
||||||
467 | DELETE FROM {db_prefix}themes |
||||||
468 | WHERE id_theme != {int:default_theme} |
||||||
469 | AND variable = {string:admin_preferences}', |
||||||
470 | array( |
||||||
471 | 'default_theme' => 1, |
||||||
472 | 'admin_preferences' => 'admin_preferences', |
||||||
473 | ) |
||||||
474 | ); |
||||||
475 | |||||||
476 | // Update the themes table. |
||||||
477 | $smcFunc['db_insert']('replace', |
||||||
478 | '{db_prefix}themes', |
||||||
479 | array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||||||
480 | array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']), |
||||||
481 | array('id_member', 'id_theme', 'variable') |
||||||
482 | ); |
||||||
483 | |||||||
484 | // Make sure we invalidate any cache. |
||||||
485 | cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0); |
||||||
486 | } |
||||||
487 | |||||||
488 | /** |
||||||
489 | * Send all the administrators a lovely email. |
||||||
490 | * - loads all users who are admins or have the admin forum permission. |
||||||
491 | * - uses the email template and replacements passed in the parameters. |
||||||
492 | * - sends them an email. |
||||||
493 | * |
||||||
494 | * @param string $template Which email template to use |
||||||
495 | * @param array $replacements An array of items to replace the variables in the template |
||||||
496 | * @param array $additional_recipients An array of arrays of info for additional recipients. Should have 'id', 'email' and 'name' for each. |
||||||
497 | */ |
||||||
498 | function emailAdmins($template, $replacements = array(), $additional_recipients = array()) |
||||||
499 | { |
||||||
500 | global $smcFunc, $sourcedir, $language, $modSettings; |
||||||
501 | |||||||
502 | // We certainly want this. |
||||||
503 | require_once($sourcedir . '/Subs-Post.php'); |
||||||
504 | |||||||
505 | // Load all members which are effectively admins. |
||||||
506 | require_once($sourcedir . '/Subs-Members.php'); |
||||||
507 | $members = membersAllowedTo('admin_forum'); |
||||||
508 | |||||||
509 | // Load their alert preferences |
||||||
510 | require_once($sourcedir . '/Subs-Notify.php'); |
||||||
511 | $prefs = getNotifyPrefs($members, 'announcements', true); |
||||||
512 | |||||||
513 | $request = $smcFunc['db_query']('', ' |
||||||
514 | SELECT id_member, member_name, real_name, lngfile, email_address |
||||||
515 | FROM {db_prefix}members |
||||||
516 | WHERE id_member IN({array_int:members})', |
||||||
517 | array( |
||||||
518 | 'members' => $members, |
||||||
519 | ) |
||||||
520 | ); |
||||||
521 | $emails_sent = array(); |
||||||
522 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||||
523 | { |
||||||
524 | if (empty($prefs[$row['id_member']]['announcements'])) |
||||||
525 | continue; |
||||||
526 | |||||||
527 | // Stick their particulars in the replacement data. |
||||||
528 | $replacements['IDMEMBER'] = $row['id_member']; |
||||||
529 | $replacements['REALNAME'] = $row['member_name']; |
||||||
530 | $replacements['USERNAME'] = $row['real_name']; |
||||||
531 | |||||||
532 | // Load the data from the template. |
||||||
533 | $emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); |
||||||
534 | |||||||
535 | // Then send the actual email. |
||||||
536 | sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1); |
||||||
537 | |||||||
538 | // Track who we emailed so we don't do it twice. |
||||||
539 | $emails_sent[] = $row['email_address']; |
||||||
540 | } |
||||||
541 | $smcFunc['db_free_result']($request); |
||||||
542 | |||||||
543 | // Any additional users we must email this to? |
||||||
544 | if (!empty($additional_recipients)) |
||||||
545 | foreach ($additional_recipients as $recipient) |
||||||
546 | { |
||||||
547 | if (in_array($recipient['email'], $emails_sent)) |
||||||
548 | continue; |
||||||
549 | |||||||
550 | $replacements['IDMEMBER'] = $recipient['id']; |
||||||
551 | $replacements['REALNAME'] = $recipient['name']; |
||||||
552 | $replacements['USERNAME'] = $recipient['name']; |
||||||
553 | |||||||
554 | // Load the template again. |
||||||
555 | $emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']); |
||||||
556 | |||||||
557 | // Send off the email. |
||||||
558 | sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1); |
||||||
559 | } |
||||||
560 | } |
||||||
561 | |||||||
562 | ?> |