Browse Source

Merge pull request #1461 from Oldiesmann/release-2.1

+ Add ability to report user profiles and kill "Send email" feature (see details)
Michael Eshom 11 years ago
parent
commit
c921ae8d65

+ 1 - 4
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']))
 	{
@@ -1289,7 +1286,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

+ 1 - 1
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']),
 	);

+ 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',

+ 761 - 137
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.
@@ -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));
 
@@ -454,11 +479,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,
 			)
@@ -545,6 +572,69 @@ function ModBlockGroupRequests()
 	return 'group_requests_block';
 }
 
+/**
+ * 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 posts...
  * @todo this needs to be given its own file?
@@ -640,9 +730,9 @@ function ReportedPosts()
 				SELECT id_report, id_board, id_topic, id_msg
 				FROM {db_prefix}log_reported
 				WHERE id_report IN ({array_int:report_list})
-					AND ' . $user_inf['mod_cache']['bq'],
+					AND ' . $user_info['mod_cache']['bq'],
 				array(
-					'id_report' => $_GET['rid'],
+					'report_list' => $toClose,
 				)
 			);
 
@@ -686,9 +776,11 @@ 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 ' . ($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);
@@ -707,10 +799,12 @@ function ReportedPosts()
 			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();
@@ -811,157 +905,415 @@ function ReportedPosts()
 }
 
 /**
- * Act as an entrace for all group related activity.
- * @todo As for most things in this file, this needs to be moved somewhere appropriate?
+ * Browse all the reported users...
  */
-function ModerateGroups()
+function ReportedMembers()
 {
-	global $txt, $context, $scripturl, $user_info;
-
-	// You need to be allowed to moderate groups...
-	if ($user_info['mod_cache']['gq'] == '0=1')
-		isAllowedTo('manage_membergroups');
+	global $txt, $context, $scripturl, $user_info, $smcFunc;
 
-	// Load the group templates.
 	loadTemplate('ModerationCenter');
 
-	// Setup the subactions...
-	$subactions = array(
-		'requests' => 'GroupRequests',
-		'view' => 'ViewGroups',
-	);
+	// Set an empty var for the server response.
+	$context['report_member_action'] = '';
 
-	if (!isset($_GET['sa']) || !isset($subactions[$_GET['sa']]))
-		$_GET['sa'] = 'view';
-	$context['sub_action'] = $_GET['sa'];
+	// Put the open and closed options into tabs, because we can...
+	$context[$context['moderation_menu_name']]['tab_data'] = array(
+		'title' => $txt['mc_reported_members'],
+		'help' => '',
+		'description' => $txt['mc_reported_members_desc'],
+	);
 
-	// Call the relevant function.
-	$subactions[$context['sub_action']]();
-}
+	isAllowedTo('moderate_forum');
 
-/**
- * How many open reports do we have?
- */
-function recountOpenReports()
-{
-	global $user_info, $context, $smcFunc;
+	// Are they wanting to view a particular report?
+	if (!empty($_REQUEST['report']))
+		return MemberReport();
 
-	$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);
+	// Set up the comforting bits...
+	$context['page_title'] = $txt['mc_reported_members'];
+	$context['sub_template'] = 'reported_members';
 
-	$_SESSION['rc'] = array(
-		'id' => $user_info['id'],
-		'time' => time(),
-		'reports' => $open_reports,
-	);
+	// Are we viewing open or closed reports?
+	$context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0;
 
-	$context['open_mod_reports'] = $open_reports;
-}
+	// Are we doing any work?
+	if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid']))
+	{
+		checkSession('get');
+		$_GET['rid'] = (int) $_GET['rid'];
 
-/**
- * Get details about the moderation report... specified in
- * $_REQUEST['report'].
- */
-function ModReport()
-{
-	global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc;
+		// Update the report...
+		$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}',
+			array(
+				'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0,
+				'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0,
+				'id_report' => $_GET['rid'],
+			)
+		);
 
-	// Have to at least give us something
-	if (empty($_REQUEST['report']))
-		fatal_lang_error('mc_no_modreport_specified');
+		// Get the board, topic and message for this report
+		$request = $smcFunc['db_query']('', '
+			SELECT id_member, membername
+			FROM {db_prefix}log_reported
+			WHERE id_report = {int:id_report}',
+			array(
+				'id_report' => $_GET['rid'],
+			)
+		);
 
-	// Integers only please
-	$_REQUEST['report'] = (int) $_REQUEST['report'];
+		// Set up the data for the log...
+		$extra = array('report' => $_GET['rid']);
+		list($extra['member'], $extra['membername']) = $smcFunc['db_fetch_row']($request);
+		$smcFunc['db_free_result']($request);
 
-	// 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' => $_REQUEST['report'],
-		)
-	);
+		// Stick this in string format for consistency
+		$extra['member'] = (string)$extra['member'];
 
-	// So did we find anything?
-	if (!$smcFunc['db_num_rows']($request))
-		fatal_lang_error('mc_no_modreport_found');
+		// Tell the user about it.
+		$context['report_member_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
 
-	// Woohoo we found a report and they can see it!  Bad news is we have more work to do
-	$row = $smcFunc['db_fetch_assoc']($request);
-	$smcFunc['db_free_result']($request);
+		// Log this action
+		logAction($context['report_member_action'] . '_user_report', $extra);
 
-	// If they are adding a comment then... add a comment.
-	if (isset($_POST['add_comment']) && !empty($_POST['mod_comment']))
+		// Time to update.
+		updateSettings(array('last_mod_report_action' => time()));
+		recountOpenMemberReports();
+	}
+	elseif (isset($_POST['close']) && isset($_POST['close_selected']))
 	{
 		checkSession();
 
-		$newComment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
+		// All the ones to update...
+		$toClose = array();
+		foreach ($_POST['close'] as $rid)
+			$toClose[] = (int) $rid;
 
-		// In it goes.
-		if (!empty($newComment))
+		if (!empty($toClose))
 		{
-			$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',
-				),
+			// Get the data for each of these reports
+			$request = $smcFunc['db_query']('', '
+				SELECT id_report, id_member, membername
+				FROM {db_prefix}log_reported
+				WHERE id_report IN ({array_int:report_list})',
 				array(
-					$user_info['id'], $user_info['name'], 'reportc', '',
-					$_REQUEST['report'], $newComment, time(),
-				),
-				array('id_comment')
+					'report_list' => $toClose,
+				)
 			);
-			$last_comment = $smcFunc['db_insert_id']('{db_prefix}log_comments', '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/MsgReportReply-Notify.php', 'MsgReportReply_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),
-				array('id_task')
-			);
+			while ($reports = $smcFunc['db_fetch_assoc']($request))
+			{
+				$report_data = array(
+					'report' => $reports['id_report'],
+					'membername' => $reports['membername'],
+					'member' => (string)$reports['id_member'],
+				);
 
-			// Redirect to prevent double submittion.
-			redirectexit($scripturl . '?action=moderate;area=reports;report=' . $_REQUEST['report']);
-		}
-	}
+				// Log that this report was closed
+				logAction('close_user_report', $report_data);
+			}
 
-	$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(
+			$smcFunc['db_free_result']($request);
+
+			$smcFunc['db_query']('', '
+				UPDATE {db_prefix}log_reported
+				SET closed = {int:is_closed}
+				WHERE id_report IN ({array_int:report_list})',
+				array(
+					'report_list' => $toClose,
+					'is_closed' => 1,
+				)
+			);
+
+			// Time to update.
+			updateSettings(array('last_mod_report_action' => time()));
+			recountOpenMemberReports();
+		}
+
+		// Go on and tell the result.
+		$context['report_member_action'] = 'close_all';
+	}
+
+	// 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 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=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_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 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();
+	for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
+	{
+		$report_ids[] = $row['id_report'];
+		$context['reports'][$row['id_report']] = 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_user'],
+			),
+			'comments' => array(),
+			'time_started' => timeformat($row['time_started']),
+			'last_updated' => timeformat($row['time_updated']),
+			'num_reports' => $row['num_reports'],
+			'closed' => $row['closed'],
+			'ignore' => $row['ignore_all']
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// 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))
+		{
+			$context['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);
+	}
+
+	$context['report_manage_bans'] = allowedTo('manage_bans');
+}
+
+/**
+ * Act as an entrace for all group related activity.
+ * @todo As for most things in this file, this needs to be moved somewhere appropriate?
+ */
+function ModerateGroups()
+{
+	global $txt, $context, $scripturl, $user_info;
+
+	// You need to be allowed to moderate groups...
+	if ($user_info['mod_cache']['gq'] == '0=1')
+		isAllowedTo('manage_membergroups');
+
+	// Load the group templates.
+	loadTemplate('ModerationCenter');
+
+	// Setup the subactions...
+	$subactions = array(
+		'requests' => 'GroupRequests',
+		'view' => 'ViewGroups',
+	);
+
+	if (!isset($_GET['sa']) || !isset($subactions[$_GET['sa']]))
+		$_GET['sa'] = 'view';
+	$context['sub_action'] = $_GET['sa'];
+
+	// Call the relevant function.
+	$subactions[$context['sub_action']]();
+}
+
+/**
+ * How many open reports do we have?
+ */
+function recountOpenReports()
+{
+	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}
+			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(
+		'id' => $user_info['id'],
+		'time' => time(),
+		'reports' => $open_reports,
+	);
+
+	$context['open_mod_reports'] = $open_reports;
+}
+
+/**
+ * How many open reports do we have?
+ */
+function recountOpenMemberReports()
+{
+	global $user_info, $context, $smcFunc;
+
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(*)
+		FROM {db_prefix}log_reported
+		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['rmc'] = array(
+		'id' => $user_info['id'],
+		'time' => time(),
+		'reports' => $open_reports,
+	);
+
+	$context['open_member_reports'] = $open_reports;
+}
+
+/**
+ * Get details about the moderation report... specified in
+ * $_REQUEST['report'].
+ */
+function ModReport()
+{
+	global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc;
+
+	// Have to at least give us something
+	if (empty($_REQUEST['report']))
+		fatal_lang_error('mc_no_modreport_specified');
+
+	// Integers only please
+	$_REQUEST['report'] = (int) $_REQUEST['report'];
+
+	// 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']) . '
+			AND lr.id_board != {int:not_a_reported_post}
+		LIMIT 1',
+		array(
+			'id_report' => $_REQUEST['report'],
+			'not_a_reported_post' => 0,
+		)
+	);
+
+	// So did we find anything?
+	if (!$smcFunc['db_num_rows']($request))
+		fatal_lang_error('mc_no_modreport_found');
+
+	// Woohoo we found a report and they can see it!  Bad news is we have more work to do
+	$row = $smcFunc['db_fetch_assoc']($request);
+	$smcFunc['db_free_result']($request);
+
+	// If they are adding a comment then... add a comment.
+	if (isset($_POST['add_comment']) && !empty($_POST['mod_comment']))
+	{
+		checkSession();
+
+		$newComment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
+
+		// In it goes.
+		if (!empty($newComment))
+		{
+			$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',
+				),
+				array(
+					$user_info['id'], $user_info['name'], 'reportc', '',
+					$_REQUEST['report'], $newComment, time(),
+				),
+				array('id_comment')
+			);
+			$last_comment = $smcFunc['db_insert_id']('{db_prefix}log_comments', '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/MsgReportReply-Notify.php', 'MsgReportReply_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),
+				array('id_task')
+			);
+
+			// Redirect to prevent double submittion.
+			redirectexit($scripturl . '?action=moderate;area=reports;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'],
@@ -1146,6 +1498,278 @@ function ModReport()
 	$context['sub_template'] = 'viewmodreport';
 }
 
+function MemberReport()
+{
+	global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc;
+
+	// Have to at least give us something
+	if (empty($_REQUEST['report']))
+		fatal_lang_error('mc_no_modreport_specified');
+
+	// Integers only please
+	$_REQUEST['report'] = (int) $_REQUEST['report'];
+
+	// 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_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.id_report = {int:id_report}
+			AND lr.id_board = {int:not_a_reported_post}
+		LIMIT 1',
+		array(
+			'id_report' => $_REQUEST['report'],
+			'not_a_reported_post' => 0,
+		)
+	);
+
+	// So did we find anything?
+	if (!$smcFunc['db_num_rows']($request))
+		fatal_lang_error('mc_no_modreport_found');
+
+	// Woohoo we found a report and they can see it!  Bad news is we have more work to do
+	$row = $smcFunc['db_fetch_assoc']($request);
+	$smcFunc['db_free_result']($request);
+
+	// If they are adding a comment then... add a comment.
+	if (isset($_POST['add_comment']) && !empty($_POST['mod_comment']))
+	{
+		checkSession();
+
+		$newComment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
+
+		// In it goes.
+		if (!empty($newComment))
+		{
+			$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',
+				),
+				array(
+					$user_info['id'], $user_info['name'], 'reportc', '',
+					$_REQUEST['report'], $newComment, time(),
+				),
+				array('id_comment')
+			);
+			$last_comment = $smcFunc['db_insert_id']('{db_prefix}log_comments', '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/MemberReportReply-Notify.php', 'MemberReportReply_Notify_Background', serialize(array(
+					'report_id' => $_REQUEST['report'],
+					'comment_id' => $last_comment,
+					'sender_name' => $user_info['name'],
+					'time' => time(),
+				)), 0),
+				array('id_task')
+			);
+
+			// Redirect to prevent double submittion.
+			redirectexit($scripturl . '?action=moderate;area=memberreports;report=' . $_REQUEST['report']);
+		}
+	}
+
+	$context['report'] = array(
+		'id' => $row['id_report'],
+		'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']),
+		'num_reports' => $row['num_reports'],
+		'closed' => $row['closed'],
+		'ignore' => $row['ignore_all']
+	);
+
+	// 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' => $context['report']['id'],
+		)
+	);
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$context['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' => $context['report']['id'],
+		)
+	);
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$context['report']['mod_comments'][] = array(
+			'id' => $row['id_comment'],
+			'message' => parse_bbc($row['body']),
+			'time' => timeformat($row['log_time']),
+			'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);
+
+	// What have the other moderators done to this message?
+	require_once($sourcedir . '/Modlog.php');
+	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' => '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=memberreports;report=' . $context['report']['id'],
+		'default_sort_col' => 'time',
+		'get_items' => array(
+			'function' => 'list_getModLogEntries',
+			'params' => array(
+				'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.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.
+		'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';
+
+	// Finally we are done :P
+	loadTemplate('ModerationCenter');
+	$context['page_title'] = sprintf($txt['mc_viewmemberreport'], $context['report']['user']['name']);
+	$context['sub_template'] = 'viewmemberreport';
+}
+
 /**
  * Show a notice sent to a user.
  */

+ 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'];

+ 2 - 0
Sources/Profile-Modify.php

@@ -1900,6 +1900,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)),

+ 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']];

+ 9 - 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,

+ 209 - 200
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 porfiles to mods and admins
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -16,144 +16,7 @@ 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.
- */
-function EmailUser()
-{
-	global $context, $user_info, $smcFunc, $txt, $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;
-
-	// 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'],
-			)
-		);
-
-		$context['form_hidden_vars']['uid'] = (int) $_REQUEST['uid'];
-	}
-	elseif (isset($_REQUEST['msg']))
-	{
-		$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'],
-			)
-		);
-
-		$context['form_hidden_vars']['msg'] = (int) $_REQUEST['msg'];
-	}
-
-	if (empty($request) || $smcFunc['db_num_rows']($request) == 0)
-		fatal_lang_error('cant_find_user_email');
-
-	$row = $smcFunc['db_fetch_assoc']($request);
-	$smcFunc['db_free_result']($request);
-
-	// Are you sure you got the address?
-	if (empty($row['email']))
-		fatal_lang_error('cant_find_user_email');
-
-	// Can they actually do this?
-	$context['show_email_address'] = showEmailAddress(!empty($row['hide_email']), $row['id_member']);
-	if ($context['show_email_address'] === 'no')
-		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'],
-	);
-
-	// 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';
-
-	// Are we actually sending it?
-	if (isset($_POST['send']) && isset($_POST['email_body']))
-	{
-		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'],
-		);
-
-		// 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);
-
-		// 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();
-	}
-
-	$context['sub_template'] = 'custom_email';
-	$context['page_title'] = $txt['send_email'];
-}
-
-/**
- * Report a post to the moderator... ask for a comment.
+ * 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.
@@ -162,7 +25,7 @@ function EmailUser()
  */
 function ReportToModerator()
 {
-	global $txt, $topic, $context, $smcFunc;
+	global $txt, $topic, $context, $smcFunc, $scripturl;
 
 	$context['robot_no_index'] = true;
 
@@ -170,40 +33,87 @@ function ReportToModerator()
 	is_not_guest();
 
 	// You can't use this if it's off or you are not allowed to do it.
-	isAllowedTo('report_any');
+	// 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');
 
 	// If they're posting, it should be processed by ReportToModerator2.
 	if ((isset($_POST[$context['session_var']]) || isset($_POST['save'])) && empty($context['post_errors']))
 		ReportToModerator2();
 
-	// We need a message ID to check!
-	if (empty($_REQUEST['msg']) && empty($_REQUEST['mid']))
+	// 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);
 
 	// 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'];
+	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'];
 
-	// 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);
+	// Set up some form values
+	$context['report_type'] = isset($_REQUEST['msg']) ? 'msg' : 'u';
+	$context['reported_item'] = isset($_REQUEST['msg']) ? $_REQUEST['msg'] : $_REQUEST['u'];
+
+	if (isset($_REQUEST['msg']))
+	{
+		// 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);
+
+
+		// 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'];
+
+		// 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'],
+			)
+		);
+
+		if ($smcFunc['db_num_rows']($result) == 0)
+			fatal_lang_error('no_user', false);
+		list($_REQUEST['u'], $display_name, $username) = $smcFunc['db_fetch_row']($result);
+
+		$context['current_user'] = $_REQUEST['u'];
+		$context['submit_url'] = $scripturl . '?action=reporttm;u=' . $_REQUEST['u'];
+	}
+
+	$context['comment_body'] = !isset($_POST['comment']) ? '' : trim($_POST['comment']);
+
+	$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 +137,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,7 +154,10 @@ 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');
@@ -275,32 +179,6 @@ function ReportToModerator2()
 	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 +191,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 +295,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 +324,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);	
+}
+
 ?>

+ 11 - 36
Sources/Security.php

@@ -498,6 +498,17 @@ function banPermissions()
 	}
 	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 +1146,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.

+ 25 - 2
Sources/Subs.php

@@ -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;
 
+	$total_mod_reports = 0;
+
 	if (!empty($user_info['mod_cache']) && $user_info['mod_cache']['bq'] != '0=1' && $context['open_mod_reports'] > 0)
 	{
-		$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;
+	}
+}
+
+?>

+ 3 - 12
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 '
@@ -639,11 +631,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 +947,4 @@ function template_single_post($message, $force_alternate = null)
 				<hr class="post_separator">';
 }
 
-?>
+?>

+ 238 - 0
Themes/default/ModerationCenter.template.php

@@ -561,6 +561,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)
 {
@@ -780,4 +863,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>';
+}
+
 ?>

+ 2 - 2
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'])

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

@@ -164,11 +164,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 +232,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 +603,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 +626,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)
@@ -724,7 +715,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 +737,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>';

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

@@ -0,0 +1,87 @@
+<?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;
+
+	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="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 - 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>';
-}
-
-?>

+ 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/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.';

+ 11 - 1
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';
 
@@ -85,6 +87,10 @@ $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_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';
 $txt['show_notice_text'] = 'Text';
@@ -146,4 +152,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';
 ?>

+ 3 - 9
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.
@@ -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'),