Explorar o código

! Logging and error handling: a little step of refactoring of fatal errors (wip).
! Quick code documentation on Subs-Recent.php
! Cleaning fatal error messages.
! Code documentation (incomplete) for Themes.
! Admin search should load ManageLanguages as well.

Spuds %!s(int64=13) %!d(string=hai) anos
pai
achega
0e5ff1fc89

+ 1 - 1
Sources/Admin.php

@@ -720,7 +720,7 @@ function AdminSearchInternal()
 	// All the files we need to include.
 	$include_files = array(
 		'ManageSettings', 'ManageBoards', 'ManageNews', 'ManageAttachments', 'ManageCalendar', 'ManageMail', 'ManagePaid', 'ManagePermissions',
-		'ManagePosts', 'ManageRegistration', 'ManageSearch', 'ManageSearchEngines', 'ManageServer', 'ManageSmileys',
+		'ManagePosts', 'ManageRegistration', 'ManageSearch', 'ManageSearchEngines', 'ManageServer', 'ManageSmileys', 'ManageLanguages',
 	);
 	foreach ($include_files as $file)
 		require_once($sourcedir . '/' . $file . '.php');

+ 2 - 2
Sources/Display.php

@@ -483,7 +483,7 @@ function Display()
 	$context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
 	$context['is_approved'] = $topicinfo['approved'];
 
-	// We don't want to show the poll icon in the topic class here, so pretend it's not one.
+	// @todo Tricks? We don't want to show the poll icon in the topic class here, so pretend it's not one.
 	$context['is_poll'] = false;
 	determineTopicClass($context);
 
@@ -1058,7 +1058,7 @@ function Display()
 /**
  * Callback for the message display.
  * It actually gets and prepares the message context.
- * This functionb will start over from the beginning if reset is set to true, which is
+ * This function will start over from the beginning if reset is set to true, which is
  * useful for showing an index before or after the posts.
  * @param bool $reset, default false.
  */

+ 77 - 500
Sources/Errors.php

@@ -18,23 +18,6 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/**
- * Handle fatal errors - like connection errors or load average problems.
- * This calls show_db_error(), which is used for database connection error handling.
- * @todo when awake: clean up this terrible terrible ugliness.
- *
- * @param bool $loadavg - whether it's a load average problem...
- */
-function db_fatal_error($loadavg = false)
-{
-	global $sourcedir;
-
-	show_db_error($loadavg);
-
-	// Since we use "or db_fatal_error();" this is needed...
-	return false;
-}
-
 /**
  * Log an error, if the error logging is enabled.
  * filename and line should be __FILE__ and __LINE__, respectively.
@@ -119,307 +102,6 @@ function log_error($error_message, $error_type = 'general', $file = null, $line
 	return $error_message;
 }
 
-/**
- * This function logs an action in the respective log. (database log)
- * @example logAction('remove', array('starter' => $id_member_started));
- *
- * @param string $action
- * @param array $extra = array()
- * @param string $log_type, options 'moderate', 'admin', ...etc.
- */
-function logAction($action, $extra = array(), $log_type = 'moderate')
-{
-	global $modSettings, $user_info, $smcFunc, $sourcedir;
-
-	$log_types = array(
-		'moderate' => 1,
-		'user' => 2,
-		'admin' => 3,
-	);
-
-	if (!is_array($extra))
-		trigger_error('logAction(): data is not an array with action \'' . $action . '\'', E_USER_NOTICE);
-
-	// Pull out the parts we want to store separately, but also make sure that the data is proper
-	if (isset($extra['topic']))
-	{
-		if (!is_numeric($extra['topic']))
-			trigger_error('logAction(): data\'s topic is not a number', E_USER_NOTICE);
-		$topic_id = empty($extra['topic']) ? '0' : (int)$extra['topic'];
-		unset($extra['topic']);
-	}
-	else
-		$topic_id = '0';
-
-	if (isset($extra['message']))
-	{
-		if (!is_numeric($extra['message']))
-			trigger_error('logAction(): data\'s message is not a number', E_USER_NOTICE);
-		$msg_id = empty($extra['message']) ? '0' : (int)$extra['message'];
-		unset($extra['message']);
-	}
-	else
-		$msg_id = '0';
-
-	// Is there an associated report on this?
-	if (in_array($action, array('move', 'remove', 'split', 'merge')))
-	{
-		$request = $smcFunc['db_query']('', '
-			SELECT id_report
-			FROM {db_prefix}log_reported
-			WHERE {raw:column_name} = {int:reported}
-			LIMIT 1',
-			array(
-				'column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic',
-				'reported' => !empty($msg_id) ? $msg_id : $topic_id,
-		));
-
-		// Alright, if we get any result back, update open reports.
-		if ($smcFunc['db_num_rows']($request) > 0)
-		{
-			require_once($sourcedir . '/ModerationCenter.php');
-			updateSettings(array('last_mod_report_action' => time()));
-			recountOpenReports();
-		}
-		$smcFunc['db_free_result']($request);
-	}
-
-	// No point in doing anything else, if the log isn't even enabled.
-	if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type]))
-		return false;
-
-	if (isset($extra['member']) && !is_numeric($extra['member']))
-		trigger_error('logAction(): data\'s member is not a number', E_USER_NOTICE);
-
-	if (isset($extra['board']))
-	{
-		if (!is_numeric($extra['board']))
-			trigger_error('logAction(): data\'s board is not a number', E_USER_NOTICE);
-		$board_id = empty($extra['board']) ? '0' : (int)$extra['board'];
-		unset($extra['board']);
-	}
-	else
-		$board_id = '0';
-
-	if (isset($extra['board_to']))
-	{
-		if (!is_numeric($extra['board_to']))
-			trigger_error('logAction(): data\'s board_to is not a number', E_USER_NOTICE);
-		if (empty($board_id))
-		{
-			$board_id = empty($extra['board_to']) ? '0' : (int)$extra['board_to'];
-			unset($extra['board_to']);
-		}
-	}
-
-	$smcFunc['db_insert']('',
-		'{db_prefix}log_actions',
-		array(
-			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
-			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
-		),
-		array(
-			time(), $log_types[$log_type], $user_info['id'], $user_info['ip'], $action,
-			$board_id, $topic_id, $msg_id, serialize($extra),
-		),
-		array('id_action')
-	);
-
-	return $smcFunc['db_insert_id']('{db_prefix}log_actions', 'id_action');
-}
-
-/**
- * Debugging.
- */
-function db_debug_junk()
-{
-	global $context, $scripturl, $boarddir, $modSettings, $boarddir;
-	global $db_cache, $db_count, $db_show_debug, $cache_count, $cache_hits, $txt;
-
-	// Add to Settings.php if you want to show the debugging information.
-	if (!isset($db_show_debug) || $db_show_debug !== true || (isset($_GET['action']) && $_GET['action'] == 'viewquery') || WIRELESS)
-		return;
-
-	if (empty($_SESSION['view_queries']))
-		$_SESSION['view_queries'] = 0;
-	if (empty($context['debug']['language_files']))
-		$context['debug']['language_files'] = array();
-	if (empty($context['debug']['sheets']))
-		$context['debug']['sheets'] = array();
-
-	$files = get_included_files();
-	$total_size = 0;
-	for ($i = 0, $n = count($files); $i < $n; $i++)
-	{
-		if (file_exists($files[$i]))
-			$total_size += filesize($files[$i]);
-		$files[$i] = strtr($files[$i], array($boarddir => '.'));
-	}
-
-	$warnings = 0;
-	if (!empty($db_cache))
-	{
-		foreach ($db_cache as $q => $qq)
-		{
-			if (!empty($qq['w']))
-				$warnings += count($qq['w']);
-		}
-
-		$_SESSION['debug'] = &$db_cache;
-	}
-
-	// Gotta have valid HTML ;).
-	$temp = ob_get_contents();
-	if (function_exists('ob_clean'))
-		ob_clean();
-	else
-	{
-		ob_end_clean();
-		ob_start('ob_sessrewrite');
-	}
-
-	echo preg_replace('~</body>\s*</html>~', '', $temp), '
-<div class="smalltext" style="text-align: left; margin: 1ex;">
-	', $txt['debug_templates'], count($context['debug']['templates']), ': <em>', implode('</em>, <em>', $context['debug']['templates']), '</em>.<br />
-	', $txt['debug_subtemplates'], count($context['debug']['sub_templates']), ': <em>', implode('</em>, <em>', $context['debug']['sub_templates']), '</em>.<br />
-	', $txt['debug_language_files'], count($context['debug']['language_files']), ': <em>', implode('</em>, <em>', $context['debug']['language_files']), '</em>.<br />
-	', $txt['debug_stylesheets'], count($context['debug']['sheets']), ': <em>', implode('</em>, <em>', $context['debug']['sheets']), '</em>.<br />
-	', $txt['debug_files_included'], count($files), ' - ', round($total_size / 1024), $txt['debug_kb'], ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_include_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_include_info" style="display: none;"><em>', implode('</em>, <em>', $files), '</em></span>)<br />';
-
-	if (!empty($modSettings['cache_enable']) && !empty($cache_hits))
-	{
-		$entries = array();
-		$total_t = 0;
-		$total_s = 0;
-		foreach ($cache_hits as $cache_hit)
-		{
-			$entries[] = $cache_hit['d'] . ' ' . $cache_hit['k'] . ': ' . sprintf($txt['debug_cache_seconds_bytes'], comma_format($cache_hit['t'], 5), $cache_hit['s']);
-			$total_t += $cache_hit['t'];
-			$total_s += $cache_hit['s'];
-		}
-
-		echo '
-	', $txt['debug_cache_hits'], $cache_count, ': ', sprintf($txt['debug_cache_seconds_bytes_total'], comma_format($total_t, 5), comma_format($total_s)), ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_cache_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_cache_info" style="display: none;"><em>', implode('</em>, <em>', $entries), '</em></span>)<br />';
-	}
-
-	echo '
-	<a href="', $scripturl, '?action=viewquery" target="_blank" class="new_win">', $warnings == 0 ? sprintf($txt['debug_queries_used'], (int) $db_count) : sprintf($txt['debug_queries_used_and_warnings'], (int) $db_count, $warnings), '</a><br />
-	<br />';
-
-	if ($_SESSION['view_queries'] == 1 && !empty($db_cache))
-		foreach ($db_cache as $q => $qq)
-		{
-			$is_select = substr(trim($qq['q']), 0, 6) == 'SELECT' || preg_match('~^INSERT(?: IGNORE)? INTO \w+(?:\s+\([^)]+\))?\s+SELECT .+$~s', trim($qq['q'])) != 0;
-			// Temporary tables created in earlier queries are not explainable.
-			if ($is_select)
-			{
-				foreach (array('log_topics_unread', 'topics_posted_in', 'tmp_log_search_topics', 'tmp_log_search_messages') as $tmp)
-					if (strpos(trim($qq['q']), $tmp) !== false)
-					{
-						$is_select = false;
-						break;
-					}
-			}
-			// But actual creation of the temporary tables are.
-			elseif (preg_match('~^CREATE TEMPORARY TABLE .+?SELECT .+$~s', trim($qq['q'])) != 0)
-				$is_select = true;
-
-			// Make the filenames look a bit better.
-			if (isset($qq['f']))
-				$qq['f'] = preg_replace('~^' . preg_quote($boarddir, '~') . '~', '...', $qq['f']);
-
-			echo '
-	<strong>', $is_select ? '<a href="' . $scripturl . '?action=viewquery;qq=' . ($q + 1) . '#qq' . $q . '" target="_blank" class="new_win" style="text-decoration: none;">' : '', nl2br(str_replace("\t", '&nbsp;&nbsp;&nbsp;', htmlspecialchars(ltrim($qq['q'], "\n\r")))) . ($is_select ? '</a></strong>' : '</strong>') . '<br />
-	&nbsp;&nbsp;&nbsp;';
-			if (!empty($qq['f']) && !empty($qq['l']))
-				echo sprintf($txt['debug_query_in_line'], $qq['f'], $qq['l']);
-
-			if (isset($qq['s'], $qq['t']) && isset($txt['debug_query_which_took_at']))
-				echo sprintf($txt['debug_query_which_took_at'], round($qq['t'], 8), round($qq['s'], 8)) . '<br />';
-			elseif (isset($qq['t']))
-				echo sprintf($txt['debug_query_which_took'], round($qq['t'], 8)) . '<br />';
-			echo '
-	<br />';
-		}
-
-	echo '
-	<a href="' . $scripturl . '?action=viewquery;sa=hide">', $txt['debug_' . (empty($_SESSION['view_queries']) ? 'show' : 'hide') . '_queries'], '</a>
-</div></body></html>';
-}
-
-/**
- * Logs the last database error into a file.
- * Attempts to use the backup file first, to store the last database error
- * and only update Settings.php if the first was successful.
- */
-function updateLastDatabaseError()
-{
-	global $boarddir;
-
-	// Find out this way if we can even write things on this filesystem.
-	// In addition, store things first in the backup file
-
-	$last_settings_change = @filemtime($boarddir . '/Settings.php');
-
-	// Make sure the backup file is there...
-	$file = $boarddir . '/Settings_bak.php';
-	if ((!file_exists($file) || filesize($file) == 0) && !copy($boarddir . '/Settings.php', $file))
-			return false;
-
-	// ...and writable!
-	if (!is_writable($file))
-	{
-		chmod($file, 0755);
-		if (!is_writable($file))
-		{
-			chmod($file, 0775);
-			if (!is_writable($file))
-			{
-				chmod($file, 0777);
-				if (!is_writable($file))
-						return false;
-			}
-		}
-	}
-
-	// Put the new timestamp.
-	$data = file_get_contents($file);
-	$data = preg_replace('~\$db_last_error = \d+;~', '$db_last_error = ' . time() . ';', $data);
-
-	// Open the backup file for writing
-	if ($fp = @fopen($file, 'w'))
-	{
-		// Reset the file buffer.
-		set_file_buffer($fp, 0);
-
-		// Update the file.
-		$t = flock($fp, LOCK_EX);
-		$bytes = fwrite($fp, $data);
-		flock($fp, LOCK_UN);
-		fclose($fp);
-
-		// Was it a success?
-		// ...only relevant if we're still dealing with the same good ole' settings file.
-		clearstatcache();
-		if (($bytes == strlen($data)) && (filemtime($boarddir . '/Settings.php') === $last_settings_change))
-		{
-			// This is our new Settings file...
-			// At least this one is an atomic operation
-			@copy($file, $boarddir . '/Settings.php');
-			return true;
-		}
-		else
-		{
-			// Oops. Someone might have been faster
-			// or we have no more disk space left, troubles, troubles...
-			// Copy the file back and run for your life!
-			@copy($boarddir . '/Settings.php', $file);
-		}
-	}
-
-	return false;
-}
-
 /**
  * An irrecoverable error. This function stops execution and displays an error message.
  * It logs the error message if $log is specified.
@@ -633,49 +315,16 @@ function setup_fatal_error_context($error_message)
 }
 
 /**
- * Show an error message for the connection problems... or load average.
- * It is called by db_fatal_error() function.
+ * Show a message for the (full block) maintenance mode.
  * It shows a complete page independent of language files or themes.
- * It is used only if there's no way to connect to the database or the load averages
- * are too high to do so.
+ * It is used only if $maintenance = 2 in Settings.php.
  * It stops further execution of the script.
- * @param bool $loadavg - whether it's a load average problem...
  */
-function show_db_error($loadavg = false)
+function display_maintenance_message()
 {
-	global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings;
-	global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
+	global $maintenance, $mtitle, $mmessage;
 
-	// Don't cache this page!
-	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-	header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
-	header('Cache-Control: no-cache');
-
-	// Send the right error codes.
-	header('HTTP/1.1 503 Service Temporarily Unavailable');
-	header('Status: 503 Service Temporarily Unavailable');
-	header('Retry-After: 3600');
-
-	if ($loadavg == false)
-	{
-		// For our purposes, we're gonna want this on if at all possible.
-		$modSettings['cache_enable'] = '1';
-
-		if (($temp = cache_get_data('db_last_error', 600)) !== null)
-			$db_last_error = max($db_last_error, $temp);
-
-		if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
-		{
-			// Avoid writing to the Settings.php file if at all possible; use shared memory instead.
-			cache_put_data('db_last_error', time(), 600);
-			if (($temp = cache_get_data('db_last_error', 600)) == null)
-				updateLastDatabaseError();
-
-			// Language files aren't loaded yet :(.
-			$db_error = @$smcFunc['db_error']($db_connection);
-			@mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
-		}
-	}
+	set_fatal_error_headers();
 
 	if (!empty($maintenance))
 		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -689,22 +338,43 @@ function show_db_error($loadavg = false)
 		', $mmessage, '
 	</body>
 </html>';
-	// If this is a load average problem, display an appropriate message (but we still don't have language files!)
-	elseif ($loadavg)
-		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-	<head>
-		<meta name="robots" content="noindex" />
-		<title>Temporarily Unavailable</title>
-	</head>
-	<body>
-		<h3>Temporarily Unavailable</h3>
-		Due to high stress on the server the forum is temporarily unavailable.  Please try again later.
-	</body>
-</html>';
+
+	die();
+}
+
+/**
+ * Show an error message for the connection problems.
+ * It shows a complete page independent of language files or themes.
+ * It is used only if there's no way to connect to the database.
+ * It stops further execution of the script.
+ */
+function display_db_error()
+{
+	global $sourcedir, $mbname, $modSettings;
+	global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
+
+	set_fatal_error_headers();
+
+	// For our purposes, we're gonna want this on if at all possible.
+	$modSettings['cache_enable'] = '1';
+
+	if (($temp = cache_get_data('db_last_error', 600)) !== null)
+		$db_last_error = max($db_last_error, $temp);
+
+	if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
+	{
+		// Avoid writing to the Settings.php file if at all possible; use shared memory instead.
+		cache_put_data('db_last_error', time(), 600);
+		if (($temp = cache_get_data('db_last_error', 600)) == null)
+			logLastDatabaseError();
+
+		// Language files aren't loaded yet :(.
+		$db_error = @$smcFunc['db_error']($db_connection);
+		@mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
+	}
+
 	// What to do?  Language files haven't and can't be loaded yet...
-	else
-		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 	<head>
 		<meta name="robots" content="noindex" />
@@ -716,145 +386,52 @@ function show_db_error($loadavg = false)
 	</body>
 </html>';
 
-	die;
+	die();
 }
 
 /**
- * Put this user in the online log.
- *
- * @param bool $force = false
+ * Show an error message for load average blocking problems.
+ * It shows a complete page independent of language files or themes.
+ * It is used only if the load averages are too high to continue execution.
+ * It stops further execution of the script.
  */
-function writeLog($force = false)
+function display_loadavg_error()
 {
-	global $user_info, $user_settings, $context, $modSettings, $settings, $topic, $board, $smcFunc, $sourcedir;
-
-	// If we are showing who is viewing a topic, let's see if we are, and force an update if so - to make it accurate.
-	if (!empty($settings['display_who_viewing']) && ($topic || $board))
-	{
-		// Take the opposite approach!
-		$force = true;
-		// Don't update for every page - this isn't wholly accurate but who cares.
-		if ($topic)
-		{
-			if (isset($_SESSION['last_topic_id']) && $_SESSION['last_topic_id'] == $topic)
-				$force = false;
-			$_SESSION['last_topic_id'] = $topic;
-		}
-	}
-
-	// Are they a spider we should be tracking? Mode = 1 gets tracked on its spider check...
-	if (!empty($user_info['possibly_robot']) && !empty($modSettings['spider_mode']) && $modSettings['spider_mode'] > 1)
-	{
-		require_once($sourcedir . '/ManageSearchEngines.php');
-		logSpider();
-	}
-
-	// Don't mark them as online more than every so often.
-	if (!empty($_SESSION['log_time']) && $_SESSION['log_time'] >= (time() - 8) && !$force)
-		return;
-
-	if (!empty($modSettings['who_enabled']))
-	{
-		$serialized = $_GET + array('USER_AGENT' => $_SERVER['HTTP_USER_AGENT']);
-
-		// In the case of a dlattach action, session_var may not be set.
-		if (!isset($context['session_var']))
-			$context['session_var'] = $_SESSION['session_var'];
-
-		unset($serialized['sesc'], $serialized[$context['session_var']]);
-		$serialized = serialize($serialized);
-	}
-	else
-		$serialized = '';
-
-	// Guests use 0, members use their session ID.
-	$session_id = $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id();
-
-	// Grab the last all-of-SMF-specific log_online deletion time.
-	$do_delete = cache_get_data('log_online-update', 30) < time() - 30;
-
-	// If the last click wasn't a long time ago, and there was a last click...
-	if (!empty($_SESSION['log_time']) && $_SESSION['log_time'] >= time() - $modSettings['lastActive'] * 20)
-	{
-		if ($do_delete)
-		{
-			$smcFunc['db_query']('delete_log_online_interval', '
-				DELETE FROM {db_prefix}log_online
-				WHERE log_time < {int:log_time}
-					AND session != {string:session}',
-				array(
-					'log_time' => time() - $modSettings['lastActive'] * 60,
-					'session' => $session_id,
-				)
-			);
-
-			// Cache when we did it last.
-			cache_put_data('log_online-update', time(), 30);
-		}
-
-		$smcFunc['db_query']('', '
-			UPDATE {db_prefix}log_online
-			SET log_time = {int:log_time}, ip = IFNULL(INET_ATON({string:ip}), 0), url = {string:url}
-			WHERE session = {string:session}',
-			array(
-				'log_time' => time(),
-				'ip' => $user_info['ip'],
-				'url' => $serialized,
-				'session' => $session_id,
-			)
-		);
-
-		// Guess it got deleted.
-		if ($smcFunc['db_affected_rows']() == 0)
-			$_SESSION['log_time'] = 0;
-	}
-	else
-		$_SESSION['log_time'] = 0;
-
-	// Otherwise, we have to delete and insert.
-	if (empty($_SESSION['log_time']))
-	{
-		if ($do_delete || !empty($user_info['id']))
-			$smcFunc['db_query']('', '
-				DELETE FROM {db_prefix}log_online
-				WHERE ' . ($do_delete ? 'log_time < {int:log_time}' : '') . ($do_delete && !empty($user_info['id']) ? ' OR ' : '') . (empty($user_info['id']) ? '' : 'id_member = {int:current_member}'),
-				array(
-					'current_member' => $user_info['id'],
-					'log_time' => time() - $modSettings['lastActive'] * 60,
-				)
-			);
-
-		$smcFunc['db_insert']($do_delete ? 'ignore' : 'replace',
-			'{db_prefix}log_online',
-			array('session' => 'string', 'id_member' => 'int', 'id_spider' => 'int', 'log_time' => 'int', 'ip' => 'raw', 'url' => 'string'),
-			array($session_id, $user_info['id'], empty($_SESSION['id_robot']) ? 0 : $_SESSION['id_robot'], time(), 'IFNULL(INET_ATON(\'' . $user_info['ip'] . '\'), 0)', $serialized),
-			array('session')
-		);
-	}
-
-	// Mark your session as being logged.
-	$_SESSION['log_time'] = time();
+	// If this is a load average problem, display an appropriate message (but we still don't have language files!)
 
-	// Well, they are online now.
-	if (empty($_SESSION['timeOnlineUpdated']))
-		$_SESSION['timeOnlineUpdated'] = time();
+	set_fatal_error_headers();
 
-	// Set their login time, if not already done within the last minute.
-	if (SMF != 'SSI' && !empty($user_info['last_login']) && $user_info['last_login'] < time() - 60)
-	{
-		// Don't count longer than 15 minutes.
-		if (time() - $_SESSION['timeOnlineUpdated'] > 60 * 15)
-			$_SESSION['timeOnlineUpdated'] = time();
+	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+	<head>
+		<meta name="robots" content="noindex" />
+		<title>Temporarily Unavailable</title>
+	</head>
+	<body>
+		<h3>Temporarily Unavailable</h3>
+		Due to high stress on the server the forum is temporarily unavailable.  Please try again later.
+	</body>
+</html>';
 
-		$user_settings['total_time_logged_in'] += time() - $_SESSION['timeOnlineUpdated'];
-		updateMemberData($user_info['id'], array('last_login' => time(), 'member_ip' => $user_info['ip'], 'member_ip2' => $_SERVER['BAN_CHECK_IP'], 'total_time_logged_in' => $user_settings['total_time_logged_in']));
+	die();
+}
 
-		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
-			cache_put_data('user_settings-' . $user_info['id'], $user_settings, 60);
+/**
+ * Small utility function for fatal error pages.
+ * Used by display_db_error(), display_loadavg_error(),
+ * display_maintenance_message()
+ */
+function set_fatal_error_headers()
+{
+	// Don't cache this page!
+	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+	header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+	header('Cache-Control: no-cache');
 
-		$user_info['total_time_logged_in'] += time() - $_SESSION['timeOnlineUpdated'];
-		$_SESSION['timeOnlineUpdated'] = time();
-	}
+	// Send the right error codes.
+	header('HTTP/1.1 503 Service Temporarily Unavailable');
+	header('Status: 503 Service Temporarily Unavailable');
+	header('Retry-After: 3600');
 }
 
 ?>

+ 3 - 3
Sources/Load.php

@@ -44,7 +44,7 @@ function reloadSettings()
 		);
 		$modSettings = array();
 		if (!$request)
-			db_fatal_error();
+			display_db_error();
 		while ($row = $smcFunc['db_fetch_row']($request))
 			$modSettings[$row[0]] = $row[1];
 		$smcFunc['db_free_result']($request);
@@ -163,7 +163,7 @@ function reloadSettings()
 		}
 
 		if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
-			db_fatal_error(true);
+			display_loadavg_error();
 	}
 
 	// Is post moderation alive and well?
@@ -2359,7 +2359,7 @@ function loadDatabase()
 
 	// Safe guard here, if there isn't a valid connection lets put a stop to it.
 	if (!$db_connection)
-		db_fatal_error();
+		display_db_error();
 
 	// If in SSI mode fix up the prefix.
 	if (SMF == 'SSI')

+ 1 - 0
Sources/Profile-Modify.php

@@ -2681,6 +2681,7 @@ function profileSaveAvatarData(&$value)
 					return 'bad_avatar';
 				elseif ($modSettings['avatar_action_too_large'] == 'option_download_and_resize')
 				{
+					// @todo remove this if appropriate
 					require_once($sourcedir . '/Subs-Graphics.php');
 					if (downloadAvatar($profile_vars['avatar'], $memID, $modSettings['avatar_max_width_external'], $modSettings['avatar_max_height_external']))
 					{

+ 9 - 4
Sources/Subs-Boards.php

@@ -18,9 +18,6 @@ if (!defined('SMF'))
 	marking them read, collapsing categories, or quick moderation.  It defines
 	the following list of functions:
 
-	void markBoardsRead(array boards)
-		// !!!
-
 	void MarkRead()
 		// !!!
 
@@ -77,7 +74,12 @@ if (!defined('SMF'))
 		- the parent might be several levels higher than the child.
 */
 
-// Mark a board or multiple boards read.
+/**
+ * Mark a board or multiple boards read.
+ *
+ * @param array $boards
+ * @param bool $unread
+ */
 function markBoardsRead($boards, $unread = false)
 {
 	global $user_info, $modSettings, $smcFunc;
@@ -140,6 +142,9 @@ function markBoardsRead($boards, $unread = false)
 	}
 
 	// Get rid of useless log_topics data, because log_mark_read is better for it - even if marking unread - I think so...
+	// @todo look at this...
+	// The call to markBoardsRead() in Display() used to be simply
+	// marking log_boards (the previous query only)
 	$result = $smcFunc['db_query']('', '
 		SELECT MIN(id_topic)
 		FROM {db_prefix}log_topics

+ 2 - 2
Sources/Subs-Db-mysql.php

@@ -61,12 +61,12 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix,
 		if (!empty($db_options['non_fatal']))
 			return null;
 		else
-			db_fatal_error();
+			display_db_error();
 	}
 
 	// Select the database, unless told not to
 	if (empty($db_options['dont_select_db']) && !@mysql_select_db($db_name, $connection) && empty($db_options['non_fatal']))
-		db_fatal_error();
+		display_db_error();
 
 	// This makes it possible to have SMF automatically change the sql_mode and autocommit if needed.
 	if (isset($mysql_set_mode) && $mysql_set_mode === true)

+ 1 - 1
Sources/Subs-Db-postgresql.php

@@ -65,7 +65,7 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, &$db_prefix
 		}
 		else
 		{
-			db_fatal_error();
+			display_db_error();
 		}
 	}
 

+ 1 - 1
Sources/Subs-Db-sqlite.php

@@ -64,7 +64,7 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix,
 		if (!empty($db_options['non_fatal']))
 			return null;
 		else
-			db_fatal_error();
+			display_db_error();
 	}
 	$db_in_transact = false;
 

+ 1 - 0
Sources/Subs-Members.php

@@ -1372,6 +1372,7 @@ function populateDuplicateMembers(&$members)
 }
 
 // Generate a random validation code.
+// @todo Err. Whatcha doin' here.
 function generateValidationCode()
 {
 	global $smcFunc, $modSettings;

+ 12 - 6
Sources/Subs-Recent.php

@@ -1,6 +1,8 @@
 <?php
 
 /**
+ * This file contains a couple of functions for the latests posts on forum.
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -14,11 +16,11 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	!!!
-
-*/
-
-// Get the latest posts of a forum.
+/**
+ * Get the latest posts of a forum.
+ *
+ * @param array $latestPostOptions
+ */
 function getLastPosts($latestPostOptions)
 {
 	global $scripturl, $txt, $user_info, $modSettings, $smcFunc, $context;
@@ -89,7 +91,11 @@ function getLastPosts($latestPostOptions)
 	return $posts;
 }
 
-// Callback-function for the cache for getLastPosts().
+/**
+ * Callback-function for the cache for getLastPosts().
+ *
+ * @param array $latestPostOptions
+ */
 function cache_getLastPosts($latestPostOptions)
 {
 	return array(

+ 3 - 148
Sources/Subs.php

@@ -137,24 +137,12 @@ if (!defined('SMF'))
 		- used to parse PHP code from inside [code] and [php] tags.
 		- returns the code with highlighted HTML.
 
-	void writeLog(bool force = false)
-		// !!!
-
 	void redirectexit(string setLocation = '', bool use_refresh = false)
 		// !!!
 
 	void obExit(bool do_header = true, bool do_footer = do_header)
 		// !!!
 
-	int logAction($action, $extra = array())
-		// !!!
-
-	void trackStats($stats = array())
-		- caches statistics changes, and flushes them if you pass nothing.
-		- if '+' is used as a value, it will be incremented.
-		- does not actually commit the changes until the end of the page view.
-		- depends on the trackStats setting.
-
 	array url_image_size(string url)
 		- uses getimagesize() to determine the size of a file.
 		- attempts to connect to the server first so it won't time out.
@@ -178,9 +166,6 @@ if (!defined('SMF'))
 	void template_footer()
 		// !!!
 
-	void db_debug_junk()
-		// !!!
-
 	void getAttachmentFilename(string filename, int id_attach, bool new = true)
 		// !!!
 
@@ -2547,6 +2532,7 @@ function redirectexit($setLocation = '', $refresh = false)
 
 	// In case we have mail to send, better do that - as obExit doesn't always quite make it...
 	if (!empty($context['flush_mail']))
+		// @todo this relies on 'flush_mail' being only set in AddMailQueue itself... :\
 		AddMailQueue(true);
 
 	$add = preg_match('~^(ftp|http)[s]?://~', $setLocation) == 0 && substr($setLocation, 0, 6) != 'about:';
@@ -2616,6 +2602,7 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
 
 	// If we have mail to send, send it.
 	if (!empty($context['flush_mail']))
+		// @todo this relies on 'flush_mail' being only set in AddMailQueue itself... :\
 		AddMailQueue(true);
 
 	$do_header = $header === null ? !$header_done : $header;
@@ -2677,7 +2664,7 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
 
 			// (since this is just debugging... it's okay that it's after </html>.)
 			if (!isset($_REQUEST['xml']))
-				db_debug_junk();
+				displayDebug();
 		}
 	}
 
@@ -2713,138 +2700,6 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
 		exit;
 }
 
-/**
- * Track Statistics.
- *
- * @param array $stats = array()
- */
-function trackStats($stats = array())
-{
-	global $modSettings, $smcFunc;
-	static $cache_stats = array();
-
-	if (empty($modSettings['trackStats']))
-		return false;
-	if (!empty($stats))
-		return $cache_stats = array_merge($cache_stats, $stats);
-	elseif (empty($cache_stats))
-		return false;
-
-	$setStringUpdate = '';
-	$insert_keys = array();
-	$date = strftime('%Y-%m-%d', forum_time(false));
-	$update_parameters = array(
-		'current_date' => $date,
-	);
-	foreach ($cache_stats as $field => $change)
-	{
-		$setStringUpdate .= '
-			' . $field . ' = ' . ($change === '+' ? $field . ' + 1' : '{int:' . $field . '}') . ',';
-
-		if ($change === '+')
-			$cache_stats[$field] = 1;
-		else
-			$update_parameters[$field] = $change;
-		$insert_keys[$field] = 'int';
-	}
-
-	$smcFunc['db_query']('', '
-		UPDATE {db_prefix}log_activity
-		SET' . substr($setStringUpdate, 0, -1) . '
-		WHERE date = {date:current_date}',
-		$update_parameters
-	);
-	if ($smcFunc['db_affected_rows']() == 0)
-	{
-		$smcFunc['db_insert']('ignore',
-			'{db_prefix}log_activity',
-			array_merge($insert_keys, array('date' => 'date')),
-			array_merge($cache_stats, array($date)),
-			array('date')
-		);
-	}
-
-	// Don't do this again.
-	$cache_stats = array();
-
-	return true;
-}
-
-// Get the size of a specified image with better error handling.
-function url_image_size($url)
-{
-	global $sourcedir;
-
-	// Make sure it is a proper URL.
-	$url = str_replace(' ', '%20', $url);
-
-	// Can we pull this from the cache... please please?
-	if (($temp = cache_get_data('url_image_size-' . md5($url), 240)) !== null)
-		return $temp;
-	$t = microtime();
-
-	// Get the host to pester...
-	preg_match('~^\w+://(.+?)/(.*)$~', $url, $match);
-
-	// Can't figure it out, just try the image size.
-	if ($url == '' || $url == 'http://' || $url == 'https://')
-	{
-		return false;
-	}
-	elseif (!isset($match[1]))
-	{
-		$size = @getimagesize($url);
-	}
-	else
-	{
-		// Try to connect to the server... give it half a second.
-		$temp = 0;
-		$fp = @fsockopen($match[1], 80, $temp, $temp, 0.5);
-
-		// Successful?  Continue...
-		if ($fp != false)
-		{
-			// Send the HEAD request (since we don't have to worry about chunked, HTTP/1.1 is fine here.)
-			fwrite($fp, 'HEAD /' . $match[2] . ' HTTP/1.1' . "\r\n" . 'Host: ' . $match[1] . "\r\n" . 'User-Agent: PHP/SMF' . "\r\n" . 'Connection: close' . "\r\n\r\n");
-
-			// Read in the HTTP/1.1 or whatever.
-			$test = substr(fgets($fp, 11), -1);
-			fclose($fp);
-
-			// See if it returned a 404/403 or something.
-			if ($test < 4)
-			{
-				$size = @getimagesize($url);
-
-				// This probably means allow_url_fopen is off, let's try GD.
-				if ($size === false && function_exists('imagecreatefromstring'))
-				{
-					include_once($sourcedir . '/Subs-Package.php');
-
-					// It's going to hate us for doing this, but another request...
-					$image = @imagecreatefromstring(fetch_web_data($url));
-					if ($image !== false)
-					{
-						$size = array(imagesx($image), imagesy($image));
-						imagedestroy($image);
-					}
-				}
-			}
-		}
-	}
-
-	// If we didn't get it, we failed.
-	if (!isset($size))
-		$size = false;
-
-	// If this took a long time, we may never have to do it again, but then again we might...
-	if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $t)) > 0.8)
-		cache_put_data('url_image_size-' . md5($url), $size, 240);
-
-	// Didn't work.
-	return $size;
-}
-
 function determineTopicClass(&$topic_context)
 {
 	// Set topic class depending on locked status and number of replies.

+ 97 - 104
Sources/Themes.php

@@ -1,6 +1,26 @@
 <?php
 
 /**
+ * This file concerns itself almost completely with theme administration.
+ * Its tasks include changing theme settings, installing and removing
+ * themes, choosing the current theme, and editing themes.
+ *
+ * @todo Update this for the new package manager?
+ *
+ * Creating and distributing theme packages:
+ * 	There isn't that much required to package and distribute your own themes...
+ * just do the following:
+ * - create a theme_info.xml file, with the root element theme-info.
+ * - its name should go in a name element, just like description.
+ * - your name should go in author. (email in the email attribute.)
+ * - any support website for the theme should be in website.
+ * - layers and templates (non-default) should go in those elements ;).
+ * - if the images dir isn't images, specify in the images element.
+ * - any extra rows for themes should go in extra, serialized.
+ * (as in array(variable => value).)
+ * - tar and gzip the directory - and you're done!
+ * - please include any special license in a license.txt file.
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -14,104 +34,14 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	This file concerns itself almost completely with theme administration.
-	Its tasks include changing theme settings, installing and removing
-	themes, choosing the current theme, and editing themes.  This is done in:
-
-	void ThemesMain()
-		- manages the action and delegates control to the proper sub action.
-		- loads both the Themes and Settings language files.
-		- checks the session by GET or POST to verify the sent data.
-		- requires the user not be a guest.
-		- is accessed via ?action=admin;area=theme.
-
-	void ThemeAdmin()
-		- administrates themes and their settings, as well as global theme
-		  settings.
-		- sets the settings theme_allow, theme_guests, and knownThemes.
-		- loads the template Themes.
-		- requires the admin_forum permission.
-		- accessed with ?action=admin;area=theme;sa=admin.
-
-	void ThemeList()
-		- lists the available themes.
-		- provides an interface to reset the paths of all the installed themes.
-
-	void SetThemeOptions()
-		// !!!
-
-	void SetThemeSettings()
-		- saves and requests global theme settings. ($settings)
-		- loads the Admin language file.
-		- calls ThemeAdmin() if no theme is specified. (the theme center.)
-		- requires an administrator.
-		- accessed with ?action=admin;area=theme;sa=settings&th=xx.
-
-	void RemoveTheme()
-		- removes an installed theme.
-		- requires an administrator.
-		- accessed with ?action=admin;area=theme;sa=remove.
-
-	void PickTheme()
-		- allows user or administrator to pick a new theme with an interface.
-		- can edit everyone's (u = 0), guests' (u = -1), or a specific user's.
-		- uses the Themes template. (pick sub template.)
-		- accessed with ?action=admin;area=theme;sa=pick.
-
-	void ThemeInstall()
-		- installs new themes, either from a gzip or copy of the default.
-		- requires an administrator.
-		- puts themes in $boardurl/Themes.
-		- assumes the gzip has a root directory in it. (ie default.)
-		- accessed with ?action=admin;area=theme;sa=install.
-
-	void WrapAction()
-		- allows the theme to take care of actions.
-		- happens if $settings['catch_action'] is set and action isn't found
-		  in the action array.
-		- can use a template, layers, sub_template, filename, and/or function.
-
-	void SetJavaScript()
-		- sets a theme option without outputting anything.
-		- can be used with javascript, via a dummy image... (which doesn't
-		  require the page to reload.)
-		- requires someone who is logged in.
-		- accessed via ?action=jsoption;var=variable;val=value;session_var=sess_id.
-		- does not log access to the Who's Online log. (in index.php..)
-
-	void EditTheme()
-		- shows an interface for editing the templates.
-		- uses the Themes template and edit_template/edit_style sub template.
-		- accessed via ?action=admin;area=theme;sa=edit
-
-	function convert_template($output_dir, $old_template = '')
-		// !!!
-
-	function phpcodefix(string string)
-		// !!!
-
-	function makeStyleChanges(&$old_template)
-		// !!!
-
-	// !!! Update this for the new package manager?
-	Creating and distributing theme packages:
-	---------------------------------------------------------------------------
-		There isn't that much required to package and distribute your own
-		themes... just do the following:
-		- create a theme_info.xml file, with the root element theme-info.
-		- its name should go in a name element, just like description.
-		- your name should go in author. (email in the email attribute.)
-		- any support website for the theme should be in website.
-		- layers and templates (non-default) should go in those elements ;).
-		- if the images dir isn't images, specify in the images element.
-		- any extra rows for themes should go in extra, serialized.
-		   (as in array(variable => value).)
-		- tar and gzip the directory - and you're done!
-		- please include any special license in a license.txt file.
-	// !!! Thumbnail?
-*/
-
-// Subaction handler.
+/**
+ * Subaction handler - manages the action and delegates control to the proper
+ * sub-action.
+ * It loads both the Themes and Settings language files.
+ * Checks the session by GET or POST to verify the sent data.
+ * Requires the user not be a guest. (@todo what?)
+ * Accessed via ?action=admin;area=theme.
+ */
 function ThemesMain()
 {
 	global $txt, $context, $scripturl;
@@ -171,6 +101,16 @@ function ThemesMain()
 		$subActions['admin']();
 }
 
+/**
+ * This function allows administration of themes and their settings,
+ * as well as global theme settings.
+ *  - sets the settings theme_allow, theme_guests, and knownThemes.
+ *  - requires the admin_forum permission.
+ *  - accessed with ?action=admin;area=theme;sa=admin.
+ *
+ *  @uses Themes template
+ *  @uses Admin language file
+ */
 function ThemeAdmin()
 {
 	global $context, $boarddir, $modSettings, $smcFunc;
@@ -247,6 +187,10 @@ function ThemeAdmin()
 	}
 }
 
+/**
+ * This function lists the available themes and provides an interface to reset
+ * the paths of all the installed themes.
+ */
 function ThemeList()
 {
 	global $context, $boarddir, $boardurl, $smcFunc;
@@ -365,7 +309,9 @@ function ThemeList()
 	createToken('admin-tr', 'request');
 }
 
-// Administrative global settings.
+/**
+ * Administrative global settings.
+ */
 function SetThemeOptions()
 {
 	global $txt, $context, $settings, $modSettings, $smcFunc;
@@ -741,7 +687,14 @@ function SetThemeOptions()
 	createToken('admin-sto');
 }
 
-// Administrative global settings.
+/**
+ * Administrative global settings.
+ * - saves and requests global theme settings. ($settings)
+ * - loads the Admin language file.
+ * - calls ThemeAdmin() if no theme is specified. (the theme center.)
+ * - requires admin_forum permission.
+ * - accessed with ?action=admin;area=theme;sa=settings&th=xx.
+ */
 function SetThemeSettings()
 {
 	global $txt, $context, $settings, $modSettings, $sourcedir, $smcFunc;
@@ -913,7 +866,12 @@ function SetThemeSettings()
 	createToken('admin-sts');
 }
 
-// Remove a theme from the database.
+/**
+ * Remove a theme from the database.
+ * - removes an installed theme.
+ * - requires an administrator.
+ * - accessed with ?action=admin;area=theme;sa=remove.
+ */
 function RemoveTheme()
 {
 	global $modSettings, $context, $smcFunc;
@@ -976,7 +934,15 @@ function RemoveTheme()
 	redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id']);
 }
 
-// Choose a theme from a list.
+/**
+ * Choose a theme from a list.
+ * allows an user or administrator to pick a new theme with an interface.
+ * - can edit everyone's (u = 0), guests' (u = -1), or a specific user's.
+ * - uses the Themes template. (pick sub template.)
+ * - accessed with ?action=admin;area=theme;sa=pick.
+ * @todo thought so... Might be better to split this file in ManageThemes and Themes,
+ * with centralized admin permissions on ManageThemes.
+ */
 function PickTheme()
 {
 	global $txt, $context, $modSettings, $user_info, $language, $smcFunc, $settings, $scripturl;
@@ -1303,6 +1269,13 @@ function PickTheme()
 	$context['sub_template'] = 'pick';
 }
 
+/**
+ * Installs new themes, either from a gzip or copy of the default.
+ * - puts themes in $boardurl/Themes.
+ * - assumes the gzip has a root directory in it. (ie default.)
+ * Requires admin_forum.
+ * Accessed with ?action=admin;area=theme;sa=install.
+ */
 function ThemeInstall()
 {
 	global $sourcedir, $boarddir, $boardurl, $txt, $context, $settings, $modSettings, $smcFunc;
@@ -1590,7 +1563,14 @@ function ThemeInstall()
 	redirectexit('action=admin;area=theme;sa=install;theme_id=' . $id_theme . ';' . $context['session_var'] . '=' . $context['session_id']);
 }
 
-// Possibly the simplest and best example of how to ues the template system.
+/**
+ * Possibly the simplest and best example of how to use the template system.
+ *  - allows the theme to take care of actions.
+ *  - happens if $settings['catch_action'] is set and action isn't found
+ *   in the action array.
+ *  - can use a template, layers, sub_template, filename, and/or function.
+ * @todo look at this
+ */
 function WrapAction()
 {
 	global $context, $settings, $sourcedir;
@@ -1620,7 +1600,15 @@ function WrapAction()
 		$context['sub_template'] = $settings['catch_action']['sub_template'];
 }
 
-// Set an option via javascript.
+/**
+ * Set an option via javascript.
+ * - sets a theme option without outputting anything.
+ * - can be used with javascript, via a dummy image... (which doesn't require
+ * the page to reload.)
+ * - requires someone who is logged in.
+ * - accessed via ?action=jsoption;var=variable;val=value;session_var=sess_id.
+ * - does not log access to the Who's Online log. (in index.php..)
+ */
 function SetJavaScript()
 {
 	global $settings, $user_info, $smcFunc, $options;
@@ -1695,6 +1683,11 @@ function SetJavaScript()
 	redirectexit($settings['images_url'] . '/blank.gif');
 }
 
+/**
+ * Shows an interface for editing the templates.
+ * - uses the Themes template and edit_template/edit_style sub template.
+ * - accessed via ?action=admin;area=theme;sa=edit
+ */
 function EditTheme()
 {
 	global $context, $settings, $scripturl, $boarddir, $smcFunc;

+ 1 - 0
index.php

@@ -49,6 +49,7 @@ require_once($sourcedir . '/QueryString.php');
 require_once($sourcedir . '/Session.php');
 require_once($sourcedir . '/Subs.php');
 require_once($sourcedir . '/Errors.php');
+require_once($sourcedir . '/Logging.php');
 require_once($sourcedir . '/Load.php');
 require_once($sourcedir . '/Security.php');