Kaynağa Gözat

Merge branch 'master' into lab_release-2.1

Conflicts:
	Sources/PersonalMessage.php
emanuele 12 yıl önce
ebeveyn
işleme
014ee113c4

+ 39 - 1
.gitignore

@@ -6,4 +6,42 @@ Packages/backups/*
 Packages/temp
 *.*~
 
-.DS_Store
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+*.bat
+*.session
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+*.session
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
+*.lnk
+/nbproject/private/
+/nbproject/

+ 141 - 0
Sources/Avatar.php

@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * This file handles the avatar requests. The whole point of this file is to reduce the loaded stuff to show an image
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines
+ *
+ * @copyright 2012 Simple Machines
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('Hacking attempt...');
+
+function showAvatar()
+{
+	global $smcFunc, $modSettings, $sourcedir, $maintenance;
+
+	// We need a valid ID
+	if(empty($_GET['attach']) || (string)$_GET['attach'] != (string)(int)$_GET['attach'])
+		die;
+
+	// No access in strict maintenance mode
+	if(!empty($maintenance) && $maintenance == 2)
+		die;
+
+	// This is done to clear any output that was made before now.
+	if(!empty($modSettings['enableCompressedOutput']) && !headers_sent() && ob_get_length() == 0)
+	{
+		if(@ini_get('zlib.output_compression') == '1' || @ini_get('output_handler') == 'ob_gzhandler')
+			$modSettings['enableCompressedOutput'] = 0;
+		else
+			ob_start('ob_gzhandler');
+	}
+
+	if(empty($modSettings['enableCompressedOutput']))
+	{
+		ob_start();
+		header('Content-Encoding: none');
+	}
+
+	// Better handling
+	$id_attach = (int) $_GET['attach'];
+
+	// Use cache when possible
+	if(($cache = cache_get_data('avatar_lookup_id-'. $id_attach)) != null)
+		$file = $cache;
+
+	// Get the info from the DB
+	else
+	{
+		$request = $smcFunc['db_query']('', '
+			SELECT id_folder, filename AS real_filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member
+			FROM {db_prefix}attachments
+			WHERE id_attach = {int:id_attach}
+				AND id_member > {int:blank_id_member}
+			LIMIT 1',
+			array(
+				'id_attach' => $id_attach,
+				'blank_id_member' => 0,
+			)
+		);
+
+		$file = $smcFunc['db_fetch_assoc']($request);
+
+		// Update the download counter (unless it's a thumbnail).
+		if ($file['attachment_type'] != 3)
+			$smcFunc['db_query']('attach_download_increase', '
+				UPDATE LOW_PRIORITY {db_prefix}attachments
+				SET downloads = downloads + 1
+				WHERE id_attach = {int:id_attach}',
+				array(
+					'id_attach' => $id_attach,
+				)
+			);
+
+		$file['filename'] = getAttachmentFilename($file['real_filename'], $id_attach, $file['id_folder'], false, $file['file_hash']);
+
+		// ETag time
+		$file['etag'] = '"'. function_exists('md5_file') ? md5_file($file['filename']) : md5(file_get_contents($file['filename'])). '"';
+
+		// Cache it... (Why do I randomly select a length at which to expire? Search around for RIP_JITTER :P)
+		cache_put_data('avatar_lookup_id-'. $id_attach, $file, mt_rand(850, 900));
+	}
+
+	// The file does not exists
+	if(!file_exists($file['filename']))
+	{
+		header('HTTP/1.0 404 File Not Found');
+		die('404 File Not Found');
+	}
+
+	// If it hasn't been modified since the last time this attachement was retrieved, there's no need to display it again.
+	if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
+	{
+		list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
+		if (strtotime($modified_since) >= filemtime($file['filename']))
+		{
+			ob_end_clean();
+
+			// Answer the question - no, it hasn't been modified ;).
+			header('HTTP/1.1 304 Not Modified');
+			exit;
+		}
+	}
+
+	header('Pragma: ');
+	header('Expires: '. gmdate('D, d M Y H:i:s', time() + 31536000). ' GMT');
+	header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($file['filename'])). ' GMT');
+	header('Accept-Ranges: bytes');
+	header('Connection: close');
+	header('ETag: '. $file['etag']);
+	header('Content-Type: '. $file['mime_type']);
+
+	// Since we don't do output compression for files this large...
+	if (filesize($file['filename']) > 4194304)
+	{
+		// Forcibly end any output buffering going on.
+		while (@ob_get_level() > 0)
+			@ob_end_clean();
+
+		$fp = fopen($file['filename'], 'rb');
+		while (!feof($fp))
+		{
+			print fread($fp, 8192);
+			flush();
+		}
+		fclose($fp);
+	}
+
+	// On some of the less-bright hosts, readfile() is disabled.  It's just a faster, more byte safe, version of what's in the if.
+	elseif (@readfile($file['filename']) === null)
+		print file_get_contents($file['filename']);
+
+	die();
+}

+ 20 - 38
Sources/Display.php

@@ -1306,12 +1306,12 @@ function prepareDisplayContext($reset = false)
 }
 
 /**
- * Downloads an attachment or avatar, and increments the download count.
- * It requires the view_attachments permission. (not for avatars!)
+ * Downloads an attachment, and increments the download count.
+ * It requires the view_attachments permission.
  * It disables the session parser, and clears any previous output.
  * It depends on the attachmentUploadDir setting being correct.
  * It is accessed via the query string ?action=dlattach.
- * Views to attachments and avatars do not increase hits and are not logged in the "Who's Online" log.
+ * Views to attachments do not increase hits and are not logged in the "Who's Online" log.
  */
 function Download()
 {
@@ -1328,42 +1328,24 @@ function Download()
 
 	$_REQUEST['attach'] = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) $_REQUEST['id'];
 
-	if (isset($_REQUEST['type']) && $_REQUEST['type'] == 'avatar')
-	{
-		$request = $smcFunc['db_query']('', '
-			SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member
-			FROM {db_prefix}attachments
-			WHERE id_attach = {int:id_attach}
-				AND id_member > {int:blank_id_member}
-			LIMIT 1',
-			array(
-				'id_attach' => $_REQUEST['attach'],
-				'blank_id_member' => 0,
-			)
-		);
-		$_REQUEST['image'] = true;
-	}
-	// This is just a regular attachment...
-	else
-	{
-		// This checks only the current board for $board/$topic's permissions.
-		isAllowedTo('view_attachments');
+	// This checks only the current board for $board/$topic's permissions.
+	isAllowedTo('view_attachments');
+
+	// Make sure this attachment is on this board.
+	// @todo: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
+	$request = $smcFunc['db_query']('', '
+		SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
+		FROM {db_prefix}attachments AS a
+			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
+			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
+		WHERE a.id_attach = {int:attach}
+		LIMIT 1',
+		array(
+			'attach' => $_REQUEST['attach'],
+			'current_topic' => $topic,
+		)
+	);
 
-		// Make sure this attachment is on this board.
-		// @todo: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
-		$request = $smcFunc['db_query']('', '
-			SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
-			FROM {db_prefix}attachments AS a
-				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
-				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
-			WHERE a.id_attach = {int:attach}
-			LIMIT 1',
-			array(
-				'attach' => $_REQUEST['attach'],
-				'current_topic' => $topic,
-			)
-		);
-	}
 	if ($smcFunc['db_num_rows']($request) == 0)
 		fatal_lang_error('no_access', false);
 	list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $smcFunc['db_fetch_row']($request);

+ 46 - 155
Sources/Groups.php

@@ -67,7 +67,7 @@ function Groups()
  */
 function GroupList()
 {
-	global $txt, $context, $sourcedir;
+	global $txt, $context, $sourcedir, $scripturl;
 
 	$context['page_title'] = $txt['viewing_groups'];
 
@@ -78,8 +78,14 @@ function GroupList()
 	$listOptions = array(
 		'id' => 'group_lists',
 		'title' => $context['page_title'],
+		'base_href' => $scripturl . '?action=moderate;area=viewgroups;sa=view',
+		'default_sort_col' => 'group',
 		'get_items' => array(
-			'function' => 'list_getGroups',
+			'file' => $sourcedir . '/Subs-Membergroups.php',
+			'function' => 'list_getMembergroups',
+			'params' => array(
+				'regular',
+			),
 		),
 		'columns' => array(
 			'group' => array(
@@ -87,17 +93,30 @@ function GroupList()
 					'value' => $txt['name'],
 				),
 				'data' => array(
-					'function' => create_function('$group', '
-						global $scripturl, $context;
-
-						$output = \'<a href="\' . $scripturl . \'?action=\' . $context[\'current_action\'] . (isset($context[\'admin_area\']) ? \';area=\' . $context[\'admin_area\'] : \'\') . \';sa=members;group=\' . $group[\'id\'] . \'" \' . ($group[\'color\'] ? \'style="color: \' . $group[\'color\'] . \';"\' : \'\') . \'>\' . $group[\'name\'] . \'</a>\';
-
-						if ($group[\'desc\'])
-							$output .= \'<div class="smalltext">\' . $group[\'desc\'] . \'</div>\';
-
-						return $output;
+					'function' => create_function('$rowData', '
+						global $scripturl;
+
+						// Since the moderator group has no explicit members, no link is needed.
+						if ($rowData[\'id_group\'] == 3)
+							$group_name = $rowData[\'group_name\'];
+						else
+						{
+							$color_style = empty($rowData[\'online_color\']) ? \'\' : sprintf(\' style="color: %1$s;"\', $rowData[\'online_color\']);
+							$group_name = sprintf(\'<a href="%1$s?action=admin;area=membergroups;sa=members;group=%2$d"%3$s>%4$s</a>\', $scripturl, $rowData[\'id_group\'], $color_style, $rowData[\'group_name\']);
+						}
+
+						// Add a help option for moderator and administrator.
+						if ($rowData[\'id_group\'] == 1)
+							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_administrator" onclick="return reqOverlayDiv(this.href);">?</a>)\', $scripturl);
+						elseif ($rowData[\'id_group\'] == 3)
+							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_moderator" onclick="return reqOverlayDiv(this.href);">?</a>)\', $scripturl);
+
+						return $group_name;
 					'),
-					'style' => 'width: 50%;',
+				),
+				'sort' => array(
+					'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name',
+					'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name DESC',
 				),
 			),
 			'icons' => array(
@@ -107,6 +126,10 @@ function GroupList()
 				'data' => array(
 					'db' => 'icons',
 				),
+				'sort' => array(
+					'default' => 'mg.icons',
+					'reverse' => 'mg.icons DESC',
+				)
 			),
 			'moderators' => array(
 				'header' => array(
@@ -125,8 +148,17 @@ function GroupList()
 					'value' => $txt['membergroups_members_top'],
 				),
 				'data' => array(
-					'comma_format' => true,
-					'db' => 'num_members',
+					'function' => create_function('$rowData', '
+						global $txt;
+
+						// No explicit members for the moderator group.
+						return $rowData[\'id_group\'] == 3 ? $txt[\'membergroups_guests_na\'] : comma_format($rowData[\'num_members\']);
+					'),
+					'class' => 'centercol',
+				),
+				'sort' => array(
+					'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1',
+					'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1 DESC',
 				),
 			),
 		),
@@ -139,147 +171,6 @@ function GroupList()
 	$context['default_list'] = 'group_lists';
 }
 
-/**
- * Get the group information for the list.
- * @param int $start
- * @param int $items_per_page
- * @param int $sort
- */
-function list_getGroups($start, $items_per_page, $sort)
-{
-	global $smcFunc, $txt, $scripturl, $user_info, $settings, $context;
-
-	// Yep, find the groups...
-	$request = $smcFunc['db_query']('', '
-		SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden,
-			mg.icons, IFNULL(gm.id_member, 0) AS can_moderate
-		FROM {db_prefix}membergroups AS mg
-			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
-		WHERE mg.min_posts = {int:min_posts}
-			AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
-			AND mg.group_type != {int:is_protected}') . '
-		ORDER BY group_name',
-		array(
-			'current_member' => $user_info['id'],
-			'min_posts' => -1,
-			'mod_group' => 3,
-			'is_protected' => 1,
-		)
-	);
-	// Start collecting the data.
-	$groups = array();
-	$group_ids = array();
-	$context['can_moderate'] = allowedTo('manage_membergroups');
-	while ($row = $smcFunc['db_fetch_assoc']($request))
-	{
-		// We only list the groups they can see.
-		if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
-			continue;
-
-		$row['icons'] = explode('#', $row['icons']);
-
-		$groups[$row['id_group']] = array(
-			'id' => $row['id_group'],
-			'name' => $row['group_name'],
-			'desc' => $row['description'],
-			'color' => $row['online_color'],
-			'type' => $row['group_type'],
-			'num_members' => 0,
-			'moderators' => array(),
-			'icons' => !empty($row['icons'][0]) && !empty($row['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/' . $row['icons'][1] . '" alt="*" />', $row['icons'][0]) : '',
-		);
-
-		$context['can_moderate'] |= $row['can_moderate'];
-		$group_ids[] = $row['id_group'];
-	}
-	$smcFunc['db_free_result']($request);
-
-	// Count up the members separately...
-	if (!empty($group_ids))
-	{
-		$query = $smcFunc['db_query']('', '
-			SELECT id_group, COUNT(*) AS num_members
-			FROM {db_prefix}members
-			WHERE id_group IN ({array_int:group_list})
-			GROUP BY id_group',
-			array(
-				'group_list' => $group_ids,
-			)
-		);
-		while ($row = $smcFunc['db_fetch_assoc']($query))
-			$groups[$row['id_group']]['num_members'] += $row['num_members'];
-		$smcFunc['db_free_result']($query);
-
-		// Only do additional groups if we can moderate...
-		if ($context['can_moderate'])
-		{
-			$query = $smcFunc['db_query']('', '
-				SELECT mg.id_group, COUNT(*) AS num_members
-				FROM {db_prefix}membergroups AS mg
-					INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen}
-						AND mem.id_group != mg.id_group
-						AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
-				WHERE mg.id_group IN ({array_int:group_list})
-				GROUP BY mg.id_group',
-				array(
-					'group_list' => $group_ids,
-					'blank_screen' => '',
-				)
-			);
-			while ($row = $smcFunc['db_fetch_assoc']($query))
-				$groups[$row['id_group']]['num_members'] += $row['num_members'];
-			$smcFunc['db_free_result']($query);
-		}
-	}
-
-	// Get any group moderators.
-	// Count up the members separately...
-	if (!empty($group_ids))
-	{
-		$query = $smcFunc['db_query']('', '
-			SELECT mods.id_group, mods.id_member, mem.member_name, mem.real_name
-			FROM {db_prefix}group_moderators AS mods
-				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
-			WHERE mods.id_group IN ({array_int:group_list})',
-			array(
-				'group_list' => $group_ids,
-			)
-		);
-		while ($row = $smcFunc['db_fetch_assoc']($query))
-			$groups[$row['id_group']]['moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
-		$smcFunc['db_free_result']($query);
-	}
-
-	return $groups;
-}
-
-/**
- * How many groups are there that are visible?
- *
- * @return int, the groups count.
- */
-function list_getGroupCount()
-{
-	global $smcFunc;
-
-	$request = $smcFunc['db_query']('', '
-		SELECT COUNT(id_group) AS group_count
-		FROM {db_prefix}membergroups
-		WHERE mg.min_posts = {int:min_posts}
-			AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
-			AND mg.group_type != {int:is_protected}'),
-		array(
-			'min_posts' => -1,
-			'mod_group' => 3,
-			'is_protected' => 1,
-		)
-	);
-	list ($group_count) = $smcFunc['db_fetch_row']($request);
-	$smcFunc['db_free_result']($request);
-
-	return $group_count;
-}
-
 /**
  * Display members of a group, and allow adding of members to a group. Silly function name though ;)
  * It can be called from ManageMembergroups if it needs templating within the admin environment.

+ 4 - 4
Sources/Load.php

@@ -1531,13 +1531,13 @@ function loadTheme($id_theme = 0, $initialize = true)
 	// Some basic information...
 	if (!isset($context['html_headers']))
 		$context['html_headers'] = '';
-	if(!isset($context['javascript_files']))
+	if (!isset($context['javascript_files']))
 		$context['javascript_files'] = array();
-	if(!isset($context['css_files']))
+	if (!isset($context['css_files']))
 		$context['css_files'] = array();
-	if(!isset($context['javascript_inline']))
+	if (!isset($context['javascript_inline']))
 		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
-	if(!isset($context['javascript_vars']))
+	if (!isset($context['javascript_vars']))
 		$context['javascript_vars'] = array();
 
 	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';

+ 5 - 5
Sources/ManageAttachments.php

@@ -214,7 +214,7 @@ function ManageAttachmentSettings($return_config = false)
 
 		if (!empty($_POST['use_subdirectories_for_attachments']))
 		{
-			if(isset($_POST['use_subdirectories_for_attachments']) && empty($_POST['basedirectory_for_attachments']))
+			if (isset($_POST['use_subdirectories_for_attachments']) && empty($_POST['basedirectory_for_attachments']))
 				$_POST['basedirectory_for_attachments'] = (!empty($modSettings['basedirectory_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : $boarddir);
 
 			if (!empty($_POST['use_subdirectories_for_attachments']) && !empty($modSettings['attachment_basedirectories']))
@@ -2077,7 +2077,7 @@ function ManageAttachmentPaths()
 				$bid = -1;
 				$use_subdirectories_for_attachments = 0;
 				if (!empty($modSettings['attachment_basedirectories']))
-					foreach($modSettings['attachment_basedirectories'] as $bid => $base)
+					foreach ($modSettings['attachment_basedirectories'] as $bid => $base)
 						if (strpos($modSettings['attachmentUploadDir'][$_POST['current_dir']], $base . DIRECTORY_SEPARATOR) !==false)
 						{
 							$use_subdirectories_for_attachments = 1;
@@ -2158,7 +2158,7 @@ function ManageAttachmentPaths()
 
 		If (isset($_POST['base_dir']))
 		{
-			foreach($_POST['base_dir'] as $id => $dir)
+			foreach ($_POST['base_dir'] as $id => $dir)
 			{
 				if (!empty($dir) && $dir != $modSettings['attachmentUploadDir'][$id])
 				{
@@ -2232,11 +2232,11 @@ function ManageAttachmentPaths()
 		{
 			$errors = array();
 			if (!empty($_SESSION['errors']['dir']))
-				foreach($_SESSION['errors']['dir'] as $error)
+				foreach ($_SESSION['errors']['dir'] as $error)
 					$errors['dir'][] = $smcFunc['htmlspecialchars']($error, ENT_QUOTES);
 
 			if (!empty($_SESSION['errors']['base']))
-				foreach($_SESSION['errors']['base'] as $error)
+				foreach ($_SESSION['errors']['base'] as $error)
 					$errors['base'][] = $smcFunc['htmlspecialchars']($error, ENT_QUOTES);
 		}
 		unset($_SESSION['errors']);

+ 15 - 43
Sources/ManageMembergroups.php

@@ -123,8 +123,8 @@ function MembergroupIndex()
 					'),
 				),
 				'sort' => array(
-					'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, group_name',
-					'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, group_name DESC',
+					'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name',
+					'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, mg.group_name DESC',
 				),
 			),
 			'icons' => array(
@@ -132,27 +132,11 @@ function MembergroupIndex()
 					'value' => $txt['membergroups_icons'],
 				),
 				'data' => array(
-					'function' => create_function('$rowData', '
-						global $settings;
-
-						$icons = explode(\'#\', $rowData[\'icons\']);
-
-						// In case no icons are setup, return with nothing
-						if (empty($icons[0]) || empty($icons[1]))
-							return \'\';
-
-						// Otherwise repeat the image a given number of times.
-						else
-						{
-							$image = sprintf(\'<img src="%1$s/%2$s" alt="*" />\', $settings[\'images_url\'], $icons[1]);
-							return str_repeat($image, $icons[0]);
-						}
-					'),
-
+					'db' => 'icons',
 				),
 				'sort' => array(
-					'default' => 'icons',
-					'reverse' => 'icons DESC',
+					'default' => 'mg.icons',
+					'reverse' => 'mg.icons DESC',
 				)
 			),
 			'members' => array(
@@ -165,13 +149,13 @@ function MembergroupIndex()
 						global $txt;
 
 						// No explicit members for the moderator group.
-						return $rowData[\'id_group\'] == 3 ? $txt[\'membergroups_guests_na\'] : $rowData[\'num_members\'];
+						return $rowData[\'id_group\'] == 3 ? $txt[\'membergroups_guests_na\'] : comma_format($rowData[\'num_members\']);
 					'),
 					'class' => 'centercol',
 				),
 				'sort' => array(
-					'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, 1',
-					'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, 1 DESC',
+					'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1',
+					'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, 1 DESC',
 				),
 			),
 			'modify' => array(
@@ -232,8 +216,8 @@ function MembergroupIndex()
 					'),
 				),
 				'sort' => array(
-					'default' => 'group_name',
-					'reverse' => 'group_name DESC',
+					'default' => 'mg.group_name',
+					'reverse' => 'mg.group_name DESC',
 				),
 			),
 			'icons' => array(
@@ -241,23 +225,11 @@ function MembergroupIndex()
 					'value' => $txt['membergroups_icons'],
 				),
 				'data' => array(
-					'function' => create_function('$rowData', '
-						global $settings;
-
-						$icons = explode(\'#\', $rowData[\'icons\']);
-
-						if (empty($icons[0]) || empty($icons[1]))
-							return \'\';
-						else
-						{
-							$icon_image = sprintf(\'<img src="%1$s/%2$s" alt="*" />\', $settings[\'images_url\'], $icons[1]);
-							return str_repeat($icon_image, $icons[0]);
-						}
-					'),
+					'db' => 'icons',
 				),
 				'sort' => array(
-					'default' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, icons',
-					'reverse' => 'CASE WHEN id_group < 4 THEN id_group ELSE 4 END, icons DESC',
+					'default' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, icons',
+					'reverse' => 'CASE WHEN mg.id_group < 4 THEN mg.id_group ELSE 4 END, icons DESC',
 				)
 			),
 			'members' => array(
@@ -284,8 +256,8 @@ function MembergroupIndex()
 					'class' => 'centercol',
 				),
 				'sort' => array(
-					'default' => 'min_posts',
-					'reverse' => 'min_posts DESC',
+					'default' => 'mg.min_posts',
+					'reverse' => 'mg.min_posts DESC',
 				),
 			),
 			'modify' => array(

+ 1 - 1
Sources/MessageIndex.php

@@ -486,7 +486,7 @@ function MessageIndex()
 			if (!empty($settings['avatars_on_indexes']))
 			{
 				// Allow themers to show the latest poster's avatar along with the topic
-				if(!empty($row['avatar']))
+				if (!empty($row['avatar']))
 				{
 					if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize')
 					{

+ 1 - 1
Sources/ModerationCenter.php

@@ -880,7 +880,7 @@ function ModReport()
 	{
 		$context['report']['comments'][] = array(
 			'id' => $row['id_comment'],
-			'message' => $row['comment'],
+			'message' => strtr($row['comment'], array("\n" => '<br />')),
 			'time' => timeformat($row['time_sent']),
 			'member' => array(
 				'id' => $row['id_member'],

+ 1 - 16
Sources/PersonalMessage.php

@@ -842,10 +842,9 @@ function MessageFolder()
 		if ($context['display_mode'] == 2)
 		{
 			$context['conversation_buttons'] = array(
-				'reply' => array('text' => 'reply_to_all', 'image' => 'reply.png', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=send;f=' . $context['folder'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';pmsg=' . $context['current_pm'] . ';u=all', 'active' => true),
 				'delete' => array('text' => 'delete_conversation', 'image' => 'delete.png', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'),
 			);
-	
+
 			// Allow mods to add additional buttons here
 			call_integration_hook('integrate_conversation_buttons');
 		}
@@ -869,20 +868,6 @@ function MessageFolder()
 		elseif (!empty($context['current_pm']))
 			markMessages($display_pms, $context['current_label_id']);
 	}
-<<<<<<< HEAD
-
-	// Build the conversation button array.
-	if ($context['display_mode'] == 2)
-	{
-		$context['conversation_buttons'] = array(
-			'delete' => array('text' => 'delete_conversation', 'image' => 'delete.png', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'),
-		);
-
-		// Allow mods to add additional buttons here
-		call_integration_hook('integrate_conversation_buttons');
-	}
-=======
->>>>>>> 62e562d... Undefined: current_pm
 }
 
 /**

+ 1 - 1
Sources/Post.php

@@ -1223,7 +1223,7 @@ function Post2()
 				$keep_ids[] = (int) $dummy;
 
 		if (isset($_SESSION['temp_attachments']))
-			foreach($_SESSION['temp_attachments'] as $attachID => $attachment)
+			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
 			{
 				if ((isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files'])) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
 					continue;

+ 62 - 1
Sources/Printpage.php

@@ -239,7 +239,7 @@ function PrintTopic()
 
 	// Split the topics up so we can print them.
 	$request = $smcFunc['db_query']('', '
-		SELECT subject, poster_time, body, IFNULL(mem.real_name, poster_name) AS poster_name
+		SELECT subject, poster_time, body, IFNULL(mem.real_name, poster_name) AS poster_name, id_msg
 		FROM {db_prefix}messages AS m
 			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
 		WHERE m.id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && !allowedTo('approve_posts') ? '
@@ -264,6 +264,7 @@ function PrintTopic()
 			'time' => timeformat($row['poster_time'], false),
 			'timestamp' => forum_time(true, $row['poster_time']),
 			'body' => parse_bbc($row['body'], 'print'),
+			'id_msg' => $row['id_msg'],
 		);
 
 		if (!isset($context['topic_subject']))
@@ -271,6 +272,66 @@ function PrintTopic()
 	}
 	$smcFunc['db_free_result']($request);
 
+	// Fetch attachments so we can print them if asked, enabled and allowed
+	if (isset($_REQUEST['images']) && !empty($modSettings['attachmentEnable']) && allowedTo('view_attachments'))
+	{
+		$messages = array();
+		foreach ($context['posts'] as $temp)
+			$messages[] = $temp['id_msg'];
+		
+		// build the request
+		$request = $smcFunc['db_query']('', '
+			SELECT
+				a.id_attach, a.id_msg, a.approved, a.width, a.height, a.file_hash, a.filename, a.id_folder, a.mime_type
+			FROM {db_prefix}attachments AS a
+			WHERE a.id_msg IN ({array_int:message_list})
+				AND a.attachment_type = {int:attachment_type}',
+			array(
+				'message_list' => $messages,
+				'attachment_type' => 0,
+				'is_approved' => 1,
+			)
+		);
+		$temp = array();
+		while ($row = $smcFunc['db_fetch_assoc']($request))
+		{
+			$temp[$row['id_attach']] = $row;
+			if (!isset($context['printattach'][$row['id_msg']]))
+				$context['printattach'][$row['id_msg']] = array();
+		}
+		$smcFunc['db_free_result']($request);
+		ksort($temp);
+
+		// load them into $context so the template can use them
+		foreach ($temp as $row)
+		{
+			if (!empty($row['width']) && !empty($row['height'])) 
+			{
+				if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $row['height'] * ($modSettings['max_image_width'] / $row['width']) <= $modSettings['max_image_height']))
+				{
+					if ($row['width'] > $modSettings['max_image_width']) 
+					{
+						$row['height'] = floor($row['height'] * ($modSettings['max_image_width'] / $row['width']));
+						$row['width'] = $modSettings['max_image_width'];
+					}
+				}
+				elseif (!empty($modSettings['max_image_width']))
+				{
+					if ($row['height'] > $modSettings['max_image_height']) 
+					{
+						$row['width'] = floor($row['width'] * $modSettings['max_image_height'] / $row['height']);
+						$row['height'] = $modSettings['max_image_height'];
+					}
+				}
+				
+				$row['filename'] = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']);
+
+				// save for the template
+				$context['printattach'][$row['id_msg']][] = $row;
+			}
+		}
+	}
+	
 	// Set a canonical URL for this page.
 	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.0';
 }

+ 1 - 1
Sources/Profile.php

@@ -777,7 +777,7 @@ function loadCustomFields($memID, $area = 'summary')
 		// Parse BBCode
 		if ($row['bbc'])
 			$output_html = parse_bbc($output_html);
-		elseif($row['field_type'] == 'textarea')
+		elseif ($row['field_type'] == 'textarea')
 			// Allow for newlines at least
 			$output_html = strtr($output_html, array("\n" => '<br />'));
 

+ 4 - 3
Sources/ScheduledTasks.php

@@ -912,7 +912,7 @@ function ReduceMailQueue($number = false, $override_limit = false, $force_send =
 
 	// Now we know how many we're sending, let's send them.
 	$request = $smcFunc['db_query']('', '
-		SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html
+		SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html, time_sent
 		FROM {db_prefix}mail_queue
 		ORDER BY priority ASC, id_mail ASC
 		LIMIT ' . $number,
@@ -931,6 +931,7 @@ function ReduceMailQueue($number = false, $override_limit = false, $force_send =
 			'subject' => $row['subject'],
 			'headers' => $row['headers'],
 			'send_html' => $row['send_html'],
+			'time_sent' => $row['time_sent'],
 		);
 	}
 	$smcFunc['db_free_result']($request);
@@ -994,7 +995,7 @@ function ReduceMailQueue($number = false, $override_limit = false, $force_send =
 
 		// Hopefully it sent?
 		if (!$result)
-			$failed_emails[] = array($email['to'], $email['body'], $email['subject'], $email['headers'], $email['send_html']);
+			$failed_emails[] = array($email['to'], $email['body'], $email['subject'], $email['headers'], $email['send_html'], $email['time_sent']);
 	}
 
 	// Any emails that didn't send?
@@ -1024,7 +1025,7 @@ function ReduceMailQueue($number = false, $override_limit = false, $force_send =
 		// Add our email back to the queue, manually.
 		$smcFunc['db_insert']('insert',
 			'{db_prefix}mail_queue',
-			array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string'),
+			array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string', 'time_sent' => 'string'),
 			$failed_emails,
 			array('id_mail')
 		);

+ 2 - 1
Sources/Search.php

@@ -688,6 +688,7 @@ function PlushSearch2()
 	// The remaining words and phrases are all included.
 	$searchArray = array_merge($phraseArray, $wordArray);
 
+	$context['search_ignored'] = array();
 	// Trim everything and make sure there are no words that are the same.
 	foreach ($searchArray as $index => $value)
 	{
@@ -703,7 +704,7 @@ function PlushSearch2()
 		// Don't allow very, very short words.
 		elseif ($smcFunc['strlen']($value) < 2)
 		{
-			$context['search_errors']['search_string_small_words'] = true;
+			$context['search_ignored'][] = $value;
 			unset($searchArray[$index]);
 		}
 		else

+ 28 - 2
Sources/SendTopic.php

@@ -335,6 +335,29 @@ function ReportToModerator()
 	loadLanguage('Post');
 	loadTemplate('SendTopic');
 
+	addInlineJavascript('
+	var error_box = $("#error_box");
+	$("#report_comment").keyup(function() {
+		var post_too_long = $("#error_post_too_long");
+		if ($(this).val().length > 254)
+		{
+			if (post_too_long.length == 0)
+			{
+				error_box.show();
+				if ($.trim(error_box.html()) == \'\')
+					error_box.append("<ul id=\'error_list\'></ul>");
+
+				$("#error_list").append("<li id=\'error_post_too_long\' class=\'error\'>" + ' . JavaScriptEscape($txt['post_too_long']) . ' + "</li>");
+			}
+		}
+		else
+		{
+			post_too_long.remove();
+			if ($("#error_list li").length == 0)
+				error_box.hide();
+		}
+	});', true);
+
 	$context['comment_body'] = !isset($_POST['comment']) ? '' : trim($_POST['comment']);
 	$context['email_address'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
 
@@ -375,7 +398,10 @@ function ReportToModerator2()
 	// Make sure we have a comment and it's clean.
 	if (!isset($_POST['comment']) || $smcFunc['htmltrim']($_POST['comment']) === '')
 		$post_errors[] = 'no_comment';
-	$poster_comment = strtr($smcFunc['htmlspecialchars']($_POST['comment']), array("\r" => '', "\n" => '', "\t" => ''));
+	$poster_comment = strtr($smcFunc['htmlspecialchars']($_POST['comment']), array("\r" => '', "\t" => ''));
+
+	if ($smcFunc['strlen']($poster_comment) > 254)
+		$post_errors[] = 'post_too_long';
 
 	// Guests need to provide their address!
 	if ($user_info['is_guest'])
@@ -410,7 +436,7 @@ function ReportToModerator2()
 
 		$context['post_errors'] = array();
 		foreach ($post_errors as $post_error)
-			$context['post_errors'][] = $txt['error_' . $post_error];
+			$context['post_errors'][$post_error] = $txt['error_' . $post_error];
 
 		return ReportToModerator();
 	}

+ 1 - 1
Sources/Subs-BoardIndex.php

@@ -234,7 +234,7 @@ function getBoardIndex($boardIndexOptions)
 		if (!empty($settings['avatars_on_indexes']))
 		{
 			// Allow themers to show the latest poster's avatar along with the board
-			if(!empty($row_board['avatar']))
+			if (!empty($row_board['avatar']))
 			{
 				if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize')
 				{

+ 67 - 26
Sources/Subs-Membergroups.php

@@ -660,35 +660,59 @@ function cache_getMembergroupList()
  */
 function list_getMembergroups($start, $items_per_page, $sort, $membergroup_type)
 {
-	global $txt, $scripturl, $context, $settings, $smcFunc;
+	global $txt, $scripturl, $context, $settings, $smcFunc, $user_info;
 
 	$groups = array();
 
-	// Get the basic group data.
 	$request = $smcFunc['db_query']('substring_membergroups', '
-		SELECT id_group, group_name, min_posts, online_color, icons, 0 AS num_members
-		FROM {db_prefix}membergroups
-		WHERE min_posts ' . ($membergroup_type === 'post_count' ? '!=' : '=') . ' -1' . (allowedTo('admin_forum') ? '' : '
-			AND group_type != {int:is_protected}') . '
+		SELECT mg.id_group, mg.group_name, mg.min_posts, mg.description, mg.group_type, mg.online_color, mg.hidden,
+			mg.icons, IFNULL(gm.id_member, 0) AS can_moderate, 0 AS num_members
+		FROM {db_prefix}membergroups AS mg
+			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
+		WHERE mg.min_posts {raw:min_posts}' . (allowedTo('admin_forum') ? '' : '
+			AND mg.id_group != {int:mod_group}
+			AND mg.group_type != {int:is_protected}') . '
 		ORDER BY {raw:sort}',
 		array(
+			'current_member' => $user_info['id'],
+			'min_posts' => ($membergroup_type === 'post_count' ? '!= ' : '= ') . -1,
+			'mod_group' => 3,
 			'is_protected' => 1,
 			'sort' => $sort,
 		)
 	);
+
+	// Start collecting the data.
+	$groups = array();
+	$group_ids = array();
+	$context['can_moderate'] = allowedTo('manage_membergroups');
 	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		// We only list the groups they can see.
+		if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
+			continue;
+
+		$row['icons'] = explode('#', $row['icons']);
+
 		$groups[$row['id_group']] = array(
 			'id_group' => $row['id_group'],
 			'group_name' => $row['group_name'],
 			'min_posts' => $row['min_posts'],
+			'desc' => $row['description'],
 			'online_color' => $row['online_color'],
-			'icons' => $row['icons'],
+			'type' => $row['group_type'],
 			'num_members' => $row['num_members'],
+			'moderators' => array(),
+			'icons' => !empty($row['icons'][0]) && !empty($row['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/' . $row['icons'][1] . '" alt="*" />', $row['icons'][0]) : '',
 		);
+
+		$context['can_moderate'] |= $row['can_moderate'];
+		$group_ids[] = $row['id_group'];
+	}
 	$smcFunc['db_free_result']($request);
 
 	// If we found any membergroups, get the amount of members in them.
-	if (!empty($groups))
+	if (!empty($group_ids))
 	{
 		if ($membergroup_type === 'post_count')
 		{
@@ -698,7 +722,7 @@ function list_getMembergroups($start, $items_per_page, $sort, $membergroup_type)
 				WHERE id_post_group IN ({array_int:group_list})
 				GROUP BY id_post_group',
 				array(
-					'group_list' => array_keys($groups),
+					'group_list' => $group_ids,
 				)
 			);
 			while ($row = $smcFunc['db_fetch_assoc']($query))
@@ -714,30 +738,47 @@ function list_getMembergroups($start, $items_per_page, $sort, $membergroup_type)
 				WHERE id_group IN ({array_int:group_list})
 				GROUP BY id_group',
 				array(
-					'group_list' => array_keys($groups),
+					'group_list' => $group_ids,
 				)
 			);
 			while ($row = $smcFunc['db_fetch_assoc']($query))
 				$groups[$row['id_group']]['num_members'] += $row['num_members'];
 			$smcFunc['db_free_result']($query);
 
-			$query = $smcFunc['db_query']('', '
-				SELECT mg.id_group, COUNT(*) AS num_members
-				FROM {db_prefix}membergroups AS mg
-					INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string}
-						AND mem.id_group != mg.id_group
-						AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
-				WHERE mg.id_group IN ({array_int:group_list})
-				GROUP BY mg.id_group',
-				array(
-					'group_list' => array_keys($groups),
-					'blank_string' => '',
-				)
-			);
-			while ($row = $smcFunc['db_fetch_assoc']($query))
-				$groups[$row['id_group']]['num_members'] += $row['num_members'];
-			$smcFunc['db_free_result']($query);
+			// Only do additional groups if we can moderate...
+			if ($context['can_moderate'])
+			{
+				$query = $smcFunc['db_query']('', '
+					SELECT mg.id_group, COUNT(*) AS num_members
+					FROM {db_prefix}membergroups AS mg
+						INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string}
+							AND mem.id_group != mg.id_group
+							AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
+					WHERE mg.id_group IN ({array_int:group_list})
+					GROUP BY mg.id_group',
+					array(
+						'group_list' => $group_ids,
+						'blank_string' => '',
+					)
+				);
+				while ($row = $smcFunc['db_fetch_assoc']($query))
+					$groups[$row['id_group']]['num_members'] += $row['num_members'];
+				$smcFunc['db_free_result']($query);
+			}
 		}
+
+		$query = $smcFunc['db_query']('', '
+			SELECT mods.id_group, mods.id_member, mem.member_name, mem.real_name
+			FROM {db_prefix}group_moderators AS mods
+				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
+			WHERE mods.id_group IN ({array_int:group_list})',
+			array(
+				'group_list' => $group_ids,
+			)
+		);
+		while ($row = $smcFunc['db_fetch_assoc']($query))
+			$groups[$row['id_group']]['moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
+		$smcFunc['db_free_result']($query);
 	}
 
 	// Apply manual sorting if the 'number of members' column is selected.

+ 1 - 1
Sources/Subs.php

@@ -2902,7 +2902,7 @@ function setupThemeContext($forceload = false)
 	$context['show_pm_popup'] = $context['user']['popup_messages'] && !empty($options['popup_messages']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != 'pm');
 
 	// 2.1+: Add the PM popup here instead. Theme authors can still override it simply by editing/removing the 'fPmPopup' in the array.
-	if($context['show_pm_popup'])
+	if ($context['show_pm_popup'])
 		addInlineJavascript('
 		$(document).ready(function(){
 			new smc_Popup({

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

@@ -535,7 +535,7 @@ function template_main()
 					echo '
 											[<a href="', $scripturl, '?action=attachapprove;sa=approve;aid=', $attachment['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['approve'], '</a>]&nbsp;|&nbsp;[<a href="', $scripturl, '?action=attachapprove;sa=reject;aid=', $attachment['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['delete'], '</a>] ';
 				echo '
-											<br />', $attachment['size'], ($attachment['is_image'] ? ', ' . $attachment['real_width'] . 'x' . $attachment['real_height'] . '<br />' . $txt['attach_viewed'] : '<br />' . $txt['attach_downloaded']) . ' ' . $attachment['downloads'] . ' ' . $txt['attach_times'] . '
+											<br />', $attachment['size'], ($attachment['is_image'] ? ', ' . $attachment['real_width'] . 'x' . $attachment['real_height'] . '<br />' . sptrinf($txt['attach_viewed'], $attachment['downloads']) : '<br />' . sptrinf($txt['attach_downloaded'], $attachment['downloads'])), '
 										</div>';
 
 				echo '
@@ -946,4 +946,4 @@ function template_main()
 
 }
 
-?>
+?>

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

@@ -172,7 +172,7 @@ function template_maintenance()
 							<dd><select name="to">
 								<option value="0">', $txt['attachment_transfer_select'], '</option>';
 
-	foreach($context['attach_dirs'] as $id => $dir)
+	foreach ($context['attach_dirs'] as $id => $dir)
 		echo '
 								<option value="', $id, '">', $dir, '</option>';
 	echo '

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

@@ -368,7 +368,7 @@ function template_folder()
 		}
 		elseif ($context['can_send_pm'] && !$message['is_message_author'] && !$message['member']['is_guest'])
 		{
-			if(!empty($modSettings['onlineEnable']))
+			if (!empty($modSettings['onlineEnable']))
 				echo '
 				<li class="poster_online"><a href="', $scripturl,'?action=pm;sa=send;u=', $message['member']['id'], '" title="', $message['member']['online']['member_online_text'], '">', $txt['send_message'], ' <img src="'. $message['member']['online']['image_href']. '" alt="" /></a></li>';
 			else

+ 75 - 36
Themes/default/Printpage.template.php

@@ -12,7 +12,10 @@
 
 function template_print_above()
 {
-	global $context, $settings, $options, $txt;
+	global $context, $txt, $topic, $scripturl;
+	
+	$url_text = $scripturl . '?action=printpage;topic=' . $topic . '.0';
+	$url_images = $url_text . ';images';
 
 	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml"', $context['right_to_left'] ? ' dir="rtl"' : '', '>
@@ -22,89 +25,95 @@ function template_print_above()
 		<link rel="canonical" href="', $context['canonical_url'], '" />
 		<title>', $txt['print_page'], ' - ', $context['topic_subject'], '</title>
 		<style type="text/css">
-			body, a
-			{
+			body, a {
 				color: #000;
 				background: #fff;
 			}
-			body, td, .normaltext
-			{
+			body, td, .normaltext {
 				font-family: Verdana, arial, helvetica, serif;
 				font-size: small;
 			}
-			h1#title
-			{
+			h1#title {
 				font-size: large;
 				font-weight: bold;
 			}
-			h2#linktree
-			{
+			h2#linktree {
 				margin: 1em 0 2.5em 0;
 				font-size: small;
 				font-weight: bold;
 			}
-			dl#posts
-			{
+			dl#posts {
 				width: 90%;
 				margin: 0;
 				padding: 0;
 				list-style: none;
 			}
-			div.postheader, #poll_data
-			{
+			div.postheader, #poll_data {
 				border: solid #000;
 				border-width: 1px 0;
 				padding: 4px 0;
 			}
-			div.postbody
-			{
+			div.postbody {
 				margin: 1em 0 2em 2em;
 			}
-			table
-			{
+			table {
 				empty-cells: show;
 			}
-			blockquote, code
-			{
+			blockquote, code {
 				border: 1px solid #000;
 				margin: 3px;
 				padding: 1px;
 				display: block;
 			}
-			code
-			{
+			code {
 				font: x-small monospace;
 			}
-			blockquote
-			{
+			blockquote {
 				font-size: x-small;
 			}
-			.smalltext, .quoteheader, .codeheader
-			{
+			.smalltext, .quoteheader, .codeheader {
 				font-size: x-small;
 			}
-			.largetext
-			{
+			.largetext {
 				font-size: large;
 			}
-			.centertext
-			{
+			.centertext {
 				text-align: center;
 			}
-			hr
-			{
+			hr {
 				height: 1px;
 				border: 0;
 				color: black;
 				background-color: black;
 			}
-			.voted
-			{
+			.voted {
 				font-weight: bold;
 			}
+			@media print {
+				.print_options {
+					display:none;
+				}
+			}
+			@media screen {
+				.print_options {
+					margin:1em;
+				}
+			}
 		</style>
 	</head>
 	<body>
+		<div class="print_options">';
+
+	// which option is set, text or text&images
+	if (isset($_REQUEST['images']))
+		echo '
+			<a href="', $url_text, '">', $txt['print_page_text'], '</a> | <strong><a href="', $url_images, '">', $txt['print_page_images'], '</a></strong>';
+	else
+		echo '
+			<strong><a href="', $url_text, '">', $txt['print_page_text'], '</a></strong> | <a href="', $url_images, '">', $txt['print_page_images'], '</a>';
+
+	echo '
+		</div>
 		<h1 id="title">', $context['forum_name_html_safe'], '</h1>
 		<h2 id="linktree">', $context['category_name'], ' => ', (!empty($context['parent_boards']) ? implode(' => ', $context['parent_boards']) . ' => ' : ''), $context['board_name'], ' => ', $txt['topic_started'], ': ', $context['poster_name'], ' ', $txt['search_on'], ' ', $context['post_time'], '</h2>
 		<div id="posts">';
@@ -112,7 +121,7 @@ function template_print_above()
 
 function template_main()
 {
-	global $context, $settings, $options, $txt;
+	global $context, $options, $txt, $scripturl, $topic;
 
 	if (!empty($context['poll']))
 	{
@@ -132,19 +141,49 @@ function template_main()
 	}
 
 	foreach ($context['posts'] as $post)
+	{
 		echo '
 			<div class="postheader">
 				', $txt['title'], ': <strong>', $post['subject'], '</strong><br />
 				', $txt['post_by'], ': <strong>', $post['member'], '</strong> ', $txt['search_on'], ' <strong>', $post['time'], '</strong>
 			</div>
 			<div class="postbody">
-				', $post['body'], '
+				', $post['body'];
+				
+		// Show attachment images
+		if (isset($_GET['images']) && !empty($context['printattach'][$post['id_msg']]))
+		{
+			echo '
+				<hr />';
+			
+			foreach ($context['printattach'][$post['id_msg']] as $attach)
+				echo '
+					<img width="' . $attach['width'] . '" height="' . $attach['height'] . '" src="', $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attach['id_attach'] . '" alt="" />';
+		}
+			
+		echo '
 			</div>';
+	}
 }
 
 function template_print_below()
 {
-	global $context, $settings, $options;
+	global $topic, $txt, $scripturl;
+	
+	$url_text = $scripturl . '?action=printpage;topic=' . $topic . '.0';
+	$url_images = $url_text . ';images';
+	
+	echo '
+		</div>
+		<div class="print_options">';
+
+	// Show the text / image links
+	if (isset($_GET['images']))
+		echo '
+			<a href="', $url_text, '">', $txt['print_page_text'], '</a> | <strong><a href="', $url_images, '">', $txt['print_page_images'], '</a></strong>';
+	else
+		echo '
+			<strong><a href="', $url_text, '">', $txt['print_page_text'], '</a></strong> | <a href="', $url_images, '">', $txt['print_page_images'], '</a>';
 
 	echo '
 		</div>

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

@@ -26,6 +26,10 @@ function template_main()
 		echo '
 		<p class="errorbox">', implode('<br />', $context['search_errors']['messages']), '</p>';
 
+	if (!empty($context['search_ignored']))
+		echo '
+		<p class="noticebox">', $txt['search_warning_ignored_word' . (count($context['search_ignored']) == 1 ? '' : 's')], ': ', implode(', ', $context['search_ignored']), '</p>';
+
 	// Simple Search?
 	if ($context['simple_search'])
 	{
@@ -238,7 +242,7 @@ function template_results()
 {
 	global $context, $settings, $options, $txt, $scripturl, $message;
 
-	if (isset($context['did_you_mean']) || empty($context['topics']))
+	if (isset($context['did_you_mean']) || empty($context['topics']) || !empty($context['search_ignored']))
 	{
 		echo '
 	<div id="search_results">
@@ -254,6 +258,10 @@ function template_results()
 			echo '
 			<p>', $txt['search_did_you_mean'], ' <a href="', $scripturl, '?action=search2;params=', $context['did_you_mean_params'], '">', $context['did_you_mean'], '</a>.</p>';
 
+		if (!empty($context['search_ignored']))
+			echo '
+			<p>', $txt['search_warning_ignored_word' . (count($context['search_ignored']) == 1 ? '' : 's')], ': ', implode(', ', $context['search_ignored']), '</p>';
+
 		echo '
 			<form action="', $scripturl, '?action=search2" method="post" accept-charset="', $context['character_set'], '">
 				<dl class="settings">

+ 13 - 8
Themes/default/SendTopic.template.php

@@ -209,18 +209,23 @@ function template_report()
 
 	if (!empty($context['post_errors']))
 	{
-		echo '
-				<div class="errorbox">
-					<ul>';
+	echo '
+				<div id="error_box" class="errorbox">
+					<ul id="error_list">';
 
-		foreach ($context['post_errors'] as $error)
+		foreach ($context['post_errors'] as $key => $error)
 			echo '
-						<li class="error">', $error, '</li>';
+						<li id="error_', $key, '" class="error">', $error, '</li>';
 
 		echo '
-					</ul>
-				</div>';
+					</ul>';
 	}
+	else
+		echo '
+				<div style="display:none" id="error_box" class="errorbox">';
+
+		echo '
+				</div>';
 
 	echo '
 						<p class="noticebox">', $txt['report_to_mod_func'], '</p>
@@ -243,7 +248,7 @@ function template_report()
 								<label for="report_comment">', $txt['enter_comment'], '</label>:
 							</dt>
 							<dd>
-								<input type="text" id="report_comment" name="comment" size="50" value="', $context['comment_body'], '" maxlength="255" />
+								<textarea type="text" id="report_comment" name="comment" rows="5">', $context['comment_body'], '</textarea>
 							</dd>';
 
 	if ($context['require_verification'])

+ 3 - 0
Themes/default/css/index.css

@@ -2723,6 +2723,9 @@ dl.send_mail dd {
 #report_topic dl.settings dd {
 	width: 79%;
 }
+#report_comment {
+	width: 70%;
+}
 
 /* Styles for the split topic section.
 ---------------------------------------------------- */

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

@@ -97,7 +97,7 @@ function template_html_above()
 	<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21" />';
 
 	// Save some database hits, if a width for multiple wrappers is set in admin.
-	if(!empty($settings['forum_width']))
+	if (!empty($settings['forum_width']))
 		echo '
 	<style type="text/css">#wrapper, .frame {width: ', $settings['forum_width'], ';}</style>';
 

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

@@ -315,7 +315,6 @@ $txt['support_latest_fetch'] = 'Retrieving support information...';
 $txt['edit_permissions_info'] = 'Change restrictions and available features, globally or in specific boards.';
 $txt['membergroups_members'] = 'Regular Members';
 $txt['membergroups_guests'] = 'Guests';
-$txt['membergroups_guests_na'] = 'n/a';
 $txt['membergroups_add_group'] = 'Add group';
 $txt['membergroups_permissions'] = 'Permissions';
 

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

@@ -25,6 +25,7 @@ $txt['not_a_user'] = 'The user whose profile you are trying to view does not exi
 $txt['not_a_topic'] = 'This topic doesn\'t exist on this board.';
 $txt['not_approved_topic'] = 'This topic has not been approved yet.';
 $txt['email_in_use'] = 'That email address (%1$s) is being used by a registered member already. If you feel this is a mistake, go to the login page and use the password reminder with that address.';
+$txt['attachments_no_write'] = 'The attachments directory is not writable';
 
 $txt['didnt_select_vote'] = 'You didn\'t select a vote option.';
 $txt['poll_error'] = 'Either that poll doesn\'t exist, the poll has been locked, or you tried to vote twice.';
@@ -240,6 +241,8 @@ $txt['error_no_question'] = 'No question was filled in for this poll.';
 $txt['error_no_message'] = 'The message body was left empty.';
 $txt['error_long_message'] = 'The message exceeds the maximum allowed length (%1$d characters).';
 $txt['error_no_comment'] = 'The comment field was left empty.';
+// duplicate of post_too_long in Post.{language}.php
+$txt['error_post_too_long'] = 'Your message is too long. Please go back and shorten it, then try again.';
 $txt['error_session_timeout'] = 'Your session timed out while posting. Please try to re-submit your message.';
 $txt['error_no_to'] = 'No recipients specified.';
 $txt['error_bad_to'] = 'One or more \'to\'-recipients could not be found.';

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

@@ -13,6 +13,7 @@ $txt['membergroups_modify'] = 'Modify';
 $txt['membergroups_add_group'] = 'Add group';
 $txt['membergroups_regular'] = 'Regular groups';
 $txt['membergroups_post'] = 'Post count based groups';
+$txt['membergroups_guests_na'] = 'n/a';
 
 $txt['membergroups_group_name'] = 'Membergroup name';
 $txt['membergroups_new_board'] = 'Visible Boards';

+ 1 - 1
Themes/default/languages/ManageSettings.english.php

@@ -305,7 +305,7 @@ $txt['core_settings_item_dr_desc'] = 'Enabling this feature will allow users to
 $txt['core_settings_item_cp'] = 'Advanced Profile Fields';
 $txt['core_settings_item_cp_desc'] = 'This enables you to hide standard profile fields, add profile fields to registration, and create new profile fields for your forum.';
 $txt['core_settings_item_ih'] = 'Integration Hooks Management';
-$txt['core_settings_item_ih_desc'] = 'This feature allows you to enable or disable any intergartion hooks added by modifications. Changing hooks can prevent your forum from working properly, so use this feature only if you know what you are doing.';
+$txt['core_settings_item_ih_desc'] = 'This feature allows you to enable or disable any integration hooks added by modifications. Changing hooks can prevent your forum from working properly, so use this feature only if you know what you are doing.';
 $txt['core_settings_item_k'] = 'Karma';
 $txt['core_settings_item_k_desc'] = 'Karma is a feature that shows the popularity of a member. Members, if allowed, can \'applaud\' or \'smite\' other members, which is how their popularity is calculated.';
 $txt['core_settings_item_ml'] = 'Moderation, Administration and User Logs';

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

@@ -67,6 +67,7 @@ $txt['right_align'] = 'Right align';
 $txt['superscript'] = 'Superscript';
 $txt['subscript'] = 'Subscript';
 $txt['table_tr'] = 'Insert table row';
+// post_too_long seems unused (duplicate in Errors: error_post_too_long
 $txt['post_too_long'] = 'Your message is too long. Please go back and shorten it, then try again.';
 $txt['horizontal_rule'] = 'Horizontal Rule';
 $txt['font_size'] = 'Font size';

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

@@ -101,6 +101,8 @@ $txt['search_error_max_percentage'] = 'Invalid percentage of words to be skipped
 $txt['error_string_too_long'] = 'Search string must be less than %1$d characters long.';
 
 $txt['search_adjust_query'] = 'Adjust Search Parameters';
+$txt['search_warning_ignored_word'] = 'The following term has been ignored in your search because too short';
+$txt['search_warning_ignored_words'] = 'The following terms have been ignored in your search because too short';
 $txt['search_adjust_submit'] = 'Revise Search';
 $txt['search_did_you_mean'] = 'You may have meant to search for';
 

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

@@ -364,6 +364,8 @@ $txt['you_have_many_msgs'] = 'You\'ve got %2$d messages... Click <a href="%1$s">
 $txt['total_boards'] = 'Total Boards';
 
 $txt['print_page'] = 'Print Page';
+$txt['print_page_text'] = 'Text only';
+$txt['print_page_images'] = 'Text with Images';
 
 $txt['valid_email'] = 'This must be a valid email address.';
 
@@ -564,9 +566,8 @@ $txt['mlist_search_results'] = 'Search results for';
 $txt['mlist_search_by'] = 'Search by %1$s';
 $txt['mlist_menu_view'] = 'View the memberlist';
 
-$txt['attach_downloaded'] = 'downloaded';
-$txt['attach_viewed'] = 'viewed';
-$txt['attach_times'] = 'times';
+$txt['attach_downloaded_many'] = 'downloaded %1$d times';
+$txt['attach_viewed_many'] = 'viewed %1$d times';
 
 $txt['settings'] = 'Settings';
 $txt['never'] = 'Never';
@@ -829,4 +830,4 @@ $txt['debug_tokens'] = 'Tokens: ';
 $txt['debug_browser'] = 'Browser ID: ';
 $txt['debug_hooks'] = 'Hooks called: ';
 
-?>
+?>

+ 18 - 10
index.php

@@ -44,19 +44,10 @@ require_once(dirname(__FILE__) . '/Settings.php');
 if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
 	$cachedir = $boarddir . '/cache';
 
-// And important includes.
+// Without those we can't go anywhere
 require_once($sourcedir . '/QueryString.php');
-require_once($sourcedir . '/Session.php');
 require_once($sourcedir . '/Subs.php');
-require_once($sourcedir . '/Errors.php');
-require_once($sourcedir . '/Logging.php');
 require_once($sourcedir . '/Load.php');
-require_once($sourcedir . '/Security.php');
-require_once($sourcedir . '/Class-BrowserDetect.php');
-
-// Using an pre-PHP 5.1 version?
-if (version_compare(PHP_VERSION, '5.1', '<'))
-	require_once($sourcedir . '/Subs-Compat.php');
 
 // If $maintenance is set specifically to 2, then we're upgrading or something.
 if (!empty($maintenance) && $maintenance == 2)
@@ -84,6 +75,23 @@ if (isset($_GET['scheduled']))
 	require_once($sourcedir . '/ScheduledTasks.php');
 	AutoTask();
 }
+// Displaying attached avatars
+elseif (isset($_GET['action']) && $_GET['action'] == 'dlattach' && isset($_GET['type']) && $_GET['type'] == 'avatar')
+{
+	require_once($sourcedir. '/Avatar.php');
+	showAvatar();
+}
+
+// And important includes.
+require_once($sourcedir . '/Session.php');
+require_once($sourcedir . '/Errors.php');
+require_once($sourcedir . '/Logging.php');
+require_once($sourcedir . '/Security.php');
+require_once($sourcedir . '/Class-BrowserDetect.php');
+
+// Using an pre-PHP 5.1 version?
+if (version_compare(PHP_VERSION, '5.1', '<'))
+	require_once($sourcedir . '/Subs-Compat.php');
 
 // Check if compressed output is enabled, supported, and not already being done.
 if (!empty($modSettings['enableCompressedOutput']) && !headers_sent())

+ 1 - 1
other/install.php

@@ -1214,7 +1214,7 @@ function DatabasePopulation()
 		$smcFunc['db_optimize_table']($table) != -1 or $db_messed = true;
 
 		// Optimizing one sqlite table, optimizes them all
-		if($db_type == 'sqlite')
+		if ($db_type == 'sqlite')
 			break;
 
 		if (!empty($db_messed))