Browse Source

Merge pull request #35 from emanuele45/master

Testing needed on deny access to boards
Spuds 13 years ago
parent
commit
1371bc4d5c

+ 1 - 1
Sources/Display.php

@@ -1145,7 +1145,7 @@ function prepareDisplayContext($reset = false)
 	}
 
 	$memberContext[$message['id_member']]['ip'] = $message['poster_ip'];
-	$context['show_profile_buttons'] = $settings['show_profile_buttons'] && (!empty($memberContext[$message['id_member']]['can_view_profile']) || (!empty($memberContext[$message['id_member']]['website']['url']) && !isset($context['disabled_fields']['website'])) || (in_array($memberContext[$message['id_member']]['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) || $context['can_send_pm']);
+	$memberContext[$message['id_member']]['show_profile_buttons'] = $settings['show_profile_buttons'] && (!empty($memberContext[$message['id_member']]['can_view_profile']) || (!empty($memberContext[$message['id_member']]['website']['url']) && !isset($context['disabled_fields']['website'])) || (in_array($memberContext[$message['id_member']]['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) || $context['can_send_pm']);
 
 	// Do the censor thang.
 	censorText($message['body']);

+ 5 - 2
Sources/Load.php

@@ -452,7 +452,7 @@ function loadUserSettings()
 		$user_info['query_see_board'] = '1=1';
 	// Otherwise just the groups in $user_info['groups'].
 	else
-		$user_info['query_see_board'] = '(FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0' . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')';
+		$user_info['query_see_board'] = '((FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0' . (!empty($modSettings['denyBoardsAccess']) ? ') AND (FIND_IN_SET(' . implode(', b.deny_member_groups) = 0 AND FIND_IN_SET(', $user_info['groups']) . ', b.deny_member_groups) = 0)' : '') . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')';
 
 	// Build the list of boards they WANT to see.
 	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
@@ -556,7 +556,7 @@ function loadBoard()
 	{
 		$request = $smcFunc['db_query']('', '
 			SELECT
-				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups,
+				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
 				b.id_parent, c.name AS cname, IFNULL(mem.id_member, 0) AS id_moderator,
 				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
 				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
@@ -609,6 +609,7 @@ function loadBoard()
 
 			// Load the membergroups allowed, and check permissions.
 			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
+			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
 
 			do
 			{
@@ -678,6 +679,8 @@ function loadBoard()
 
 		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
 			$board_info['error'] = 'access';
+		if (count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
+			$board_info['error'] = 'access';
 
 		// Build up the linktree.
 		$context['linktree'] = array_merge(

+ 16 - 7
Sources/ManageBoards.php

@@ -402,6 +402,7 @@ function EditBoard()
 		// Some things that need to be setup for a new board.
 		$curBoard = array(
 			'member_groups' => array(0, -1),
+			'deny_group' => array(),
 			'category' => (int) $_REQUEST['cat']
 		);
 		$context['board_order'] = array();
@@ -443,13 +444,15 @@ function EditBoard()
 		-1 => array(
 			'id' => '-1',
 			'name' => $txt['parent_guests_only'],
-			'checked' => in_array('-1', $curBoard['member_groups']),
+			'allow' => in_array('-1', $curBoard['member_groups']),
+			'deny' => in_array('-1', $curBoard['deny_groups']),
 			'is_post_group' => false,
 		),
 		0 => array(
 			'id' => '0',
 			'name' => $txt['parent_members_only'],
-			'checked' => in_array('0', $curBoard['member_groups']),
+			'allow' => in_array('0', $curBoard['member_groups']),
+			'deny' => in_array('0', $curBoard['deny_groups']),
 			'is_post_group' => false,
 		)
 	);
@@ -473,7 +476,8 @@ function EditBoard()
 		$context['groups'][(int) $row['id_group']] = array(
 			'id' => $row['id_group'],
 			'name' => trim($row['group_name']),
-			'checked' => in_array($row['id_group'], $curBoard['member_groups']),
+			'allow' => in_array($row['id_group'], $curBoard['member_groups']),
+			'deny' => in_array($row['id_group'], $curBoard['deny_groups']),
 			'is_post_group' => $row['min_posts'] != -1,
 		);
 	}
@@ -594,7 +598,6 @@ function EditBoard2()
 
 	require_once($sourcedir . '/Subs-Boards.php');
 
-
 	// Mode: modify aka. don't delete.
 	if (isset($_POST['edit']) || isset($_POST['add']))
 	{
@@ -623,10 +626,15 @@ function EditBoard2()
 		$boardOptions['access_groups'] = array();
 
 		if (!empty($_POST['groups']))
-			foreach ($_POST['groups'] as $group)
-				$boardOptions['access_groups'][] = (int) $group;
+			foreach ($_POST['groups'] as $group => $action)
+			{
+				if ($action == 'allow')
+					$boardOptions['access_groups'][] = (int) $group;
+				elseif ($action == 'deny')
+					$boardOptions['deny_groups'][] = (int) $group;
+			}
 
-		if (strlen(implode(',', $boardOptions['access_groups'])) > 255)
+		if (strlen(implode(',', $boardOptions['access_groups'])) > 255 || strlen(implode(',', $boardOptions['deny_groups'])) > 255)
 			fatal_lang_error('too_many_groups', false);
 
 		// Change '1 & 2' to '1 & 2', but not '&' to '&'...
@@ -791,6 +799,7 @@ function EditBoardSettings($return_config = false)
 			array('check', 'recycle_enable', 'onclick' => 'document.getElementById(\'recycle_board\').disabled = !this.checked;'),
 			array('select', 'recycle_board', $recycle_boards),
 			array('check', 'allow_ignore_boards'),
+			array('check', 'denyBoardsAccess'),
 	);
 
 	call_integration_hook('integrate_modify_board_settings', array(&$config_vars));

+ 3 - 5
Sources/PersonalMessage.php

@@ -411,10 +411,6 @@ function MessageFolder()
 
 	$context['sort_direction'] = $descending ? 'down' : 'up';
 
-	// Why would you want access to your sent items if you're not allowed to send anything?
-	if ($context['folder'] == 'sent')
-		isAllowedTo('pm_send');
-
 	// Set the text to resemble the current folder.
 	$pmbox = $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'];
 	$txt['delete_all'] = str_replace('PMBOX', $pmbox, $txt['delete_all']);
@@ -856,7 +852,7 @@ function MessageFolder()
  */
 function prepareMessageContext($type = 'subject', $reset = false)
 {
-	global $txt, $scripturl, $modSettings, $context, $messages_request, $memberContext, $recipients, $smcFunc;
+	global $txt, $scripturl, $modSettings, $settings, $context, $messages_request, $memberContext, $recipients, $smcFunc;
 	global $user_info, $subjects_request;
 
 	// Count the current message number....
@@ -945,6 +941,8 @@ function prepareMessageContext($type = 'subject', $reset = false)
 		$memberContext[$message['id_member_from']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member_from']]['warning_status'] && ($context['user']['can_mod'] || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member_from'] == $user_info['id'])));
 	}
 
+		$memberContext[$message['id_member_from']]['show_profile_buttons'] = $settings['show_profile_buttons'] && (!empty($memberContext[$message['id_member_from']]['can_view_profile']) || (!empty($memberContext[$message['id_member_from']]['website']['url']) && !isset($context['disabled_fields']['website'])) || (in_array($memberContext[$message['id_member_from']]['show_email'], array('yes', 'yes_permission_override', 'no_through_forum'))) || $context['can_send_pm']);
+
 	// Censor all the important text...
 	censorText($message['body']);
 	censorText($message['subject']);

+ 198 - 26
Sources/PostModeration.php

@@ -307,7 +307,7 @@ function UnapprovedPosts()
  */
 function UnapprovedAttachments()
 {
-	global $txt, $scripturl, $context, $user_info, $sourcedir, $smcFunc;
+	global $txt, $scripturl, $context, $user_info, $sourcedir, $smcFunc, $modSettings;
 
 	$context['page_title'] = $txt['mc_unapproved_attachments'];
 
@@ -377,26 +377,158 @@ function UnapprovedAttachments()
 		}
 	}
 
-	// How many unapproved attachments in total?
-	$request = $smcFunc['db_query']('', '
-		SELECT COUNT(*)
-		FROM {db_prefix}attachments AS a
-			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
-			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
-		WHERE a.approved = {int:not_approved}
-			AND a.attachment_type = {int:attachment_type}
-			AND {query_see_board}
-			' . $approve_query,
-		array(
-			'not_approved' => 0,
-			'attachment_type' => 0,
-		)
+	require_once($sourcedir . '/Subs-List.php');
+
+	$listOptions = array(
+		'id' => 'mc_unapproved_attach',
+		'width' => '100%',
+		'items_per_page' => $modSettings['defaultMaxMessages'],
+		'no_items_label' => $txt['mc_unapproved_attachments_none_found'],
+		'base_href' => $scripturl . '?action=moderate;area=attachmod;sa=attachments',
+		'default_sort_col' => 'attach_name',
+		'get_items' => array(
+			'function' => 'list_getUnapprovedAttachments',
+			'params' => array(
+				$approve_query,
+			),
+		),
+		'get_count' => array(
+			'function' => 'list_getNumUnapprovedAttachments',
+			'params' => array(
+				$approve_query,
+			),
+		),
+		'columns' => array(
+			'attach_name' => array(
+				'header' => array(
+					'value' => $txt['mc_unapproved_attach_name'],
+				),
+				'data' => array(
+					'db' => 'filename',
+				),
+				'sort' => array(
+					'default' => 'a.filename',
+					'reverse' => 'a.filename DESC',
+				),
+			),
+			'attach_size' => array(
+				'header' => array(
+					'value' => $txt['mc_unapproved_attach_size'],
+				),
+				'data' => array(
+					'db' => 'size',
+				),
+				'sort' => array(
+					'default' => 'a.size',
+					'reverse' => 'a.size DESC',
+				),
+			),
+			'attach_poster' => array(
+				'header' => array(
+					'value' => $txt['mc_unapproved_attach_poster'],
+				),
+				'data' => array(
+					'function' => create_function('$data', '
+						return $data[\'poster\'][\'link\'];'
+					)
+				),
+				'sort' => array(
+					'default' => 'm.id_member',
+					'reverse' => 'm.id_member DESC',
+				),
+			),
+			'date' => array(
+				'header' => array(
+					'value' => $txt['date'],
+					'style' => 'width: 18%;',
+				),
+				'data' => array(
+					'db' => 'time',
+					'class' => 'smalltext',
+					'style' => 'white-space:nowrap;',
+				),
+				'sort' => array(
+					'default' => 'm.poster_time',
+					'reverse' => 'm.poster_time DESC',
+				),
+			),
+			'message' => array(
+				'header' => array(
+					'value' => $txt['post'],
+				),
+				'data' => array(
+					'function' => create_function('$data', '
+						return \'<a href="\' . $data[\'message\'][\'href\'] . \'">\' . shorten_subject($data[\'message\'][\'subject\'], 20) . \'</a>\';'
+					),
+					'class' => 'smalltext',
+					'style' => 'width:15em;',
+				),
+				'sort' => array(
+					'default' => 'm.subject',
+					'reverse' => 'm.subject DESC',
+				),
+			),
+			'action' => array(
+				'header' => array(
+					'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);" checked="checked" />',
+					'style' => 'width: 4%;',
+				),
+				'data' => array(
+					'sprintf' => array(
+						'format' => '<input type="checkbox" name="item[]" value="%1$d" checked="checked" class="input_check" />',
+						'params' => array(
+							'id' => false,
+						),
+					),
+					'style' => 'text-align: center;',
+				),
+			),
+		),
+		'form' => array(
+			'href' => $scripturl . '?action=moderate;area=attachmod;sa=attachments',
+			'include_sort' => true,
+			'include_start' => true,
+			'hidden_fields' => array(
+				$context['session_var'] => $context['session_id'],
+			),
+			'token' => 'mod-ap',
+		),
+		'additional_rows' => array(
+			array(
+				'position' => 'bottom_of_list',
+				'value' => '
+					<select name="do" onchange="if (this.value != 0 &amp;&amp; confirm(\'' . $txt['mc_unapproved_sure'] . '\')) submit();">
+						<option value="0">' . $txt['with_selected'] . ':</option>
+						<option value="0">-------------------</option>
+						<option value="approve">&nbsp;--&nbsp;' . $txt['approve'] . '</option>
+						<option value="delete">&nbsp;--&nbsp;' . $txt['delete'] . '</option>
+					</select>
+					<noscript><input type="submit" name="ml_go" value="' . $txt['go'] . '" class="button_submit" /></noscript>',
+				'align' => 'right',
+			),
+		),
 	);
-	list ($context['total_unapproved_attachments']) = $smcFunc['db_fetch_row']($request);
-	$smcFunc['db_free_result']($request);
 
-	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=attachmod;sa=attachments', $_GET['start'], $context['total_unapproved_attachments'], 10);
-	$context['start'] = $_GET['start'];
+	// Create the request list.
+	createToken('mod-ap');
+	createList($listOptions);
+
+	$context['sub_template'] = 'unapproved_attachments';
+}
+
+/**
+ * Callback function for UnapprovedAttachments
+ * retrieve all the attachments waiting for approval the approver can approve
+ * 
+ * @param int $start
+ * @param int $items_per_page
+ * @param string $sort
+ * @param string $approve_query additional restrictions based on the boards the approver can see
+ * @return array, an array of unapproved attachments
+ */
+function list_getUnapprovedAttachments($start, $items_per_page, $sort, $approve_query)
+{
+	global $smcFunc, $scripturl;
 
 	// Get all unapproved attachments.
 	$request = $smcFunc['db_query']('', '
@@ -412,19 +544,24 @@ function UnapprovedAttachments()
 		WHERE a.approved = {int:not_approved}
 			AND a.attachment_type = {int:attachment_type}
 			AND {query_see_board}
-			' . $approve_query . '
-		LIMIT ' . $context['start'] . ', 10',
+			{raw:approve_query}
+		ORDER BY {raw:sort}
+		LIMIT {int:start}, {int:items_per_page}',
 		array(
 			'not_approved' => 0,
 			'attachment_type' => 0,
+			'start' => $start,
+			'sort' => $sort,
+			'items_per_page' => $items_per_page,
+			'approve_query' => $approve_query,
 		)
 	);
-	$context['unapproved_items'] = array();
-	for ($i = 1; $row = $smcFunc['db_fetch_assoc']($request); $i++)
+
+	$unapproved_items = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
 	{
-		$context['unapproved_items'][] = array(
+		$unapproved_items[] = array(
 			'id' => $row['id_attach'],
-			'alternate' => $i % 2,
 			'filename' => $row['filename'],
 			'size' => round($row['size'] / 1024, 2),
 			'time' => timeformat($row['poster_time']),
@@ -456,7 +593,42 @@ function UnapprovedAttachments()
 	}
 	$smcFunc['db_free_result']($request);
 
-	$context['sub_template'] = 'unapproved_attachments';
+	return $unapproved_items;
+}
+
+/**
+ * Callback function for UnapprovedAttachments
+ * count all the attachments waiting for approval the approver can approve
+ * 
+ * @param int $start
+ * @param int $items_per_page
+ * @param string $sort
+ * @param string $approve_query additional restrictions based on the boards the approver can see
+ * @return int, the number of unapproved attachments
+ */
+function list_getNumUnapprovedAttachments($approve_query)
+{
+	global $smcFunc;
+
+	// How many unapproved attachments in total?
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(*)
+		FROM {db_prefix}attachments AS a
+			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
+			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
+		WHERE a.approved = {int:not_approved}
+			AND a.attachment_type = {int:attachment_type}
+			AND {query_see_board}
+			' . $approve_query,
+		array(
+			'not_approved' => 0,
+			'attachment_type' => 0,
+		)
+	);
+	list ($total_unapproved_attachments) = $smcFunc['db_fetch_row']($request);
+	$smcFunc['db_free_result']($request);
+
+	return $total_unapproved_attachments;
 }
 
 /**

+ 1 - 5
Sources/Profile.php

@@ -427,7 +427,7 @@ function ModifyProfile($post_errors = array())
 				}
 
 				// Does this require session validating?
-				if (!empty($area['validate']))
+				if (!empty($area['validate']) || (isset($_REQUEST['save']) && !$context['user']['is_owner']))
 					$security_checks['validate'] = true;
 
 				// Permissions for good measure.
@@ -512,10 +512,6 @@ function ModifyProfile($post_errors = array())
 	// Right - are we saving - if so let's save the old data first.
 	if ($context['completed_save'])
 	{
-		// If it's someone elses profile then validate the session.
-		if (!$context['user']['is_owner'])
-			validateSession();
-
 		// Clean up the POST variables.
 		$_POST = htmltrim__recursive($_POST);
 		$_POST = htmlspecialchars__recursive($_POST);

+ 1 - 0
Sources/Recent.php

@@ -1283,6 +1283,7 @@ function UnreadTopics()
 			)
 		);
 
+		$context['topics'][$row['id_topic']]['first_post']['started_by'] = sprintf($txt['topic_started_by'], $context['topics'][$row['id_topic']]['first_post']['member']['link'], $context['topics'][$row['id_topic']]['board']['link']);
 		determineTopicClass($context['topics'][$row['id_topic']]);
 	}
 	$smcFunc['db_free_result']($request);

+ 6 - 0
Sources/Subs-BoardIndex.php

@@ -252,11 +252,17 @@ function getBoardIndex($boardIndexOptions)
 		{
 			$this_last_post['href'] = $scripturl . '?topic=' . $row_board['id_topic'] . '.msg' . ($user_info['is_guest'] ? $row_board['id_msg'] : $row_board['new_from']) . (empty($row_board['is_read']) ? ';boardseen' : '') . '#new';
 			$this_last_post['link'] = '<a href="' . $this_last_post['href'] . '" title="' . $row_board['subject'] . '">' . $row_board['short_subject'] . '</a>';
+			/* The board's and children's 'last_post's have:
+			time, timestamp (a number that represents the time.), id (of the post), topic (topic id.),
+			link, href, subject, start (where they should go for the first unread post.),
+			and member. (which has id, name, link, href, username in it.) */
+			$this_last_post['last_post_message'] = sprintf($txt['last_post_message'], $this_last_post['member']['link'], $this_last_post['link'], $this_last_post['time']);
 		}
 		else
 		{
 			$this_last_post['href'] = '';
 			$this_last_post['link'] = $txt['not_applicable'];
+			$this_last_post['last_post_message'] = '';
 		}
 
 		// Set the last post in the parent board.

+ 9 - 1
Sources/Subs-Boards.php

@@ -625,6 +625,13 @@ function modifyBoard($board_id, &$boardOptions)
 		$boardUpdateParameters['member_groups'] = implode(',', $boardOptions['access_groups']);
 	}
 
+	// And who isn't.
+	if (isset($boardOptions['deny_groups']))
+	{
+		$boardUpdates[] = 'deny_member_groups = {string:deny_groups}';
+		$boardUpdateParameters['deny_groups'] = implode(',', $boardOptions['deny_groups']);
+	}
+
 	if (isset($boardOptions['board_name']))
 	{
 		$boardUpdates[] = 'name = {string:board_name}';
@@ -1067,7 +1074,7 @@ function getBoardTree()
 		SELECT
 			IFNULL(b.id_board, 0) AS id_board, b.id_parent, b.name AS board_name, b.description, b.child_level,
 			b.board_order, b.count_posts, b.member_groups, b.id_theme, b.override_theme, b.id_profile, b.redirect,
-			b.num_posts, b.num_topics, c.id_cat, c.name AS cat_name, c.cat_order, c.can_collapse
+			b.num_posts, b.num_topics, b.deny_member_groups, c.id_cat, c.name AS cat_name, c.cat_order, c.can_collapse
 		FROM {db_prefix}categories AS c
 			LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)
 		ORDER BY c.cat_order, b.child_level, b.board_order',
@@ -1109,6 +1116,7 @@ function getBoardTree()
 				'order' => $row['board_order'],
 				'name' => $row['board_name'],
 				'member_groups' => explode(',', $row['member_groups']),
+				'deny_groups' => explode(',', $row['deny_member_groups']),
 				'description' => $row['description'],
 				'count_posts' => empty($row['count_posts']),
 				'posts' => $row['num_posts'],

+ 1 - 0
Sources/Subs.php

@@ -2968,6 +2968,7 @@ function setupThemeContext($forceload = false)
 		'total_members' => comma_format($modSettings['totalMembers']),
 		'latest_member' => $context['common_stats']['latest_member'],
 	);
+	$context['common_stats']['boardindex_total_posts'] = sprintf($txt['boardindex_total_posts'], $context['common_stats']['total_posts'], $context['common_stats']['total_topics'], $context['common_stats']['total_members']);
 
 	if (empty($settings['theme_version']))
 		$context['html_headers'] .= '

+ 2 - 9
Themes/default/BoardIndex.template.php

@@ -181,16 +181,9 @@ function template_main()
 					</td>
 					<td class="lastpost">';
 
-				/* The board's and children's 'last_post's have:
-				time, timestamp (a number that represents the time.), id (of the post), topic (topic id.),
-				link, href, subject, start (where they should go for the first unread post.),
-				and member. (which has id, name, link, href, username in it.) */
 				if (!empty($board['last_post']['id']))
 					echo '
-						<p><strong>', $txt['last_post'], '</strong>  ', $txt['by'], ' ', $board['last_post']['member']['link'] , '<br />
-						', $txt['in'], ' ', $board['last_post']['link'], '<br />
-						', $txt['on'], ' ', $board['last_post']['time'],'
-						</p>';
+						<p>', $board['last_post']['last_post_message'], '</p>';
 				echo '
 					</td>
 				</tr>';
@@ -398,7 +391,7 @@ function template_info_center()
 				</h4>
 			</div>
 			<p>
-				', $context['common_stats']['total_posts'], ' ', $txt['posts_made'], ' ', $txt['in'], ' ', $context['common_stats']['total_topics'], ' ', $txt['topics'], ' ', $txt['by'], ' ', $context['common_stats']['total_members'], ' ', $txt['members'], '. ', !empty($settings['show_latest_member']) ? $txt['latest_member'] . ': <strong> ' . $context['common_stats']['latest_member']['link'] . '</strong>' : '', '<br />
+				', $context['common_stats']['boardindex_total_posts'], ' ', !empty($settings['show_latest_member']) ? $txt['latest_member'] . ': <strong> ' . $context['common_stats']['latest_member']['link'] . '</strong>' : '', '<br />
 				', (!empty($context['latest_post']) ? $txt['latest_post'] . ': <strong>&quot;' . $context['latest_post']['link'] . '&quot;</strong>  ( ' . $context['latest_post']['time'] . ' )<br />' : ''), '
 				<a href="', $scripturl, '?action=recent">', $txt['recent_view'], '</a>', $context['show_stats'] ? '<br />
 				<a href="' . $scripturl . '?action=stats">' . $txt['more_stats'] . '</a>' : '', '

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

@@ -350,7 +350,7 @@ function template_main()
 								</li>';
 
 			// Show the profile, website, email address, and personal message buttons.
-			if ($context['show_profile_buttons'])
+			if ($message['member']['show_profile_buttons'])
 			{
 				echo '
 								<li class="profile">

+ 44 - 3
Themes/default/ManageBoards.template.php

@@ -257,6 +257,10 @@ function template_modify_board()
 {
 	global $context, $settings, $options, $scripturl, $txt, $modSettings;
 
+	if (!empty($modSettings['denyBoardsAccess']))
+		echo '
+		<div class="information">', $txt['boardsaccess_option_desc'], '</div>';
+
 	// The main table header.
 	echo '
 	<div id="manage_boards">
@@ -358,16 +362,53 @@ function template_modify_board()
 						</dt>
 						<dd>';
 
+	if (!empty($modSettings['denyBoardsAccess']))
+		echo '
+							<table>
+								<tr>
+									<td></td>
+									<th>', $txt['permissions_option_on'], '</th>
+									<th>', $txt['permissions_option_off'], '</th>
+									<th>', $txt['permissions_option_deny'], '</th>
+								</tr>';
+
 	// List all the membergroups so the user can choose who may access this board.
 	foreach ($context['groups'] as $group)
-		echo '
+		if (empty($modSettings['denyBoardsAccess']))
+			echo '
 							<label for="groups_', $group['id'], '">
-								<input type="checkbox" name="groups[]" value="', $group['id'], '" id="groups_', $group['id'], '"', $group['checked'] ? ' checked="checked"' : '', ' class="input_check" />
+								<input type="checkbox" name="groups[', $group['id'], ']" value="allow" id="groups_', $group['id'], '"', $group['checked'] ? ' checked="checked"' : '', ' class="input_check" />
 								<span', $group['is_post_group'] ? ' class="post_group" title="' . $txt['mboards_groups_post_group'] . '"' : '', $group['id'] == 0 ? ' class="regular_members" title="' . $txt['mboards_groups_regular_members'] . '"' : '', '>
 									', $group['name'], '
 								</span>
 							</label><br />';
-	echo '
+		else
+			echo '
+								<tr>
+									<td>
+										<label for="groups_', $group['id'], '_a">
+											<span', $group['is_post_group'] ? ' class="post_group" title="' . $txt['mboards_groups_post_group'] . '"' : '', $group['id'] == 0 ? ' class="regular_members" title="' . $txt['mboards_groups_regular_members'] . '"' : '', '>
+												', $group['name'], '
+											</span>
+										</label>
+									</td>
+									<td>
+										<input type="radio" name="groups[', $group['id'], ']" value="allow" id="groups_', $group['id'], '"', $group['allow'] ? ' checked="checked"' : '', ' class="input_radio" />
+									</td>
+									<td>
+										<input type="radio" name="groups[', $group['id'], ']" value="ignore" id="groups_', $group['id'], '"', !$group['allow'] && !$group['deny'] ? ' checked="checked"' : '', ' class="input_radio" />
+									</td>
+									<td>
+										<input type="radio" name="groups[', $group['id'], ']" value="deny" id="groups_', $group['id'], '"', $group['deny'] ? ' checked="checked"' : '', ' class="input_radio" />
+									</td>
+';
+
+	if (!empty($modSettings['denyBoardsAccess']))
+		echo '
+							</table>
+						</dd>';
+	else
+		echo '
 							<em>', $txt['check_all'], '</em> <input type="checkbox" class="input_check" onclick="invertAll(this, this.form, \'groups[]\');" /><br />
 							<br />
 						</dd>';

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

@@ -78,16 +78,9 @@ function template_main()
 					</td>
 					<td class="lastpost">';
 
-			/* The board's and children's 'last_post's have:
-			time, timestamp (a number that represents the time.), id (of the post), topic (topic id.),
-			link, href, subject, start (where they should go for the first unread post.),
-			and member. (which has id, name, link, href, username in it.) */
 			if (!empty($board['last_post']['id']))
 				echo '
-						<p><strong>', $txt['last_post'], '</strong>  ', $txt['by'], ' ', $board['last_post']['member']['link'], '<br />
-						', $txt['in'], ' ', $board['last_post']['link'], '<br />
-						', $txt['on'], ' ', $board['last_post']['time'],'
-						</p>';
+						<p>', $board['last_post']['last_post_message'], '</p>';
 
 			echo '
 					</td>

+ 7 - 87
Themes/default/ModerationCenter.template.php

@@ -438,96 +438,16 @@ function template_unapproved_attachments()
 	// Show all the attachments still oustanding.
 	echo '
 	<div id="modcenter">
-		<form action="', $scripturl, '?action=moderate;area=attachmod;sa=attachments;start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
-			<div class="cat_bar">
-				<h3 class="catbg">', $txt['mc_unapproved_attachments'], '</h3>
-			</div>
-			<div class="information">
-				', $txt['mc_unapproved_attachments_desc'], '
-			</div>';
-
-	// The ever popular approve button, with the massively unpopular delete.
-	$approve_button = create_button('approve.png', 'approve', 'approve', 'class="centericon"');
-	$remove_button = create_button('delete.png', 'remove_message', 'remove', 'class="centericon"');
-
-	// None awaiting?
-	if (empty($context['unapproved_items']))
-		echo '
-			<div class="windowbg">
-				<span class="topslice"><span></span></span>
-				<div class="content">
-					<p class="centertext">', $txt['mc_unapproved_attachments_none_found'], '</p>
-				</div>
-				<span class="botslice"><span></span></span>
-			</div>';
-	else
-		echo '
-			<div class="pagesection">
-				<div class="floatleft">
-					', $txt['pages'], ': ', $context['page_index'], '
-				</div>
-			</div>
-			<table class="table_grid" width="100%">
-			<thead>
-				<tr class="catbg">
-					<th class="first_th">', $txt['mc_unapproved_attach_name'], '</th>
-					<th>', $txt['mc_unapproved_attach_size'], '</th>
-					<th>', $txt['mc_unapproved_attach_poster'], '</th>
-					<th>', $txt['date'], '</th>
-					<th class="last_th" nowrap="nowrap" align="center"><input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" checked="checked" /></th>
-				</tr>
-			</thead>
-			<tbody>';
-
-	foreach ($context['unapproved_items'] as $item)
-	{
-		echo '
-				<tr class="', $item['alternate'] ? 'windowbg' : 'windowbg2', '">
-					<td>
-						', $item['filename'], '
-					</td>
-					<td align="right">
-						', $item['size'], $txt['kilobyte'], '
-					</td>
-					<td>
-						', $item['poster']['link'], '
-					</td>
-					<td class="smalltext">
-						', $item['time'], '<br />', $txt['in'], ' <a href="', $item['message']['href'], '">', $item['message']['subject'], '</a>
-					</td>
-					<td width="4%" align="center">
-						<input type="checkbox" name="item[]" value="', $item['id'], '" checked="checked" class="input_check" />
-					</td>
-				</tr>';
-	}
-
-	if (!empty($context['unapproved_items']))
-		echo '
-			</tbody>
-			</table>';
-
-		echo '
-			<div class="pagesection">
-				<div class="floatright">
-					<select name="do" onchange="if (this.value != 0 &amp;&amp; confirm(\'', $txt['mc_unapproved_sure'], '\')) submit();">
-						<option value="0">', $txt['with_selected'], ':</option>
-						<option value="0">-------------------</option>
-						<option value="approve">&nbsp;--&nbsp;', $txt['approve'], '</option>
-						<option value="delete">&nbsp;--&nbsp;', $txt['delete'], '</option>
-					</select>
-					<noscript><input type="submit" name="ml_go" value="', $txt['go'], '" class="button_submit" /></noscript>
-				</div>';
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt['mc_unapproved_attachments'], '</h3>
+		</div>
+		<div class="information">
+			', $txt['mc_unapproved_attachments_desc'], '
+		</div>';
 
-	if (!empty($context['unapproved_items']))
-		echo '
-				<div class="floatleft">
-					<div class="pagelinks">', $txt['pages'], ': ', $context['page_index'], '</div>
-				</div>';
+	template_show_list('mc_unapproved_attach');
 
 	echo '
-			</div>
-			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
-		</form>
 	</div>
 	<br class="clear" />';
 }

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

@@ -291,14 +291,15 @@ function template_folder()
 				</li>';
 
 				// Show the profile, website, email address, and personal message buttons.
-				if ($settings['show_profile_buttons'])
+				if ($message['member']['show_profile_buttons'])
 				{
 					echo '
 				<li class="profile">
 					<ul>';
 
 					// Show the profile button
-					echo '
+					if ($message['member']['can_view_profile'])
+						echo '
 						<li><a href="', $message['member']['href'], '">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/icons/profile_sm.png" alt="' . $txt['view_profile'] . '" title="' . $txt['view_profile'] . '" />' : $txt['view_profile']), '</a></li>';
 
 					// Don't show an icon if they haven't specified a website.

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

@@ -182,8 +182,7 @@ function template_unread()
 									', $topic['is_sticky'] ? '<strong>' : '', '<span title="', $topic[(empty($settings['message_index_preview_first']) ? 'last_post' : 'first_post')]['preview'], '"><span id="msg_' . $topic['first_post']['id'] . '">', $topic['first_post']['link'], '</span>', $topic['is_sticky'] ? '</strong>' : '', '
 									<a href="', $topic['new_href'], '" id="newicon', $topic['first_post']['id'], '"><span class="new_posts">' . $txt['new'] . '</span></a>
 									<p>
-										', $txt['started_by'], ' <strong>', $topic['first_post']['member']['link'], '</strong>
-										', $txt['in'], ' <em>', $topic['board']['link'], '</em>
+										', $topic['first_post']['started_by'], '
 										<small id="pages', $topic['first_post']['id'], '">', $topic['pages'], '</small>
 									</p>
 								</div>
@@ -366,8 +365,7 @@ function template_replies()
 									', $topic['is_sticky'] ? '<strong>' : '', '<span title="', $topic[(empty($settings['message_index_preview_first']) ? 'last_post' : 'first_post')]['preview'], '"><span id="msg_' . $topic['first_post']['id'] . '">', $topic['first_post']['link'], '</span>', $topic['is_sticky'] ? '</strong>' : '', '
 									<a href="', $topic['new_href'], '" id="newicon', $topic['first_post']['id'], '"><span class="new_posts">' . $txt['new'] . '</span></a>
 									<p>
-										', $txt['started_by'], ' <strong>', $topic['first_post']['member']['link'], '</strong>
-										', $txt['in'], ' <em>', $topic['board']['link'], '</em>
+										', $topic['first_post']['started_by'], '
 										<small id="pages', $topic['first_post']['id'], '">', $topic['pages'], '</small>
 									</p>
 								</div>

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

@@ -88,6 +88,8 @@ $txt['recycle_board'] = 'Board for recycled topics';
 $txt['recycle_board_unselected_notice'] = 'You have enabled the recycling of topics without specifying a board to place them in.  This feature will not be enabled until you specify a board to place recycled topics into.';
 $txt['countChildPosts'] = 'Count child\'s posts in parent\'s totals';
 $txt['allow_ignore_boards'] = 'Allow boards to be ignored';
+$txt['denyBoardsAccess'] = 'Enable the option to deny boards access to a group';
+$txt['boardsaccess_option_desc'] = 'For each permission you can pick either \'Allow\' (A), \'Ignore\' (X), or <span class="alert">\'Deny\' (D)</span>.<br /><br />Remember that if you deny access, any member - whether moderator or otherwise - that is in that group will be denied that as well.<br />For this reason, you should use deny carefully, only when <strong>necessary</strong>. Ignore, on the other hand, denies unless otherwise granted.';
 
 $txt['mboards_select_destination'] = 'Select destination for board \'<strong>%1$s</strong>\'';
 $txt['mboards_cancel_moving'] = 'Cancel moving';

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

@@ -104,8 +104,11 @@ $txt['error_occured'] = 'An Error Has Occurred!';
 $txt['at'] = 'at';
 $txt['logout'] = 'Logout';
 $txt['started_by'] = 'Started by';
+$txt['topic_started_by'] = 'Started by  <strong>%1$s</strong> in <em>%2$s</em>';
 $txt['replies'] = 'Replies';
 $txt['last_post'] = 'Last post';
+$txt['last_post_message'] = '<strong>Last post</strong> by %1$s<br />in %2$s<br />on %3$s';
+$txt['boardindex_total_posts'] = '%1$d Posts in %2$d Topics by %3$d Members.';
 $txt['admin_login'] = 'Administration Login';
 // Use numeric entities in the below string.
 $txt['topic'] = 'Topic';

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

@@ -495,6 +495,7 @@ CREATE TABLE {$db_prefix}boards (
   unapproved_posts smallint(5) NOT NULL default '0',
   unapproved_topics smallint(5) NOT NULL default '0',
   redirect varchar(255) NOT NULL default '',
+  deny_member_groups varchar(255) NOT NULL default '',
   PRIMARY KEY (id_board),
   UNIQUE categories (id_cat, id_board),
   KEY id_parent (id_parent),

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

@@ -673,6 +673,7 @@ CREATE TABLE {$db_prefix}boards (
   unapproved_posts smallint NOT NULL default '0',
   unapproved_topics smallint NOT NULL default '0',
   redirect varchar(255) NOT NULL default '',
+  deny_member_groups varchar(255) NOT NULL default '',
   PRIMARY KEY (id_board)
 );
 

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

@@ -507,6 +507,7 @@ CREATE TABLE {$db_prefix}boards (
   unapproved_posts smallint NOT NULL default '0',
   unapproved_topics smallint NOT NULL default '0',
   redirect varchar(255) NOT NULL default ''
+  deny_member_groups varchar(255) NOT NULL default '',
 );
 
 #

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

@@ -166,3 +166,11 @@ INSERT INTO {$db_prefix}scheduled_tasks
 VALUES
 	(0, 180, 1, 'd', 0, 'remove_topic_redirect');
 ---#
+
+/******************************************************************************/
+--- Adding support for deny boards access
+/******************************************************************************/
+---# Adding new columns to boards...
+ALTER TABLE {$db_prefix}boards
+ADD COLUMN deny_member_groups varchar(255) NOT NULL DEFAULT '';
+---#

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

@@ -220,3 +220,12 @@ INSERT INTO {$db_prefix}scheduled_tasks
 VALUES
 	(0, 180, 1, 'd', 0, 'remove_topic_redirect');
 ---#
+
+/******************************************************************************/
+--- Adding support for deny boards access
+/******************************************************************************/
+---# Adding new columns to boards...
+ALTER TABLE {$db_prefix}boards
+ADD COLUMN deny_member_groups varchar(255) NOT NULL DEFAULT '';
+---#
+

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

@@ -187,3 +187,11 @@ INSERT INTO {$db_prefix}scheduled_tasks
 VALUES
 	(0, 180, 1, 'd', 0, 'remove_topic_redirect');
 ---#
+
+/******************************************************************************/
+--- Adding support for deny boards access
+/******************************************************************************/
+---# Adding new columns to boards...
+ALTER TABLE {$db_prefix}boards
+ADD COLUMN deny_member_groups varchar(255) NOT NULL DEFAULT '';
+---#