Selaa lähdekoodia

Merge branch 'release-2.1' of git://github.com/SimpleMachines/SMF2.1.git into cust_fields

Suki 10 vuotta sitten
vanhempi
commit
ee3dad93d5
89 muutettua tiedostoa jossa 2918 lisäystä ja 987 poistoa
  1. 4 1
      Sources/Admin.php
  2. 2 0
      Sources/BoardIndex.php
  3. 13 5
      Sources/Display.php
  4. 3 2
      Sources/Errors.php
  5. 4 1
      Sources/Groups.php
  6. 8 8
      Sources/Load.php
  7. 5 0
      Sources/LogInOut.php
  8. 2 0
      Sources/ManageAttachments.php
  9. 1 1
      Sources/ManageBans.php
  10. 1 0
      Sources/ManageBoards.php
  11. 2 0
      Sources/ManageMaintenance.php
  12. 2 0
      Sources/ManageMembergroups.php
  13. 2 0
      Sources/ManageNews.php
  14. 2 0
      Sources/ManagePaid.php
  15. 2 0
      Sources/ManagePermissions.php
  16. 2 0
      Sources/ManageRegistration.php
  17. 1 0
      Sources/ManageSmileys.php
  18. 5 2
      Sources/MessageIndex.php
  19. 206 143
      Sources/ModerationCenter.php
  20. 13 5
      Sources/Modlog.php
  21. 6 0
      Sources/PackageGet.php
  22. 4 1
      Sources/Packages.php
  23. 2 0
      Sources/PersonalMessage.php
  24. 9 0
      Sources/Profile-Modify.php
  25. 2 0
      Sources/Profile-View.php
  26. 12 0
      Sources/Profile.php
  27. 1 0
      Sources/Recent.php
  28. 6 0
      Sources/Register.php
  29. 5 0
      Sources/Reminder.php
  30. 227 197
      Sources/ReportToMod.php
  31. 543 0
      Sources/ReportedPosts.php
  32. 3 0
      Sources/Search.php
  33. 13 37
      Sources/Security.php
  34. 1 0
      Sources/Stats.php
  35. 5 0
      Sources/Subs-Auth.php
  36. 529 0
      Sources/Subs-ReportedPosts.php
  37. 28 5
      Sources/Subs.php
  38. 135 0
      Sources/tasks/MemberReport-Notify.php
  39. 161 0
      Sources/tasks/MemberReportReply-Notify.php
  40. 0 1
      Themes/default/Admin.template.php
  41. 0 1
      Themes/default/BoardIndex.template.php
  42. 4 16
      Themes/default/Display.template.php
  43. 2 2
      Themes/default/Errors.template.php
  44. 2 2
      Themes/default/GenericControls.template.php
  45. 5 5
      Themes/default/Help.template.php
  46. 3 3
      Themes/default/Likes.template.php
  47. 5 6
      Themes/default/Login.template.php
  48. 0 2
      Themes/default/ManageBans.template.php
  49. 0 1
      Themes/default/ManageBoards.template.php
  50. 0 1
      Themes/default/ManageMaintenance.template.php
  51. 0 2
      Themes/default/ManageMembergroups.template.php
  52. 0 1
      Themes/default/ManageNews.template.php
  53. 0 1
      Themes/default/ManagePaid.template.php
  54. 1 1
      Themes/default/ManageSmileys.template.php
  55. 0 1
      Themes/default/MessageIndex.template.php
  56. 256 170
      Themes/default/ModerationCenter.template.php
  57. 106 105
      Themes/default/Packages.template.php
  58. 2 4
      Themes/default/PersonalMessage.template.php
  59. 6 6
      Themes/default/Post.template.php
  60. 13 33
      Themes/default/Profile.template.php
  61. 2 4
      Themes/default/Register.template.php
  62. 1 3
      Themes/default/Reminder.template.php
  63. 105 0
      Themes/default/ReportToMod.template.php
  64. 317 0
      Themes/default/ReportedPosts.template.php
  65. 2 2
      Themes/default/Reports.template.php
  66. 0 1
      Themes/default/Search.template.php
  67. 0 179
      Themes/default/SendTopic.template.php
  68. 0 1
      Themes/default/Stats.template.php
  69. 2 2
      Themes/default/Wireless.template.php
  70. 4 4
      Themes/default/css/index.css
  71. 2 0
      Themes/default/languages/Admin.english.php
  72. 2 0
      Themes/default/languages/Alerts.english.php
  73. 36 0
      Themes/default/languages/EmailTemplates.english.php
  74. 3 0
      Themes/default/languages/ManagePermissions.english.php
  75. 1 0
      Themes/default/languages/ManageSmileys.english.php
  76. 24 3
      Themes/default/languages/ModerationCenter.english.php
  77. 6 0
      Themes/default/languages/Modlog.english.php
  78. 5 0
      Themes/default/languages/Post.english.php
  79. 6 1
      Themes/default/languages/Profile.english.php
  80. 4 10
      Themes/default/languages/index.english.php
  81. 1 2
      index.php
  82. 2 2
      other/install.php
  83. 1 0
      other/install_2-1_mysql.sql
  84. 1 1
      other/install_2-1_postgresql.sql
  85. 1 0
      other/install_2-1_sqlite.sql
  86. 1 0
      other/install_2-1_sqlite3.sql
  87. 4 0
      other/upgrade_2-1_mysql.sql
  88. 4 0
      other/upgrade_2-1_postgresql.sql
  89. 4 0
      other/upgrade_2-1_sqlite.sql

+ 4 - 1
Sources/Admin.php

@@ -29,7 +29,7 @@ function AdminMain()
 	// Load the language and templates....
 	loadLanguage('Admin');
 	loadTemplate('Admin', 'admin');
-	loadJavascriptFile('admin.js', array('default_theme' => true), 'admin.js');
+	loadJavascriptFile('admin.js', array('default_theme' => true), 'smf_admin');
 
 	// No indexing evil stuff.
 	$context['robot_no_index'] = true;
@@ -561,6 +561,9 @@ function AdminHome()
 		'http://www.simplemachines.org/redirect/smf_support',
 		'http://www.simplemachines.org/redirect/customize_support'
 	);
+
+	if ($context['admin_area'] == 'admin')
+		loadJavascriptFile('admin.js', array('default_theme' => true, 'defer' => false), 'smf_admin');
 }
 
 /**

+ 2 - 0
Sources/BoardIndex.php

@@ -130,6 +130,8 @@ function BoardIndex()
 
 	// Allow mods to add additional buttons here
 	call_integration_hook('integrate_mark_read_button');
+
+	loadJavascriptFile('fader.js', array('default_theme' => true, 'defer' => false), 'smf_fader');
 }
 
 /**

+ 13 - 5
Sources/Display.php

@@ -378,9 +378,6 @@ function Display()
 	// Did we report a post to a moderator just now?
 	$context['report_sent'] = isset($_GET['reportsent']);
 
-	// Did we send this topic to a friend?
-	$context['topic_sent'] = isset($_GET['topicsent']);
-
 	// Let's get nosey, who is viewing this topic?
 	if (!empty($settings['display_who_viewing']))
 	{
@@ -1140,7 +1137,7 @@ function Display()
 		$context['wireless_moderate'] = isset($_GET['moderate']) ? ';moderate' : '';
 	}
 
-	// You can't link an existing topoic to the calendar unless you can modify the first post...
+	// You can't link an existing topic to the calendar unless you can modify the first post...
 	$context['calendar_post'] &= allowedTo('modify_any') || (allowedTo('modify_own') && $context['user']['started']);
 
 	// Load up the "double post" sequencing magic.
@@ -1211,6 +1208,17 @@ function Display()
 	call_integration_hook('integrate_display_buttons', array(&$context['normal_buttons']));
 	// Note: integrate_mod_buttons is no more necessary and deprecated, but is kept for backward compatibility with 2.0
 	call_integration_hook('integrate_mod_buttons', array(&$context['mod_buttons']));
+
+	// Load the drafts js file
+	if ($context['drafts_autosave'])
+		loadJavascriptFile('drafts.js', array('default_theme' => true, 'defer' => false), 'smf_drafts');
+
+	// Spellcheck
+	if ($context['show_spellchecking'])
+		loadJavascriptFile('spellcheck.js', array('default_theme' => true, 'defer' => false), 'smf_spellcheck');
+
+	// topic.js
+	loadJavascriptFile('topic.js', array('default_theme' => true, 'defer' => false), 'smf_topic');
 }
 
 /**
@@ -1289,7 +1297,7 @@ function prepareDisplayContext($reset = false)
 		$memberContext[$message['id_member']]['group'] = $txt['guest_title'];
 		$memberContext[$message['id_member']]['link'] = $message['poster_name'];
 		$memberContext[$message['id_member']]['email'] = $message['poster_email'];
-		$memberContext[$message['id_member']]['show_email'] = showEmailAddress(true, 0);
+		$memberContext[$message['id_member']]['show_email'] = allowedTo('admin_forum');
 		$memberContext[$message['id_member']]['is_guest'] = true;
 	}
 	else

+ 3 - 2
Sources/Errors.php

@@ -374,8 +374,9 @@ function display_maintenance_message()
 function display_db_error()
 {
 	global $mbname, $modSettings, $maintenance;
-	global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
+	global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc, $sourcedir;
 
+	require_once($sourcedir . '/Logging.php');
 	set_fatal_error_headers();
 
 	// For our purposes, we're gonna want this on if at all possible.
@@ -457,4 +458,4 @@ function set_fatal_error_headers()
 	header('Retry-After: 3600');
 }
 
-?>
+?>

+ 4 - 1
Sources/Groups.php

@@ -434,6 +434,9 @@ function MembergroupMembers()
 	$context['sub_template'] = 'group_members';
 	$context['page_title'] = $txt['membergroups_members_title'] . ': ' . $context['group']['name'];
 	createToken('mod-mgm');
+
+	if ($context['group']['assignable'])
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 }
 
 /**
@@ -804,7 +807,7 @@ function list_getGroupRequestCount($where, $where_parameters)
  * @param int $start The result to start with
  * @param int $items_per_page The number of items per page
  * @param string $sort An SQL sort expression (column/direction)
- * @param string $where Data for the WHERE clause 
+ * @param string $where Data for the WHERE clause
  * @param string $where_parameters Parameter values to be inerted into the WHERE clause
  * @return array An array of group requests
  * Each group request has:

+ 8 - 8
Sources/Load.php

@@ -1140,7 +1140,7 @@ function loadMemberContext($user, $display_custom_fields = false)
 		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
 		'link' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '">' . $profile['real_name'] . '</a>',
 		'email' => $profile['email_address'],
-		'show_email' => showEmailAddress(!empty($profile['hide_email']), $profile['id_member']),
+		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
 		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
 		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
 	);
@@ -1811,7 +1811,7 @@ function loadTheme($id_theme = 0, $initialize = true)
 	loadJavascriptFile('smf_jquery_plugins.js', array('default_theme' => true));
 
 	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
-	loadJavascriptFile('script.js', array('default_theme' => true), 'smf_scripts');
+	loadJavascriptFile('script.js', array('default_theme' => true, 'defer' => false), 'smf_scripts');
 	loadJavascriptFile('theme.js', array(), 'theme_scripts');
 
 	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
@@ -1940,7 +1940,7 @@ function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
 			loadLanguage('Errors');
 			echo '
 <div class="alert errorbox">
-	<a href="', $scripturl . '?action=admin;area=theme;sa=list;th=1;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
+	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
 </div>';
 		}
 
@@ -2007,9 +2007,9 @@ function loadSubTemplate($sub_template_name, $fatal = false)
  */
 function loadCSSFile($filename, $params = array(), $id = '')
 {
-	global $settings, $context;
+	global $settings, $context, $modSettings;
 
-	$params['seed'] = (!isset($params['seed']) || $params['seed'] === true) ? '?alph21' : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
+	$params['seed'] = (!isset($params['seed']) || $params['seed'] === true) ? $modSettings['browser_cache'] : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
 	$params['force_current'] = !empty($params['force_current']) ? $params['force_current'] : false;
 	$theme = !empty($params['default_theme']) ? 'default_theme' : 'theme';
 
@@ -2056,13 +2056,13 @@ function loadCSSFile($filename, $params = array(), $id = '')
  *  - ['validate'] (true/false): if true script will validate the local file exists
  *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
  *
- * @param string $id An ID to stik on the end of the filename
+ * @param string $id An ID to stick on the end of the filename
  */
 function loadJavascriptFile($filename, $params = array(), $id = '')
 {
-	global $settings, $context;
+	global $settings, $context, $modSettings;
 
-	$params['seed'] = (!isset($params['seed']) || $params['seed'] === true) ? '?alph21' : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
+	$params['seed'] = (!isset($params['seed']) || $params['seed'] === true) ? $modSettings['browser_cache'] : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
 	$params['force_current'] = !empty($params['force_current']) ? $params['force_current'] : false;
 	$theme = !empty($params['default_theme']) ? 'default_theme' : 'theme';
 

+ 5 - 0
Sources/LogInOut.php

@@ -42,6 +42,7 @@ function Login()
 	{
 		loadLanguage('Login');
 		loadTemplate('Login');
+
 		$context['sub_template'] = 'login';
 
 		if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
@@ -69,6 +70,9 @@ function Login()
 	else
 		unset($_SESSION['login_url']);
 
+	// Need some js goodies.
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
+
 	// Create a one time token.
 	createToken('login');
 }
@@ -173,6 +177,7 @@ function Login2()
 		$context['sub_template'] = 'login';
 	}
 
+
 	// Set up the default/fallback stuff.
 	$context['default_username'] = isset($_POST['user']) ? preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($_POST['user'])) : '';
 	$context['default_password'] = '';

+ 2 - 0
Sources/ManageAttachments.php

@@ -284,6 +284,7 @@ function ManageAvatarSettings($return_config = false)
 		array('title', 'avatar_server_stored'),
 			array('warning', empty($testImg) ? 'avatar_img_enc_warning' : ''),
 			array('permissions', 'profile_server_avatar', 0, $txt['avatar_server_stored_groups']),
+			array('warning', !$context['valid_avatar_dir'] ? 'avatar_directory_wrong' : ''),
 			array('text', 'avatar_directory', 40, 'invalid' => !$context['valid_avatar_dir']),
 			array('text', 'avatar_url', 40),
 		// External avatars?
@@ -311,6 +312,7 @@ function ManageAvatarSettings($return_config = false)
 			array('warning', 'avatar_paranoid_warning'),
 			array('check', 'avatar_paranoid'),
 		'',
+			array('warning', !$context['valid_custom_avatar_dir'] ? 'custom_avatar_dir_wrong' : ''),
 			array('text', 'custom_avatar_dir', 40, 'subtext' => $txt['custom_avatar_dir_desc'], 'invalid' => !$context['valid_custom_avatar_dir']),
 			array('text', 'custom_avatar_url', 40),
 	);

+ 1 - 1
Sources/ManageBans.php

@@ -1641,7 +1641,7 @@ function BanEditTrigger()
 		redirectexit('action=admin;area=ban;sa=edit' . (!empty($ban_group) ? ';bg=' . $ban_group : ''));
 	}
 
-	loadJavascriptFile('suggest.js', array('default_theme' => true), 'suggest.js');
+	loadJavascriptFile('suggest.js', array('default_theme' => true), 'smf_suggest');
 
 	if (empty($ban_id))
 	{

+ 1 - 0
Sources/ManageBoards.php

@@ -596,6 +596,7 @@ function EditBoard()
 	{
 		$context['sub_template'] = 'modify_board';
 		$context['page_title'] = $txt['boardsEdit'];
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 	}
 	else
 	{

+ 2 - 0
Sources/ManageMaintenance.php

@@ -197,6 +197,8 @@ function MaintainMembers()
 
 	if (isset($_GET['done']) && $_GET['done'] == 'recountposts')
 		$context['maintenance_finished'] = $txt['maintain_recountposts'];
+
+	loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 }
 
 /**

+ 2 - 0
Sources/ManageMembergroups.php

@@ -1172,6 +1172,8 @@ function EditMembergroup()
 	if (!empty($context['possible_icons']))
 		loadJavascriptFile('icondropdown.js', array('validate' => true));
 
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
+
 	// Finally, get all the groups this could be inherited off.
 	$request = $smcFunc['db_query']('', '
 		SELECT id_group, group_name

+ 2 - 0
Sources/ManageNews.php

@@ -424,6 +424,8 @@ function SelectMailingMembers()
 	$smcFunc['db_free_result']($request);
 
 	$context['can_send_pm'] = allowedTo('pm_send');
+
+	loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 }
 
 /**

+ 2 - 0
Sources/ManagePaid.php

@@ -1345,6 +1345,8 @@ function ModifyUserSubscription()
 		$context['sub']['start']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['start']['month'] == 12 ? 1 : $context['sub']['start']['month'] + 1, 0, $context['sub']['start']['month'] == 12 ? $context['sub']['start']['year'] + 1 : $context['sub']['start']['year']));
 		$context['sub']['end']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['sub']['end']['month'] == 12 ? 1 : $context['sub']['end']['month'] + 1, 0, $context['sub']['end']['month'] == 12 ? $context['sub']['end']['year'] + 1 : $context['sub']['end']['year']));
 	}
+
+	loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 }
 
 /**

+ 2 - 0
Sources/ManagePermissions.php

@@ -1113,6 +1113,7 @@ function setPermissionLevel($level, $group, $profile = 'null')
 		'profile_upload_avatar',
 		'profile_remote_avatar',
 		'profile_remove_own',
+		'report_user',
 	));
 	$groupLevels['board']['standard'] = array_merge($groupLevels['board']['restrict'], array(
 		'poll_vote',
@@ -2205,6 +2206,7 @@ function loadIllegalGuestPermissions()
 		'profile_upload_avatar',
 		'remove',
 		'report_any',
+		'report_user',
 		'send_email_to_members',
 		'send_mail',
 		'split_any',

+ 2 - 0
Sources/ManageRegistration.php

@@ -156,10 +156,12 @@ function AdminRegister()
 	}
 	else
 		$context['member_groups'] = array();
+
 	// Basic stuff.
 	$context['sub_template'] = 'admin_register';
 	$context['page_title'] = $txt['registration_center'];
 	createToken('admin-regc');
+	loadJavascriptFile('register.js', array('default_theme' => true, 'defer' => false), 'smf_register');
 }
 
 /**

+ 1 - 0
Sources/ManageSmileys.php

@@ -137,6 +137,7 @@ function EditSmileySettings($return_config = false)
 			array('check', 'smiley_sets_enable'),
 			array('check', 'smiley_enable', 'subtext' => $txt['smileys_enable_note']),
 			array('text', 'smileys_url', 40),
+			array('warning', !is_dir($context['smileys_dir']) ? 'setting_smileys_dir_wrong' : ''),
 			array('text', 'smileys_dir', 'invalid' => !$context['smileys_dir_found'], 40),
 		'',
 			// Message icons.

+ 5 - 2
Sources/MessageIndex.php

@@ -559,7 +559,7 @@ function MessageIndex()
 		{
 			// We'll use this in a minute
 			$boards_allowed = boardsAllowedTo('post_new');
-			
+
 			// How many boards can you do this on besides this one?
 			$context['can_move_any'] = count($boards_allowed) > 1;
 		}
@@ -685,6 +685,9 @@ function MessageIndex()
 	// Allow adding new buttons easily.
 	// Note: $context['normal_buttons'] is added for backward compatibility with 2.0, but is deprecated and should not be used
 	call_integration_hook('integrate_messageindex_buttons', array(&$context['normal_buttons']));
+
+	// Javascript for inline editing.
+	loadJavascriptFile('topic.js', array('default_theme' => true, 'defer' => false), 'smf_topic');
 }
 
 /**
@@ -826,7 +829,7 @@ function QuickModeration()
 			)
 		);
 		while ($row = $smcFunc['db_fetch_assoc']($request))
-		{		
+		{
 			if (!empty($board))
 			{
 				if ($row['id_board'] != $board || ($modSettings['postmod_active'] && !$row['approved'] && !allowedTo('approve_posts')))

+ 206 - 143
Sources/ModerationCenter.php

@@ -32,9 +32,10 @@ function ModerationMain($dont_call = false)
 	$context['can_moderate_boards'] = $user_info['mod_cache']['bq'] != '0=1';
 	$context['can_moderate_groups'] = $user_info['mod_cache']['gq'] != '0=1';
 	$context['can_moderate_approvals'] = $modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap']);
+	$context['can_moderate_users'] = allowedTo('moderate_forum');	
 
 	// Everyone using this area must be allowed here!
-	if (!$context['can_moderate_boards'] && !$context['can_moderate_groups'] && !$context['can_moderate_approvals'])
+	if (!$context['can_moderate_boards'] && !$context['can_moderate_groups'] && !$context['can_moderate_approvals'] && !$context['can_moderate_users'])
 		isAllowedTo('access_mod_center');
 
 	// We're gonna want a menu of some kind.
@@ -124,11 +125,11 @@ function ModerationMain($dont_call = false)
 				'reports' => array(
 					'label' => $txt['mc_reported_posts'],
 					'enabled' => $context['can_moderate_boards'],
-					'file' => 'ModerationCenter.php',
+					'file' => 'ReportedPosts.php',
 					'function' => 'ReportedPosts',
 					'icon' => 'reports.png',
 					'subsections' => array(
-						'open' => array($txt['mc_reportedp_active']),
+						'show' => array($txt['mc_reportedp_active']),
 						'closed' => array($txt['mc_reportedp_closed']),
 					),
 				),
@@ -138,16 +139,6 @@ function ModerationMain($dont_call = false)
 			'title' => $txt['mc_groups'],
 			'enabled' => $context['can_moderate_groups'],
 			'areas' => array(
-				'userwatch' => array(
-					'label' => $txt['mc_watched_users_title'],
-					'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
-					'function' => 'ViewWatchedUsers',
-					'icon' => 'members_watched.png',
-					'subsections' => array(
-						'member' => array($txt['mc_watched_users_member']),
-						'post' => array($txt['mc_watched_users_post']),
-					),
-				),
 				'groups' => array(
 					'label' => $txt['mc_group_requests'],
 					'file' => 'Groups.php',
@@ -163,6 +154,32 @@ function ModerationMain($dont_call = false)
 				),
 			),
 		),
+		'members' => array(
+			'title' => $txt['mc_members'],
+			'enabled' => $context['can_moderate_users'] || ($modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards']),
+			'areas' => array(
+				'userwatch' => array(
+					'label' => $txt['mc_watched_users_title'],
+					'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
+					'function' => 'ViewWatchedUsers',
+					'icon' => 'members_watched.png',
+					'subsections' => array(
+						'member' => array($txt['mc_watched_users_member']),
+						'post' => array($txt['mc_watched_users_post']),
+					),
+				),
+				'memberreports' => array(
+					'label' => $txt['mc_reported_members_title'],
+					'enabled' => $context['can_moderate_users'],
+					'function' => 'ReportedMembers',
+					'icon' => 'members_watched.png',
+					'subsections' => array(
+						'open' => array($txt['mc_reportedp_active']),
+						'closed' => array($txt['mc_reportedp_closed']),	
+					),
+				),
+			),
+		)
 	);
 
 	// Make sure the administrator has a valid session...
@@ -248,6 +265,14 @@ function ModerationHome()
 		$valid_blocks['r'] = 'ReportedPosts';
 		$valid_blocks['w'] = 'WatchedUsers';
 	}
+	if ($context['can_moderate_users'])
+	{
+		// This falls under the category of moderating users as well...
+		if (!$context['can_moderate_boards'])
+			$valid_blocks['w'] = 'WatchedUsers';
+
+		$valid_blocks['rm'] = 'ReportedMembers';
+	}
 
 	call_integration_hook('integrate_mod_centre_blocks', array(&$valid_blocks));
 
@@ -312,10 +337,15 @@ function ModBlockNotes()
 {
 	global $context, $smcFunc, $scripturl, $txt, $user_info;
 
+	// Set a nice and informative message.
+	$context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : array();
+	unset($_SESSION['rc_confirmation']);
+
 	// Are we saving a note?
 	if (isset($_GET['modnote']) && isset($_POST['makenote']) && isset($_POST['new_note']))
 	{
 		checkSession();
+		validateToken('mod-modnote-add');
 
 		$_POST['new_note'] = $smcFunc['htmlspecialchars'](trim($_POST['new_note']));
 		// Make sure they actually entered something.
@@ -339,6 +369,9 @@ function ModBlockNotes()
 			cache_put_data('moderator_notes_total', null, 240);
 		}
 
+		// Everything went better than expected!
+		$_SESSION['rc_confirmation'] = 'message_saved';
+
 		// Redirect otherwise people can resubmit.
 		redirectexit('action=moderate');
 	}
@@ -347,15 +380,15 @@ function ModBlockNotes()
 	if (isset($_GET['notes']) && isset($_GET['delete']) && is_numeric($_GET['delete']))
 	{
 		checkSession('get');
+		validateToken('mod-modnote-del', 'get');
 
 		// Lets delete it.
 		$smcFunc['db_query']('', '
 			DELETE FROM {db_prefix}log_comments
 			WHERE id_comment = {int:note}
-				AND comment_type = {string:type}',
+				AND comment_type = {literal:modnote}',
 			array(
 				'note' => $_GET['delete'],
-				'type' => 'modnote',
 			)
 		);
 
@@ -363,6 +396,9 @@ function ModBlockNotes()
 		cache_put_data('moderator_notes', null, 240);
 		cache_put_data('moderator_notes_total', null, 240);
 
+		// Tell them the message was deleted.
+		$_SESSION['rc_confirmation'] = 'message_deleted';
+
 		redirectexit('action=moderate');
 	}
 
@@ -373,9 +409,8 @@ function ModBlockNotes()
 			SELECT COUNT(*)
 			FROM {db_prefix}log_comments AS lc
 				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
-			WHERE lc.comment_type = {string:modnote}',
+			WHERE lc.comment_type = {literal:modnote}',
 			array(
-				'modnote' => 'modnote',
 			)
 		);
 		list ($moderator_notes_total) = $smcFunc['db_fetch_row']($request);
@@ -393,11 +428,10 @@ function ModBlockNotes()
 				lc.log_time, lc.body, lc.id_comment AS id_note
 			FROM {db_prefix}log_comments AS lc
 				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
-			WHERE lc.comment_type = {string:modnote}
+			WHERE lc.comment_type = {literal:modnote}
 			ORDER BY id_comment DESC
 			LIMIT {int:offset}, 10',
 			array(
-				'modnote' => 'modnote',
 				'offset' => $offset,
 			)
 		);
@@ -428,6 +462,16 @@ function ModBlockNotes()
 		);
 	}
 
+	// Add a confirm on deleting a modnote
+	addInlineJavascript('
+	$(\'.delete_modnote\').on(\'click\', function(){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_delete_confirm']) .');
+	});', true);
+
+	// Couple tokens for add/delete modnotes
+	createToken('mod-modnote-add');
+	createToken('mod-modnote-del', 'get');
+
 	return 'notes';
 }
 
@@ -454,11 +498,13 @@ function ModBlockReportedPosts()
 			FROM {db_prefix}log_reported AS lr
 				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
 			WHERE ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
+				AND lr.id_board != {int:not_a_reported_post}
 				AND lr.closed = {int:not_closed}
 				AND lr.ignore_all = {int:not_ignored}
 			ORDER BY lr.time_updated DESC
 			LIMIT 10',
 			array(
+				'not_a_reported_post' => 0,
 				'not_closed' => 0,
 				'not_ignored' => 0,
 			)
@@ -546,36 +592,96 @@ function ModBlockGroupRequests()
 }
 
 /**
- * Browse all the reported posts...
- * @todo this needs to be given its own file?
+ * Show a list of the most recent reported posts.
+ */
+function ModBlockReportedMembers()
+{
+	global $context, $user_info, $scripturl, $smcFunc;
+
+	// Got the info already?
+	$cachekey = md5(serialize((int) allowedTo('moderate_forum')));
+	$context['reported_users'] = array();
+	if (!allowedTo('moderate_forum'))
+		return 'reported_users_block';
+
+	if (($reported_posts = cache_get_data('reported_users_' . $cachekey, 90)) === null)
+	{
+		// By George, that means we in a position to get the reports, jolly good.
+		$request = $smcFunc['db_query']('', '
+			SELECT lr.id_report, lr.id_member,
+				lr.num_reports, IFNULL(mem.real_name, lr.membername) AS user_name,
+				IFNULL(mem.id_member, 0) AS id_user
+			FROM {db_prefix}log_reported AS lr
+				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
+			WHERE lr.id_board = {int:not_a_reported_post}
+				AND lr.closed = {int:not_closed}
+				AND lr.ignore_all = {int:not_ignored}
+			ORDER BY lr.time_updated DESC
+			LIMIT 10',
+			array(
+				'not_a_reported_post' => 0,
+				'not_closed' => 0,
+				'not_ignored' => 0,
+			)
+		);
+		$reported_users = array();
+		while ($row = $smcFunc['db_fetch_assoc']($request))
+			$reported_users[] = $row;
+		$smcFunc['db_free_result']($request);
+
+		// Cache it.
+		cache_put_data('reported_users_' . $cachekey, $reported_posts, 90);
+	}
+
+	$context['reported_users'] = array();
+	foreach ($reported_users as $i => $row)
+	{
+		$context['reported_users'][] = array(
+			'id' => $row['id_report'],
+			'alternate' => $i % 2,
+			'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
+			'user' => array(
+				'id' => $row['id_user'],
+				'name' => $row['user_name'],
+				'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
+				'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
+			),
+			'comments' => array(),
+			'num_reports' => $row['num_reports'],
+		);
+	}
+
+	return 'reported_users_block';
+}
+
+/**
+ * Browse all the reported users...
  */
-function ReportedPosts()
+function ReportedMembers()
 {
 	global $txt, $context, $scripturl, $user_info, $smcFunc;
 
 	loadTemplate('ModerationCenter');
 
 	// Set an empty var for the server response.
-	$context['report_post_action'] = '';
+	$context['report_member_action'] = '';
 
 	// Put the open and closed options into tabs, because we can...
 	$context[$context['moderation_menu_name']]['tab_data'] = array(
-		'title' => $txt['mc_reported_posts'],
+		'title' => $txt['mc_reported_members'],
 		'help' => '',
-		'description' => $txt['mc_reported_posts_desc'],
+		'description' => $txt['mc_reported_members_desc'],
 	);
 
-	// This comes under the umbrella of moderating posts.
-	if ($user_info['mod_cache']['bq'] == '0=1')
-		isAllowedTo('moderate_forum');
+	isAllowedTo('moderate_forum');
 
 	// Are they wanting to view a particular report?
 	if (!empty($_REQUEST['report']))
-		return ModReport();
+		return MemberReport();
 
 	// Set up the comforting bits...
-	$context['page_title'] = $txt['mc_reported_posts'];
-	$context['sub_template'] = 'reported_posts';
+	$context['page_title'] = $txt['mc_reported_members'];
+	$context['sub_template'] = 'reported_members';
 
 	// Are we viewing open or closed reports?
 	$context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0;
@@ -590,8 +696,7 @@ function ReportedPosts()
 		$smcFunc['db_query']('', '
 			UPDATE {db_prefix}log_reported
 			SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . '
-			WHERE id_report = {int:id_report}
-				AND ' . $user_info['mod_cache']['bq'],
+			WHERE id_report = {int:id_report}',
 			array(
 				'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0,
 				'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0,
@@ -601,7 +706,7 @@ function ReportedPosts()
 
 		// Get the board, topic and message for this report
 		$request = $smcFunc['db_query']('', '
-			SELECT id_board, id_topic, id_msg
+			SELECT id_member, membername
 			FROM {db_prefix}log_reported
 			WHERE id_report = {int:id_report}',
 			array(
@@ -611,18 +716,21 @@ function ReportedPosts()
 
 		// Set up the data for the log...
 		$extra = array('report' => $_GET['rid']);
-		list ($extra['board'], $extra['topic'], $extra['message']) = $smcFunc['db_fetch_row']($request);
+		list($extra['member'], $extra['membername']) = $smcFunc['db_fetch_row']($request);
 		$smcFunc['db_free_result']($request);
 
+		// Stick this in string format for consistency
+		$extra['member'] = (string)$extra['member'];
+
 		// Tell the user about it.
-		$context['report_post_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
+		$context['report_member_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
 
 		// Log this action
-		logAction($context['report_post_action'] . '_report', $extra);
+		logAction($context['report_member_action'] . '_user_report', $extra);
 
 		// Time to update.
 		updateSettings(array('last_mod_report_action' => time()));
-		recountOpenReports();
+		recountOpenMemberReports();
 	}
 	elseif (isset($_POST['close']) && isset($_POST['close_selected']))
 	{
@@ -637,26 +745,24 @@ function ReportedPosts()
 		{
 			// Get the data for each of these reports
 			$request = $smcFunc['db_query']('', '
-				SELECT id_report, id_board, id_topic, id_msg
+				SELECT id_report, id_member, membername
 				FROM {db_prefix}log_reported
-				WHERE id_report IN ({array_int:report_list})
-					AND ' . $user_inf['mod_cache']['bq'],
+				WHERE id_report IN ({array_int:report_list})',
 				array(
-					'id_report' => $_GET['rid'],
+					'report_list' => $toClose,
 				)
 			);
 
 			while ($reports = $smcFunc['db_fetch_assoc']($request))
 			{
 				$report_data = array(
-					'report' => $row['id_report'],
-					'board' => $row['id_board'],
-					'topic' => $row['id_topic'],
-					'message' => $row['id_msg'],
+					'report' => $reports['id_report'],
+					'membername' => $reports['membername'],
+					'member' => (string)$reports['id_member'],
 				);
 
 				// Log that this report was closed
-				logAction('close_report', $report_data);
+				logAction('close_user_report', $report_data);
 			}
 
 			$smcFunc['db_free_result']($request);
@@ -664,8 +770,7 @@ function ReportedPosts()
 			$smcFunc['db_query']('', '
 				UPDATE {db_prefix}log_reported
 				SET closed = {int:is_closed}
-				WHERE id_report IN ({array_int:report_list})
-					AND ' . $user_info['mod_cache']['bq'],
+				WHERE id_report IN ({array_int:report_list})',
 				array(
 					'report_list' => $toClose,
 					'is_closed' => 1,
@@ -674,11 +779,11 @@ function ReportedPosts()
 
 			// Time to update.
 			updateSettings(array('last_mod_report_action' => time()));
-			recountOpenReports();
+			recountOpenMemberReports();
 		}
 
 		// Go on and tell the result.
-		$context['report_post_action'] = 'close_all';
+		$context['report_member_action'] = 'close_all';
 	}
 
 	// How many entries are we viewing?
@@ -686,61 +791,52 @@ function ReportedPosts()
 		SELECT COUNT(*)
 		FROM {db_prefix}log_reported AS lr
 		WHERE lr.closed = {int:view_closed}
-			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']),
+			AND lr.id_board = {int:not_a_reported_post}',
 		array(
 			'view_closed' => $context['view_closed'],
+			'not_a_reported_post' => 0,
 		)
 	);
 	list ($context['total_reports']) = $smcFunc['db_fetch_row']($request);
 	$smcFunc['db_free_result']($request);
 
 	// So, that means we can page index, yes?
-	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10);
+	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=memberreports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10);
 	$context['start'] = $_GET['start'];
 
 	// By George, that means we in a position to get the reports, golly good.
 	$request = $smcFunc['db_query']('', '
-		SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
-			lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
-			IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author
+		SELECT lr.id_report, lr.id_member, lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
+			IFNULL(mem.real_name, lr.membername) AS user_name, IFNULL(mem.id_member, 0) AS id_user
 		FROM {db_prefix}log_reported AS lr
 			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
 		WHERE lr.closed = {int:view_closed}
-			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
+			AND lr.id_board = {int:not_a_reported_post}
 		ORDER BY lr.time_updated DESC
 		LIMIT ' . $context['start'] . ', 10',
 		array(
 			'view_closed' => $context['view_closed'],
+			'not_a_reported_post' => 0,
 		)
 	);
 	$context['reports'] = array();
 	$report_ids = array();
-	$report_boards_ids = array();
 	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
 	{
 		$report_ids[] = $row['id_report'];
-		$report_boards_ids[] = $row['id_board'];
 		$context['reports'][$row['id_report']] = array(
 			'id' => $row['id_report'],
 			'alternate' => $i % 2,
-			'topic' => array(
-				'id' => $row['id_topic'],
-				'id_msg' => $row['id_msg'],
-				'id_board' => $row['id_board'],
-				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
-			),
-			'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'],
-			'author' => array(
-				'id' => $row['id_author'],
-				'name' => $row['author_name'],
-				'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'],
-				'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
+			'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
+			'user' => array(
+				'id' => $row['id_user'],
+				'name' => $row['user_name'],
+				'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
+				'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
 			),
 			'comments' => array(),
 			'time_started' => timeformat($row['time_started']),
 			'last_updated' => timeformat($row['time_updated']),
-			'subject' => $row['subject'],
-			'body' => parse_bbc($row['body']),
 			'num_reports' => $row['num_reports'],
 			'closed' => $row['closed'],
 			'ignore' => $row['ignore_all']
@@ -748,29 +844,6 @@ function ReportedPosts()
 	}
 	$smcFunc['db_free_result']($request);
 
-	// Get the names of boards those topics are in. Slightly faster this way.
-	if (!empty($report_boards_ids))
-	{
-		$report_boards_ids = array_unique($report_boards_ids);
-		$board_names = array();
-		$request = $smcFunc['db_query']('', '
-			SELECT id_board, name
-			FROM {db_prefix}boards
-			WHERE id_board IN ({array_int:boards})',
-			array(
-				'boards' => $report_boards_ids,
-			)
-		);
-
-		while ($row = $smcFunc['db_fetch_assoc']($request))
-			$board_names[$row['id_board']] = $row['name'];
-		$smcFunc['db_free_result']($request);
-
-		foreach ($context['reports'] as $id_report => $report)
-			if (!empty($board_names[$report['topic']['id_board']]))
-				$context['reports'][$id_report]['topic']['board_name'] = $board_names[$report['topic']['id_board']];
-	}
-
 	// Now get all the people who reported it.
 	if (!empty($report_ids))
 	{
@@ -801,13 +874,7 @@ function ReportedPosts()
 		$smcFunc['db_free_result']($request);
 	}
 
-	// Get the boards where the current user can remove any message.
-	$context['report_remove_any_boards'] = $user_info['is_admin'] ? $report_boards_ids : array_intersect($report_boards_ids, boardsAllowedTo('remove_any'));
 	$context['report_manage_bans'] = allowedTo('manage_bans');
-
-	// Do we deleted a message?
-	if (isset($_REQUEST['done']))
-		$context['report_post_action'] = 'message_deleted';
 }
 
 /**
@@ -842,38 +909,35 @@ function ModerateGroups()
 /**
  * How many open reports do we have?
  */
-function recountOpenReports()
+function recountOpenMemberReports()
 {
 	global $user_info, $context, $smcFunc;
 
 	$request = $smcFunc['db_query']('', '
 		SELECT COUNT(*)
 		FROM {db_prefix}log_reported
-		WHERE ' . $user_info['mod_cache']['bq'] . '
-			AND closed = {int:not_closed}
-			AND ignore_all = {int:not_ignored}',
+		WHERE closed = {int:not_closed}
+			AND ignore_all = {int:not_ignored}
+			AND id_board = {int:not_a_reported_post}',
 		array(
 			'not_closed' => 0,
 			'not_ignored' => 0,
+			'not_a_reported_post' => 0,
 		)
 	);
 	list ($open_reports) = $smcFunc['db_fetch_row']($request);
 	$smcFunc['db_free_result']($request);
 
-	$_SESSION['rc'] = array(
+	$_SESSION['rmc'] = array(
 		'id' => $user_info['id'],
 		'time' => time(),
 		'reports' => $open_reports,
 	);
 
-	$context['open_mod_reports'] = $open_reports;
+	$context['open_member_reports'] = $open_reports;
 }
 
-/**
- * Get details about the moderation report... specified in
- * $_REQUEST['report'].
- */
-function ModReport()
+function MemberReport()
 {
 	global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc;
 
@@ -886,16 +950,17 @@ function ModReport()
 
 	// Get the report details, need this so we can limit access to a particular board
 	$request = $smcFunc['db_query']('', '
-		SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
+		SELECT lr.id_report, lr.id_member,
 			lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
-			IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author
+			IFNULL(mem.real_name, lr.membername) AS user_name, IFNULL(mem.id_member, 0) AS id_user
 		FROM {db_prefix}log_reported AS lr
 			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
 		WHERE lr.id_report = {int:id_report}
-			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
+			AND lr.id_board = {int:not_a_reported_post}
 		LIMIT 1',
 		array(
 			'id_report' => $_REQUEST['report'],
+			'not_a_reported_post' => 0,
 		)
 	);
 
@@ -935,13 +1000,9 @@ function ModReport()
 			$smcFunc['db_insert']('insert',
 				'{db_prefix}background_tasks',
 				array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
-				array('$sourcedir/tasks/MsgReportReply-Notify.php', 'MsgReportReply_Notify_Background', serialize(array(
+				array('$sourcedir/tasks/MemberReportReply-Notify.php', 'MemberReportReply_Notify_Background', serialize(array(
 					'report_id' => $_REQUEST['report'],
 					'comment_id' => $last_comment,
-					'msg_id' => $row['id_msg'],
-					'topic_id' => $row['id_topic'],
-					'board_id' => $row['id_board'],
-					'sender_id' => $user_info['id'],
 					'sender_name' => $user_info['name'],
 					'time' => time(),
 				)), 0),
@@ -949,30 +1010,23 @@ function ModReport()
 			);
 
 			// Redirect to prevent double submittion.
-			redirectexit($scripturl . '?action=moderate;area=reports;report=' . $_REQUEST['report']);
+			redirectexit($scripturl . '?action=moderate;area=memberreports;report=' . $_REQUEST['report']);
 		}
 	}
 
 	$context['report'] = array(
 		'id' => $row['id_report'],
-		'topic_id' => $row['id_topic'],
-		'board_id' => $row['id_board'],
-		'message_id' => $row['id_msg'],
-		'message_href' => $scripturl . '?msg=' . $row['id_msg'],
-		'message_link' => '<a href="' . $scripturl . '?msg=' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
-		'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'],
-		'author' => array(
-			'id' => $row['id_author'],
-			'name' => $row['author_name'],
-			'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'],
-			'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
+		'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
+		'user' => array(
+			'id' => $row['id_user'],
+			'name' => $row['user_name'],
+			'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
+			'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
 		),
 		'comments' => array(),
 		'mod_comments' => array(),
 		'time_started' => timeformat($row['time_started']),
 		'last_updated' => timeformat($row['time_updated']),
-		'subject' => $row['subject'],
-		'body' => parse_bbc($row['body']),
 		'num_reports' => $row['num_reports'],
 		'closed' => $row['closed'],
 		'ignore' => $row['ignore_all']
@@ -1039,28 +1093,37 @@ function ModReport()
 	require_once($sourcedir . '/Subs-List.php');
 	loadLanguage('Modlog');
 
+	// Find their ID in the serialized action string...
+	$user_id_length = strlen((string)$context['report']['user']['id']);
+	$member = 's:6:"member";s:' . $user_id_length . ':"' . $context['report']['user']['id'] . '";}';
+
 	// This is all the information from the moderation log.
+	// Note that we use "raw" here to prevent SMF from escaping things we don't want escaped
 	$listOptions = array(
-		'id' => 'moderation_actions_list',
+		'id' => 'memreport_actions_list',
 		'title' => $txt['mc_modreport_modactions'],
 		'items_per_page' => 15,
 		'no_items_label' => $txt['modlog_no_entries_found'],
-		'base_href' => $scripturl . '?action=moderate;area=reports;report=' . $context['report']['id'],
+		'base_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $context['report']['id'],
 		'default_sort_col' => 'time',
 		'get_items' => array(
 			'function' => 'list_getModLogEntries',
 			'params' => array(
-				'lm.id_topic = {int:id_topic}',
-				array('id_topic' => $context['report']['topic_id']),
+				'lm.extra LIKE {raw:member}
+					AND lm.action LIKE {raw:report}',
+				array('member' => '\'%' . $member . '\'', 'report' => '\'%_user_report\''),
 				1,
+				true,
 			),
 		),
 		'get_count' => array(
 			'function' => 'list_getModLogEntryCount',
 			'params' => array(
-				'lm.id_topic = {int:id_topic}',
-				array('id_topic' => $context['report']['topic_id']),
+				'lm.extra LIKE {string:member}
+				AND lm.action LIKE {string:report}',
+				array('member' => '%' . $member . '%', 'report' => '%_user_report'),
 				1,
+				true,
 			),
 		),
 		// This assumes we are viewing by user.
@@ -1142,8 +1205,8 @@ function ModReport()
 
 	// Finally we are done :P
 	loadTemplate('ModerationCenter');
-	$context['page_title'] = sprintf($txt['mc_viewmodreport'], $context['report']['subject'], $context['report']['author']['name']);
-	$context['sub_template'] = 'viewmodreport';
+	$context['page_title'] = sprintf($txt['mc_viewmemberreport'], $context['report']['user']['name']);
+	$context['sub_template'] = 'viewmemberreport';
 }
 
 /**

+ 13 - 5
Sources/Modlog.php

@@ -311,12 +311,13 @@ function ViewModlog()
  * @param $query_string
  * @param $query_params
  * @param $log_type
+ * @param $ignore_boards
  */
-function list_getModLogEntryCount($query_string = '', $query_params = array(), $log_type = 1)
+function list_getModLogEntryCount($query_string = '', $query_params = array(), $log_type = 1, $ignore_boards = false)
 {
 	global $smcFunc, $user_info;
 
-	$modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : ($user_info['mod_cache']['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board'))));
+	$modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : (($user_info['mod_cache']['bq'] == '0=1' || $ignore_boards) ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board'))));
 
 	$result = $smcFunc['db_query']('', '
 		SELECT COUNT(*)
@@ -351,12 +352,13 @@ function list_getModLogEntryCount($query_string = '', $query_params = array(), $
  * @param $query_string
  * @param $query_params
  * @param $log_type
+ * @param $ignore_boards 
  */
-function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '', $query_params = array(), $log_type = 1)
+function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '', $query_params = array(), $log_type = 1, $ignore_boards = false)
 {
 	global $scripturl, $txt, $smcFunc, $user_info;
 
-	$modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : ($user_info['mod_cache']['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board'))));
+	$modlog_query = allowedTo('admin_forum') || $user_info['mod_cache']['bq'] == '1=1' ? '1=1' : (($user_info['mod_cache']['bq'] == '0=1' || $ignore_boards) ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr($user_info['mod_cache']['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr($user_info['mod_cache']['bq'], array('id_board' => 't.id_board'))));
 
 	// Can they see the IP address?
 	$seeIP = allowedTo('moderate_forum');
@@ -617,7 +619,13 @@ function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '
 				$entries[$k]['extra'][$type] = sprintf($txt['modlog_id'], $entry['extra'][$type]);
 
 		if (isset($entry['extra']['report']))
-			$entries[$k]['extra']['report'] = '<a href="' . $scripturl . '?action=moderate;area=reports;report=' . $entry['extra']['report'] . '">' . $txt['modlog_report'] . '</a>';
+		{
+			// Member profile reports go in a different area
+			if (stristr($entry['action'], 'user_report'))
+				$entries[$k]['extra']['report'] = '<a href="' . $scripturl . '?action=moderate;area=memberreports;report=' . $entry['extra']['report'] . '">' . $txt['modlog_report'] . '</a>';
+			else
+				$entries[$k]['extra']['report'] = '<a href="' . $scripturl . '?action=moderate;area=reports;report=' . $entry['extra']['report'] . '">' . $txt['modlog_report'] . '</a>';
+		}
 
 		if (empty($entries[$k]['action_text']))
 			$entries[$k]['action_text'] = isset($txt['modlog_ac_' . $entry['action']]) ? $txt['modlog_ac_' . $entry['action']] : $entry['action'];

+ 6 - 0
Sources/PackageGet.php

@@ -170,6 +170,12 @@ function PackageServers()
 			$ftp->close();
 		}
 	}
+
+	addInlineJavascript('
+	$(\'.new_package_content\').hide();
+	$(\'.download_new_package\').on(\'click\', function() {
+		$(\'.new_package_content\').css(\'display\') == \'none\' ? $(\'.new_package_content\').show(\'slow\') : $(\'.new_package_content\').hide(\'slow\'); 
+	});', true);
 }
 
 /**

+ 4 - 1
Sources/Packages.php

@@ -86,6 +86,9 @@ function Packages()
 		),
 	);
 
+	if ($context['sub_action'] == 'browse')
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
+
 	// Call the function we're handing control to.
 	$subActions[$context['sub_action']]();
 }
@@ -1927,7 +1930,7 @@ function ViewOperations()
 					'install_id' => $install_id,
 				)
 			);
-			
+
 			if ($smcFunc['db_num_rows']($request) == 1)
 			{
 				list ($old_themes) = $smcFunc['db_fetch_row']($request);

+ 2 - 0
Sources/PersonalMessage.php

@@ -1753,6 +1753,8 @@ function MessagePost()
 	if (!WIRELESS)
 	{
 		loadTemplate('PersonalMessage');
+		loadJavascriptFile('PersonalMessage.js', array('default_theme' => true, 'defer' => false), 'smf_pms');
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
 		$context['sub_template'] = 'send';
 	}
 

+ 9 - 0
Sources/Profile-Modify.php

@@ -1300,6 +1300,8 @@ function editBuddyIgnoreLists($memID)
 		),
 	);
 
+	loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
+
 	// Pass on to the actual function.
 	$context['sub_template'] = $subActions[$context['list_area']][0];
 	$subActions[$context['list_area']][0]($memID);
@@ -1819,6 +1821,7 @@ function authentication($memID, $saving = false)
 	$context['member']['openid_uri'] = $cur_profile['openid_uri'];
 	$context['auth_method'] = empty($cur_profile['openid_uri']) ? 'password' : 'openid';
 	$context['sub_template'] = 'authentication_method';
+	loadJavascriptFile('register.js', array('default_theme' => true, 'defer' => false), 'smf_register');
 }
 
 /**
@@ -1900,6 +1903,8 @@ function alert_configuration($memID)
 		'moderation' => array(
 			'msg_report' => array('alert' => 'yes', 'email' => 'yes', 'permission' => array('name' => 'moderate_board', 'is_board' => true)),
 			'msg_report_reply' => array('alert' => 'yes', 'email' => 'yes', 'permission' => array('name' => 'moderate_board', 'is_board' => true)),
+			'member_report' => array('alert' => 'yes', 'email' => 'yes', 'permission' => array('name' => 'moderate_forum', 'is_board' => false)),
+			'member_report_reply' => array('alert' => 'yes', 'email' => 'yes', 'permission' => array('name' => 'moderate_forum', 'is_board' => false)),
 		),
 		'members' => array(
 			'member_register' => array('alert' => 'yes', 'email' => 'yes', 'permission' => array('name' => 'moderate_forum', 'is_board' => false)),
@@ -2736,6 +2741,10 @@ function profileLoadSignatureData()
 		$context['member']['signature'] = $_POST['signature'];
 	}
 
+	// Load the spell checker?
+	if ($context['show_spellchecking'])
+		loadJavascriptFile('spellcheck.js', array('default_theme' => true, 'defer' => false), 'smf_spellcheck');
+
 	return true;
 }
 

+ 2 - 0
Sources/Profile-View.php

@@ -317,6 +317,8 @@ function fetch_alerts($memID, $all = false)
 			$alerts[$id_alert]['extra']['topic_msg'] = $topics[$alert['extra']['topic']];
 		if ($alert['content_type'] == 'msg')
 			$alerts[$id_alert]['extra']['msg_msg'] = $msgs[$alert['content_id']];
+		if ($alert['content_type'] == 'profile')
+			$alerts[$id_alert]['extra']['profile_msg'] = '<a href="' . $scripturl . '?action=profile;u=' . $alerts[$id_alert]['content_id'] . '">' . $alerts[$id_alert]['extra']['user_name'] . '</a>';
 
 		if (!empty($memberContext[$alert['sender_id']]))
 			$alerts[$id_alert]['sender'] = &$memberContext[$alert['sender_id']];

+ 12 - 0
Sources/Profile.php

@@ -326,6 +326,15 @@ function ModifyProfile($post_errors = array())
 						'any' => array('pm_send'),
 					),
 				),
+				'report' => array(
+					'label' => $txt['report_profile'],
+					'custom_url' => $scripturl . '?action=reporttm;' . $context['session_var'] . '=' . $context['session_id'],
+					'icon' => 'warning.png',
+					'permission' => array(
+						'own' => array(),
+						'any' => array('moderate_forum', 'report_user'),
+					),
+				),
 				'issuewarning' => array(
 					'label' => $txt['profile_issue_warning'],
 					'enabled' => $modSettings['warning_settings'][0] == 1,
@@ -555,6 +564,9 @@ function ModifyProfile($post_errors = array())
 	if (WIRELESS && $context['sub_template'] == 'summary' && WIRELESS_PROTOCOL != 'wap')
 		$context['sub_template'] = WIRELESS_PROTOCOL . '_profile';
 
+	if (!WIRELESS)
+		loadJavascriptFile('profile.js', array('default_theme' => true, 'defer' => false), 'smf_profile');
+
 	// These will get populated soon!
 	$post_errors = array();
 	$profile_vars = array();

+ 1 - 0
Sources/Recent.php

@@ -1016,6 +1016,7 @@ function UnreadTopics()
 				SELECT t.id_topic, t.id_board, t.id_last_msg, IFNULL(lmr.id_msg, 0) AS id_msg' . (!in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? ', ' . $_REQUEST['sort'] . ' AS sort_key' : '') . '
 				FROM {db_prefix}messages AS m
 					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
+					LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)
 					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' . (isset($sortKey_joins[$_REQUEST['sort']]) ? $sortKey_joins[$_REQUEST['sort']] : '') . '
 				WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
 					AND t.id_board = {int:current_board}' : '') . ($modSettings['postmod_active'] ? '

+ 6 - 0
Sources/Register.php

@@ -89,6 +89,10 @@ function Register($reg_errors = array())
 	$context['sub_template'] = $current_step == 1 ? 'registration_agreement' : 'registration_form';
 	$context['page_title'] = $current_step == 1 ? $txt['registration_agreement'] : $txt['registration_form'];
 
+	// Kinda need this.
+	if ($context['sub_template'] == 'registration_form')
+		loadJavascriptFile('register.js', array('default_theme' => true, 'defer' => false), 'smf_register');
+
 	// Add the register chain to the link tree.
 	$context['linktree'][] = array(
 		'url' => $scripturl . '?action=register',
@@ -697,6 +701,8 @@ function Activate()
 		'never_expire' => false,
 		'description' => $txt['activate_success']
 	);
+
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
 }
 
 /**

+ 5 - 0
Sources/Reminder.php

@@ -188,6 +188,8 @@ function setPassword()
 		'memID' => (int) $_REQUEST['u']
 	);
 
+	loadJavascriptFile('register.js', array('default_theme' => true, 'defer' => false), 'smf_register');
+
 	// Tokens!
 	createToken('remind-sp');
 }
@@ -270,6 +272,8 @@ function setPassword2()
 		'never_expire' => false,
 		'description' => $txt['reminder_password_set']
 	);
+
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
 	createToken('login');
 }
 
@@ -316,6 +320,7 @@ function SecretAnswerInput()
 
 	$context['sub_template'] = 'ask';
 	createToken('remind-sai');
+	loadJavascriptFile('register.js', array('default_theme' => true, 'defer' => false), 'smf_register');
 }
 
 function SecretAnswer2()

+ 227 - 197
Sources/SendTopic.php → Sources/ReportToMod.php

@@ -1,7 +1,7 @@
 <?php
 
 /**
- * The functions in this file deal with sending topics to a friend or moderator
+ * The functions in this file deal with reporting posts or profiles to mods and admins
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -16,194 +16,121 @@ if (!defined('SMF'))
 	die('No direct access...');
 
 /**
- * Allow a user to send an email.
- * Send an email to the user - allow the sender to write the message.
- * Can either be passed a user ID as uid or a message id as msg.
- * Does not check permissions for a message ID as there is no information disclosed.
+ * Report a post or profile to the moderator... ask for a comment.
+ * Gathers data from the user to report abuse to the moderator(s).
+ * Uses the ReportToModerator template, main sub template.
+ * Requires the report_any permission.
+ * Uses ReportToModerator2() if post data was sent.
+ * Accessed through ?action=reporttm.
  */
-function EmailUser()
+function ReportToModerator()
 {
-	global $context, $user_info, $smcFunc, $txt, $scripturl, $sourcedir;
+	global $txt, $topic, $context, $smcFunc, $scripturl, $sourcedir;
 
-	// Can the user even see this information?
-	if ($user_info['is_guest'])
-		fatal_lang_error('no_access', false);
 
-	isAllowedTo('send_email_to_members');
-
-	// Don't index anything here.
 	$context['robot_no_index'] = true;
+	$context['comment_body'] = '';
 
-	// Load the template.
-	loadTemplate('SendTopic');
-
-	// Are we sending to a user?
-	$context['form_hidden_vars'] = array();
-	if (isset($_REQUEST['uid']))
-	{
-		$request = $smcFunc['db_query']('', '
-			SELECT email_address AS email, real_name AS name, id_member, hide_email
-			FROM {db_prefix}members
-			WHERE id_member = {int:id_member}',
-			array(
-				'id_member' => (int) $_REQUEST['uid'],
-			)
-		);
+	// No guests!
+	is_not_guest();
 
-		$context['form_hidden_vars']['uid'] = (int) $_REQUEST['uid'];
-	}
-	elseif (isset($_REQUEST['msg']))
+	// You can't use this if it's off or you are not allowed to do it.
+	// If we don't have the ID of something to report, we'll die with a no_access error below
+	if (isset($_REQUEST['msg']))
+		isAllowedTo('report_msg');
+	elseif (isset($_REQUEST['u']))
+		isAllowedTo('report_user');
+
+	// Previewing or modifying?
+	if (isset($_POST['preview']) && !isset($_POST['save']))
 	{
-		$request = $smcFunc['db_query']('', '
-			SELECT IFNULL(mem.email_address, m.poster_email) AS email, IFNULL(mem.real_name, m.poster_name) AS name, IFNULL(mem.id_member, 0) AS id_member, hide_email
-			FROM {db_prefix}messages AS m
-				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
-			WHERE m.id_msg = {int:id_msg}',
-			array(
-				'id_msg' => (int) $_REQUEST['msg'],
-			)
-		);
+		require_once($sourcedir . '/Subs-Post.php');
 
-		$context['form_hidden_vars']['msg'] = (int) $_REQUEST['msg'];
-	}
+		// Set up the preview message.
+		$context['preview_message'] = $smcFunc['htmlspecialchars']($_POST['comment'], ENT_QUOTES);
+		preparsecode($context['preview_message']);
 
-	if (empty($request) || $smcFunc['db_num_rows']($request) == 0)
-		fatal_lang_error('cant_find_user_email');
+		// We censor for your protection...
+		censorText($context['preview_message']);
 
-	$row = $smcFunc['db_fetch_assoc']($request);
-	$smcFunc['db_free_result']($request);
+		$context['comment_body'] = !empty($_POST['comment']) ? trim($_POST['comment']) : '';
+	}
 
-	// Are you sure you got the address?
-	if (empty($row['email']))
-		fatal_lang_error('cant_find_user_email');
+	// If they're posting, it should be processed by ReportToModerator2.
+	if ((isset($_POST[$context['session_var']]) || isset($_POST['save'])) && empty($context['post_errors']) && !isset($_POST['preview']))
+		ReportToModerator2();
 
-	// Can they actually do this?
-	$context['show_email_address'] = showEmailAddress(!empty($row['hide_email']), $row['id_member']);
-	if ($context['show_email_address'] === 'no')
+	// We need a message ID or user ID to check!
+	if (empty($_REQUEST['msg']) && empty($_REQUEST['mid']) && empty($_REQUEST['u']))
 		fatal_lang_error('no_access', false);
 
-	// Setup the context!
-	$context['recipient'] = array(
-		'id' => $row['id_member'],
-		'name' => $row['name'],
-		'email' => $row['email'],
-		'email_link' => ($context['show_email_address'] == 'yes_permission_override' ? '<em>' : '') . '<a href="mailto:' . $row['email'] . '">' . $row['email'] . '</a>' . ($context['show_email_address'] == 'yes_permission_override' ? '</em>' : ''),
-		'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['name'] . '</a>' : $row['name'],
-	);
+	// For compatibility, accept mid, but we should be using msg. (not the flavor kind!)
+	if (!empty($_REQUEST['msg']) || !empty($_REQUEST['mid']))
+		$_REQUEST['msg'] = empty($_REQUEST['msg']) ? (int) $_REQUEST['mid'] : (int) $_REQUEST['msg'];
+	// msg and mid empty - assume we're reporting a user
+	elseif (!empty($_REQUEST['u']))
+		$_REQUEST['u'] = (int) $_REQUEST['u'];
 
-	// Can we see this person's email address?
-	$context['can_view_receipient_email'] = $context['show_email_address'] == 'yes' || $context['show_email_address'] == 'yes_permission_override';
+	// Set up some form values
+	$context['report_type'] = isset($_REQUEST['msg']) ? 'msg' : 'u';
+	$context['reported_item'] = isset($_REQUEST['msg']) ? $_REQUEST['msg'] : $_REQUEST['u'];
 
-	// Are we actually sending it?
-	if (isset($_POST['send']) && isset($_POST['email_body']))
+	if (isset($_REQUEST['msg']))
 	{
-		require_once($sourcedir . '/Subs-Post.php');
-
-		checkSession();
-
-		// If it's a guest sort out their names.
-		if ($user_info['is_guest'])
-		{
-			if (empty($_POST['y_name']) || $_POST['y_name'] == '_' || trim($_POST['y_name']) == '')
-				fatal_lang_error('no_name', false);
-			if (empty($_POST['y_email']))
-				fatal_lang_error('no_email', false);
-			if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['y_email']) == 0)
-				fatal_lang_error('email_invalid_character', false);
-
-			$from_name = trim($_POST['y_name']);
-			$from_email = trim($_POST['y_email']);
-		}
-		else
-		{
-			$from_name = $user_info['name'];
-			$from_email = $user_info['email'];
-		}
-
-		// Check we have a body (etc).
-		if (trim($_POST['email_body']) == '' || trim($_POST['email_subject']) == '')
-			fatal_lang_error('email_missing_data');
-
-		// We use a template in case they want to customise!
-		$replacements = array(
-			'EMAILSUBJECT' => $_POST['email_subject'],
-			'EMAILBODY' => $_POST['email_body'],
-			'SENDERNAME' => $from_name,
-			'RECPNAME' => $context['recipient']['name'],
+		// Check the message's ID - don't want anyone reporting a post they can't even see!
+		$result = $smcFunc['db_query']('', '
+			SELECT m.id_msg, m.id_member, t.id_member_started
+			FROM {db_prefix}messages AS m
+				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
+			WHERE m.id_msg = {int:id_msg}
+				AND m.id_topic = {int:current_topic}
+			LIMIT 1',
+			array(
+				'current_topic' => $topic,
+				'id_msg' => $_REQUEST['msg'],
+			)
 		);
+		if ($smcFunc['db_num_rows']($result) == 0)
+			fatal_lang_error('no_board', false);
+		list ($_REQUEST['msg'], $member, $starter) = $smcFunc['db_fetch_row']($result);
+		$smcFunc['db_free_result']($result);
 
-		// Don't let them send too many!
-		spamProtection('sendmail');
 
-		// Get the template and get out!
-		$emaildata = loadEmailTemplate('send_email', $replacements);
-		sendmail($context['recipient']['email'], $emaildata['subject'], $emaildata['body'], $from_email, 'custemail', false, 1, null, true);
+		// This is here so that the user could, in theory, be redirected back to the topic.
+		$context['start'] = $_REQUEST['start'];
+		$context['message_id'] = $_REQUEST['msg'];
 
-		// Now work out where to go!
-		if (isset($_REQUEST['uid']))
-			redirectexit('action=profile;u=' . (int) $_REQUEST['uid']);
-		elseif (isset($_REQUEST['msg']))
-			redirectexit('msg=' . (int) $_REQUEST['msg']);
-		else
-			redirectexit();
+		// The submit URL is different for users than it is for posts
+		$context['submit_url'] = $scripturl . '?action=reporttm;msg=' . $_REQUEST['msg'] . ';topic=' . $topic;
 	}
+	else
+	{
+		// Check the user's ID
+		$result = $smcFunc['db_query']('', '
+			SELECT id_member, real_name, member_name
+			FROM {db_prefix}members
+			WHERE id_member = {int:current_user}',
+			array(
+				'current_user' => $_REQUEST['u'],
+			)
+		);
 
-	$context['sub_template'] = 'custom_email';
-	$context['page_title'] = $txt['send_email'];
-}
-
-/**
- * Report a post to the moderator... ask for a comment.
- * Gathers data from the user to report abuse to the moderator(s).
- * Uses the ReportToModerator template, main sub template.
- * Requires the report_any permission.
- * Uses ReportToModerator2() if post data was sent.
- * Accessed through ?action=reporttm.
- */
-function ReportToModerator()
-{
-	global $txt, $topic, $context, $smcFunc;
-
-	$context['robot_no_index'] = true;
-
-	// No guests!
-	is_not_guest();
-
-	// You can't use this if it's off or you are not allowed to do it.
-	isAllowedTo('report_any');
-
-	// If they're posting, it should be processed by ReportToModerator2.
-	if ((isset($_POST[$context['session_var']]) || isset($_POST['save'])) && empty($context['post_errors']))
-		ReportToModerator2();
+		if ($smcFunc['db_num_rows']($result) == 0)
+			fatal_lang_error('no_user', false);
+		list($_REQUEST['u'], $display_name, $username) = $smcFunc['db_fetch_row']($result);
 
-	// We need a message ID to check!
-	if (empty($_REQUEST['msg']) && empty($_REQUEST['mid']))
-		fatal_lang_error('no_access', false);
+		$context['current_user'] = $_REQUEST['u'];
+		$context['submit_url'] = $scripturl . '?action=reporttm;u=' . $_REQUEST['u'];
+	}
 
-	// For compatibility, accept mid, but we should be using msg. (not the flavor kind!)
-	$_REQUEST['msg'] = empty($_REQUEST['msg']) ? (int) $_REQUEST['mid'] : (int) $_REQUEST['msg'];
+	$context['comment_body'] = !isset($_POST['comment']) ? '' : trim($_POST['comment']);
 
-	// Check the message's ID - don't want anyone reporting a post they can't even see!
-	$result = $smcFunc['db_query']('', '
-		SELECT m.id_msg, m.id_member, t.id_member_started
-		FROM {db_prefix}messages AS m
-			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
-		WHERE m.id_msg = {int:id_msg}
-			AND m.id_topic = {int:current_topic}
-		LIMIT 1',
-		array(
-			'current_topic' => $topic,
-			'id_msg' => $_REQUEST['msg'],
-		)
-	);
-	if ($smcFunc['db_num_rows']($result) == 0)
-		fatal_lang_error('no_board', false);
-	list ($_REQUEST['msg'], $member, $starter) = $smcFunc['db_fetch_row']($result);
-	$smcFunc['db_free_result']($result);
+	$context['page_title'] = $context['report_type'] == 'msg' ? $txt['report_to_mod'] : sprintf($txt['report_profile'], $display_name);
+	$context['notice'] = $context['report_type'] == 'msg' ? $txt['report_to_mod_func'] : $txt['report_profile_func'];
 
 	// Show the inputs for the comment, etc.
 	loadLanguage('Post');
-	loadTemplate('SendTopic');
+	loadTemplate('ReportToMod');
 
 	addInlineJavascript('
 	var error_box = $("#error_box");
@@ -227,15 +154,6 @@ function ReportToModerator()
 				error_box.hide();
 		}
 	});', true);
-
-	$context['comment_body'] = !isset($_POST['comment']) ? '' : trim($_POST['comment']);
-
-	// This is here so that the user could, in theory, be redirected back to the topic.
-	$context['start'] = $_REQUEST['start'];
-	$context['message_id'] = $_REQUEST['msg'];
-
-	$context['page_title'] = $txt['report_to_mod'];
-	$context['sub_template'] = 'report';
 }
 
 /**
@@ -253,13 +171,19 @@ function ReportToModerator2()
 	is_not_guest();
 
 	// You must have the proper permissions!
-	isAllowedTo('report_any');
+	if (isset($_REQUEST['msg']))
+		isAllowedTo('report_any');
+	else
+		isAllowedTo('report_user');
 
 	// Make sure they aren't spamming.
 	spamProtection('reporttm');
 
 	require_once($sourcedir . '/Subs-Post.php');
 
+	// Prevent double submission of this form.
+	checkSubmitOnce('check');
+
 	// No errors, yet.
 	$post_errors = array();
 
@@ -270,37 +194,12 @@ function ReportToModerator2()
 	// Make sure we have a comment and it's clean.
 	if (!isset($_POST['comment']) || $smcFunc['htmltrim']($_POST['comment']) === '')
 		$post_errors[] = 'no_comment';
+
 	$poster_comment = strtr($smcFunc['htmlspecialchars']($_POST['comment']), array("\r" => '', "\t" => ''));
 
 	if ($smcFunc['strlen']($poster_comment) > 254)
 		$post_errors[] = 'post_too_long';
 
-	// Guests need to provide their address!
-	if ($user_info['is_guest'])
-	{
-		$_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
-		if ($_POST['email'] === '')
-			$post_errors[] = 'no_email';
-		elseif (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['email']) == 0)
-			$post_errors[] = 'bad_email';
-
-		isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
-
-		$user_info['email'] = $smcFunc['htmlspecialchars']($_POST['email']);
-	}
-
-	// Could they get the right verification code?
-	if ($user_info['is_guest'] && !empty($modSettings['guests_report_require_captcha']))
-	{
-		require_once($sourcedir . '/Subs-Editor.php');
-		$verificationOptions = array(
-			'id' => 'report',
-		);
-		$context['require_verification'] = create_control_verification($verificationOptions, true);
-		if (is_array($context['require_verification']))
-			$post_errors = array_merge($post_errors, $context['require_verification']);
-	}
-
 	// Any errors?
 	if (!empty($post_errors))
 	{
@@ -313,8 +212,23 @@ function ReportToModerator2()
 		return ReportToModerator();
 	}
 
+	if (isset($_POST['msg']))
+	{
+		// Handle this elsewhere to keep things from getting too long
+		reportPost($_POST['msg'], $poster_comment);
+	}
+	else
+	{
+		reportUser($_POST['u'], $poster_comment);
+	}
+}
+
+function reportPost($msg, $reason)
+{
+	global $context, $smcFunc, $user_info;
+
 	// Get the basic topic information, and make sure they can see it.
-	$_POST['msg'] = (int) $_POST['msg'];
+	$_POST['msg'] = (int) $msg;
 
 	$request = $smcFunc['db_query']('', '
 		SELECT m.id_topic, m.id_board, m.subject, m.body, m.id_member AS id_poster, m.poster_name, mem.real_name
@@ -402,7 +316,7 @@ function ReportToModerator2()
 			),
 			array(
 				$id_report, $user_info['id'], $user_info['name'],
-				$user_info['ip'], $poster_comment, time(),
+				$user_info['ip'], $reason, time(),
 			),
 			array('id_comment')
 		);
@@ -431,4 +345,120 @@ function ReportToModerator2()
 	redirectexit('reportsent;topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg']);
 }
 
+function reportUser($id_member, $reason)
+{
+	global $context, $smcFunc, $user_info;
+
+	// Get the basic topic information, and make sure they can see it.
+	$_POST['u'] = (int) $id_member;
+
+	$request = $smcFunc['db_query']('', '
+		SELECT id_member, real_name, member_name
+		FROM {db_prefix}members
+		WHERE id_member = {int:id_member}',
+		array(
+			'id_member' => $_POST['u']
+		)
+	);
+	if ($smcFunc['db_num_rows']($request) == 0)
+		fatal_lang_error('no_user', false);
+	$user = $smcFunc['db_fetch_assoc']($request);
+	$smcFunc['db_free_result']($request);
+
+	$user_name = un_htmlspecialchars($user['real_name']) . ($user['real_name'] != $user['member_name'] ? ' (' . $user['member_name'] . ')' : '');
+	$reporterName = un_htmlspecialchars($user_info['name']) . ($user_info['name'] != $user_info['username'] && $user_info['username'] != '' ? ' (' . $user_info['username'] . ')' : '');
+
+	$request = $smcFunc['db_query']('', '
+		SELECT id_report, ignore_all
+		FROM {db_prefix}log_reported
+		WHERE id_member = {int:id_member}
+			AND id_msg = {int:not_a_reported_post}
+			AND (closed = {int:not_closed} OR ignore_all = {int:ignored})
+		ORDER BY ignore_all DESC',
+		array(
+			'id_member' => $_POST['u'],
+			'not_a_reported_post' => 0,
+			'not_closed' => 0,
+			'ignored' => 1,
+		)
+	);
+	if ($smcFunc['db_num_rows']($request) != 0)
+		list ($id_report, $ignore) = $smcFunc['db_fetch_row']($request);
+
+	$smcFunc['db_free_result']($request);
+
+	// If we're just going to ignore these, then who gives a monkeys...
+	if (!empty($ignore))
+		redirectexit('action=profile;u=' . $_POST['u']);
+
+	// Already reported? My god, we could be dealing with a real rogue here...
+	if (!empty($id_report))
+		$smcFunc['db_query']('', '
+			UPDATE {db_prefix}log_reported
+			SET num_reports = num_reports + 1, time_updated = {int:current_time}
+			WHERE id_report = {int:id_report}',
+			array(
+				'current_time' => time(),
+				'id_report' => $id_report,
+			)
+		);
+	// Otherwise, we shall make one!
+	else
+	{
+		$smcFunc['db_insert']('',
+			'{db_prefix}log_reported',
+			array(
+				'id_msg' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'id_member' => 'int', 'membername' => 'string',
+				'subject' => 'string', 'body' => 'string', 'time_started' => 'int', 'time_updated' => 'int',
+				'num_reports' => 'int', 'closed' => 'int',
+			),
+			array(
+				0, 0, 0, $user['id_member'], $user_name,
+				'', '', time(), time(), 1, 0,
+			),
+			array('id_report')
+		);
+		$id_report = $smcFunc['db_insert_id']('{db_prefix}log_reported', 'id_report');
+	}
+
+	// Now just add our report...
+	if ($id_report)
+	{
+		$smcFunc['db_insert']('',
+			'{db_prefix}log_reported_comments',
+			array(
+				'id_report' => 'int', 'id_member' => 'int', 'membername' => 'string',
+				'member_ip' => 'string', 'comment' => 'string', 'time_sent' => 'int',
+			),
+			array(
+				$id_report, $user_info['id'], $user_info['name'],
+				$user_info['ip'], $reason, time(),
+			),
+			array('id_comment')
+		);
+
+		// And get ready to notify people.
+		$smcFunc['db_insert']('insert',
+			'{db_prefix}background_tasks',
+			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
+			array('$sourcedir/tasks/MemberReport-Notify.php', 'MemberReport_Notify_Background', serialize(array(
+				'report_id' => $id_report,
+				'user_id' => $user['id_member'],
+				'user_name' => $user_name,
+				'sender_id' => $context['user']['id'],
+				'sender_name' => $context['user']['name'],
+				'comment' => $reason,
+				'time' => time(),
+			)), 0),
+			array('id_task')
+		);
+	}
+
+	// Keep track of when the mod reports get updated, that way we know when we need to look again.
+	updateSettings(array('last_mod_report_action' => time()));
+
+	// Back to the post we reported!
+	redirectexit('reportsent;action=profile;u=' . $id_member);	
+}
+
 ?>

+ 543 - 0
Sources/ReportedPosts.php

@@ -0,0 +1,543 @@
+<?php
+
+/**
+ * Handles reported posts and moderation comments.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('No direct access...');
+
+/**
+ * Sets and call a function based on the given subaction. Acts as a dispatcher function.
+ * It requires the moderate_forum permission.
+ *
+ * @uses ModerationCenter template.
+ * @uses ModerationCenter language file.
+ *
+ */
+function ReportedPosts()
+{
+	global $txt, $context, $scripturl, $user_info, $smcFunc;
+	global $sourcedir;
+
+	loadLanguage('ModerationCenter');
+	loadTemplate('ReportedPosts');
+
+	// We need this little rough gem.
+	require_once($sourcedir . '/Subs-ReportedPosts.php');
+
+	// Do we need to show a confirmation message?
+	$context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : array();
+	unset($_SESSION['rc_confirmation']);
+
+	// Set up the comforting bits...
+	$context['page_title'] = $txt['mc_reported_posts'];
+
+	// Put the open and closed options into tabs, because we can...
+	$context[$context['moderation_menu_name']]['tab_data'] = array(
+		'title' => $txt['mc_reported_posts'],
+		'help' => '',
+		'description' => $txt['mc_reported_posts_desc'],
+	);
+
+	// This comes under the umbrella of moderating posts.
+	if ($user_info['mod_cache']['bq'] == '0=1')
+		isAllowedTo('moderate_forum');
+
+	$sub_actions = array(
+		'show' => 'ShowReports',
+		'closed' => 'ShowClosedReports',
+		'handle' => 'HandleReport', // Deals with closing/opening reports.
+		'details' => 'ReportDetails', // Shows a single report and its comments.
+		'handlecomment' => 'HandleComment', // CRUD actions for moderator comments.
+		'editcomment' => 'EditComment',
+	);
+
+	// Go ahead and add your own sub-actions.
+	call_integration_hook('integrate_reported_posts', array(&$sub_actions));
+
+	// By default we call the open sub-action.
+	if (isset($_REQUEST['sa']) && isset($sub_actions[$_REQUEST['sa']]))
+		$context['sub_action'] = $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_REQUEST['sa']), ENT_QUOTES);
+
+	else
+		$context['sub_action'] = 'show';
+
+	// Hi Ho Silver Away!
+	$sub_actions[$context['sub_action']]();
+}
+
+/**
+ * Shows all currently open reported posts.
+ * Handles closing multiple reports
+ *
+ */
+function ShowReports()
+{
+	global $context, $txt, $scripturl;
+
+	// Showing closed or open ones? regardless, turn this to an integer for better handling.
+	$context['view_closed'] = 0;
+
+	// Call the right template.
+	$context['sub_template'] = 'reported_posts';
+	$context['start'] = (int) isset($_GET['start']) ? $_GET['start'] : 0;
+
+	// Before anything, we need to know just how many reports do we have.
+	$context['total_reports'] = countReports($context['view_closed']);
+
+	// Just how many items are we showing per page?
+	$context['reports_how_many'] = 10;
+
+	// So, that means we can have pagination, yes?
+	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports;sa=show', $context['start'], $context['total_reports'], $context['reports_how_many']);
+
+	// Get the reports at once!
+	$context['reports'] = getReports($context['view_closed']);
+
+	// Are we closing multiple reports?
+	if (isset($_POST['close']) && isset($_POST['close_selected']))
+	{
+		checkSession('post');
+		validateToken('mod-report-close-all');
+
+		// All the ones to update...
+		$toClose = array();
+		foreach ($_POST['close'] as $rid)
+			$toClose[] = (int) $rid;
+
+		if (!empty($toClose))
+			updateReport('closed', 1, $toClose);
+
+		// Set the confirmation message.
+		$_SESSION['rc_confirmation'] = 'close_all';
+
+		// Force a page refresh.
+		redirectexit($scripturl . '?action=moderate;area=reports');
+	}
+
+	// Show a confirmation if the user wants to disregard a report.
+	if (!$context['view_closed'])
+		addInlineJavascript('
+	$(\'.delete_message\').on(\'click\', function(){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_delete_confirm']) .');
+	});
+	$(\'.report_ignore\').on(\'click\', function(){
+		// Need to make sure to only show this when ignoring.
+		if ($(this).data(\'ignore\') == \'1\'){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) .');
+		}
+	});', true);
+
+	createToken('mod-report-close-all');
+	createToken('mod-report-ignore', 'get');
+	createToken('mod-report-closed', 'get');
+}
+
+/**
+ * Shows all currently closed reported posts.
+ *
+ */
+function ShowClosedReports()
+{
+	global $context, $txt, $scripturl;
+
+	// Showing closed ones.
+	$context['view_closed'] = 1;
+
+	// Call the right template.
+	$context['sub_template'] = 'reported_posts';
+	$context['start'] = (int) isset($_GET['start']) ? $_GET['start'] : 0;
+
+	// Before anything, we need to know just how many reports do we have.
+	$context['total_reports'] = countReports($context['view_closed']);
+
+	// Just how many items are we showing per page?
+	$context['reports_how_many'] = 10;
+
+	// So, that means we can have pagination, yes?
+	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=reports;sa=closed', $context['start'], $context['total_reports'], $context['reports_how_many']);
+
+	// Get the reports at once!
+	$context['reports'] = getReports($context['view_closed']);
+
+	// Show a confirmation if the user wants to disregard a report.
+	addInlineJavascript('
+	$(\'.delete_message\').on(\'click\', function(){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_delete_confirm']) .');
+	});
+	$(\'.report_ignore\').on(\'click\', function(){
+		// Need to make sure to only show this when ignoring.
+		if ($(this).data(\'ignore\') == \'1\'){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) .');
+		}
+	});', true);
+
+	createToken('mod-report-ignore', 'get');
+	createToken('mod-report-closed', 'get');
+}
+
+/**
+ * Shows detailed information about a report. such as report comments and moderator comments.
+ * Shows a list of moderation actions for the specific report.
+ *
+ */
+function ReportDetails()
+{
+	global $user_info, $context, $sourcedir, $scripturl, $txt;
+	global $smcFunc;
+
+	$report = array();
+	$reportComments = array();
+
+	// Have to at least give us something to work with.
+	if (empty($_REQUEST['rid']))
+		fatal_lang_error('mc_reportedp_none_found');
+
+	// Integers only please
+	$report_id = (int) $_REQUEST['rid'];
+
+	// Get the report details.
+	$report = getReportDetails($report_id);
+
+	if(!$report)
+		fatal_lang_error('mc_no_modreport_found');
+
+	// Build the report data.
+	$context['report'] = array(
+		'id' => $report['id_report'],
+		'topic_id' => $report['id_topic'],
+		'board_id' => $report['id_board'],
+		'message_id' => $report['id_msg'],
+		'message_href' => $scripturl . '?msg=' . $report['id_msg'],
+		'message_link' => '<a href="' . $scripturl . '?msg=' . $report['id_msg'] . '">' . $report['subject'] . '</a>',
+		'report_href' => $scripturl . '?action=moderate;area=reports;rid=' . $report['id_report'],
+		'author' => array(
+			'id' => $report['id_author'],
+			'name' => $report['author_name'],
+			'link' => $report['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $report['id_author'] . '">' . $report['author_name'] . '</a>' : $report['author_name'],
+			'href' => $scripturl . '?action=profile;u=' . $report['id_author'],
+		),
+		'comments' => array(),
+		'mod_comments' => array(),
+		'time_started' => timeformat($report['time_started']),
+		'last_updated' => timeformat($report['time_updated']),
+		'subject' => $report['subject'],
+		'body' => parse_bbc($report['body']),
+		'num_reports' => $report['num_reports'],
+		'closed' => $report['closed'],
+		'ignore' => $report['ignore_all']
+	);
+
+	$reportComments = getReportComments($report_id);
+
+	if (!empty($reportComments))
+		$context['report'] = array_merge($context['report'], $reportComments);
+
+	// What have the other moderators done to this message?
+	require_once($sourcedir . '/Modlog.php');
+	require_once($sourcedir . '/Subs-List.php');
+	loadLanguage('Modlog');
+
+	// This is all the information from the moderation log.
+	$listOptions = array(
+		'id' => 'moderation_actions_list',
+		'title' => $txt['mc_modreport_modactions'],
+		'items_per_page' => 15,
+		'no_items_label' => $txt['modlog_no_entries_found'],
+		'base_href' => $scripturl . '?action=moderate;area=reports;sa=details;rid=' . $context['report']['id'],
+		'default_sort_col' => 'time',
+		'get_items' => array(
+			'function' => 'list_getModLogEntries',
+			'params' => array(
+				'lm.id_topic = {int:id_topic}',
+				array('id_topic' => $context['report']['topic_id']),
+				1,
+			),
+		),
+		'get_count' => array(
+			'function' => 'list_getModLogEntryCount',
+			'params' => array(
+				'lm.id_topic = {int:id_topic}',
+				array('id_topic' => $context['report']['topic_id']),
+				1,
+			),
+		),
+		// This assumes we are viewing by user.
+		'columns' => array(
+			'action' => array(
+				'header' => array(
+					'value' => $txt['modlog_action'],
+				),
+				'data' => array(
+					'db' => 'action_text',
+					'class' => 'smalltext',
+				),
+				'sort' => array(
+					'default' => 'lm.action',
+					'reverse' => 'lm.action DESC',
+				),
+			),
+			'time' => array(
+				'header' => array(
+					'value' => $txt['modlog_date'],
+				),
+				'data' => array(
+					'db' => 'time',
+					'class' => 'smalltext',
+				),
+				'sort' => array(
+					'default' => 'lm.log_time',
+					'reverse' => 'lm.log_time DESC',
+				),
+			),
+			'moderator' => array(
+				'header' => array(
+					'value' => $txt['modlog_member'],
+				),
+				'data' => array(
+					'db' => 'moderator_link',
+					'class' => 'smalltext',
+				),
+				'sort' => array(
+					'default' => 'mem.real_name',
+					'reverse' => 'mem.real_name DESC',
+				),
+			),
+			'position' => array(
+				'header' => array(
+					'value' => $txt['modlog_position'],
+				),
+				'data' => array(
+					'db' => 'position',
+					'class' => 'smalltext',
+				),
+				'sort' => array(
+					'default' => 'mg.group_name',
+					'reverse' => 'mg.group_name DESC',
+				),
+			),
+			'ip' => array(
+				'header' => array(
+					'value' => $txt['modlog_ip'],
+				),
+				'data' => array(
+					'db' => 'ip',
+					'class' => 'smalltext',
+				),
+				'sort' => array(
+					'default' => 'lm.ip',
+					'reverse' => 'lm.ip DESC',
+				),
+			),
+		),
+	);
+
+	// Create the watched user list.
+	createList($listOptions);
+
+	// Make sure to get the correct tab selected.
+	if ($context['report']['closed'])
+		$context[$context['moderation_menu_name']]['current_subsection'] = 'closed';
+
+	addInlineJavascript('
+	$(\'.deleteModComment\').on(\'click\', function() {
+		return confirm('. (JavaScriptEscape($txt['mc_reportedp_delete_confirm'])) .');
+});', true);
+
+	// Finally we are done :P
+	$context['page_title'] = sprintf($txt['mc_viewmodreport'], $context['report']['subject'], $context['report']['author']['name']);
+	$context['sub_template'] = 'viewmodreport';
+
+	// We can ignore a report from this page too so show the confirmation on here as well.
+	addInlineJavascript('
+	$(\'.report_ignore\').on(\'click\', function(){
+		// Need to make sure to only show this when ignoring.
+		if ($(this).data(\'ignore\') == \'1\'){
+			return confirm('. JavaScriptEscape($txt['mc_reportedp_ignore_confirm']) .');
+		}
+	});', true);
+
+	createToken('mod-reportC-add');
+	createToken('mod-reportC-delete', 'get');
+
+	// We can "un-disregard" and close a report from here so add their respective tokens.
+	createToken('mod-report-ignore', 'get');
+	createToken('mod-report-closed', 'get');
+}
+
+/**
+ * Creates/Deletes moderator comments.
+ *
+ */
+function HandleComment()
+{
+	global $smcFunc, $scripturl, $user_info;
+
+	$comment = array();
+
+	// The report ID is a must.
+	if (empty($_REQUEST['rid']))
+		fatal_lang_error('mc_reportedp_none_found');
+
+	// Integers only please.
+	$report_id = (int) $_REQUEST['rid'];
+
+	// If they are adding a comment then... add a comment.
+	if (isset($_POST['add_comment']) && !empty($_POST['mod_comment']))
+	{
+		checkSession();
+		validateToken('mod-reportC-add');
+
+		$new_comment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
+
+		saveModComment($report_id, array($report_id, $new_comment, time()));
+
+		// Everything went better than expected!
+		$_SESSION['rc_confirmation'] = 'message_saved';
+	}
+
+	// Deleting a comment?
+	if (isset($_REQUEST['delete']) && isset($_REQUEST['mid']))
+	{
+		checkSession('get');
+		validateToken('mod-reportC-delete', 'get');
+
+		if (empty($_REQUEST['mid']))
+			fatal_lang_error('mc_reportedp_comment_none_found');
+
+		$comment_id = (int) $_REQUEST['mid'];
+
+		// We need to verify some data, so lets load the comment details once more!
+		$comment = getCommentModDetails($comment_id);
+
+		// Perhaps somebody else already deleted this fine gem...
+		if (empty($comment))
+			fatal_lang_error('report_action_message_delete_issue');
+
+		// Can you actually do this?
+		$comment_owner = $user_info['id'] == $context['comment']['id_member'];
+
+		// Nope! sorry.
+		if (!allowedTo('admin_forum') || !$comment_owner)
+			fatal_lang_error('report_action_message_delete_cannot');
+
+		// All good!
+		deleteModComment($comment_id);
+
+		// Tell them the message was deleted.
+		$_SESSION['rc_confirmation'] = 'message_deleted';
+	}
+
+	//Redirect to prevent double submission.
+	redirectexit($scripturl . '?action=moderate;area=reports;sa=details;rid=' . $report_id);
+}
+
+/**
+ * Shows a textarea for editing a moderator comment.
+ * Handles the edited comment and stores it on the DB.
+ *
+ */
+function EditComment()
+{
+	global $smcFunc, $context, $txt, $scripturl, $user_info;
+
+	$comment = array();
+
+	checkSession(isset($_REQUEST['save']) ? 'post' : 'get');
+
+	// The report ID is a must.
+	if (empty($_REQUEST['rid']))
+		fatal_lang_error('mc_reportedp_none_found');
+
+	if (empty($_REQUEST['mid']))
+		fatal_lang_error('mc_reportedp_comment_none_found');
+
+	// Integers only please.
+	$context['report_id'] = (int) $_REQUEST['rid'];
+	$context['comment_id'] = (int) $_REQUEST['mid'];
+
+	$context['comment'] = getCommentModDetails($context['comment_id']);
+
+	if (empty($context['comment']))
+		fatal_lang_error('mc_reportedp_comment_none_found');
+
+	// Set up the comforting bits...
+	$context['page_title'] = $txt['mc_reported_posts'];
+	$context['sub_template'] = 'edit_comment';
+
+	if (isset($_REQUEST['save']) && isset($_POST['edit_comment']) && !empty($_POST['mod_comment']))
+	{
+		validateToken('mod-reportC-edit');
+
+		// Make sure there is some data to edit on the DB.
+		if (empty($context['comment']))
+			fatal_lang_error('report_action_message_edit_issue');
+
+		// Still there, good, now lets see if you can actually edit it...
+		$comment_owner = $user_info['id'] == $context['comment']['id_member'];
+
+		// So, you aren't neither an admin or the comment owner huh? that's too bad.
+		if (!allowedTo('admin_forum') || !$comment_owner)
+			fatal_lang_error('report_action_message_edit_cannot');
+
+		// All good!
+		$edited_comment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
+
+		editModComment($context['comment_id'], $edited_comment);
+
+		$_SESSION['rc_confirmation'] = 'message_edited';
+
+		redirectexit($scripturl . '?action=moderate;area=reports;sa=details;rid=' . $context['report_id']);
+	}
+
+	createToken('mod-reportC-edit');
+}
+
+/**
+ * Performs closing/ignoring actions for a given report.
+ *
+ */
+function HandleReport()
+{
+	global $scripturl;
+
+	checkSession('get');
+
+	// We need to do something!
+	if (empty($_GET['rid']) && (!isset($_GET['ignore']) || !isset($_GET['closed'])))
+		fatal_lang_error('mc_reportedp_none_found');
+
+	// What are we gonna do?
+	$action = isset($_GET['ignore']) ? 'ignore' : 'closed';
+
+	validateToken('mod-report-'. $action, 'get');
+
+	// Are we disregarding or "un-disregarding"? "un-disregarding" thats a funny word!
+	$value = (int) $_GET[$action];
+
+	// Figuring out.
+	$message = $action == 'ignore' ? ($value ? 'ignore' : 'unignore') : ($value ? 'close' : 'open');
+
+	// Integers only please.
+	$report_id = (int) $_REQUEST['rid'];
+
+	// Update the DB entry
+	updateReport($action, $value, $report_id);
+
+	// So, time to show a confirmation message, lets do some trickery!
+	$_SESSION['rc_confirmation'] = $message;
+
+	// Done!
+	redirectexit($scripturl . '?action=moderate;area=reports');
+}
+?>

+ 3 - 0
Sources/Search.php

@@ -45,7 +45,10 @@ function PlushSearch1()
 	loadLanguage('Search');
 	// Don't load this in XML mode.
 	if (!isset($_REQUEST['xml']))
+	{
 		loadTemplate('Search');
+		loadJavascriptFile('suggest.js', array('default_theme' => true, 'defer' => false), 'smf_suggest');
+	}
 
 	// Check the user's permissions.
 	isAllowedTo('search_posts');

+ 13 - 37
Sources/Security.php

@@ -155,6 +155,7 @@ function is_not_guest($message = '')
 	else
 	{
 		loadTemplate('Login');
+		loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
 		$context['sub_template'] = 'kick_guest';
 		$context['robot_no_index'] = true;
 	}
@@ -493,11 +494,22 @@ function banPermissions()
 		$context['open_mod_reports'] = $_SESSION['rc']['reports'];
 	elseif ($_SESSION['mc']['bq'] != '0=1')
 	{
-		require_once($sourcedir . '/ModerationCenter.php');
+		require_once($sourcedir . '/Subs-ReportedPosts.php');
 		recountOpenReports();
 	}
 	else
 		$context['open_mod_reports'] = 0;
+
+	if (isset($_SESSION['rmc']) && $_SESSION['rmc']['time'] > $modSettings['last_mod_report_action'] && $_SESSION['rmc']['id'] == $user_info['id'])
+		$contexct['open_member_reports'] = $_SESSION['rmc']['reports'];
+	elseif (allowedTo('moderate_forum'))
+	{
+		require_once($sourcedir . '/ModerationCenter.php');
+		recountOpenMemberReports();
+	}
+	else
+		$context['open_member_reports'] = 0;
+
 }
 
 /**
@@ -1135,42 +1147,6 @@ function boardsAllowedTo($permissions, $check_access = true, $simple = true)
 	return $boards;
 }
 
-/**
- * Returns whether an email address should be shown and how.
- * Possible outcomes are
- *  'yes': show the full email address
- *  'yes_permission_override': show the full email address, either you
- *   are a moderator or it's your own email address.
- *  'no_through_forum': don't show the email address, but do allow
- *    things to be mailed using the built-in forum mailer.
- *  'no': keep the email address hidden.
- *
- * @param bool $userProfile_hideEmail
- * @param int $userProfile_id
- * @return string (yes, yes_permission_override, no_through_forum, no)
- */
-function showEmailAddress($userProfile_hideEmail, $userProfile_id)
-{
-	global $user_info;
-
-	// Should this user's email address be shown?
-	// If you're guest and the forum is set to hide email for guests: no.
-	// If the user is post-banned: no.
-	// If it's your own profile and you've set your address hidden: yes_permission_override.
-	// If you're a moderator with sufficient permissions: yes_permission_override.
-	// If the user has set their email address to be hidden: no.
-	// Otherwise: no_through_forum.
-
-	if ($user_info['is_guest'] || isset($_SESSION['ban']['cannot_post']))
-		return 'no';
-	elseif ((!$user_info['is_guest'] && $user_info['id'] == $userProfile_id && !$userProfile_hideEmail) || allowedTo('moderate_forum'))
-		return 'yes_permission_override';
-	elseif ($userProfile_hideEmail)
-		return 'no';
-	else
-		return 'no_through_forum';
-}
-
 /**
  * This function attempts to protect from spammed messages and the like.
  * The time taken depends on error_type - generally uses the modSetting.

+ 1 - 0
Sources/Stats.php

@@ -70,6 +70,7 @@ function DisplayStats()
 
 	loadLanguage('Stats');
 	loadTemplate('Stats');
+	loadJavascriptFile('stats.js', array('default_theme' => true, 'defer' => false), 'smf_stats');
 
 	// Build the link tree......
 	$context['linktree'][] = array(

+ 5 - 0
Sources/Subs-Auth.php

@@ -158,6 +158,9 @@ function KickGuest()
 	loadTemplate('Login');
 	createToken('login');
 
+	// Need some js goodies.
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
+
 	// Never redirect to an attachment
 	if (strpos($_SERVER['REQUEST_URL'], 'dlattach') === false)
 		$_SESSION['login_url'] = $_SERVER['REQUEST_URL'];
@@ -178,6 +181,7 @@ function InMaintenance()
 	loadLanguage('Login');
 	loadTemplate('Login');
 	createToken('login');
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
 
 	// Send a 503 header, so search engines don't bother indexing while we're in maintenance mode.
 	header('HTTP/1.1 503 Service Temporarily Unavailable');
@@ -203,6 +207,7 @@ function adminLogin($type = 'admin')
 
 	loadLanguage('Admin');
 	loadTemplate('Login');
+	loadJavascriptFile('sha1.js', array('default_theme' => true), 'smf_sha1');
 
 	// Validate what type of session check this is.
 	$types = array();

+ 529 - 0
Sources/Subs-ReportedPosts.php

@@ -0,0 +1,529 @@
+<?php
+
+/**
+ * Perform CRUD actions for reported posts and moderation comments.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('No direct access...');
+
+/**
+ * Updates a report with the given parameters. Logs each action via logAction()
+ *
+ * @param string $action The action to perform. Accepts "closed" and "ignore".
+ * @param integer $value The new value to update.
+ * @params integer|array $report_id The affected report(s).
+ */
+function updateReport($action, $value, $report_id)
+{
+	global $smcFunc, $user_info, $context;
+
+	// Don't bother.
+	if (empty($action) || empty($report_id))
+		return false;
+
+	// Add the "_all" thingy.
+	if ($action == 'ignore')
+		$action = 'ignore_all';
+
+	// Update the report...
+	$smcFunc['db_query']('', '
+		UPDATE {db_prefix}log_reported
+		SET  {raw:action} = {string:value}
+		'. (is_array($report_id) ? 'WHERE id_report IN ({array_int:id_report})' : 'WHERE id_report = {int:id_report}') .'
+			AND ' . $user_info['mod_cache']['bq'],
+		array(
+			'action' => $action,
+			'value' => $value,
+			'id_report' => $report_id,
+		)
+	);
+
+	// From now on, lets work with arrays, makes life easier.
+	$report_id = (array) $report_id;
+
+	// Get the board, topic and message for this report
+	$request = $smcFunc['db_query']('', '
+		SELECT id_board, id_topic, id_msg, id_report
+		FROM {db_prefix}log_reported
+		WHERE id_report IN ({array_int:id_report})',
+		array(
+			'id_report' => $report_id,
+		)
+	);
+
+	// Set up the data for the log...
+	$extra = array();
+
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+		$extra[$row['id_report']] = array(
+			'report' => $row['id_report'],
+			'board' => $row['id_board'],
+			'message' => $row['id_msg'],
+			'topic' => $row['id_topic'],
+		);
+
+	$smcFunc['db_free_result']($request);
+
+	// Back to "ignore".
+	if ($action == 'ignore_all')
+		$action = 'ignore';
+
+	$log_report = $action == 'ignore' ? (!empty($value) ? 'ignore' : 'unignore') : (!empty($value) ? 'close' : 'open');
+
+	// Log this action.
+	if (!empty($extra))
+		foreach ($extra as $report)
+			logAction($log_report . '_report', $report);
+
+	// Time to update.
+	updateSettings(array('last_mod_report_action' => time()));
+	recountOpenReports();
+}
+
+/**
+ * Counts how many reports are in total. Used for creating pagination.
+ *
+ * @param int $closed 1 for counting closed reports, 0 for open ones.
+ * @return integer How many reports.
+
+ */
+function countReports($closed = 0)
+{
+	global $smcFunc, $user_info;
+
+	$total_reports = 0;
+
+	// How many entries are we viewing?
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(*)
+		FROM {db_prefix}log_reported AS lr
+		WHERE lr.closed = {int:view_closed}
+			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']),
+		array(
+			'view_closed' => (int) $closed,
+		)
+	);
+	list ($total_reports) = $smcFunc['db_fetch_row']($request);
+	$smcFunc['db_free_result']($request);
+
+	return $total_reports;
+}
+
+/**
+ * Get all possible reports the current user can see.
+ *
+ * @param int $closed 1 for closed reports, 0 for open ones.
+ * @return array the reports data with the report ID as key.
+ */
+function getReports($closed = 0)
+{
+	global $smcFunc, $context, $user_info, $scripturl;
+
+	// Lonely, standalone var.
+	$reports = array();
+
+	// By George, that means we in a position to get the reports, golly good.
+	$request = $smcFunc['db_query']('', '
+		SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
+			lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
+			IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author
+		FROM {db_prefix}log_reported AS lr
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
+		WHERE lr.closed = {int:view_closed}
+			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
+		ORDER BY lr.time_updated DESC
+		LIMIT ' . $context['start'] . ', 10',
+		array(
+			'view_closed' => (int) $closed,
+		)
+	);
+
+	$report_ids = array();
+	$report_boards_ids = array();
+	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
+	{
+		$report_ids[] = $row['id_report'];
+		$report_boards_ids[] = $row['id_board'];
+		$reports[$row['id_report']] = array(
+			'id' => $row['id_report'],
+			'alternate' => $i % 2,
+			'topic' => array(
+				'id' => $row['id_topic'],
+				'id_msg' => $row['id_msg'],
+				'id_board' => $row['id_board'],
+				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
+			),
+			'report_href' => $scripturl . '?action=moderate;area=reports;sa=details;rid=' . $row['id_report'],
+			'author' => array(
+				'id' => $row['id_author'],
+				'name' => $row['author_name'],
+				'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'],
+				'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
+			),
+			'comments' => array(),
+			'time_started' => timeformat($row['time_started']),
+			'last_updated' => timeformat($row['time_updated']),
+			'subject' => $row['subject'],
+			'body' => parse_bbc($row['body']),
+			'num_reports' => $row['num_reports'],
+			'closed' => $row['closed'],
+			'ignore' => $row['ignore_all']
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// Get the names of boards those topics are in. Slightly faster this way.
+	if (!empty($report_boards_ids))
+	{
+		$report_boards_ids = array_unique($report_boards_ids);
+		$board_names = array();
+		$request = $smcFunc['db_query']('', '
+			SELECT id_board, name
+			FROM {db_prefix}boards
+			WHERE id_board IN ({array_int:boards})',
+			array(
+				'boards' => $report_boards_ids,
+			)
+		);
+
+		while ($row = $smcFunc['db_fetch_assoc']($request))
+			$board_names[$row['id_board']] = $row['name'];
+
+		$smcFunc['db_free_result']($request);
+
+		foreach ($reports as $id_report => $report)
+			if (!empty($board_names[$report['topic']['id_board']]))
+				$reports[$id_report]['topic']['board_name'] = $board_names[$report['topic']['id_board']];
+	}
+
+	// Now get all the people who reported it.
+	if (!empty($report_ids))
+	{
+		$request = $smcFunc['db_query']('', '
+			SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment,
+				IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter
+			FROM {db_prefix}log_reported_comments AS lrc
+				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
+			WHERE lrc.id_report IN ({array_int:report_list})',
+			array(
+				'report_list' => $report_ids,
+			)
+		);
+		while ($row = $smcFunc['db_fetch_assoc']($request))
+		{
+			$reports[$row['id_report']]['comments'][] = array(
+				'id' => $row['id_comment'],
+				'message' => $row['comment'],
+				'time' => timeformat($row['time_sent']),
+				'member' => array(
+					'id' => $row['id_member'],
+					'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
+					'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
+					'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
+				),
+			);
+		}
+		$smcFunc['db_free_result']($request);
+	}
+
+	// Get the boards where the current user can remove any message.
+	$context['report_remove_any_boards'] = $user_info['is_admin'] ? $report_boards_ids : array_intersect($report_boards_ids, boardsAllowedTo('remove_any'));
+	$context['report_manage_bans'] = allowedTo('manage_bans');
+
+	return $reports;
+}
+
+/**
+ * Recount all open reports. Sets a SESSION var with the updated info.
+ *
+ * @return int the update open report count.
+ */
+function recountOpenReports()
+{
+	global $user_info, $smcFunc;
+
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(*)
+		FROM {db_prefix}log_reported
+		WHERE ' . $user_info['mod_cache']['bq'] . '
+			AND closed = {int:not_closed}
+			AND ignore_all = {int:not_ignored}',
+		array(
+			'not_closed' => 0,
+			'not_ignored' => 0,
+		)
+	);
+	list ($open_reports) = $smcFunc['db_fetch_row']($request);
+	$smcFunc['db_free_result']($request);
+
+	$_SESSION['rc'] = array(
+		'id' => $user_info['id'],
+		'time' => time(),
+		'reports' => $open_reports,
+	);
+
+	return $open_reports;
+}
+
+/**
+ * Gets additional information for a specific report.
+ *
+ * @param int $report_id The report ID to get the info from.
+ * @return array|bool the report data. Boolean false if no report_id was provided.
+ */
+function getReportDetails($report_id)
+{
+	global $smcFunc, $user_info;
+
+	if (empty($report_id))
+		return false;
+
+	// Get the report details, need this so we can limit access to a particular board.
+	$request = $smcFunc['db_query']('', '
+		SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.body,
+			lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
+			IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author
+		FROM {db_prefix}log_reported AS lr
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
+		WHERE lr.id_report = {int:id_report}
+			AND ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
+		LIMIT 1',
+		array(
+			'id_report' => $report_id,
+		)
+	);
+
+	// So did we find anything?
+	if (!$smcFunc['db_num_rows']($request))
+		return false;
+
+	// Woohoo we found a report and they can see it!
+	$row = $smcFunc['db_fetch_assoc']($request);
+	$smcFunc['db_free_result']($request);
+
+	return $row;
+}
+
+/**
+ * Gets both report comments as well as any moderator comment.
+ *
+ * @param int $report_id The report ID to get the info from.
+ * @return array|bool an associative array with 2 keys comments and mod_comments. Boolean false if no report_id was provided.
+ */
+function getReportComments($report_id)
+{
+	global $smcFunc, $scripturl;
+
+	if (empty($report_id))
+		return false;
+
+	$report = array(
+		'comments' => array(),
+		'mod_comments' => array()
+	);
+
+	// So what bad things do the reporters have to say about it?
+	$request = $smcFunc['db_query']('', '
+		SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, lrc.member_ip,
+			IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter
+		FROM {db_prefix}log_reported_comments AS lrc
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
+		WHERE lrc.id_report = {int:id_report}',
+		array(
+			'id_report' => $report_id,
+		)
+	);
+
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$report['comments'][] = array(
+			'id' => $row['id_comment'],
+			'message' => strtr($row['comment'], array("\n" => '<br>')),
+			'time' => timeformat($row['time_sent']),
+			'member' => array(
+				'id' => $row['id_member'],
+				'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
+				'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
+				'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
+				'ip' => !empty($row['member_ip']) && allowedTo('moderate_forum') ? '<a href="' . $scripturl . '?action=trackip;searchip=' . $row['member_ip'] . '">' . $row['member_ip'] . '</a>' : '',
+			),
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// Hang about old chap, any comments from moderators on this one?
+	$request = $smcFunc['db_query']('', '
+		SELECT lc.id_comment, lc.id_notice, lc.log_time, lc.body,
+			IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS moderator
+		FROM {db_prefix}log_comments AS lc
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
+		WHERE lc.id_notice = {int:id_report}
+			AND lc.comment_type = {literal:reportc}',
+		array(
+			'id_report' => $report_id,
+		)
+	);
+
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$report['mod_comments'][] = array(
+			'id' => $row['id_comment'],
+			'message' => parse_bbc($row['body']),
+			'time' => timeformat($row['log_time']),
+			'can_edit' => allowedTo('admin_forum') || (($user_info['id'] == $row['id_member']) && allowedTo('moderate_forum')),
+			'member' => array(
+				'id' => $row['id_member'],
+				'name' => $row['moderator'],
+				'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['moderator'] . '</a>' : $row['moderator'],
+				'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
+			),
+		);
+	}
+
+	$smcFunc['db_free_result']($request);
+
+	return $report;
+}
+
+/**
+ * Gets specific details about a moderator comment. It also adds a permission for editing/deleting the comment,
+ * by default only admins and the author of the comment can edit/delete it.
+ *
+ * @param int $comment_id The moderator comment ID to get the info from.
+ * @return array|bool an array with the fetched data. Boolean false if no report_id was provided.
+ */
+function getCommentModDetails($comment_id)
+{
+	global $smcFunc, $user_info;
+
+	$comment = array();
+
+	if (empty($comment_id))
+		return false;
+
+	$request = $smcFunc['db_query']('', '
+		SELECT id_comment, id_notice, log_time, body, id_member
+		FROM {db_prefix}log_comments
+		WHERE id_comment = {int:id_comment}
+			AND comment_type = {literal:reportc}',
+		array(
+			'id_comment' => $comment_id,
+		)
+	);
+
+	$comment = $smcFunc['db_fetch_assoc']($request);
+
+	$smcFunc['db_free_result']($request);
+
+	// Add the permission
+	if (!empty($comment))
+		$comment['can_edit'] = allowedTo('admin_forum') || (($user_info['id'] == $comment['id_member']) && allowedTo('moderate_forum'));
+
+	return $comment;
+}
+
+/**
+ * Inserts a new moderator comment to the DB.
+ *
+ * @param int $report_id The report ID is used to fire a notification about the event.
+ * @param array $data a formatted array of data to be inserted. Should be already properly sanitized.
+ * @return bool  Boolean false if no data was provided.
+ */
+function saveModComment($report_id, $data)
+{
+	global $smcFunc, $user_info;
+
+	if (empty($data))
+		return false;
+
+	$data = array_merge(array($user_info['id'], $user_info['name'], 'reportc', ''), $data);
+
+	$smcFunc['db_insert']('',
+		'{db_prefix}log_comments',
+		array(
+			'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
+			'id_notice' => 'int', 'body' => 'string', 'log_time' => 'int',
+		),
+		$data,
+		array('id_comment')
+	);
+	$last_comment = $smcFunc['db_insert_id']('{db_prefix}log_comments', 'id_comment');
+
+	$report = getReportDetails($report_id);
+
+	// And get ready to notify people.
+	if (!empty($report))
+		$smcFunc['db_insert']('insert',
+			'{db_prefix}background_tasks',
+			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
+			array('$sourcedir/tasks/MsgReportReply-Notify.php', 'MsgReportReply_Notify_Background', serialize(array(
+				'report_id' => $report_id,
+				'comment_id' => $last_comment,
+				'msg_id' => $report['id_msg'],
+				'topic_id' => $report['id_topic'],
+				'board_id' => $report['id_board'],
+				'sender_id' => $user_info['id'],
+				'sender_name' => $user_info['name'],
+				'time' => time(),
+			)), 0),
+			array('id_task')
+		);
+}
+
+/**
+ * Saves the new information whenever a moderator comment is edited.
+ *
+ * @param int $comment_id The edited moderator comment ID.
+ * @param array $data The new data to de inserted. Should be already properly sanitized.
+ * @return bool  Boolean false if no data or no comment ID was provided.
+ */
+function editModComment($comment_id, $edited_comment)
+{
+	global $smcFunc;
+
+	if (empty($comment_id) || empty($edited_comment))
+		return false;
+
+	$smcFunc['db_query']('', '
+		UPDATE {db_prefix}log_comments
+		SET  body = {string:body}
+		WHERE id_comment = {int:id_comment}',
+		array(
+			'body' => $edited_comment,
+			'id_comment' => $comment_id,
+		)
+	);
+}
+
+/**
+ * Deletes a moderator comment from the DB.
+ *
+ * @param int $comment_id The moderator comment ID used to identify which report will be deleted.
+ * @return bool  Boolean false if no data was provided.
+ */
+function deleteModComment($comment_id)
+{
+	global $smcFunc;
+
+	if (empty($comment_id))
+		return false;
+
+	$smcFunc['db_query']('', '
+		DELETE FROM {db_prefix}log_comments
+		WHERE id_comment = {int:comment_id}',
+		array(
+			'comment_id' => $comment_id,
+		)
+	);
+}
+?>

+ 28 - 5
Sources/Subs.php

@@ -3347,7 +3347,7 @@ function template_css()
 		// Try to keep only what's useful.
 		$repl = array($boardurl . '/Themes/' => '', $boardurl . '/' => '');
 		foreach ($context['css_files'] as $file)
-			$context['debug']['sheets'][] = strtr($file['filename'], $repl); 
+			$context['debug']['sheets'][] = strtr($file['filename'], $repl);
 	}
 
 	if (!empty($context['css_header']))
@@ -3715,7 +3715,7 @@ function setupMenuContext()
 	if (!isset($context['allow_calendar_event']))
 	{
 		$context['allow_calendar_event'] = $context['allow_calendar'] && allowedTo('calendar_post');
-		
+
 		// If you don't allow events not linked to posts and you're not an admin, we have more work to do...
 		if ($context['allow_calendar'] && $context['allow_calendar_event'] && empty($modSettings['cal_allow_unlinked']) && !$user_info['is_admin'])
 		{
@@ -3812,8 +3812,13 @@ function setupMenuContext()
 						'title' => $txt['mc_reported_posts'],
 						'href' => $scripturl . '?action=moderate;area=reports',
 						'show' => !empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1',
-						'is_last' => true,
 					),
+					'reported_members' => array(
+						'title' => $txt['mc_reported_members'],
+						'href' => $scripturl . '?action=moderate;area=memberreports',
+						'show' => allowedTo('moderate_forum'),
+						'is_last' => true,
+					)
 				),
 			),
 			'calendar' => array(
@@ -3959,17 +3964,35 @@ function setupMenuContext()
 	if (isset($context['menu_buttons'][$current_action]))
 		$context['menu_buttons'][$current_action]['active_button'] = true;
 
-	if (!empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1' && $context['open_mod_reports'] > 0)
+	$total_mod_reports = 0;
+
+	if (!empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1' && !empty($context['open_mod_reports']))
 	{
-		$context['menu_buttons']['moderate']['title'] .= ' <span class="amt">' . $context['open_mod_reports'] . '</span>';
+		$total_mod_reports = $context['open_mod_reports'];
 		$context['menu_buttons']['moderate']['sub_buttons']['reports']['title'] .= ' <span class="amt">' . $context['open_mod_reports'] . '</span>';
 	}
 
+	/**
+	 * @todo For some reason, $context['open_member_reports'] isn't getting set
+	 */
+	if (allowedTo('moderate_forum') && !empty($context['open_member_reports']))
+	{
+		$total_mod_reports += $context['open_member_reports'];
+		$context['menu_buttons']['moderate']['sub_buttons']['reported_members']['title'] .= ' <span class="amt">' . $context['open_member_reports'] . '</span>';
+	}
+
 	if (!empty($context['unapproved_members']))
 	{
 		$context['menu_buttons']['admin']['sub_buttons']['memberapprove']['title'] .= ' <span class="amt">' . $context['unapproved_members'] . '</span>';
 		$context['menu_buttons']['admin']['title'] .= ' <span class="amt">' . $context['unapproved_members'] . '</span>';
 	}
+
+	// Do we have any open reports?
+	if ($total_mod_reports > 0)
+	{
+		$context['menu_buttons']['moderate']['title'] .= ' <span class="amt">' . $total_mod_reports . '</span>';
+	}
+	
 }
 
 /**

+ 135 - 0
Sources/tasks/MemberReport-Notify.php

@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * This task handles notifying users when another member's profile gets reported.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+class MemberReport_Notify_Background extends SMF_BackgroundTask
+{
+	public function execute()
+	{
+		global $smcFunc, $sourcedir, $modSettings, $language, $scripturl;
+
+		// Anyone with moderate_forum can see this report
+		require_once($sourcedir . '/Subs-Members.php');
+		$members = membersAllowedTo('moderate_forum');
+
+		// And don't send it to them if they're the one who reported it.
+		$members = array_diff($members, array($this->_details['sender_id']));
+
+		// Having successfully figured this out, now let's get the preferences of everyone.
+		require_once($sourcedir . '/Subs-Notify.php');
+		$prefs = getNotifyPrefs($members, 'member_report', true);
+
+		// So now we find out who wants what.
+		$alert_bits = array(
+			'alert' => 0x01,
+			'email' => 0x02,
+		);
+		$notifies = array();
+
+		foreach ($prefs as $member => $pref_option)
+		{
+			foreach ($alert_bits as $type => $bitvalue)
+				if ($pref_option['member_report'] & $bitvalue)
+					$notifies[$type][] = $member;
+		}
+
+		// Firstly, anyone who wants alerts.
+		if (!empty($notifies['alert']))
+		{
+			// Alerts are relatively easy.
+			$insert_rows = array();
+			foreach ($notifies['alert'] as $member)
+			{
+				$insert_rows[] = array(
+					'alert_time' => $this->_details['time'],
+					'id_member' => $member,
+					'id_member_started' => $this->_details['sender_id'],
+					'member_name' => $this->_details['sender_name'],
+					'content_type' => 'profile',
+					'content_id' => $this->_details['user_id'],
+					'content_action' => 'report',
+					'is_read' => 0,
+					'extra' => serialize(
+						array(
+							'report_link' => '?action=moderate;area=memberreports;report=' . $this->_details['report_id'], // We don't put $scripturl in these!
+							'user_name' => $this->_details['membername'],
+						)
+					),
+				);
+			}
+
+			$smcFunc['db_insert']('insert',
+				'{db_prefix}user_alerts',
+				array('alert_time' => 'int', 'id_member' => 'int', 'id_member_started' => 'int',
+					'member_name' => 'string', 'content_type' => 'string', 'content_id' => 'int',
+					'content_action' => 'string', 'is_read' => 'int', 'extra' => 'string'),
+				$insert_rows,
+				array('id_alert')
+			);
+
+			// And update the count of alerts for those people.
+			updateMemberData($notifies['alert'], array('alerts' => '+'));
+		}
+
+		// Secondly, anyone who wants emails.
+		if (!empty($notifies['email']))
+		{
+			// Emails are a bit complicated. We have to do language stuff.
+			require_once($sourcedir . '/Subs-Post.php');
+			require_once($sourcedir . '/ScheduledTasks.php');
+			loadEssentialThemeData();
+
+			// First, get everyone's language and details.
+			$emails = array();
+			$request = $smcFunc['db_query']('', '
+				SELECT id_member, lngfile, email_address
+				FROM {db_prefix}members
+				WHERE id_member IN ({array_int:members})',
+				array(
+					'members' => $notifies['email'],
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+			{
+				if (empty($row['lngfile']))
+					$row['lngfile'] = $language;
+				$emails[$row['lngfile']][$row['id_member']] = $row['email_address'];
+			}
+			$smcFunc['db_free_result']($request);
+
+			// Iterate through each language, load the relevant templates and set up sending.
+			foreach ($emails as $this_lang => $recipients)
+			{
+				$replacements = array(
+					'MEMBERNAME' => $member_name,
+					'REPORTERNAME' => $this->_details['sender_name'],
+					'PROFILELINK' => $scripturl . '?action=profile;u=' . $this->_details['user_id'],
+					'REPORTLINK' => $scripturl . '?action=moderate;area=reports;report=' . $this->_details['report_id'],
+					'COMMENT' => $this->_details['comment'],
+				);
+
+				$emaildata = loadEmailTemplate('report_member_profile', $replacements, empty($modSettings['userLanguage']) ? $language : $this_lang);
+
+				// And do the actual sending...
+				foreach ($recipients as $id_member => $email_address)
+					sendmail($email_address, $emaildata['subject'], $emaildata['body'], null, 'ureport' . $this->_details['report_id'], false, 2);
+			}
+		}
+
+		// And now we're all done.
+		return true;
+	}
+}
+
+?>

+ 161 - 0
Sources/tasks/MemberReportReply-Notify.php

@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * This task handles notifying users when they've commented to a moderation report and
+ * someone else replies to them.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+class MemberReportReply_Notify_Background extends SMF_BackgroundTask
+{
+	public function execute()
+	{
+		global $smcFunc, $sourcedir, $modSettings, $language, $scripturl;
+
+		// Let's see. Let us, first of all, establish the list of possible people.
+		$possible_members = array();
+		$request = $smcFunc['db_query']('', '
+			SELECT id_member
+			FROM {db_prefix}log_comments
+			WHERE id_notice = {int:report}
+				AND comment_type = {literal:reportc}
+				AND id_comment < {int:last_comment}',
+			array(
+				'report' => $this->_details['report_id'],
+				'last_comment' => $this->_details['comment_id'],
+			)
+		);
+		while ($row = $smcFunc['db_fetch_row']($request))
+			$possible_members[] = $row[0];
+		$smcFunc['db_free_result']($request);
+
+		// Presumably, there are some people?
+		if (!empty($possible_members))
+		{
+			$possible_members = array_flip(array_flip($possible_members));
+			$possible_members = array_diff($possible_members, array($this->_details['sender_id']));
+		}
+		if (empty($possible_members))
+			return true;
+
+		// We need to know who can moderate this board - and therefore who can see this report.
+		// First up, people who have moderate_board in the board this topic was in.
+		require_once($sourcedir . '/Subs-Members.php');
+		$members = membersAllowedTo('moderate_forum');
+
+		// Having successfully figured this out, now let's get the preferences of everyone.
+		require_once($sourcedir . '/Subs-Notify.php');
+		$prefs = getNotifyPrefs($members, 'member_report_reply', true);
+
+		// So now we find out who wants what.
+		$alert_bits = array(
+			'alert' => 0x01,
+			'email' => 0x02,
+		);
+		$notifies = array();
+
+		foreach ($prefs as $member => $pref_option)
+		{
+			foreach ($alert_bits as $type => $bitvalue)
+			{
+				if ($pref_option['member_report_reply'] & $bitvalue)
+					$notifies[$type][] = $member;
+			}
+		}
+
+		// Firstly, anyone who wants alerts.
+		if (!empty($notifies['alert']))
+		{
+			// Alerts are relatively easy.
+			$insert_rows = array();
+			foreach ($notifies['alert'] as $member)
+			{
+				$insert_rows[] = array(
+					'alert_time' => $this->_details['time'],
+					'id_member' => $member,
+					'id_member_started' => $this->_details['sender_id'],
+					'member_name' => $this->_details['sender_name'],
+					'content_type' => 'profile',
+					'content_id' => $this->_details['user_id'],
+					'content_action' => 'report_reply',
+					'is_read' => 0,
+					'extra' => serialize(
+						array(
+							'report_link' => '?action=moderate;area=memberreports;report=' . $this->_details['report_id'], // We don't put $scripturl in these!
+							'user_name' => $this->_details['user_name'],
+						)
+					),
+				);
+			}
+
+			$smcFunc['db_insert']('insert',
+				'{db_prefix}user_alerts',
+				array('alert_time' => 'int', 'id_member' => 'int', 'id_member_started' => 'int',
+					'member_name' => 'string', 'content_type' => 'string', 'content_id' => 'int',
+					'content_action' => 'string', 'is_read' => 'int', 'extra' => 'string'),
+				$insert_rows,
+				array('id_alert')
+			);
+
+			// And update the count of alerts for those people.
+			updateMemberData($notifies['alert'], array('alerts' => '+'));
+		}
+
+		// Secondly, anyone who wants emails.
+		if (!empty($notifies['email']))
+		{
+			// Emails are a bit complicated. We have to do language stuff.
+			require_once($sourcedir . '/Subs-Post.php');
+			require_once($sourcedir . '/ScheduledTasks.php');
+			loadEssentialThemeData();
+
+			// First, get everyone's language and details.
+			$emails = array();
+			$request = $smcFunc['db_query']('', '
+				SELECT id_member, lngfile, email_address
+				FROM {db_prefix}members
+				WHERE id_member IN ({array_int:members})',
+				array(
+					'members' => $notifies['email'],
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+			{
+				if (empty($row['lngfile']))
+					$row['lngfile'] = $language;
+				$emails[$row['lngfile']][$row['id_member']] = $row['email_address'];
+			}
+			$smcFunc['db_free_result']($request);
+
+			// Iterate through each language, load the relevant templates and set up sending.
+			foreach ($emails as $this_lang => $recipients)
+			{
+				$replacements = array(
+					'MEMBERNAME' => $this->_details['member_name'],
+					'COMMENTERNAME' => $this->_details['sender_name'],
+					'PROFILELINK' => $scripturl . 'action=profile;u=' . $this->_details['user_id'],
+					'REPORTLINK' => $scripturl . '?action=moderate;area=userreports;report=' . $this->_details['report_id'],
+				);
+
+				$emaildata = loadEmailTemplate('reply_to_user_reports', $replacements, empty($modSettings['userLanguage']) ? $language : $this_lang);
+
+				// And do the actual sending...
+				foreach ($recipients as $id_member => $email_address)
+					sendmail($email_address, $emaildata['subject'], $emaildata['body'], null, 'urptrpy' . $this->_details['comment_id'], false, 3);
+			}
+		}
+
+		// And now we're all done.
+		return true;
+	}
+}
+
+?>

+ 0 - 1
Themes/default/Admin.template.php

@@ -112,7 +112,6 @@ function template_admin()
 
 	// This sets the announcements and current versions themselves ;).
 	echo '
-					<script src="', $settings['default_theme_url'], '/scripts/admin.js?alp21"></script>
 					<script><!-- // --><![CDATA[
 						var oAdminIndex = new smf_AdminIndex({
 							sSelf: \'oAdminCenter\',

+ 0 - 1
Themes/default/BoardIndex.template.php

@@ -38,7 +38,6 @@ function template_newsfader()
 					</ul>
 				</div>
 			</div>
-			<script src="', $settings['default_theme_url'], '/scripts/fader.js"></script>
 			<script><!-- // --><![CDATA[
 				// Create a news fader object.
 				var oNewsFader = new smc_NewsFader({

+ 4 - 16
Themes/default/Display.template.php

@@ -22,14 +22,6 @@ function template_main()
 				', $txt['report_sent'], '
 			</div>';
 	}
-	// Topic was sent successfully
-	elseif ($context['topic_sent'])
-	{
-		echo '
-			<div class="infobox">
-				', $txt['topic_sent'], '
-			</div>';
-	}
 
 	// Show the anchor for the top and for the first message. If the first message is new, say so.
 	echo '
@@ -339,7 +331,6 @@ function template_main()
 	// draft autosave available and the user has it enabled?
 	if (!empty($context['drafts_autosave']) && !empty($options['drafts_autosave_enabled']))
 		echo '
-			<script src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
 			<script><!-- // --><![CDATA[
 				var oDraftAutoSave = new smf_DraftAutoSave({
 					sSelf: \'oDraftAutoSave\',
@@ -354,11 +345,9 @@ function template_main()
 
 	if ($context['show_spellchecking'])
 		echo '
-			<form action="', $scripturl, '?action=spellcheck" method="post" accept-charset="', $context['character_set'], '" name="spell_form" id="spell_form" target="spellWindow"><input type="hidden" name="spellstring" value=""></form>
-				<script src="', $settings['default_theme_url'], '/scripts/spellcheck.js"></script>';
+			<form action="', $scripturl, '?action=spellcheck" method="post" accept-charset="', $context['character_set'], '" name="spell_form" id="spell_form" target="spellWindow"><input type="hidden" name="spellstring" value=""></form>';
 
 	echo '
-				<script src="', $settings['default_theme_url'], '/scripts/topic.js"></script>
 				<script><!-- // --><![CDATA[';
 
 	if (!empty($context['some_likes']))
@@ -639,11 +628,10 @@ function template_single_post($message, $force_alternate = null)
 		}
 
 	}
-
 	// Otherwise, show the guest's email.
-	elseif (!empty($message['member']['email']) && in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum')) && $context['can_send_email'])
+	elseif (!empty($message['member']['email']) && $message['member']['show_email'])
 		echo '
-								<li class="email"><a href="', $scripturl, '?action=emailuser;sa=email;msg=', $message['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<span class="generic_icons mail centericon" title="' . $txt['email'] . '"></span>' : $txt['email']), '</a></li>';
+								<li class="email"><a href="mailto:' . $message['member']['email'] . '" rel="nofollow">', ($settings['use_image_buttons'] ? '<span class="generic_icons mail centericon" title="' . $txt['email'] . '"></span>' : $txt['email']), '</a></li>';
 
 	// Show the IP to this user for this post - because you can moderate?
 	if (!empty($context['can_moderate_forum']) && !empty($message['member']['ip']))
@@ -956,4 +944,4 @@ function template_single_post($message, $force_alternate = null)
 				<hr class="post_separator">';
 }
 
-?>
+?>

+ 2 - 2
Themes/default/Errors.template.php

@@ -192,14 +192,14 @@ function template_error_log()
 
 function template_show_file()
 {
-	global $context, $settings;
+	global $context, $settings, $modSettings;
 
 	echo '<!DOCTYPE html>
 <html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
 	<head>
 		<title>', $context['file_data']['file'], '</title>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
 	</head>
 	<body>
 		<table class="errorfile_table">';

+ 2 - 2
Themes/default/GenericControls.template.php

@@ -169,7 +169,7 @@ function template_control_richedit_buttons($editor_id)
 			<span id="throbber" style="display:none"><img src="' . $settings['images_url'] . '/loading_sm.gif" alt="" class="centericon">&nbsp;</span>
 			<span id="draft_lastautosave" ></span>
 		</span>
-		<script src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/drafts.js', $modSettings['browser_cache'] ,'"></script>
 		<script><!-- // --><![CDATA[
 			var oDraftAutoSave = new smf_DraftAutoSave({
 				sSelf: \'oDraftAutoSave\',
@@ -197,7 +197,7 @@ function template_control_richedit_buttons($editor_id)
 			<span id="throbber" style="display:none"><img src="' . $settings['images_url'] . '/loading_sm.gif" alt="" class="centericon">&nbsp;</span>
 			<span id="draft_lastautosave" ></span>
 		</span>
-		<script src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/drafts.js', $modSettings['browser_cache'] ,'"></script>
 		<script><!-- // --><![CDATA[
 			var oDraftAutoSave = new smf_DraftAutoSave({
 				sSelf: \'oDraftAutoSave\',

+ 5 - 5
Themes/default/Help.template.php

@@ -12,7 +12,7 @@
 
 function template_popup()
 {
-	global $context, $settings, $txt;
+	global $context, $settings, $txt, $modSettings;
 
 	// Since this is a popup of its own we need to start the html, etc.
 	echo '<!DOCTYPE html>
@@ -21,8 +21,8 @@ function template_popup()
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<meta name="robots" content="noindex">
 		<title>', $context['page_title'], '</title>
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
-		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
 	</head>
 	<body id="help_popup">
 		<div class="windowbg description">
@@ -44,8 +44,8 @@ function template_find_members()
 		<title>', $txt['find_members'], '</title>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<meta name="robots" content="noindex">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
-		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
 		<script><!-- // --><![CDATA[
 			var membersAdded = [];
 			function addMember(name)

+ 3 - 3
Themes/default/Likes.template.php

@@ -12,7 +12,7 @@
 
 function template_popup()
 {
-	global $context, $settings, $txt;
+	global $context, $settings, $txt, $modSettings;
 
 	// Since this is a popup of its own we need to start the html, etc.
 	echo '<!DOCTYPE html>
@@ -21,8 +21,8 @@ function template_popup()
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<meta name="robots" content="noindex">
 		<title>', $context['page_title'], '</title>
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
-		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
 	</head>
 	<body id="likes_popup">
 		<div class="windowbg">

+ 5 - 6
Themes/default/Login.template.php

@@ -15,9 +15,12 @@ function template_login()
 {
 	global $context, $settings, $scripturl, $modSettings, $txt;
 
-	echo '
-		<script src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
+	// AJAX request?
+	if ($context['from_ajax'])
+		echo '
+		<script src="', $settings['default_theme_url'], '/scripts/sha1.js', $modSettings['browser_cache'] ,'"></script>';
 
+	echo '
 		<div class="tborder login">
 			<div class="cat_bar">
 				<h3 class="catbg">
@@ -97,7 +100,6 @@ function template_kick_guest()
 
 	// This isn't that much... just like normal login but with a message at the top.
 	echo '
-	<script src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 	<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
 		<div class="tborder login">
 			<div class="cat_bar">
@@ -169,7 +171,6 @@ function template_maintenance()
 
 	// Display the administrator's message at the top.
 	echo '
-<script src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 <form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
 	<div class="tborder login" id="maintenance_mode">
 		<div class="cat_bar">
@@ -210,8 +211,6 @@ function template_admin_login()
 
 	// Since this should redirect to whatever they were doing, send all the get data.
 	echo '
-<script src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
-
 <form action="', $scripturl, $context['get_data'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin" onsubmit="hash', ucfirst($context['sessionCheckType']), 'Password(this, \'', $context['user']['username'], '\', \'', $context['session_id'], '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');">
 	<div class="tborder login" id="admin_login">
 		<div class="cat_bar">

+ 0 - 2
Themes/default/ManageBans.template.php

@@ -299,8 +299,6 @@ function template_ban_edit_trigger()
 			<input type="hidden" name="', $context['admin-bet_token_var'], '" value="', $context['admin-bet_token'], '">
 		</form>
 	</div>
-
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oAddMemberSuggest = new smc_AutoSuggest({
 			sSelf: \'oAddMemberSuggest\',

+ 0 - 1
Themes/default/ManageBoards.template.php

@@ -574,7 +574,6 @@ function template_modify_board()
 		</form>
 	</div>
 
-<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 <script><!-- // --><![CDATA[
 	var oModeratorSuggest = new smc_AutoSuggest({
 		sSelf: \'oModeratorSuggest\',

+ 0 - 1
Themes/default/ManageMaintenance.template.php

@@ -342,7 +342,6 @@ function template_maintain_members()
 		</div>
 	</div>
 
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oAttributeMemberSuggest = new smc_AutoSuggest({
 			sSelf: \'oAttributeMemberSuggest\',

+ 0 - 2
Themes/default/ManageMembergroups.template.php

@@ -333,7 +333,6 @@ function template_edit_group()
 			<input type="hidden" name="', $context['admin-mmg_token_var'], '" value="', $context['admin-mmg_token'], '">
 		</form>
 	</div>
-		<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 		<script><!-- // --><![CDATA[
 			var oModeratorSuggest = new smc_AutoSuggest({
 				sSelf: \'oModeratorSuggest\',
@@ -681,7 +680,6 @@ function template_group_members()
 
 	if (!empty($context['group']['assignable']))
 		echo '
-		<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 		<script><!-- // --><![CDATA[
 			var oAddMemberSuggest = new smc_AutoSuggest({
 				sSelf: \'oAddMemberSuggest\',

+ 0 - 1
Themes/default/ManageNews.template.php

@@ -148,7 +148,6 @@ function template_email_members()
 			]
 		});
 	// ]]></script>
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oMemberSuggest = new smc_AutoSuggest({
 			sSelf: \'oMemberSuggest\',

+ 0 - 1
Themes/default/ManagePaid.template.php

@@ -339,7 +339,6 @@ function template_modify_user_subscription()
 			</div>
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
 		</form>
-		<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 		<script><!-- // --><![CDATA[
 		var oAddMemberSuggest = new smc_AutoSuggest({
 			sSelf: \'oAddMemberSuggest\',

+ 1 - 1
Themes/default/ManageSmileys.template.php

@@ -39,7 +39,7 @@ function template_editsets()
 
 	if (empty($modSettings['disable_smf_js']))
 		echo '
-	<script src="', $scripturl, '?action=viewsmfile;filename=latest-smileys.js"></script>';
+	<script src="', $scripturl, '?action=viewsmfile;filename=latest-smileys.js', $modSettings['browser_cache'] ,'"></script>';
 
 	echo '
 	<script><!-- // --><![CDATA[

+ 0 - 1
Themes/default/MessageIndex.template.php

@@ -391,7 +391,6 @@ function template_main()
 
 	// Javascript for inline editing.
 	echo '
-<script src="', $settings['default_theme_url'], '/scripts/topic.js"></script>
 <script><!-- // --><![CDATA[
 	var oQuickModifyTopic = new QuickModifyTopic({
 		aHidePrefixes: Array("lockicon", "stickyicon", "pages", "newicon"),

+ 256 - 170
Themes/default/ModerationCenter.template.php

@@ -75,7 +75,7 @@ function template_group_requests_block()
 				</ul>
 			</div>
 		</div>
-		
+
 	<script><!-- // --><![CDATA[
 		var oGroupRequestsPanelToggle = new smc_Toggle({
 			bToggleEnabled: true,
@@ -109,73 +109,6 @@ function template_group_requests_block()
 	// ]]></script>';
 }
 
-// A block to show the current top reported posts.
-function template_reported_posts_block()
-{
-	global $context, $txt, $scripturl;
-
-	echo '
-		<div class="cat_bar">
-			<h3 class="catbg">
-				<span id="reported_posts_toggle" class="', !empty($context['admin_prefs']['mcrp']) ? 'toggle_down' : 'toggle_up', ' floatright" style="display: none;"></span>
-				<a href="', $scripturl, '?action=moderate;area=reports" id="reported_posts_link">', $txt['mc_recent_reports'], '</a>
-			</h3>
-		</div>
-		<div class="windowbg" id="reported_posts_panel">
-			<div class="content modbox">
-				<ul class="reset">';
-
-		foreach ($context['reported_posts'] as $report)
-			echo '
-					<li class="smalltext">
-						<a href="', $report['report_href'], '">', $report['subject'], '</a> ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], '
-					</li>';
-
-		// Don't have any watched users right now?
-		if (empty($context['reported_posts']))
-			echo '
-					<li>
-						<strong class="smalltext">', $txt['mc_recent_reports_none'], '</strong>
-					</li>';
-
-		echo '
-				</ul>
-			</div>
-		</div>
-		
-	<script><!-- // --><![CDATA[
-		var oReportedPostsPanelToggle = new smc_Toggle({
-			bToggleEnabled: true,
-			bCurrentlyCollapsed: ', !empty($context['admin_prefs']['mcrp']) ? 'true' : 'false', ',
-			aSwappableContainers: [
-				\'reported_posts_panel\'
-			],
-			aSwapImages: [
-				{
-					sId: \'reported_posts_toggle\',
-					altExpanded: ', JavaScriptEscape($txt['hide']), ',
-					altCollapsed: ', JavaScriptEscape($txt['show']), '
-				}
-			],
-			aSwapLinks: [
-				{
-					sId: \'reported_posts_link\',
-					msgExpanded: ', JavaScriptEscape($txt['mc_recent_reports']), ',
-					msgCollapsed: ', JavaScriptEscape($txt['mc_recent_reports']), '
-				}
-			],
-			oThemeOptions: {
-				bUseThemeSettings: true,
-				sOptionName: \'admin_preferences\',
-				sSessionVar: smf_session_var,
-				sSessionId: smf_session_id,
-				sThemeId: \'1\',
-				sAdditionalVars: \';admin_key=mcrp\'
-			}
-		});
-	// ]]></script>';
-}
-
 function template_watched_users()
 {
 	global $context, $txt, $scripturl;
@@ -208,7 +141,7 @@ function template_watched_users()
 				</ul>
 			</div>
 		</div>
-		
+
 	<script><!-- // --><![CDATA[
 		var oWatchedUsersToggle = new smc_Toggle({
 			bToggleEnabled: true,
@@ -247,6 +180,15 @@ function template_notes()
 {
 	global $context, $txt, $scripturl;
 
+	// Let them know the action was a success.
+	if (!empty($context['report_post_action']))
+	{
+		echo '
+			<div class="infobox">
+				', $txt['report_action_'. $context['report_post_action']], '
+			</div>';
+	}
+
 	echo '
 		<div class="modnotes">
 			<form action="', $scripturl, '?action=moderate;area=index;modnote" method="post">
@@ -264,7 +206,7 @@ function template_notes()
 			// Cycle through the notes.
 			foreach ($context['notes'] as $note)
 				echo '
-							<li class="smalltext"><a href="', $note['delete_href'], '"><span class="generic_icons del_small"></span></a>', $note['time'] ,' <strong>', $note['author']['link'], ':</strong> ', $note['text'], '</li>';
+							<li class="smalltext"><a href="', $note['delete_href'], ';', $context['mod-modnote-del_token_var'], '=', $context['mod-modnote-del_token'], '" class="delete_modnote"><span class="generic_icons del_small"></span></a>', $note['time'] ,' <strong>', $note['author']['link'], ':</strong> ', $note['text'], '</li>';
 
 			echo '
 						</ul>
@@ -277,6 +219,7 @@ function template_notes()
 						<div class="floatleft post_note">
 						<input type="text" name="new_note" value="', $txt['mc_click_add_note'], '" style="width: 95%;" onclick="if (this.value == \'', $txt['mc_click_add_note'], '\') this.value = \'\';" class="input_text">
 						</div>
+						<input type="hidden" name="', $context['mod-modnote-add_token_var'], '" value="', $context['mod-modnote-add_token'], '">
 						<input type="submit" name="makenote" value="', $txt['mc_add_note'], '" class="button_submit">
 					</div>
 				</div>
@@ -285,102 +228,6 @@ function template_notes()
 		</div>';
 }
 
-function template_reported_posts()
-{
-	global $context, $txt, $scripturl;
-
-	// Let them know the action was a success.
-	if (!empty($context['report_post_action']) && !empty($txt['report_action_'. $context['report_post_action']]))
-	{
-		echo '
-			<div class="infobox">
-				', $txt['report_action_'. $context['report_post_action']], '
-			</div>';
-	}
-
-	echo '
-	<form id="reported_posts" action="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
-		<div class="cat_bar">
-			<h3 class="catbg">
-				', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], '
-			</h3>
-		</div>
-		<div class="pagesection">
-			<div class="pagelinks">', $context['page_index'], '</div>
-		</div>';
-
-	// Make the buttons.
-	$close_button = create_button('close.png', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'class="centericon"');
-	$details_button = create_button('details.png', 'mc_reportedp_details', 'mc_reportedp_details', 'class="centericon"');
-	$ignore_button = create_button('ignore.png', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'class="centericon"');
-	$unignore_button = create_button('ignore.png', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'class="centericon"');
-	$ban_button = create_button('close.png', 'mc_reportedp_ban', 'mc_reportedp_ban', 'class="centericon"');
-	$delete_button = create_button('delete.png', 'mc_reportedp_delete', 'mc_reportedp_delete', 'class="centericon"');
-
-	foreach ($context['reports'] as $report)
-	{
-		echo '
-		<div class="generic_list_wrapper ', $report['alternate'] ? 'windowbg' : 'windowbg2', '">
-			<div class="content">
-				<h5>
-					<strong>', !empty($report['topic']['board_name']) ? '<a href="' . $scripturl . '?board=' . $report['topic']['id_board'] . '.0">' . $report['topic']['board_name'] . '</a>' : '??', ' / <a href="', $report['topic']['href'], '">', $report['subject'], '</a></strong> ', $txt['mc_reportedp_by'], ' <strong>', $report['author']['link'], '</strong>
-				</h5>
-				<div class="smalltext">
-					', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], '&nbsp;-&nbsp;';
-
-		// Prepare the comments...
-		$comments = array();
-		foreach ($report['comments'] as $comment)
-			$comments[$comment['member']['id']] = $comment['member']['link'];
-
-		echo '
-					', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), '
-				</div>
-				<hr>
-				', $report['body'], '
-				<br>
-				<ul class="quickbuttons">
-					<li><a href="', $report['report_href'], '">', $details_button, '</a></li>
-					<li><a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';ignore=', (int) !$report['ignore'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '" ', !$report['ignore'] ? 'onclick="return confirm(\'' . $txt['mc_reportedp_ignore_confirm'] . '\');"' : '', '>', $report['ignore'] ? $unignore_button : $ignore_button, '</a></li>
-					<li><a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';close=', (int) !$report['closed'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '">', $close_button, '</a></li>';
-
-		// Delete message button.
-		if (!$report['closed'] && (is_array($context['report_remove_any_boards']) && in_array($report['topic']['id_board'], $context['report_remove_any_boards'])))
-			echo '
-					<li><a href="', $scripturl, '?action=deletemsg;topic=', $report['topic']['id'] ,'.0;msg=', $report['topic']['id_msg'] ,';modcenter;', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(\'' , $txt['mc_reportedp_delete_confirm'] , '\');">', $delete_button, '</a></li>';
-
-		// Ban this user button.
-		if (!$report['closed'] && !empty($context['report_manage_bans']))
-			echo '
-					<li><a href="', $scripturl, '?action=admin;area=ban;sa=add', (!empty($report['author']['id']) ? ';u='. $report['author']['id'] : ';msg='. $report['topic']['id_msg']) ,';', $context['session_var'], '=', $context['session_id'], '">', $ban_button, '</a></li>';
-
-		echo '
-					<li>', !$context['view_closed'] ? '<input type="checkbox" name="close[]" value="' . $report['id'] . '" class="input_check">' : '', '</li>
-				</ul>
-			</div>
-		</div>';
-	}
-
-	// Were none found?
-	if (empty($context['reports']))
-		echo '
-		<div class="windowbg2">
-			<div class="content">
-				<p class="centertext">', $txt['mc_reportedp_none_found'], '</p>
-			</div>
-		</div>';
-
-	echo '
-		<div class="pagesection">
-			<div class="pagelinks floatleft">', $context['page_index'], '</div>
-			<div class="floatright">
-				', !$context['view_closed'] ? '<input type="submit" name="close_selected" value="' . $txt['mc_reportedp_close_selected'] . '" class="button_submit">' : '', '
-			</div>
-		</div>
-		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
-	</form>';
-}
-
 // Show a list of all the unapproved posts
 function template_unapproved_posts()
 {
@@ -473,6 +320,7 @@ function template_unapproved_posts()
 	</div>';
 }
 
+
 function template_viewmodreport()
 {
 	global $context, $scripturl, $txt;
@@ -561,6 +409,89 @@ function template_viewmodreport()
 	</div>';
 }
 
+function template_viewmemberreport()
+{
+	global $context, $scripturl, $txt;
+
+	echo '
+	<div id="modcenter">
+		<form action="', $scripturl, '?action=moderate;area=memberreports;report=', $context['report']['id'], '" method="post" accept-charset="', $context['character_set'], '">
+			<div class="cat_bar">
+				<h3 class="catbg">
+					', sprintf($txt['mc_viewmemberreport'], $context['report']['user']['link']), '
+				</h3>
+			</div>
+			<div class="title_bar">
+				<h3 class="titlebg">
+					<span class="floatleft">
+						', sprintf($txt['mc_memberreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']), '
+					</span>
+					<span class="floatright">';
+
+		// Make the buttons.
+		$close_button = create_button('close.png', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'class="centericon"');
+		$ignore_button = create_button('ignore.png', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'class="centericon"');
+		$unignore_button = create_button('ignore.png', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'class="centericon"');
+
+		echo '
+						<a href="', $scripturl, '?action=moderate;area=memberreports;ignore=', (int) !$context['report']['ignore'], ';rid=', $context['report']['id'], ';', $context['session_var'], '=', $context['session_id'], '" ', !$context['report']['ignore'] ? 'onclick="return confirm(\'' . $txt['mc_reportedp_ignore_confirm'] . '\');"' : '', '>', $context['report']['ignore'] ? $unignore_button : $ignore_button, '</a>
+						<a href="', $scripturl, '?action=moderate;area=memberreports;close=', (int) !$context['report']['closed'], ';rid=', $context['report']['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $close_button, '</a>
+					</span>
+				</h3>
+			</div>
+			<br>
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mc_memberreport_whoreported_title'], '</h3>
+			</div>';
+
+	foreach ($context['report']['comments'] as $comment)
+		echo '
+			<div class="windowbg">
+				<div class="content">
+					<p class="smalltext">', sprintf($txt['mc_modreport_whoreported_data'], $comment['member']['link'] . (empty($comment['member']['id']) && !empty($comment['member']['ip']) ? ' (' . $comment['member']['ip'] . ')' : ''), $comment['time']), '</p>
+					<p>', $comment['message'], '</p>
+				</div>
+			</div>';
+
+	echo '
+			<br>
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mc_modreport_mod_comments'], '</h3>
+			</div>';
+			
+		if (empty($context['report']['mod_comments']))
+		echo '
+				<div class="description">
+					<p class="centertext">', $txt['mc_modreport_no_mod_comment'], '</p>
+				</div>';
+	
+		echo '
+			<div class="windowbg2">
+				<div class="content">';
+
+	foreach ($context['report']['mod_comments'] as $comment)
+		echo
+					'<p>', $comment['member']['link'], ': ', $comment['message'], ' <em class="smalltext">(', $comment['time'], ')</em></p>';
+
+		echo '
+					<textarea rows="2" cols="60" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 60%; min-width: 60%' : 'width: 60%') . ';" name="mod_comment"></textarea>
+					<div>
+						<input type="submit" name="add_comment" value="', $txt['mc_modreport_add_mod_comment'], '" class="button_submit">
+					</div>
+				</div>
+			</div>
+			<br>';
+
+	$alt = false;
+
+	template_show_list('memreport_actions_list');
+
+	echo '
+			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+		</form>
+	</div>';
+}
+
 // Callback function for showing a watched users post in the table.
 function template_user_watch_post_callback($post)
 {
@@ -633,7 +564,7 @@ function template_moderation_settings()
 // Show a notice sent to a user.
 function template_show_notice()
 {
-	global $txt, $settings, $context;
+	global $txt, $settings, $context, $modSettings;
 
 	// We do all the HTML for this one!
 	echo '<!DOCTYPE html>
@@ -641,14 +572,14 @@ function template_show_notice()
 	<head>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<title>', $context['page_title'], '</title>
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
 	</head>
 	<body>
 		<div class="cat_bar">
 			<h3 class="catbg">', $txt['show_notice'], '</h3>
 		</div>
-		<div class="cat_bar">
-			<h3 class="catbg">', $txt['show_notice_subject'], ': ', $context['notice_subject'], '</h3>
+		<div class="title_bar">
+			<h3 class="titlebg">', $txt['show_notice_subject'], ': ', $context['notice_subject'], '</h3>
 		</div>
 		<div class="windowbg">
 			<div class="content">
@@ -780,4 +711,159 @@ function template_warn_template()
 	// ]]></script>';
 }
 
+// A block to show the current top reported member profiles.
+function template_reported_members_block()
+{
+	global $context, $txt, $scripturl;
+
+	echo '
+		<div class="cat_bar">
+			<h3 class="catbg">
+				<span id="reported_members_toggle" class="', !empty($context['admin_prefs']['mcru']) ? 'toggle_down' : 'toggle_up', ' floatright" style="display: none;"></span>
+				<a href="', $scripturl, '?action=moderate;area=memberreports" id="reported_members_link">', $txt['mc_recent_member_reports'], '</a>
+			</h3>
+		</div>
+		<div class="windowbg" id="reported_users_panel">
+			<div class="content modbox">
+				<ul class="reset">';
+
+		foreach ($context['reported_members'] as $report)
+			echo '
+					<li class="smalltext">
+						<a href="', $report['report_href'], '">', $report['user_name'], '</a>
+					</li>';
+
+		// Don't have any reported members right now?
+		if (empty($context['reported_members']))
+			echo '
+					<li>
+						<strong class="smalltext">', $txt['mc_recent_reports_none'], '</strong>
+					</li>';
+
+		echo '
+				</ul>
+			</div>
+		</div>
+		
+	<script><!-- // --><![CDATA[
+		var oReportedPostsPanelToggle = new smc_Toggle({
+			bToggleEnabled: true,
+			bCurrentlyCollapsed: ', !empty($context['admin_prefs']['mcrm']) ? 'true' : 'false', ',
+			aSwappableContainers: [
+				\'reported_posts_panel\'
+			],
+			aSwapImages: [
+				{
+					sId: \'reported_members_toggle\',
+					altExpanded: ', JavaScriptEscape($txt['hide']), ',
+					altCollapsed: ', JavaScriptEscape($txt['show']), '
+				}
+			],
+			aSwapLinks: [
+				{
+					sId: \'reported_members_link\',
+					msgExpanded: ', JavaScriptEscape($txt['mc_recent_member_reports']), ',
+					msgCollapsed: ', JavaScriptEscape($txt['mc_recent_member_reports']), '
+				}
+			],
+			oThemeOptions: {
+				bUseThemeSettings: true,
+				sOptionName: \'admin_preferences\',
+				sSessionVar: smf_session_var,
+				sSessionId: smf_session_id,
+				sThemeId: \'1\',
+				sAdditionalVars: \';admin_key=mcrm\'
+			}
+		});
+	// ]]></script>';
+}
+
+function template_reported_members()
+{
+	global $context, $txt, $scripturl;
+
+	// Let them know the action was a success.
+	if (!empty($context['report_post_action']) && !empty($txt['report_action_'. $context['report_post_action']]))
+	{
+		echo '
+			<div class="infobox">
+				', $txt['report_action_'. $context['report_post_action']], '
+			</div>';
+	}
+
+	echo '
+	<form id="reported_members" action="', $scripturl, '?action=moderate;area=memberreports', $context['view_closed'] ? ';sa=closed' : '', ';start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
+		<div class="cat_bar">
+			<h3 class="catbg">
+				', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], '
+			</h3>
+		</div>
+		<div class="pagesection">
+			<div class="pagelinks">', $context['page_index'], '</div>
+		</div>';
+
+	// Make the buttons.
+	$close_button = create_button('close.png', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'class="centericon"');
+	$details_button = create_button('details.png', 'mc_reportedp_details', 'mc_reportedp_details', 'class="centericon"');
+	$ignore_button = create_button('ignore.png', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'class="centericon"');
+	$unignore_button = create_button('ignore.png', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'class="centericon"');
+	$ban_button = create_button('close.png', 'mc_reportedp_ban', 'mc_reportedp_ban', 'class="centericon"');
+
+	foreach ($context['reports'] as $report)
+	{
+		echo '
+		<div class="generic_list_wrapper ', $report['alternate'] ? 'windowbg' : 'windowbg2', '">
+			<div class="content">
+				<h5>
+					<strong><a href="', $report['user']['href'], '">', $report['user']['name'], '</a></strong>
+				</h5>
+				<div class="smalltext">
+					', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], '&nbsp;-&nbsp;';
+
+		// Prepare the comments...
+		$comments = array();
+		foreach ($report['comments'] as $comment)
+			$comments[$comment['member']['id']] = $comment['member']['link'];
+
+		echo '
+					', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), '
+				</div>
+				<hr>
+				<ul class="quickbuttons">
+					<li><a href="', $report['report_href'], '">', $details_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=memberreports', $context['view_closed'] ? ';sa=closed' : '', ';ignore=', (int) !$report['ignore'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '" ', !$report['ignore'] ? 'onclick="return confirm(\'' . $txt['mc_reportedp_ignore_confirm'] . '\');"' : '', '>', $report['ignore'] ? $unignore_button : $ignore_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=memberreports', $context['view_closed'] ? ';sa=closed' : '', ';close=', (int) !$report['closed'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '">', $close_button, '</a></li>';
+
+		// Ban this user button.
+		if (!$report['closed'] && !empty($context['report_manage_bans']) && !empty($report['user']['id']))
+			echo '
+					<li><a href="', $scripturl, '?action=admin;area=ban;sa=add;u=', $report['user']['id'] ,';', $context['session_var'], '=', $context['session_id'], '">', $ban_button, '</a></li>';
+
+		echo '
+					<li>', !$context['view_closed'] ? '<input type="checkbox" name="close[]" value="' . $report['id'] . '" class="input_check">' : '', '</li>
+				</ul>
+			</div>
+		</div>';
+	}
+
+	// Were none found?
+	if (empty($context['reports']))
+		echo '
+		<div class="windowbg2">
+			<div class="content">
+				<p class="centertext">', $txt['mc_reportedp_none_found'], '</p>
+			</div>
+		</div>';
+
+	echo '
+		<div class="pagesection">
+			<div class="pagelinks floatleft">', $context['page_index'], '</div>
+			<div class="floatright">
+				', !$context['view_closed'] ? '<input type="submit" name="close_selected" value="' . $txt['mc_reportedp_close_selected'] . '" class="button_submit">' : '', '
+			</div>
+		</div>
+		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+	</form>';
+}
+
 ?>

+ 106 - 105
Themes/default/Packages.template.php

@@ -664,7 +664,6 @@ function template_browse()
 			]
 		});
 	// ]]></script>
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 			var oAddVersionSuggest = new smc_AutoSuggest({
 			sSelf: \'oAddVersionSuggest\',
@@ -690,139 +689,141 @@ function template_servers()
 	echo '
 	<div id="admin_form_wrapper">
 		<div class="cat_bar">
-			<h3 class="catbg">', $txt['download_new_package'], '</h3>
-		</div>';
-
-	if ($context['package_download_broken'])
-	{
-		echo '
-		<div class="cat_bar">
-			<h3 class="catbg">', $txt['package_ftp_necessary'], '</h3>
+			<h3 class="catbg">' . $txt['package_upload_title'] . '</h3>
 		</div>
 		<div class="windowbg">
 			<div class="content">
-				<p>
-					', $txt['package_ftp_why_download'], '
-				</p>
-				<form action="', $scripturl, '?action=admin;area=packages;get" method="post" accept-charset="', $context['character_set'], '">
+				<form action="' . $scripturl . '?action=admin;area=packages;get;sa=upload" method="post" accept-charset="', $context['character_set'], '" enctype="multipart/form-data" style="margin-bottom: 0;">
 					<dl class="settings">
 						<dt>
-							<label for="ftp_server">', $txt['package_ftp_server'], ':</label>
-						</dt>
-						<dd>
-							<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $context['package_ftp']['server'], '" class="input_text">
-							<label for="ftp_port">', $txt['package_ftp_port'], ':&nbsp;</label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', $context['package_ftp']['port'], '" class="input_text">
-						</dd>
-						<dt>
-							<label for="ftp_username">', $txt['package_ftp_username'], ':</label>
-						</dt>
-						<dd>
-							<input type="text" size="50" name="ftp_username" id="ftp_username" value="', $context['package_ftp']['username'], '" style="width: 99%;" class="input_text">
-						</dd>
-						<dt>
-							<label for="ftp_password">', $txt['package_ftp_password'], ':</label>
-						</dt>
-						<dd>
-							<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
-						</dd>
-						<dt>
-							<label for="ftp_path">', $txt['package_ftp_path'], ':</label>
+							<strong>' . $txt['package_upload_select'] . ':</strong>
 						</dt>
 						<dd>
-							<input type="text" size="50" name="ftp_path" id="ftp_path" value="', $context['package_ftp']['path'], '" style="width: 99%;" class="input_text">
+							<input type="file" name="package" size="38" class="input_file">
 						</dd>
 					</dl>
-					<div class="righttext">
-						<input type="submit" value="', $txt['package_proceed'], '" class="button_submit">
-					</div>
+					<hr class="hrcolor">
+					<input type="submit" value="' . $txt['package_upload'] . '" class="button_submit">
+					<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
 				</form>
 			</div>
-		</div>';
-	}
+		</div>
+		<br>
+		<div class="cat_bar">
+			<h3 class="catbg"><a class="download_new_package">', $txt['download_new_package'], '</a></h3>
+		</div>
+		<div class="new_package_content">';
 
-	echo '
-		<div class="windowbg2">
-			<div class="content">
-				<fieldset>
-					<legend>' . $txt['package_servers'] . '</legend>
-					<ul class="package_servers">';
-	foreach ($context['servers'] as $server)
+	if ($context['package_download_broken'])
+	{
 		echo '
-						<li class="flow_auto">
-							<span class="floatleft">' . $server['name'] . '</span>
-							<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=remove;server=' . $server['id'] . ';', $context['session_var'], '=', $context['session_id'], '">[ ' . $txt['delete'] . ' ]</a></span>
-							<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=browse;server=' . $server['id'] . '">[ ' . $txt['package_browse'] . ' ]</a></span>
-						</li>';
-	echo '
-					</ul>
-				</fieldset>
-				<fieldset>
-					<legend>' . $txt['add_server'] . '</legend>
-					<form action="' . $scripturl . '?action=admin;area=packages;get;sa=add" method="post" accept-charset="', $context['character_set'], '">
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['package_ftp_necessary'], '</h3>
+			</div>
+			<div class="windowbg">
+				<div class="content">
+					<p>
+						', $txt['package_ftp_why_download'], '
+					</p>
+					<form action="', $scripturl, '?action=admin;area=packages;get" method="post" accept-charset="', $context['character_set'], '">
 						<dl class="settings">
 							<dt>
-								<strong>' . $txt['server_name'] . ':</strong>
+								<label for="ftp_server">', $txt['package_ftp_server'], ':</label>
 							</dt>
 							<dd>
-								<input type="text" name="servername" size="44" value="SMF" class="input_text">
+								<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $context['package_ftp']['server'], '" class="input_text">
+								<label for="ftp_port">', $txt['package_ftp_port'], ':&nbsp;</label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', $context['package_ftp']['port'], '" class="input_text">
 							</dd>
 							<dt>
-								<strong>' . $txt['serverurl'] . ':</strong>
+								<label for="ftp_username">', $txt['package_ftp_username'], ':</label>
 							</dt>
 							<dd>
-								<input type="text" name="serverurl" size="44" value="http://" class="input_text">
+								<input type="text" size="50" name="ftp_username" id="ftp_username" value="', $context['package_ftp']['username'], '" style="width: 99%;" class="input_text">
 							</dd>
-						</dl>
-						<div class="righttext">
-							<input type="submit" value="' . $txt['add_server'] . '" class="button_submit">
-							<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
-						</div>
-					</form>
-				</fieldset>
-				<fieldset>
-					<legend>', $txt['package_download_by_url'], '</legend>
-					<form action="', $scripturl, '?action=admin;area=packages;get;sa=download;byurl;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
-						<dl class="settings">
 							<dt>
-								<strong>' . $txt['serverurl'] . ':</strong>
+								<label for="ftp_password">', $txt['package_ftp_password'], ':</label>
 							</dt>
 							<dd>
-								<input type="text" name="package" size="44" value="http://" class="input_text">
+								<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
 							</dd>
 							<dt>
-								<strong>', $txt['package_download_filename'], ':</strong>
+								<label for="ftp_path">', $txt['package_ftp_path'], ':</label>
 							</dt>
 							<dd>
-								<input type="text" name="filename" size="44" class="input_text"><br>
-								<span class="smalltext">', $txt['package_download_filename_info'], '</span>
+								<input type="text" size="50" name="ftp_path" id="ftp_path" value="', $context['package_ftp']['path'], '" style="width: 99%;" class="input_text">
 							</dd>
 						</dl>
 						<div class="righttext">
-							<input type="submit" value="', $txt['download'], '" class="button_submit">
+							<input type="submit" value="', $txt['package_proceed'], '" class="button_submit">
 						</div>
 					</form>
-				</fieldset>
-			</div>
-		</div>
-		<br>
-		<div class="cat_bar">
-			<h3 class="catbg">' . $txt['package_upload_title'] . '</h3>
-		</div>
-		<div class="windowbg">
-			<div class="content">
-				<form action="' . $scripturl . '?action=admin;area=packages;get;sa=upload" method="post" accept-charset="', $context['character_set'], '" enctype="multipart/form-data" style="margin-bottom: 0;">
-					<dl class="settings">
-						<dt>
-							<strong>' . $txt['package_upload_select'] . ':</strong>
-						</dt>
-						<dd>
-							<input type="file" name="package" size="38" class="input_file">
-						</dd>
-					</dl>
-					<hr class="hrcolor">
-					<input type="submit" value="' . $txt['package_upload'] . '" class="button_submit">
-					<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
-				</form>
+				</div>
+			</div>';
+	}
+
+	echo '
+			<div class="windowbg2">
+				<div class="content">
+					<fieldset>
+						<legend>' . $txt['package_servers'] . '</legend>
+						<ul class="package_servers">';
+	foreach ($context['servers'] as $server)
+		echo '
+							<li class="flow_auto">
+								<span class="floatleft">' . $server['name'] . '</span>
+								<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=remove;server=' . $server['id'] . ';', $context['session_var'], '=', $context['session_id'], '">[ ' . $txt['delete'] . ' ]</a></span>
+								<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=browse;server=' . $server['id'] . '">[ ' . $txt['package_browse'] . ' ]</a></span>
+							</li>';
+	echo '
+						</ul>
+					</fieldset>
+					<fieldset>
+						<legend>' . $txt['add_server'] . '</legend>
+						<form action="' . $scripturl . '?action=admin;area=packages;get;sa=add" method="post" accept-charset="', $context['character_set'], '">
+							<dl class="settings">
+								<dt>
+									<strong>' . $txt['server_name'] . ':</strong>
+								</dt>
+								<dd>
+									<input type="text" name="servername" size="44" value="SMF" class="input_text">
+								</dd>
+								<dt>
+									<strong>' . $txt['serverurl'] . ':</strong>
+								</dt>
+								<dd>
+									<input type="text" name="serverurl" size="44" value="http://" class="input_text">
+								</dd>
+							</dl>
+							<div class="righttext">
+								<input type="submit" value="' . $txt['add_server'] . '" class="button_submit">
+								<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
+							</div>
+						</form>
+					</fieldset>
+					<fieldset>
+						<legend>', $txt['package_download_by_url'], '</legend>
+						<form action="', $scripturl, '?action=admin;area=packages;get;sa=download;byurl;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
+							<dl class="settings">
+								<dt>
+									<strong>' . $txt['serverurl'] . ':</strong>
+								</dt>
+								<dd>
+									<input type="text" name="package" size="44" value="http://" class="input_text">
+								</dd>
+								<dt>
+									<strong>', $txt['package_download_filename'], ':</strong>
+								</dt>
+								<dd>
+									<input type="text" name="filename" size="44" class="input_text"><br>
+									<span class="smalltext">', $txt['package_download_filename_info'], '</span>
+								</dd>
+							</dl>
+							<div class="righttext">
+								<input type="submit" value="', $txt['download'], '" class="button_submit">
+							</div>
+						</form>
+					</fieldset>
+				</div>
 			</div>
 		</div>
 	</div>';
@@ -1271,17 +1272,17 @@ function template_ftp_required()
 
 function template_view_operations()
 {
-	global $context, $txt, $settings;
+	global $context, $txt, $settings, $modSettings;
 
 	echo '<!DOCTYPE html>
 <html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
 	<head>
 		<title>', $txt['operation_title'], '</title>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/admin.css?alp21">
-		<script src="', $settings['default_theme_url'], '/scripts/script.js?alp21"></script>
-		<script src="', $settings['default_theme_url'], '/scripts/theme.js?alp21"></script>
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/admin.css', $modSettings['browser_cache'] ,'">
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/theme.js', $modSettings['browser_cache'] ,'"></script>
 	</head>
 	<body>
 		<div class="padding windowbg">

+ 2 - 4
Themes/default/PersonalMessage.template.php

@@ -350,9 +350,9 @@ function template_folder()
 						<li><a href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank" class="new_win">', ($settings['use_image_buttons'] ? '<span class="generic_icons www centericon" title="' . $message['member']['website']['title'] . '"></span>' : $txt['www']), '</a></li>';
 
 					// Don't show the email address if they want it hidden.
-					if (in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum')) && $context['can_send_email'])
+					if ($message['member']['show_email'])
 						echo '
-						<li><a href="', $scripturl, '?action=emailuser;sa=email;uid=', $message['member']['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<span class="generic_icons mail centericon" title="' . $txt['email'] . '"></span>' : $txt['email']), '</a></li>';
+						<li><a href="mailto:', $message['member']['email'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<span class="generic_icons mail centericon" title="' . $txt['email'] . '"></span>' : $txt['email']), '</a></li>';
 
 					// Since we know this person isn't a guest, you *can* message them.
 					if ($context['can_send_pm'])
@@ -1267,8 +1267,6 @@ function template_send()
 	</div><br class="clear">';
 
 	echo '
-		<script src="', $settings['default_theme_url'], '/scripts/PersonalMessage.js?alp21"></script>
-		<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 		<script><!-- // --><![CDATA[
 			var oPersonalMessageSend = new smf_PersonalMessageSend({
 				sSelf: \'oPersonalMessageSend\',

+ 6 - 6
Themes/default/Post.template.php

@@ -916,7 +916,7 @@ function template_main()
 // The template for the spellchecker.
 function template_spellcheck()
 {
-	global $context, $settings, $txt;
+	global $context, $settings, $txt, $modSettings;
 
 	// The style information that makes the spellchecker look... like the forum hopefully!
 	echo '<!DOCTYPE html>
@@ -924,7 +924,7 @@ function template_spellcheck()
 	<head>
 		<title>', $txt['spell_check'], '</title>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
 		<style type="text/css">
 			body, td
 			{
@@ -957,8 +957,8 @@ function template_spellcheck()
 			var spell_formname = window.opener.spell_formname;
 			var spell_fieldname = window.opener.spell_fieldname;
 		// ]]></script>
-		<script src="', $settings['default_theme_url'], '/scripts/spellcheck.js"></script>
-		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/spellcheck.js', $modSettings['browser_cache'] ,'"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
 		<script><!-- // --><![CDATA[
 			', $context['spell_js'], '
 		// ]]></script>
@@ -990,14 +990,14 @@ function template_spellcheck()
 
 function template_quotefast()
 {
-	global $context, $settings, $txt;
+	global $context, $settings, $txt, $modSettings;
 
 	echo '<!DOCTYPE html>
 <html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
 	<head>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<title>', $txt['retrieving_quote'], '</title>
-		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
+		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'] ,'"></script>
 	</head>
 	<body>
 		', $txt['retrieving_quote'], '

+ 13 - 33
Themes/default/Profile.template.php

@@ -15,9 +15,6 @@ function template_profile_above()
 {
 	global $context, $settings, $scripturl, $modSettings, $txt;
 
-	echo '
-	<script src="', $settings['default_theme_url'], '/scripts/profile.js"></script>';
-
 	// Prevent Chrome from auto completing fields when viewing/editing other members profiles
 	if (isBrowser('is_chrome') && !$context['user']['is_owner'])
 		echo '
@@ -164,11 +161,10 @@ function template_summary()
 				</div>
 				', $context['member']['avatar']['image'], '
 				<ul class="reset">';
-	// @TODO fix the <ul> when no fields are visible
-	// What about if we allow email only via the forum??
-	if ($context['member']['show_email'] === 'yes' || $context['member']['show_email'] === 'no_through_forum' || $context['member']['show_email'] === 'yes_permission_override' && $context['can_send_email'])
+	// Email is only visible if it's your profile or you have the moderate_forum permission
+	if ($context['member']['show_email'])
 		echo '
-					<li><a href="', $scripturl, '?action=emailuser;sa=email;uid=', $context['member']['id'], '" title="', $context['member']['show_email'] == 'yes' || $context['member']['show_email'] == 'yes_permission_override' ? $context['member']['email'] : '', '" rel="nofollow"><span class="generic_icons mail" title="' . $txt['email'] . '"></span></a></li>';
+					<li><a href="mailto:', $context['member']['email'], '" title="', $context['member']['email'], '" rel="nofollow"><span class="generic_icons mail" title="' . $txt['email'] . '"></span></a></li>';
 
 	// Don't show an icon if they haven't specified a website.
 	if ($context['member']['website']['url'] !== '' && !isset($context['disabled_fields']['website']))
@@ -233,19 +229,11 @@ function template_summary()
 					<dt>', $txt['profile_posts'], ': </dt>
 					<dd>', $context['member']['posts'], ' (', $context['member']['posts_per_day'], ' ', $txt['posts_per_day'], ')</dd>';
 
-	if ($context['can_send_email'])
+	if ($context['member']['show_email'])
 	{
-		// Only show the email address fully if it's not hidden - and we reveal the email.
-		if ($context['member']['show_email'] == 'yes')
-			echo '
-						<dt>', $txt['email'], ': </dt>
-						<dd><a href="', $scripturl, '?action=emailuser;sa=email;uid=', $context['member']['id'], '">', $context['member']['email'], '</a></dd>';
-
-		// ... Or if the one looking at the profile is an admin they can see it anyway.
-		elseif ($context['member']['show_email'] == 'yes_permission_override')
-			echo '
-						<dt>', $txt['email'], ': </dt>
-						<dd><em><a href="', $scripturl, '?action=emailuser;sa=email;uid=', $context['member']['id'], '">', $context['member']['email'], '</a></em></dd>';
+		echo '
+					<dt>', $txt['email'], ': </dt>
+					<dd><a href="mailto:', $context['member']['email'], '">', $context['member']['email'], '</a></dd>';
 	}
 
 	if (!empty($modSettings['titlesEnable']) && !empty($context['member']['title']))
@@ -612,7 +600,7 @@ function template_editBuddies()
 				<th class="first_th" scope="col" width="20%">', $txt['name'], '</th>
 				<th scope="col">', $txt['status'], '</th>';
 
-	if ($context['can_send_email'])
+	if (allowedTo('moderate_forum'))
 		echo '
 				<th scope="col">', $txt['email'], '</th>';
 
@@ -635,9 +623,9 @@ function template_editBuddies()
 			<tr class="', $alternate ? 'windowbg' : 'windowbg2', '">
 				<td>', $buddy['link'], '</td>
 				<td align="center"><a href="', $buddy['online']['href'], '"><img src="', $buddy['online']['image_href'], '" alt="', $buddy['online']['text'], '" title="', $buddy['online']['text'], '"></a></td>';
-		if ($context['can_send_email'])
+		if ($buddy['show_email'])
 			echo '
-				<td align="center">', ($buddy['show_email'] == 'no' ? '' : '<a href="' . $scripturl . '?action=emailuser;sa=email;uid=' . $buddy['id'] . '" rel="nofollow"><span class="generic_icons mail icon" title="' . $txt['email'] . ' ' . $buddy['name'] . '"></span></a>'), '</td>';
+				<td align="center"><a href="mailto:' . $buddy['email'] . '" rel="nofollow"><span class="generic_icons mail icon" title="' . $txt['email'] . ' ' . $buddy['name'] . '"></span></a></td>';
 
 		// If these are off, don't show them
 		foreach ($buddy_fields as $key => $column)
@@ -685,7 +673,6 @@ function template_editBuddies()
 			</div>
 		</div>
 	</form>
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oAddBuddySuggest = new smc_AutoSuggest({
 			sSelf: \'oAddBuddySuggest\',
@@ -724,7 +711,7 @@ function template_editIgnoreList()
 				<th class="first_th" scope="col" width="20%">', $txt['name'], '</th>
 				<th scope="col">', $txt['status'], '</th>';
 
-	if ($context['can_send_email'])
+	if (allowedTo('moderate_forum'))
 		echo '
 				<th scope="col">', $txt['email'], '</th>';
 
@@ -746,9 +733,9 @@ function template_editIgnoreList()
 			<tr class="', $alternate ? 'windowbg' : 'windowbg2', '">
 				<td>', $member['link'], '</td>
 				<td align="center"><a href="', $member['online']['href'], '"><img src="', $member['online']['image_href'], '" alt="', $member['online']['text'], '" title="', $member['online']['text'], '"></a></td>';
-		if ($context['can_send_email'])
+		if ($member['show_email'])
 			echo '
-				<td align="center">', ($member['show_email'] == 'no' ? '' : '<a href="' . $scripturl . '?action=emailuser;sa=email;uid=' . $member['id'] . '" rel="nofollow"><span class="generic_icons mail icon" title="' . $txt['email'] . ' ' . $member['name'] . '"></span></a>'), '</td>';
+				<td align="center"><a href="mailto:' . $member['email'] . '" rel="nofollow"><span class="generic_icons mail icon" title="' . $txt['email'] . ' ' . $member['name'] . '"></span></a></td>';
 		echo '
 				<td align="center"><a href="', $scripturl, '?action=profile;u=', $context['id_member'], ';area=lists;sa=ignore;remove=', $member['id'], ';', $context['session_var'], '=', $context['session_id'], '"><span class="generic_icons delete" title="', $txt['ignore_remove'], '"></span></a></td>
 			</tr>';
@@ -787,7 +774,6 @@ function template_editIgnoreList()
 			</div>
 		</div>
 	</form>
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oAddIgnoreSuggest = new smc_AutoSuggest({
 			sSelf: \'oAddIgnoreSuggest\',
@@ -2849,11 +2835,6 @@ function template_profile_signature_modify()
 		echo '
 								<span class="smalltext">', $context['signature_warning'], '</span>';
 
-	// Load the spell checker?
-	if ($context['show_spellchecking'])
-		echo '
-								<script src="', $settings['default_theme_url'], '/scripts/spellcheck.js"></script>';
-
 	// Some javascript used to count how many characters have been used so far in the signature.
 	echo '
 								<script><!-- // --><![CDATA[
@@ -3088,7 +3069,6 @@ function template_authentication_method()
 
 	// The main header!
 	echo '
-		<script src="', $settings['default_theme_url'], '/scripts/register.js"></script>
 		<form action="', $scripturl, '?action=profile;area=authentication;save" method="post" accept-charset="', $context['character_set'], '" name="creator" id="creator" enctype="multipart/form-data">
 			<div class="cat_bar">
 				<h3 class="catbg">

+ 2 - 4
Themes/default/Register.template.php

@@ -51,7 +51,6 @@ function template_registration_form()
 	global $context, $settings, $scripturl, $txt, $modSettings;
 
 	echo '
-		<script src="', $settings['default_theme_url'], '/scripts/register.js"></script>
 		<script><!-- // --><![CDATA[
 			function verifyAgree()
 			{
@@ -462,7 +461,7 @@ function template_coppa_form()
 // Show a window containing the spoken verification code.
 function template_verification_sound()
 {
-	global $context, $settings, $txt, $scripturl;
+	global $context, $settings, $txt, $scripturl, $modSettings;
 
 	echo '<!DOCTYPE html>
 <html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
@@ -470,7 +469,7 @@ function template_verification_sound()
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<title>', $txt['visual_verification_sound'], '</title>
 		<meta name="robots" content="noindex">
-		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21">
+		<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'] ,'">
 		<style type="text/css">';
 
 	// Just show the help text and a "close window" link.
@@ -506,7 +505,6 @@ function template_admin_register()
 	global $context, $settings, $scripturl, $txt, $modSettings;
 
 	echo '
-	<script src="', $settings['default_theme_url'], '/scripts/register.js"></script>
 	<div id="admincenter">
 		<div id="admin_form_wrapper">
 			<form id="postForm" class="windowbg2" action="', $scripturl, '?action=admin;area=regcenter" method="post" accept-charset="', $context['character_set'], '" name="postForm">

+ 1 - 3
Themes/default/Reminder.template.php

@@ -87,7 +87,6 @@ function template_set_password()
 	global $context, $settings, $txt, $scripturl, $modSettings;
 
 	echo '
-	<script src="', $settings['default_theme_url'], '/scripts/register.js"></script>
 	<br>
 	<form action="', $scripturl, '?action=reminder;sa=setpassword2" name="reminder_form" id="reminder_form" method="post" accept-charset="', $context['character_set'], '">
 		<div class="tborder login">
@@ -133,10 +132,9 @@ function template_set_password()
 
 function template_ask()
 {
-	global $context, $settings, $txt, $scripturl, $modSettings;
+	global $context, $txt, $scripturl, $modSettings;
 
 	echo '
-	<script src="', $settings['default_theme_url'], '/scripts/register.js"></script>
 	<br>
 	<form action="', $scripturl, '?action=reminder;sa=secret2" method="post" accept-charset="', $context['character_set'], '" name="creator" id="creator">
 		<div class="tborder login">

+ 105 - 0
Themes/default/ReportToMod.template.php

@@ -0,0 +1,105 @@
+<?php
+/**
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+//------------------------------------------------------------------------------
+/*	This template contains two humble sub templates - main. Its job is pretty
+	simple: it collects the information we need to actually send the topic.
+
+	The report sub template gets shown from:
+		'?action=reporttm;topic=##.##;msg=##'
+		'?action=reporttm;u=#'
+	It should submit to:
+		'?action=reporttm;topic=' . $context['current_topic'] . '.' . $context['start']
+		'?action=reporttm;u=#'
+	It only needs to send the following fields:
+		comment: an additional comment to give the moderator.
+		sc: the session id, or $context['session_id'].
+*/
+
+function template_main()
+{
+	global $context, $txt, $scripturl;
+
+	// Want to see your master piece?
+	echo '
+	<div id="preview_section"', isset($context['preview_message']) ? '' : ' style="display: none;"', '>
+		<div class="cat_bar">
+			<h3 class="catbg">
+				<span>', $txt['preview'], '</span>
+			</h3>
+		</div>
+		<div class="windowbg">
+			<div class="content">
+				<div class="post" id="preview_body">
+					', empty($context['preview_message']) ? '<br>' : $context['preview_message'], '
+				</div>
+			</div>
+		</div>
+	</div><br>';
+
+	echo '
+	<div id="report_form">
+		<form action="', $context['submit_url'], '" method="post" accept-charset="', $context['character_set'], '">
+			<input type="hidden" name="', $context['report_type'], '" value="', $context['reported_item'], '">
+				<div class="cat_bar">
+					<h3 class="catbg">', $context['page_title'], '</h3>
+				</div>
+				<div class="windowbg">
+					<div class="content">';
+
+	if (!empty($context['post_errors']))
+	{
+		echo '
+						<div id="error_box" class="errorbox">
+							<ul id="error_list">';
+
+		foreach ($context['post_errors'] as $key => $error)
+			echo '
+								<li id="error_', $key, '" class="error">', $error, '</li>';
+
+		echo '
+							</ul>';
+	}
+	else
+		echo '
+							<div style="display:none" id="error_box" class="errorbox">';
+
+	echo '
+						</div>';
+
+	echo '
+						<p class="noticebox">', $context['notice'], '</p>
+						<br>
+						<dl class="settings" id="report_post">';
+
+	echo '
+							<dt>
+								<label for="report_comment">', $txt['enter_comment'], '</label>:
+							</dt>
+							<dd>
+								<textarea type="text" id="report_comment" name="comment" rows="5">', $context['comment_body'], '</textarea>
+							</dd>';
+
+	echo '
+						</dl>
+						<div class="flow_auto">
+							<input type="submit" name="preview" value="', $txt['preview'] , '" class="button_submit">
+							<input type="submit" name="save" value="', $txt['rtm10'], '" style="margin-left: 1ex;" class="button_submit">
+							<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+						</div>
+					</div>
+				</div>
+		</form>
+	</div>';
+}
+
+?>

+ 317 - 0
Themes/default/ReportedPosts.template.php

@@ -0,0 +1,317 @@
+<?php
+/**
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2014 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+function template_reported_posts()
+{
+	global $context, $txt, $scripturl;
+
+	// Let them know the action was a success.
+	if (!empty($context['report_post_action']))
+	{
+		echo '
+			<div class="infobox">
+				', $txt['report_action_'. $context['report_post_action']], '
+			</div>';
+	}
+
+	echo '
+	<form id="reported_posts" action="', $scripturl, '?action=moderate;area=reports;sa=show', $context['view_closed'] ? ';closed' : '', ';start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
+		<div class="cat_bar">
+			<h3 class="catbg">
+				', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], '
+			</h3>
+		</div>';
+
+	// Make the buttons.
+	$close_button = create_button('close.png', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['view_closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'class="centericon"');
+	$details_button = create_button('details.png', 'mc_reportedp_details', 'mc_reportedp_details', 'class="centericon"');
+	$ignore_button = create_button('ignore.png', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'class="centericon"');
+	$unignore_button = create_button('ignore.png', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'class="centericon"');
+	$ban_button = create_button('close.png', 'mc_reportedp_ban', 'mc_reportedp_ban', 'class="centericon"');
+	$delete_button = create_button('delete.png', 'mc_reportedp_delete', 'mc_reportedp_delete', 'class="centericon"');
+
+	foreach ($context['reports'] as $report)
+	{
+		echo '
+		<div class="generic_list_wrapper ', $report['alternate'] ? 'windowbg' : 'windowbg2', '">
+			<div class="content">
+				<h5>
+					<strong>', !empty($report['topic']['board_name']) ? '<a href="' . $scripturl . '?board=' . $report['topic']['id_board'] . '.0">' . $report['topic']['board_name'] . '</a>' : '??', ' / <a href="', $report['topic']['href'], '">', $report['subject'], '</a></strong> ', $txt['mc_reportedp_by'], ' <strong>', $report['author']['link'], '</strong>
+				</h5>
+				<div class="smalltext">
+					', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], '&nbsp;-&nbsp;';
+
+		// Prepare the comments...
+		$comments = array();
+		foreach ($report['comments'] as $comment)
+			$comments[$comment['member']['id']] = $comment['member']['link'];
+
+		echo '
+					', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), '
+				</div>
+				<hr>
+				', $report['body'], '
+				<br>
+				<ul class="quickbuttons">
+					<li><a href="', $report['report_href'], '">', $details_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=reports;sa=handle;ignore=', (int) !$report['ignore'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], ';', $context['mod-report-ignore_token_var'], '=', $context['mod-report-ignore_token'], '" class="report_ignore" data-ignore="', (int) !$report['ignore'], '">', $report['ignore'] ? $unignore_button : $ignore_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=reports;sa=handle;closed=', (int) !$report['closed'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], ';', $context['mod-report-closed_token_var'], '=', $context['mod-report-closed_token'], '">', $close_button, '</a></li>';
+
+		// Delete message button.
+		if (!$report['closed'] && (is_array($context['report_remove_any_boards']) && in_array($report['topic']['id_board'], $context['report_remove_any_boards'])))
+			echo '
+					<li><a href="', $scripturl, '?action=deletemsg;topic=', $report['topic']['id'] ,'.0;msg=', $report['topic']['id_msg'] ,';modcenter;', $context['session_var'], '=', $context['session_id'], '" class="delete_message">', $delete_button, '</a></li>';
+
+		// Ban this user button.
+		if (!$report['closed'] && !empty($context['report_manage_bans']))
+			echo '
+					<li><a href="', $scripturl, '?action=admin;area=ban;sa=add', (!empty($report['author']['id']) ? ';u='. $report['author']['id'] : ';msg='. $report['topic']['id_msg']) ,';', $context['session_var'], '=', $context['session_id'], '">', $ban_button, '</a></li>';
+
+		echo '
+					<li>', !$context['view_closed'] ? '<input type="checkbox" name="close[]" value="' . $report['id'] . '" class="input_check">' : '', '</li>
+				</ul>
+			</div>
+		</div>';
+	}
+
+	// Were none found?
+	if (empty($context['reports']))
+		echo '
+		<div class="windowbg2">
+			<div class="content">
+				<p class="centertext">', $txt['mc_reportedp_none_found'], '</p>
+			</div>
+		</div>';
+
+	echo '
+		<div class="pagesection">
+			', !empty($context['total_reports']) && $context['total_reports'] >= $context['reports_how_many'] ? '<div class="pagelinks floatleft">'. $context['page_index']. '</div>' : '' ,'
+			<div class="floatright">', !$context['view_closed'] ? '
+				<input type="hidden" name="'. $context['mod-report-close-all_token_var'] .'" value="'. $context['mod-report-close-all_token'] .'">
+				<input type="submit" name="close_selected" value="' . $txt['mc_reportedp_close_selected'] . '" class="button_submit">' : '', '
+			</div>
+		</div>
+		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+	</form>';
+}
+
+
+// A block to show the current top reported posts.
+function template_reported_posts_block()
+{
+	global $context, $txt, $scripturl;
+
+	echo '
+		<div class="cat_bar">
+			<h3 class="catbg">
+				<span id="reported_posts_toggle" class="', !empty($context['admin_prefs']['mcrp']) ? 'toggle_down' : 'toggle_up', ' floatright" style="display: none;"></span>
+				<a href="', $scripturl, '?action=moderate;area=reports" id="reported_posts_link">', $txt['mc_recent_reports'], '</a>
+			</h3>
+		</div>
+		<div class="windowbg" id="reported_posts_panel">
+			<div class="content modbox">
+				<ul class="reset">';
+
+		foreach ($context['reported_posts'] as $report)
+			echo '
+					<li class="smalltext">
+						<a href="', $report['report_href'], '">', $report['subject'], '</a> ', $txt['mc_reportedp_by'], ' ', $report['author']['link'], '
+					</li>';
+
+		// Don't have any watched users right now?
+		if (empty($context['reported_posts']))
+			echo '
+					<li>
+						<strong class="smalltext">', $txt['mc_recent_reports_none'], '</strong>
+					</li>';
+
+		echo '
+				</ul>
+			</div>
+		</div>
+
+	<script><!-- // --><![CDATA[
+		var oReportedPostsPanelToggle = new smc_Toggle({
+			bToggleEnabled: true,
+			bCurrentlyCollapsed: ', !empty($context['admin_prefs']['mcrp']) ? 'true' : 'false', ',
+			aSwappableContainers: [
+				\'reported_posts_panel\'
+			],
+			aSwapImages: [
+				{
+					sId: \'reported_posts_toggle\',
+					altExpanded: ', JavaScriptEscape($txt['hide']), ',
+					altCollapsed: ', JavaScriptEscape($txt['show']), '
+				}
+			],
+			aSwapLinks: [
+				{
+					sId: \'reported_posts_link\',
+					msgExpanded: ', JavaScriptEscape($txt['mc_recent_reports']), ',
+					msgCollapsed: ', JavaScriptEscape($txt['mc_recent_reports']), '
+				}
+			],
+			oThemeOptions: {
+				bUseThemeSettings: true,
+				sOptionName: \'admin_preferences\',
+				sSessionVar: smf_session_var,
+				sSessionId: smf_session_id,
+				sThemeId: \'1\',
+				sAdditionalVars: \';admin_key=mcrp\'
+			}
+		});
+	// ]]></script>';
+}
+
+
+function template_viewmodreport()
+{
+	global $context, $scripturl, $txt;
+
+	// Let them know the action was a success.
+	if (!empty($context['report_post_action']))
+	{
+		echo '
+			<div class="infobox">
+				', $txt['report_action_'. $context['report_post_action']], '
+			</div>';
+	}
+
+	echo '
+	<div id="modcenter">
+		<form action="', $scripturl, '?action=moderate;area=reports;sa=handlecomment;rid=', $context['report']['id'], '" method="post" accept-charset="', $context['character_set'], '">
+			<div class="cat_bar">
+				<h3 class="catbg">
+					', sprintf($txt['mc_viewmodreport'], $context['report']['message_link'], $context['report']['author']['link']), '
+				</h3>
+			</div>
+			<div class="title_bar">
+				<h3 class="titlebg">
+					<span class="floatleft">
+						', sprintf($txt['mc_modreport_summary'], $context['report']['num_reports'], $context['report']['last_updated']), '
+					</span>
+					<span class="floatright">';
+
+		// Make the buttons.
+		$close_button = create_button('close.png', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', $context['report']['closed'] ? 'mc_reportedp_open' : 'mc_reportedp_close', 'class="centericon"');
+		$ignore_button = create_button('ignore.png', 'mc_reportedp_ignore', 'mc_reportedp_ignore', 'class="centericon"');
+		$unignore_button = create_button('ignore.png', 'mc_reportedp_unignore', 'mc_reportedp_unignore', 'class="centericon"');
+
+		echo '
+						<a href="', $scripturl, '?action=moderate;area=reports;sa=handle;ignore=', (int) !$context['report']['ignore'], ';rid=', $context['report']['id'], ';', $context['session_var'], '=', $context['session_id'], ';', $context['mod-report-ignore_token_var'], '=', $context['mod-report-ignore_token'], '" class="report_ignore" data-ignore="', !$context['report']['ignore'] ,'">', $context['report']['ignore'] ? $unignore_button : $ignore_button, '</a>
+						<a href="', $scripturl, '?action=moderate;area=reports;sa=handle;closed=', (int) !$context['report']['closed'], ';rid=', $context['report']['id'], ';', $context['session_var'], '=', $context['session_id'], ';', $context['mod-report-closed_token_var'], '=', $context['mod-report-closed_token'], '">', $close_button, '</a>
+					</span>
+				</h3>
+			</div>
+			<div class="windowbg2">
+				<div class="content">
+					', $context['report']['body'], '
+				</div>
+			</div>
+			<br>
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mc_modreport_whoreported_title'], '</h3>
+			</div>';
+
+	foreach ($context['report']['comments'] as $comment)
+		echo '
+			<div class="windowbg">
+				<div class="content">
+					<p class="smalltext">', sprintf($txt['mc_modreport_whoreported_data'], $comment['member']['link'] . (empty($comment['member']['id']) && !empty($comment['member']['ip']) ? ' (' . $comment['member']['ip'] . ')' : ''), $comment['time']), '</p>
+					<p>', $comment['message'], '</p>
+				</div>
+			</div>';
+
+	echo '
+			<br>
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mc_modreport_mod_comments'], '</h3>
+			</div>
+				<div>';
+
+	if (empty($context['report']['mod_comments']))
+		echo '
+				<div class="information">
+					<p class="centertext">', $txt['mc_modreport_no_mod_comment'], '</p>
+				</div>';
+
+	foreach ($context['report']['mod_comments'] as $comment)
+	{
+		echo
+						'<div class="cat_bar">
+						<h4 class="catbg">', $comment['member']['link'], ':  <em class="smalltext">(', $comment['time'], ')</em>', ($comment['can_edit'] ? '<span class="floatright"><a href="' . $scripturl . '?action=moderate;area=reports;sa=editcomment;rid='. $context['report']['id'] .';mid='. $comment['id'] .';'. $context['session_var'] .'='. $context['session_id'] .'">'. $txt['mc_reportedp_comment_edit'] .'</a> | <a href="' . $scripturl . '?action=moderate;area=reports;sa=handlecomment;rid='. $context['report']['id'] .';mid='. $comment['id'] .';delete;'. $context['session_var'] .'='. $context['session_id']. ';'. $context['mod-reportC-delete_token_var'] .'='. $context['mod-reportC-delete_token'] .'" class="deleteModComment">'. $txt['mc_reportedp_comment_delete'] .'</a></span>' : '') ,'
+						</div></h4>';
+
+		echo '
+						<div class="windowbg">
+							<p class="content">', $comment['message'], '</p>
+						</div>';
+	}
+
+	echo '
+					<div class="title_bar">
+						<h3 class="titlebg">
+							<span class="floatleft">
+								', $txt['mc_reportedp_new_comment'], '
+							</span>
+						</h3>
+					</div>
+					<div class="content">
+						<textarea rows="2" cols="60" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 60%; min-width: 60%' : 'width: 60%') . ';" name="mod_comment"></textarea>
+						<div class="padding">
+							<input type="submit" name="add_comment" value="', $txt['mc_modreport_add_mod_comment'], '" class="button_submit">
+							<input type="hidden" name="', $context['mod-reportC-add_token_var'], '" value="', $context['mod-reportC-add_token'], '">
+						</div>
+					</div>
+				</div>
+			<br>';
+
+	template_show_list('moderation_actions_list');
+
+	echo '
+			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+		</form>
+	</div>';
+}
+
+function template_edit_comment()
+{
+	global $context, $scripturl, $txt;
+
+	echo '
+	<div id="modcenter">
+		<form action="', $scripturl, '?action=moderate;area=reports;sa=editcomment;mid=', $context['comment_id'], ';rid=', $context['report_id'] ,';save" method="post" accept-charset="', $context['character_set'], '">';
+
+	echo '
+			<br>
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mc_modreport_edit_mod_comment'] ,'</h3>
+			</div>
+			<div class="windowbg2">
+				<div class="content">';
+
+	echo '
+					<textarea rows="6" cols="60" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 60%; min-width: 60%' : 'width: 60%') . ';" name="mod_comment">', $context['comment']['body'] ,'</textarea>
+					<div>
+						<input type="submit" name="edit_comment" value="', $txt['mc_modreport_edit_mod_comment'], '" class="button_submit">
+					</div>
+				</div>
+			</div>
+			<br>';
+
+	echo '
+			<input type="hidden" name="', $context['mod-reportC-edit_token_var'], '" value="', $context['mod-reportC-edit_token'], '">
+			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
+		</form>
+	</div>';
+}
+?>

+ 2 - 2
Themes/default/Reports.template.php

@@ -144,14 +144,14 @@ function template_main()
 // Header of the print page!
 function template_print_above()
 {
-	global $context, $settings, $txt;
+	global $context, $settings $modSettings;
 
 	echo '<!DOCTYPE html>
 <html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
 	<head>
 		<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '">
 		<title>', $context['page_title'], '</title>
-		<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/report.css">
+		<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/report.css', $modSettings['browser_cache'] ,'">
 	</head>
 	<body>';
 }

+ 0 - 1
Themes/default/Search.template.php

@@ -207,7 +207,6 @@ function template_main()
 
 	echo '
 	</form>
-	<script src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script><!-- // --><![CDATA[
 		var oAddMemberSuggest = new smc_AutoSuggest({
 			sSelf: \'oAddMemberSuggest\',

+ 0 - 179
Themes/default/SendTopic.template.php

@@ -1,179 +0,0 @@
-<?php
-/**
- * Simple Machines Forum (SMF)
- *
- * @package SMF
- * @author Simple Machines http://www.simplemachines.org
- * @copyright 2014 Simple Machines and individual contributors
- * @license http://www.simplemachines.org/about/smf/license.php BSD
- *
- * @version 2.1 Alpha 1
- */
-
-//------------------------------------------------------------------------------
-/*	This template contains two humble sub templates - main. Its job is pretty
-	simple: it collects the information we need to actually send the topic.
-
-	The report sub template gets shown from:
-		'?action=reporttm;topic=##.##;msg=##'
-	It should submit to:
-		'?action=reporttm;topic=' . $context['current_topic'] . '.' . $context['start']
-	It only needs to send the following fields:
-		comment: an additional comment to give the moderator.
-		sc: the session id, or $context['session_id'].
-*/
-
-// Send an email to a user!
-function template_custom_email()
-{
-	global $context, $txt, $scripturl;
-
-	echo '
-	<div id="send_topic">
-		<form action="', $scripturl, '?action=emailuser;sa=email" method="post" accept-charset="', $context['character_set'], '">
-			<div class="cat_bar">
-				<h3 class="catbg">
-					<span class="generic_icons mail icon"></span>', $context['page_title'], '
-				</h3>
-			</div>
-			<div class="windowbg">
-				<div class="content">
-					<dl class="settings send_mail">
-						<dt>
-							<strong>', $txt['sendtopic_receiver_name'], ':</strong>
-						</dt>
-						<dd>
-							', $context['recipient']['link'], '
-						</dd>';
-
-	// Can the user see the persons email?
-	if ($context['can_view_receipient_email'])
-		echo '
-						<dt>
-							<strong>', $txt['sendtopic_receiver_email'], ':</strong>
-						</dt>
-						<dd>
-							', $context['recipient']['email_link'], '
-						</dd>
-					</dl>
-					<hr>
-					<dl class="settings send_mail">';
-
-	// If it's a guest we need their details.
-	if ($context['user']['is_guest'])
-		echo '
-						<dt>
-							<label for="y_name"><strong>', $txt['sendtopic_sender_name'], ':</strong></label>
-						</dt>
-						<dd>
-							<input type="text" id="y_name" name="y_name" size="24" maxlength="40" value="', $context['user']['name'], '" class="input_text">
-						</dd>
-						<dt>
-							<label for="y_email"><strong>', $txt['sendtopic_sender_email'], ':</strong></label><br>
-							<span class="smalltext">', $txt['send_email_disclosed'], '</span>
-						</dt>
-						<dd>
-							<input type="email" id="y_mail" name="y_email" size="24" maxlength="50" value="', $context['user']['email'], '" class="input_text" required>
-						</dt>';
-	// Otherwise show the user that we know their email.
-	else
-		echo '
-						<dt>
-							<strong>', $txt['sendtopic_sender_email'], ':</strong><br>
-							<span class="smalltext">', $txt['send_email_disclosed'], '</span>
-						</dt>
-						<dd>
-							<em>', $context['user']['email'], '</em>
-						</dd>';
-
-	echo '
-						<dt>
-							<label for="email_subject"><strong>', $txt['send_email_subject'], ':</strong></label>
-						</dt>
-						<dd>
-							<input type="text" id="email_subject" name="email_subject" size="50" maxlength="100" class="input_text" required>
-						</dd>
-						<dt>
-							<label for="email_body"><strong>', $txt['message'], ':</strong></label>
-						</dt>
-						<dd>
-							<textarea id="email_body" name="email_body" rows="10" cols="20" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 90%; min-width: 90%' : 'width: 90%') . ';" required></textarea>
-						</dd>
-					</dl>
-					<hr class="hrcolor">
-					<div class="flow_auto">
-						<input type="submit" name="send" value="', $txt['sendtopic_send'], '" class="button_submit">
-					</div>
-				</div>
-			</div>';
-
-	foreach ($context['form_hidden_vars'] as $key => $value)
-		echo '
-			<input type="hidden" name="', $key, '" value="', $value, '">';
-
-	echo '
-			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
-		</form>
-	</div>';
-}
-
-function template_report()
-{
-	global $context, $txt, $scripturl;
-
-	echo '
-	<div id="report_topic">
-		<form action="', $scripturl, '?action=reporttm;topic=', $context['current_topic'], '.', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
-			<input type="hidden" name="msg" value="' . $context['message_id'] . '">
-				<div class="cat_bar">
-					<h3 class="catbg">', $txt['report_to_mod'], '</h3>
-				</div>
-				<div class="windowbg">
-					<div class="content">';
-
-	if (!empty($context['post_errors']))
-	{
-	echo '
-				<div id="error_box" class="errorbox">
-					<ul id="error_list">';
-
-		foreach ($context['post_errors'] as $key => $error)
-			echo '
-						<li id="error_', $key, '" class="error">', $error, '</li>';
-
-		echo '
-					</ul>';
-	}
-	else
-		echo '
-				<div style="display:none" id="error_box" class="errorbox">';
-
-		echo '
-				</div>';
-
-	echo '
-						<p class="noticebox">', $txt['report_to_mod_func'], '</p>
-						<br>
-						<dl class="settings" id="report_post">';
-
-	echo '
-							<dt>
-								<label for="report_comment">', $txt['enter_comment'], '</label>:
-							</dt>
-							<dd>
-								<textarea type="text" id="report_comment" name="comment" rows="5">', $context['comment_body'], '</textarea>
-							</dd>';
-
-	echo '
-						</dl>
-						<div class="flow_auto">
-							<input type="submit" name="save" value="', $txt['rtm10'], '" style="margin-left: 1ex;" class="button_submit">
-							<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
-						</div>
-					</div>
-				</div>
-		</form>
-	</div>';
-}
-
-?>

+ 0 - 1
Themes/default/Stats.template.php

@@ -396,7 +396,6 @@ function template_main()
 		</table>
 		</div>
 	</div>
-	<script src="', $settings['default_theme_url'], '/scripts/stats.js"></script>
 	<script><!-- // --><![CDATA[
 		var oStatsCenter = new smf_StatsCenter({
 			sTableId: \'stats\',

+ 2 - 2
Themes/default/Wireless.template.php

@@ -889,7 +889,7 @@ function template_imode_below()
 // XHTMLMP (XHTML Mobile Profile) templates used for WAP 2.0 start here
 function template_wap2_above()
 {
-	global $context, $settings, $user_info;
+	global $context, $settings, $user_info, $modSettings;
 
 	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?', '>
 <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
@@ -903,7 +903,7 @@ function template_wap2_above()
 		<link rel="canonical" href="', $context['canonical_url'], '" />';
 
 	echo '
-		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/wireless.css" type="text/css" />
+		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/wireless.css" type="text/css', $modSettings['browser_cache'] ,'" />
 	</head>
 	<body>';
 }

+ 4 - 4
Themes/default/css/index.css

@@ -2716,16 +2716,16 @@ dl.send_mail dd {
 	width: 64%;
 }
 
-/* Styles for the report topic section.
+/* Styles for the report topic/user section.
 ---------------------------------------------------- */
 
-#report_topic dl {
+#report_form dl {
 	margin-bottom: 0;
 }
-#report_topic dl.settings dt {
+#report_form dl.settings dt {
 	width: 20%;
 }
-#report_topic dl.settings dd {
+#report_form dl.settings dd {
 	width: 79%;
 }
 #report_comment {

+ 2 - 0
Themes/default/languages/Admin.english.php

@@ -443,6 +443,7 @@ $txt['attachment_transfer_progress'] = 'Please wait. Transfer in progess.';
 
 $txt['mods_cat_avatars'] = 'Avatars';
 $txt['avatar_directory'] = 'Avatars directory';
+$txt['avatar_directory_wrong'] = 'The Avatars directory is not valid. This will cause several issues with your forum.';
 $txt['avatar_url'] = 'Avatars URL';
 $txt['avatar_max_width_external'] = 'Maximum width of external avatar';
 $txt['avatar_max_height_external'] = 'Maximum height of external avatar';
@@ -466,6 +467,7 @@ $txt['avatar_select_permission'] = 'Select permissions for each group';
 $txt['avatar_download_external'] = 'Download avatar at given URL';
 $txt['option_attachment_dir'] = 'Attachment directory';
 $txt['option_specified_dir'] = 'Specific directory...';
+$txt['custom_avatar_dir_wrong'] = 'The Attachments directory is not valid. This will prevent attachments from working properly.';
 $txt['custom_avatar_dir'] = 'Upload directory';
 $txt['custom_avatar_dir_desc'] = 'This should be a valid and writable directory, different than the server-stored directory.';
 $txt['custom_avatar_url'] = 'Upload URL';

+ 2 - 0
Themes/default/languages/Alerts.english.php

@@ -12,6 +12,8 @@ $txt['alerts_no_unread'] = 'No unread alerts.';
 $txt['alert_msg_like'] = '{member_link} liked your post {msg_msg}';
 $txt['alert_msg_report'] = '{member_link} <a href="{scripturl}{report_link}">reported a post</a> - {msg_msg}';
 $txt['alert_msg_report_reply'] = '{member_link} replied to <a href="{scripturl}{report_link}">the report</a> about {msg_msg}';
+$txt['alert_profile_report'] = '{member_link} <a href="{scriptur}{report_link}">reported</a> the profile of {profile_msg}';
+$txt['alert_profile_report_reply'] = '{member_link} replied to <a href="{scripturl}{report_link}">the report</a> about the profile of {profile_msg}';
 $txt['alert_member_register_standard'] = '{member_link} just signed up';
 $txt['alert_member_register_approval'] = '{member_link} just signed up (account requires approval)';
 $txt['alert_member_register_activation'] = '{member_link} just signed up (account requires activation)';

+ 36 - 0
Themes/default/languages/EmailTemplates.english.php

@@ -458,6 +458,42 @@ Moderation center: {REPORTLINK}
 
 {REGARDS}';
 
+/**
+	@additional_params: report_user_profile
+		MEMBERNAME: The display name of the reported user
+		REPORTERNAME: The name of the person reporting the profile
+		PROFILELINK: The link to the profile that was reported
+		COMMENT: The comment left by the reporter.
+ 	@description: When a user's profile is reported
+*/
+$txt['report_member_profile_subject'] = 'Reported profile: {MEMBERNAME}';
+$txt['report_member_profile_body'] = 'The profile of "{MEMBERNAME}" has been reported by {REPORTERNAME}.
+
+The profile: {PROFILELINK}
+Moderation center: {REPORTLINK}
+
+The reporter has made the following comment:
+{COMMENT}
+
+{REGARDS}';
+
+/**
+	@additional_params: report_user_profile
+		MEMBERNAME: The display name of the reported user
+		COMMENTERNAME: The name of the person who added the comment
+		PROFILELINK: The link to the profile that was reported
+ 	@description: When someone replies to a report about a profile, this can be sent to others who replied
+*/
+$txt['reply_to_member_report_subject'] = 'Follow-up to reported profile: {MEMBERNAME}';
+$txt['reply_to_member_report_body'] = 'Previously, the profile of {MEMBERNAME} was reported.
+
+Since then, {COMMENTERNAME} has added a comment to the report. More information can be found in the forum.
+
+The profile: {PROFILELINK}
+Moderation center: {REPORTLINK}
+
+{REGARDS}';
+
 /**
 	@additional_params: change_password
 		USERNAME: The user name for the member receiving the email.

+ 3 - 0
Themes/default/languages/ManagePermissions.english.php

@@ -183,6 +183,9 @@ $txt['permissionhelp_profile_remove'] = 'This permission allows a user to delete
 $txt['permissionname_profile_remove_own'] = 'Own account';
 $txt['permissionname_profile_remove_any'] = 'Any account';
 
+$txt['permissionname_report_user'] = 'Report users\' profiles';
+$txt['permissionhelp_report_user'] = 'This permission will allow members to report other users\' profiles to the admins to alert them of spam or other inappropriate content in the profile.';
+
 $txt['permissiongroup_general_board'] = 'General';
 $txt['permissionname_moderate_board'] = 'Moderate board';
 $txt['permissionhelp_moderate_board'] = 'The moderate board permission adds a few small permissions that make a moderator a real moderator. Permissions include replying to locked topics, changing the poll expire time and viewing poll results.';

+ 1 - 0
Themes/default/languages/ManageSmileys.english.php

@@ -12,6 +12,7 @@ $txt['setting_smiley_sets_enable'] = 'Enable smiley set selection by members';
 $txt['setting_smiley_enable'] = 'Enable customized smileys';
 $txt['setting_smileys_url'] = 'Base URL to all smiley sets';
 $txt['setting_smileys_dir'] = 'Absolute path to all smiley sets';
+$txt['setting_smileys_dir_wrong'] = 'The path to all smiley sets is wrong. This will prevent smileys from working';
 $txt['setting_messageIcons_enable'] = 'Enable customized message icons';
 $txt['setting_messageIcons_enable_note'] = '(otherwise, the default message icons will be used.)';
 $txt['groups_manage_smileys'] = 'Groups allowed to manage smileys and message icons';

+ 24 - 3
Themes/default/languages/ModerationCenter.english.php

@@ -5,7 +5,8 @@ $txt['moderation_center'] = 'Moderation Center';
 $txt['mc_main'] = 'Main';
 $txt['mc_logs'] = 'Logs';
 $txt['mc_posts'] = 'Posts';
-$txt['mc_groups'] = 'Members and groups';
+$txt['mc_groups'] = 'Groups';
+$txt['mc_members'] = 'Members';
 
 $txt['mc_view_groups'] = 'View Membergroups';
 
@@ -17,6 +18,7 @@ $txt['mc_watched_topics'] = 'Watched Topics';
 $txt['mc_scratch_board'] = 'Moderator Scratch Board';
 $txt['mc_latest_news'] = 'Simple Machines Latest News';
 $txt['mc_recent_reports'] = 'Recent Topic Reports';
+$txt['mc_recent_user_reports'] = 'Recent Profile Reports';
 $txt['mc_warnings'] = 'Warnings';
 $txt['mc_notes'] = 'Moderator Notes';
 
@@ -37,17 +39,20 @@ $txt['mc_reportedp_by'] = 'by';
 $txt['mc_reportedp_reported_by'] = 'Reported By';
 $txt['mc_reportedp_last_reported'] = 'Last Reported';
 $txt['mc_reportedp_none_found'] = 'No Reports Found';
-
+$txt['mc_reportedp_comment_none_found'] = 'No Moderator Comment Found';
+$txt['mc_reportedp_comment_edit'] = 'Edit';
+$txt['mc_reportedp_comment_delete'] = 'Delete';
 $txt['mc_reportedp_details'] = 'Details';
 $txt['mc_reportedp_close'] = 'Close';
 $txt['mc_reportedp_open'] = 'Open';
 $txt['mc_reportedp_ignore'] = 'Disregard';
 $txt['mc_reportedp_unignore'] = 'Undo Disregard';
 $txt['mc_reportedp_delete'] = 'Delete message';
+$txt['mc_reportedp_new_comment'] = 'Add a new comment';
 $txt['mc_reportedp_delete_confirm'] = 'Are you sure you wish to delete this message?';
 $txt['mc_reportedp_ban'] = 'Ban this user';
 // Do not use numeric entries in the below string.
-$txt['mc_reportedp_ignore_confirm'] = 'Are you sure you wish to ignore further reports about this message?\\n\\nThis will turn off further reports for all moderators of the forum.';
+$txt['mc_reportedp_ignore_confirm'] = 'Are you sure you wish to ignore further reports about this message? This will turn off further reports for all moderators of the forum.';
 $txt['mc_reportedp_close_selected'] = 'Close Selected';
 
 $txt['mc_groupr_group'] = 'Membergroups';
@@ -84,6 +89,11 @@ $txt['mc_modreport_modactions'] = 'Actions taken by other moderators';
 $txt['mc_modreport_mod_comments'] = 'Moderator Comments';
 $txt['mc_modreport_no_mod_comment'] = 'There are not currently any moderator comments';
 $txt['mc_modreport_add_mod_comment'] = 'Add Comment';
+$txt['mc_modreport_edit_mod_comment'] = 'Edit Comment';
+
+$txt['mc_viewmemberreport'] = 'Report for profile of %1$s';
+$txt['mc_memberreport_summary'] = 'There have been %1$d report(s) concerning this member.  The last report was %2$s.';
+$txt['mc_memberreport_whoreported_title'] = 'Members who have reported this member.';
 
 $txt['show_notice'] = 'Notice Text';
 $txt['show_notice_subject'] = 'Subject';
@@ -119,10 +129,17 @@ $txt['mc_warning_template_add'] = 'Add Template';
 $txt['mc_warning_template_modify'] = 'Edit Template';
 $txt['mc_warning_template_delete'] = 'Delete Selected';
 $txt['mc_warning_template_delete_confirm'] = 'Are you sure you want to delete the selected templates?';
+$txt['report_action_message_saved'] = 'You successfully created a new comment.';
+$txt['report_action_message_edited'] = 'You successfully edited the comment.';
 $txt['report_action_message_deleted'] = 'The message was successfully deleted.';
+$txt['report_action_message_edit_issue'] = 'The message you\'re trying to edit isn\'t available any more. Please check if it wasn\'t deleted already.';
+$txt['report_action_message_edit_cannot'] = 'I\'m sorry, you aren\'t allowed to edit this comment.';
+$txt['report_action_message_delete_cannot'] = 'I\'m sorry, you aren\'t allowed to delete this comment.';
+$txt['report_action_message_delete_issue'] = 'The message you\'re trying to delete isn\'t available any more. Please check if it wasn\'t deleted already.';
 $txt['report_action_ignore'] = 'The report was successfully ignored.';
 $txt['report_action_unignore'] = 'The report was successfully un-ignored.';
 $txt['report_action_close'] = 'The report was successfully closed.';
+$txt['report_action_close_all'] = 'You successfully closed all reports.';
 $txt['report_action_open'] = 'The report was successfully open.';
 $txt['report_action_close_all'] = 'You successfully closed all selected reports.';
 
@@ -146,4 +163,8 @@ $txt['mc_logoff'] = 'End Moderator Session';
 $txt['mc_click_add_note'] = 'Add a new note';
 $txt['mc_add_note'] = 'Add';
 
+// Do not use numeric entries in the below string.
+$txt['mc_reportedm_ignore_confirm'] = 'Are you sure you wish to ignore further reports about this user\'s profile?\\n\\nThis will turn off further reports for everyone.';
+$txt['mc_reported_members_title'] = 'Reported Members';
+$txt['mc_reported_members_desc'] = 'Allows you to view a list of all users whose profiles have been reported';
 ?>

+ 6 - 0
Themes/default/languages/Modlog.english.php

@@ -101,6 +101,12 @@ $txt['modlog_ac_ignore_report'] = 'Disregarded {report} on &quot;{message}&quot;
 $txt['modlog_ac_open_report'] = 'Reopened {report} on &quot;{message}&quot;';
 $txt['modlog_ac_unignore_report'] = 'Undone disregard of {report} on &quot;{message}&quot;';
 
+// Handling reports on users
+$txt['modlog_ac_close_user_report'] = 'Closed {report} on profile of {member}';
+$txt['modlog_ac_ignore_user_report'] = 'Disregarded {report} on profile of {member}';
+$txt['modlog_ac_open_user_report'] = 'Reopened {report} on profile of {member}';
+$txt['modlog_ac_unignore_user_report'] = 'Undone disregard of {report} on profile of {member}';
+
 // Poll stuff
 $txt['modlog_add_poll'] = 'Added a poll to &quot;{topic}&quot;';
 $txt['modlog_edit_poll'] = 'Edited the poll in &quot;{topic}&quot;';

+ 5 - 0
Themes/default/languages/Post.english.php

@@ -65,6 +65,11 @@ $txt['reported_by'] = 'has been reported by';
 $txt['board_moderate'] = 'on a board you moderate';
 $txt['report_comment'] = 'The reporter has made the following comment';
 
+// Use numeric entities in the below three strings.
+$txt['report_profile'] = 'Report profile of \'%1$s\'';
+$txt['reported_profile'] = 'Reported user';
+$txt['report_following_user'] = 'The profile of "%1$s" at %2$s';
+
 $txt['attach_restrict_attachmentPostLimit'] = 'maximum total size %1$d KB';
 $txt['attach_restrict_attachmentPostLimit_MB'] = 'maximum total size %1$d MB';
 $txt['attach_restrict_attachmentSizeLimit'] = 'maximum individual size %1$d KB';

+ 6 - 1
Themes/default/languages/Profile.english.php

@@ -141,13 +141,16 @@ $txt['alert_pm_new'] = 'When I receive a new personal message';
 $txt['alert_pm_reply'] = 'When a personal message I sent gets replied to';
 $txt['alert_group_moderation'] = 'Moderation';
 $txt['alert_msg_report'] = 'When a message is reported';
-$txt['alert_msg_report_reply'] = 'When a report I\'ve replied to gets replied to';
+$txt['alert_msg_report_reply'] = 'When a post report I\'ve replied to gets replied to';
 $txt['alert_group_members'] = 'Members';
 $txt['alert_member_register'] = 'When a new person registers';
 $txt['alert_warn_any'] = 'When other members receive a warning';
 $txt['alert_group_calendar'] = 'Calendar';
 $txt['alert_event_new'] = 'When a new event goes into the calendar';
 $txt['alert_request_group'] = 'When someone requests to join a group I moderate';
+$txt['alert_member_report'] = 'When another member\'s profile is reported';
+$txt['alert_member_report_reply'] = 'When a member report I\'ve replied to gets replied to';
+
 
 $txt['notifications_topics'] = 'Current Topic Notifications';
 $txt['notifications_topics_list'] = 'You are being notified of replies to the following topics';
@@ -520,4 +523,6 @@ $txt['outcome_pending'] = 'Open';
 $txt['outcome_approved'] = 'Approved by %1$s on %2$s';
 $txt['outcome_refused'] = 'Refused by %1$s on %2$s';
 $txt['outcome_refused_reason'] = 'Refused by %1$s on %2$s, reason given: %3$s';
+
+$txt['report_profile'] = 'Report This Member';
 ?>

+ 4 - 10
Themes/default/languages/index.english.php

@@ -277,7 +277,6 @@ $txt['redirect_board'] = 'Redirect Board';
 
 $txt['sendtopic_send'] = 'Send';
 $txt['report_sent'] = 'Your report has been sent successfully.';
-$txt['topic_sent'] = 'Your email has been sent successfully.';
 
 $txt['time_offset'] = 'Time Offset';
 $txt['or'] = 'or';
@@ -358,13 +357,6 @@ $txt['send_topic'] = 'Send this topic';
 $txt['watch'] = 'Watch';
 $txt['unwatch'] = 'Stop watching';
 
-$txt['sendtopic_sender_name'] = 'Your name';
-$txt['sendtopic_sender_email'] = 'Your email address';
-$txt['sendtopic_receiver_name'] = 'Recipient\'s name';
-$txt['sendtopic_receiver_email'] = 'Recipient\'s email address';
-
-$txt['allow_user_email'] = 'Allow users to email me';
-
 $txt['check_all'] = 'Check all';
 
 // Use numeric entities in the below string.
@@ -440,7 +432,7 @@ $txt['split_after_and_this_post'] = 'Split topic after and including this post.'
 $txt['select_split_posts'] = 'Select posts to split.';
 $txt['new_topic'] = 'New Topic';
 $txt['split_successful'] = 'Topic successfully split into two topics.';
-$txt['origin_topic'] = 'Origin Topic';
+$txt['origin_topic'] = 'Original Topic';
 $txt['please_select_split'] = 'Please select which posts you wish to split.';
 $txt['merge_successful'] = 'Topics successfully merged.';
 $txt['new_merged_topic'] = 'Newly Merged Topic';
@@ -469,6 +461,7 @@ $txt['cache_writable'] = 'The cache directory is not writable - this will advers
 $txt['page_created_full'] = 'Page created in %1$.3f seconds with %2$d queries.';
 
 $txt['report_to_mod_func'] = 'Use this function to inform the moderators and administrators of an abusive or wrongly posted message.';
+$txt['report_profile_func'] = 'Use this function to inform the administrators of abusive profile content, such as spam or inappropriate images.';
 
 $txt['online'] = 'Online';
 $txt['member_is_online'] = '%1$s is online';
@@ -609,6 +602,7 @@ $txt['you_likes_1'] = 'You and <a href="%1$s">1 other person</a> like this.';
 $txt['you_likes_n'] = 'You and <a href="%1$s">%2$s other people</a> like this.';
 
 $txt['report_to_mod'] = 'Report to moderator';
+$txt['report_profile'] = 'Report profile of %1$s';
 
 $txt['unread_topics_visit'] = 'Recent Unread Topics';
 $txt['unread_topics_visit_none'] = 'No unread topics found since your last visit.  <a href="' . $scripturl . '?action=unread;all">Click here to try all unread topics</a>.';
@@ -760,13 +754,13 @@ $txt['edit_permissions'] = 'Permissions';
 $txt['mc_unapproved_attachments'] = 'Unapproved Attachments';
 $txt['mc_unapproved_poststopics'] = 'Unapproved Posts and Topics';
 $txt['mc_reported_posts'] = 'Reported Posts';
+$txt['mc_reported_members'] = 'Reported Members';
 $txt['modlog_view'] = 'Moderation Log';
 $txt['calendar_menu'] = 'View Calendar';
 
 // @todo Send email strings - should move?
 $txt['send_email'] = 'Send Email';
 $txt['send_email_disclosed'] = 'Note this will be visible to the recipient.';
-$txt['send_email_subject'] = 'Email Subject';
 
 $txt['ignoring_user'] = 'You are ignoring this user.';
 $txt['show_ignore_user_post'] = 'Show me the post.';

+ 1 - 2
index.php

@@ -290,7 +290,6 @@ function smf_main()
 		'dlattach' => array('Display.php', 'Download'),
 		'editpoll' => array('Poll.php', 'EditPoll'),
 		'editpoll2' => array('Poll.php', 'EditPoll2'),
-		'emailuser' => array('SendTopic.php', 'EmailUser'),
 		'findmember' => array('Subs-Auth.php', 'JSMembers'),
 		'groups' => array('Groups.php', 'Groups'),
 		'help' => array('Help.php', 'ShowHelp'),
@@ -329,7 +328,7 @@ function smf_main()
 		'reminder' => array('Reminder.php', 'RemindMe'),
 		'removepoll' => array('Poll.php', 'RemovePoll'),
 		'removetopic2' => array('RemoveTopic.php', 'RemoveTopic2'),
-		'reporttm' => array('SendTopic.php', 'ReportToModerator'),
+		'reporttm' => array('ReportToMod.php', 'ReportToModerator'),
 		'requestmembers' => array('Subs-Auth.php', 'RequestMembers'),
 		'restoretopic' => array('RemoveTopic.php', 'RestoreTopic'),
 		'search' => array('Search.php', 'PlushSearch1'),

+ 2 - 2
other/install.php

@@ -1462,7 +1462,7 @@ function AdminAccount()
 					'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int', 'hide_email' => 'int',
 					'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string',
 					'member_ip' => 'string', 'member_ip2' => 'string', 'buddy_list' => 'string', 'pm_ignore_list' => 'string',
-					'website_title' => 'string', 'website_url' => 'string', 'location' => 'string',
+					'website_title' => 'string', 'website_url' => 'string',
 					'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string',
 					'additional_groups' => 'string', 'ignore_boards' => 'string', 'openid_uri' => 'string',
 				),
@@ -2722,4 +2722,4 @@ function template_delete_install()
 		', $txt['good_luck'];
 }
 
-?>
+?>

+ 1 - 0
other/install_2-1_mysql.sql

@@ -1894,6 +1894,7 @@ VALUES ('smfVersion', '{$smf_version}'),
 	('drafts_show_saved_enabled', '1'),
 	('drafts_keep_days', '7'),
 	('topic_move_any', '0'),
+	('browser_cache', '?alph21'),
 	('mail_limit', '5'),
 	('mail_quantity', '5');
 # --------------------------------------------------------

+ 1 - 1
other/install_2-1_postgresql.sql

@@ -1642,7 +1642,6 @@ CREATE TABLE {$db_prefix}members (
   birthdate date NOT NULL default '0001-01-01',
   website_title varchar(255) NOT NULL,
   website_url varchar(255) NOT NULL,
-  location varchar(255) NOT NULL,
   hide_email smallint NOT NULL default '0',
   show_online smallint NOT NULL default '1',
   time_format varchar(80) NOT NULL default '',
@@ -2406,6 +2405,7 @@ INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_autosave_enab
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_show_saved_enabled', '1');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_keep_days', '7');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '0');
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_limit', '5');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_quantity', '5');
 # --------------------------------------------------------

+ 1 - 0
other/install_2-1_sqlite.sql

@@ -2040,6 +2040,7 @@ INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_autosave_enab
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_show_saved_enabled', '1');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_keep_days', '7');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '0');
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_limit', '5');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_quantity', '5');
 COMMIT;

+ 1 - 0
other/install_2-1_sqlite3.sql

@@ -2040,6 +2040,7 @@ INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_autosave_enab
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_show_saved_enabled', '1');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('drafts_keep_days', '7');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '0');
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_limit', '5');
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('mail_quantity', '5');
 COMMIT;

+ 4 - 0
other/upgrade_2-1_mysql.sql

@@ -61,6 +61,10 @@ if (!isset($modSettings['allow_no_censored']))
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '1');
 ---#
 
+---# Adding new "browser_cache" setting
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
+---#
+
 /******************************************************************************/
 --- Updating legacy attachments...
 /******************************************************************************/

+ 4 - 0
other/upgrade_2-1_postgresql.sql

@@ -63,6 +63,10 @@ if (!isset($modSettings['allow_no_censored']))
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '1');
 ---#
 
+---# Adding new "browser_cache" setting
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
+---#
+
 /******************************************************************************/
 --- Updating legacy attachments...
 /******************************************************************************/

+ 4 - 0
other/upgrade_2-1_sqlite.sql

@@ -61,6 +61,10 @@ if (!isset($modSettings['allow_no_censored']))
 INSERT INTO {$db_prefix}settings (variable, value) VALUES ('topic_move_any', '1');
 ---#
 
+---# Adding new "browser_cache" setting
+INSERT INTO {$db_prefix}settings (variable, value) VALUES ('browser_cache', '?alph21');
+---#
+
 /******************************************************************************/
 --- Updating legacy attachments...
 /******************************************************************************/