Sfoglia il codice sorgente

Merge pull request #15 from emanuele45/master

I did it! :P
Spuds 12 anni fa
parent
commit
1401bbec23

+ 1 - 1
Sources/Display.php

@@ -499,7 +499,7 @@ function Display()
 	// Set the topic's information for the template.
 	// Set the topic's information for the template.
 	$context['subject'] = $topicinfo['subject'];
 	$context['subject'] = $topicinfo['subject'];
 	$context['num_views'] = $topicinfo['num_views'];
 	$context['num_views'] = $topicinfo['num_views'];
-	$context['mark_unread_time'] = $topicinfo['new_from'];
+	$context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $topicinfo['new_from'];
 
 
 	// Set a canonical URL for this page.
 	// Set a canonical URL for this page.
 	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];
 	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];

+ 261 - 12
Sources/ManageNews.php

@@ -85,7 +85,7 @@ function ManageNews()
  */
  */
 function EditNews()
 function EditNews()
 {
 {
-	global $txt, $modSettings, $context, $sourcedir, $user_info;
+	global $txt, $modSettings, $context, $sourcedir, $user_info, $scripturl;
 	global $smcFunc;
 	global $smcFunc;
 
 
 	require_once($sourcedir . '/Subs-Post.php');
 	require_once($sourcedir . '/Subs-Post.php');
@@ -131,16 +131,157 @@ function EditNews()
 		logAction('news');
 		logAction('news');
 	}
 	}
 
 
+	// We're going to want this for making our list.
+	require_once($sourcedir . '/Subs-List.php');
+
+	$context['page_title'] = $txt['admin_edit_news'];
+
+	// Use the standard templates for showing this.
+	$listOptions = array(
+		'id' => 'news_lists',
+		'get_items' => array(
+			'function' => 'list_getNews',
+		),
+		'columns' => array(
+			'news' => array(
+				'header' => array(
+					'value' => $txt['admin_edit_news'],
+				),
+				'data' => array(
+					'function' => create_function('$news', '
+
+						if (is_numeric($news[\'id\']))
+							return \'<textarea rows="3" cols="65" name="news[]" style="\' . (isBrowser(\'is_ie8\') ? \'width: 635px; max-width: 85%; min-width: 85%\' : \'width: 85%\') . \';">\' . $news[\'unparsed\'] . \'</textarea>
+							<div style="float:right" id="preview_\' . $news[\'id\'] . \'"></div>\';
+						else
+							return $news[\'unparsed\'];
+					'),
+					'style' => 'width: 50%;',
+				),
+			),
+			'preview' => array(
+				'header' => array(
+					'value' => $txt['preview'],
+				),
+				'data' => array(
+					'function' => create_function('$news', '
+
+						return \'<div id="box_preview_\' . $news[\'id\'] . \'" style="overflow: auto; width: 100%; height: 10ex;">\' . $news[\'parsed\'] . \'</div>\';
+					'),
+					'style' => 'width: 45%;',
+				),
+			),
+			'check' => array(
+				'header' => array(
+					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
+				),
+				'data' => array(
+					'function' => create_function('$news', '
+
+						if (is_numeric($news[\'id\']))
+							return \'<input type="checkbox" name="remove[]" value="\' . $news[\'id\'] . \'" class="input_check" />\';
+						else
+							return \'\';
+					'),
+					'style' => 'text-align: center',
+				),
+			),
+		),
+		'form' => array(
+			'href' => $scripturl . '?action=admin;area=news;sa=editnews',
+			'hidden_fields' => array(
+				$context['session_var'] => $context['session_id'],
+			),
+		),
+		'additional_rows' => array(
+			array(
+				'position' => 'bottom_of_list',
+				'value' => '
+				<span id="moreNewsItems_link" style="display: none;">[<a href="javascript:void(0);" onclick="addNewsItem(); return false;">' . $txt['editnews_clickadd'] . '</a>]</span>
+				<script type="text/javascript"><!-- // --><![CDATA[
+					document.getElementById(\'list_news_lists_last\').style.display = "none";
+					document.getElementById("moreNewsItems_link").style.display = "";
+					var last_preview = 0;
+					$(document).ready(function () {
+						$("div[id ^= \'preview_\']").each(function () {
+							var preview_id = $(this).attr(\'id\').split(\'_\')[1];
+							if (last_preview < preview_id)
+								last_preview = preview_id;
+							make_preview_btn(preview_id);
+						});
+					});
+
+					function make_preview_btn (preview_id)
+					{
+						$("#preview_" + preview_id).css({cursor: \'hand\', cursor: \'pointer\', });
+						$("#preview_" + preview_id).text(\'' . $txt['preview'] . '\').click(function () {
+							$.ajax({
+								type: "POST",
+								url: "' . $scripturl . '?action=xmlhttp;sa=previews;xml",
+								data: {item: "newspreview", news: $("#preview_" + preview_id).prev().val()},
+								context: document.body,
+								success: function(request){
+									if ($(request).find("error").text() == \'\')
+										$(document).find("#box_preview_" + preview_id).html($(request).text());
+									else
+										$(document).find("#box_preview_" + preview_id).text(\'' . $txt['news_error_no_news'] . '\');
+								},
+							});
+						});
+					}
+
+					function addNewsItem ()
+					{
+						last_preview++;
+						$("#list_news_lists_last").before(' . javaScriptEscape('
+						<tr class="windowbg') . ' + (last_preview % 2 == 0 ? \'\' : \'2\') + ' . javaScriptEscape('">
+							<td style="width: 50%;">
+									<textarea rows="3" cols="65" name="news[]" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 85%; min-width: 85%' : 'width: 85%') . ';"></textarea>
+									<div style="float:right" id="preview_') . ' + last_preview + ' . javaScriptEscape('"></div>
+							</td>
+							<td style="width: 45%;">
+								<div id="box_preview_') . ' + last_preview + ' . javaScriptEscape('" style="overflow: auto; width: 100%; height: 10ex;"></div>
+							</td>
+							<td></td>
+						</tr>') . ');
+						make_preview_btn(last_preview);
+					}
+					
+				// ]]></script>
+				<input type="submit" name="save_items" value="' . $txt['save'] . '" class="button_submit" /> <input type="submit" name="delete_selection" value="' . $txt['editnews_remove_selected'] . '" onclick="return confirm(\'' . $txt['editnews_remove_confirm'] . '\');" class="button_submit" />',
+				'align' => 'right',
+			),
+		),
+	);
+
+	// Create the request list.
+	createList($listOptions);
+
+	$context['sub_template'] = 'show_list';
+	$context['default_list'] = 'news_lists';
+}
+
+function list_getNews()
+{
+	global $modSettings;
+
+	$admin_current_news = array();
 	// Ready the current news.
 	// Ready the current news.
 	foreach (explode("\n", $modSettings['news']) as $id => $line)
 	foreach (explode("\n", $modSettings['news']) as $id => $line)
-		$context['admin_current_news'][$id] = array(
+		$admin_current_news[$id] = array(
 			'id' => $id,
 			'id' => $id,
 			'unparsed' => un_preparsecode($line),
 			'unparsed' => un_preparsecode($line),
 			'parsed' => preg_replace('~<([/]?)form[^>]*?[>]*>~i', '<em class="smalltext">&lt;$1form&gt;</em>', parse_bbc($line)),
 			'parsed' => preg_replace('~<([/]?)form[^>]*?[>]*>~i', '<em class="smalltext">&lt;$1form&gt;</em>', parse_bbc($line)),
 		);
 		);
 
 
-	$context['sub_template'] = 'edit_news';
-	$context['page_title'] = $txt['admin_edit_news'];
+	$admin_current_news['last'] = array(
+		'id' => 'last',
+		'unparsed' => '<div id="moreNewsItems"></div>
+		<noscript><textarea rows="3" cols="65" name="news[]" style="' . (isBrowser('is_ie8') ? 'width: 635px; max-width: 85%; min-width: 85%' : 'width: 85%') . ';"></textarea></noscript>',
+		'parsed' => '<div id="moreNewsItems_preview"></div>',
+	);
+
+	return $admin_current_news;
 }
 }
 
 
 /**
 /**
@@ -268,6 +409,63 @@ function SelectMailingMembers()
 	$context['can_send_pm'] = allowedTo('pm_send');
 	$context['can_send_pm'] = allowedTo('pm_send');
 }
 }
 
 
+/**
+ * Prepare subject and message of an email for the preview box
+ * Used in ComposeMailing and RetrievePreview (Xml.php)
+ */
+function prepareMailingForPreview ()
+{
+	global $context, $smcFunc, $modSettings, $scripturl, $user_info, $txt;
+	loadLanguage('Errors');
+
+	$processing = array('preview_subject' => 'subject', 'preview_message' => 'message');
+
+	// Use the default time format.
+	$user_info['time_format'] = $modSettings['time_format'];
+
+	$variables = array(
+		'{$board_url}',
+		'{$current_time}',
+		'{$latest_member.link}',
+		'{$latest_member.id}',
+		'{$latest_member.name}'
+	);
+
+	$html = $context['send_html'];
+
+	// We might need this in a bit
+	$cleanLatestMember = empty($context['send_html']) || $context['send_pm'] ? un_htmlspecialchars($modSettings['latestRealName']) : $modSettings['latestRealName'];
+
+	foreach ($processing as $key => $post)
+	{
+		$context[$key] = !empty($_REQUEST[$post]) ? $_REQUEST[$post] : '';
+
+		if (empty($context[$key]) && empty($_REQUEST['xml']))
+			$context['post_error']['messages'][] = $txt['error_no_' . $post];
+		elseif (!empty($_REQUEST['xml']))
+			continue;
+
+		preparsecode($context[$key]);
+		if ($html)
+		{
+			$enablePostHTML = $modSettings['enablePostHTML'];
+			$modSettings['enablePostHTML'] = $context['send_html'];
+			$context[$key] = parse_bbc($context[$key]);
+			$modSettings['enablePostHTML'] = $enablePostHTML;
+		}
+
+		// Replace in all the standard things.
+		$context[$key] = str_replace($variables,
+			array(
+				!empty($context['send_html']) ? '<a href="' . $scripturl . '">' . $scripturl . '</a>' : $scripturl,
+				timeformat(forum_time(), false),
+				!empty($context['send_html']) ? '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $cleanLatestMember . '</a>' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . ']' . $cleanLatestMember . '[/url]' : $cleanLatestMember),
+				$modSettings['latestMember'],
+				$cleanLatestMember
+			), $context[$key]);
+	}
+}
+
 /**
 /**
  * Shows a form to edit a forum mailing and its recipients.
  * Shows a form to edit a forum mailing and its recipients.
  * Called by ?action=admin;area=news;sa=mailingcompose.
  * Called by ?action=admin;area=news;sa=mailingcompose.
@@ -278,7 +476,49 @@ function SelectMailingMembers()
  */
  */
 function ComposeMailing()
 function ComposeMailing()
 {
 {
-	global $txt, $sourcedir, $context, $smcFunc;
+	global $txt, $sourcedir, $context, $smcFunc, $scripturl, $modSettings;
+
+	// Setup the template!
+	$context['page_title'] = $txt['admin_newsletters'];
+	$context['sub_template'] = 'email_members_compose';
+
+	$context['subject'] = !empty($_POST['subject']) ? $_POST['subject'] : htmlspecialchars($context['forum_name'] . ': ' . $txt['subject']);
+	$context['message'] = !empty($_POST['message']) ? $_POST['message'] : htmlspecialchars($txt['message'] . "\n\n" . $txt['regards_team'] . "\n\n" . '{$board_url}');
+
+	// Needed for the WYSIWYG editor.
+	require_once($sourcedir . '/Subs-Editor.php');
+
+	// Now create the editor.
+	$editorOptions = array(
+		'id' => 'message',
+		'value' => $context['message'],
+		'height' => '175px',
+		'width' => '100%',
+		'labels' => array(
+			'post_button' => $txt['sendtopic_send'],
+		),
+		'preview_type' => 2,
+	);
+	create_control_richedit($editorOptions);
+	// Store the ID for old compatibility.
+	$context['post_box_name'] = $editorOptions['id'];
+
+	if (isset($context['preview']))
+	{
+		require_once($sourcedir . '/Subs-Post.php');
+		$context['recipients']['members'] = !empty($_POST['members']) ? explode(',', $_POST['members']) : array();
+		$context['recipients']['exclude_members'] = !empty($_POST['exclude_members']) ? explode(',', $_POST['exclude_members']) : array();
+		$context['recipients']['groups'] = !empty($_POST['groups']) ? explode(',', $_POST['groups']) : array();
+		$context['recipients']['exclude_groups'] = !empty($_POST['exclude_groups']) ? explode(',', $_POST['exclude_groups']) : array();
+		$context['recipients']['emails'] = !empty($_POST['emails']) ? explode(';', $_POST['emails']) : array();
+		$context['email_force'] = !empty($_POST['email_force']) ? 1 : 0;
+		$context['total_emails'] = !empty($_POST['total_emails']) ? (int) $_POST['total_emails'] : 0;
+		$context['max_id_member'] = !empty($_POST['max_id_member']) ? (int) $_POST['max_id_member'] : 0;
+		$context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0;
+		$context['send_html'] = !empty($_POST['send_html']) ? '1' : '0';
+
+		return prepareMailingForPreview();
+	}
 
 
 	// Start by finding any members!
 	// Start by finding any members!
 	$toClean = array();
 	$toClean = array();
@@ -421,13 +661,6 @@ function ComposeMailing()
 	// Clean up the arrays.
 	// Clean up the arrays.
 	$context['recipients']['members'] = array_unique($context['recipients']['members']);
 	$context['recipients']['members'] = array_unique($context['recipients']['members']);
 	$context['recipients']['exclude_members'] = array_unique($context['recipients']['exclude_members']);
 	$context['recipients']['exclude_members'] = array_unique($context['recipients']['exclude_members']);
-
-	// Setup the template!
-	$context['page_title'] = $txt['admin_newsletters'];
-	$context['sub_template'] = 'email_members_compose';
-
-	$context['default_subject'] = htmlspecialchars($context['forum_name'] . ': ' . $txt['subject']);
-	$context['default_message'] = htmlspecialchars($txt['message'] . "\n\n" . $txt['regards_team'] . "\n\n" . '{$board_url}');
 }
 }
 
 
 /**
 /**
@@ -445,6 +678,12 @@ function SendMailing($clean_only = false)
 	global $txt, $sourcedir, $context, $smcFunc;
 	global $txt, $sourcedir, $context, $smcFunc;
 	global $scripturl, $modSettings, $user_info;
 	global $scripturl, $modSettings, $user_info;
 
 
+	if (isset($_POST['preview']))
+	{
+		$context['preview'] = true;
+		return ComposeMailing();
+	}
+
 	// How many to send at once? Quantity depends on whether we are queueing or not.
 	// How many to send at once? Quantity depends on whether we are queueing or not.
 	$num_at_once = empty($modSettings['mail_queue']) ? 60 : 1000;
 	$num_at_once = empty($modSettings['mail_queue']) ? 60 : 1000;
 
 
@@ -537,6 +776,10 @@ function SendMailing($clean_only = false)
 
 
 	require_once($sourcedir . '/Subs-Post.php');
 	require_once($sourcedir . '/Subs-Post.php');
 
 
+	// We are relying too much on writing to superglobals...
+	$_POST['subject'] = !empty($_POST['subject']) ? $_POST['subject'] : '';
+	$_POST['message'] = !empty($_POST['message']) ? $_POST['message'] : '';
+
 	// Save the message and its subject in $context
 	// Save the message and its subject in $context
 	$context['subject'] = htmlspecialchars($_POST['subject']);
 	$context['subject'] = htmlspecialchars($_POST['subject']);
 	$context['message'] = htmlspecialchars($_POST['message']);
 	$context['message'] = htmlspecialchars($_POST['message']);
@@ -558,6 +801,12 @@ function SendMailing($clean_only = false)
 		}
 		}
 	}
 	}
 
 
+	if (empty($_POST['message']) || empty($_POST['subject']))
+	{
+		$context['preview'] = true;
+		return ComposeMailing();
+	}
+
 	// Use the default time format.
 	// Use the default time format.
 	$user_info['time_format'] = $modSettings['time_format'];
 	$user_info['time_format'] = $modSettings['time_format'];
 
 

+ 1 - 1
Sources/MessageIndex.php

@@ -443,7 +443,7 @@ function MessageIndex()
 
 
 				// We can't pass start by reference.
 				// We can't pass start by reference.
 				$start = -1;
 				$start = -1;
-				$pages .= constructPageIndex($scripturl . '?topic=' . $row['id_topic'] . '.%1$d', $start, $row['num_replies'] + 1, $context['messages_per_page'], true);
+				$pages .= constructPageIndex($scripturl . '?topic=' . $row['id_topic'] . '.%1$d', $start, $row['num_replies'] + 1, $context['messages_per_page'], true, false);
 
 
 				// If we can use all, show all.
 				// If we can use all, show all.
 				if (!empty($modSettings['enableAllMessages']) && $row['num_replies'] + 1 < $modSettings['enableAllMessages'])
 				if (!empty($modSettings['enableAllMessages']) && $row['num_replies'] + 1 < $modSettings['enableAllMessages'])

+ 68 - 57
Sources/ModerationCenter.php

@@ -1974,72 +1974,83 @@ function ModifyWarningTemplate()
 		$_POST['template_title'] = trim($_POST['template_title']);
 		$_POST['template_title'] = trim($_POST['template_title']);
 
 
 		// Need something in both boxes.
 		// Need something in both boxes.
-		if (empty($_POST['template_body']) || empty($_POST['template_title']))
-			fatal_error($txt['mc_warning_template_error_empty']);
-
-		// Safety first.
-		$_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']);
+		if (!empty($_POST['template_body']) && !empty($_POST['template_title']))
+		{
+			// Safety first.
+			$_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']);
 
 
-		// Clean up BBC.
-		preparsecode($_POST['template_body']);
-		// But put line breaks back!
-		$_POST['template_body'] = strtr($_POST['template_body'], array('<br />' => "\n"));
+			// Clean up BBC.
+			preparsecode($_POST['template_body']);
+			// But put line breaks back!
+			$_POST['template_body'] = strtr($_POST['template_body'], array('<br />' => "\n"));
 
 
-		// Is this personal?
-		$recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0;
+			// Is this personal?
+			$recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0;
 
 
-		// If we are this far it's save time.
-		if ($context['is_edit'])
-		{
-			// Simple update...
-			$smcFunc['db_query']('', '
-				UPDATE {db_prefix}log_comments
-				SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
-				WHERE id_comment = {int:id}
-					AND comment_type = {string:warntpl}
-					AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'.
-					($recipient_id ? ' AND id_member = {int:current_member}' : ''),
-				array(
-					'personal' => $recipient_id,
-					'title' => $_POST['template_title'],
-					'body' => $_POST['template_body'],
-					'id' => $context['id_template'],
-					'warntpl' => 'warntpl',
-					'generic' => 0,
-					'current_member' => $user_info['id'],
-				)
-			);
+			// If we are this far it's save time.
+			if ($context['is_edit'])
+			{
+				// Simple update...
+				$smcFunc['db_query']('', '
+					UPDATE {db_prefix}log_comments
+					SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
+					WHERE id_comment = {int:id}
+						AND comment_type = {string:warntpl}
+						AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'.
+						($recipient_id ? ' AND id_member = {int:current_member}' : ''),
+					array(
+						'personal' => $recipient_id,
+						'title' => $_POST['template_title'],
+						'body' => $_POST['template_body'],
+						'id' => $context['id_template'],
+						'warntpl' => 'warntpl',
+						'generic' => 0,
+						'current_member' => $user_info['id'],
+					)
+				);
+
+				// If it wasn't visible and now is they've effectively added it.
+				if ($context['template_data']['personal'] && !$recipient_id)
+					logAction('add_warn_template', array('template' => $_POST['template_title']));
+				// Conversely if they made it personal it's a delete.
+				elseif (!$context['template_data']['personal'] && $recipient_id)
+					logAction('delete_warn_template', array('template' => $_POST['template_title']));
+				// Otherwise just an edit.
+				else
+					logAction('modify_warn_template', array('template' => $_POST['template_title']));
+			}
+			else
+			{
+				$smcFunc['db_insert']('',
+					'{db_prefix}log_comments',
+					array(
+						'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
+						'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
+					),
+					array(
+						$user_info['id'], $user_info['name'], 'warntpl', $recipient_id,
+						$_POST['template_title'], $_POST['template_body'], time(),
+					),
+					array('id_comment')
+				);
 
 
-			// If it wasn't visible and now is they've effectively added it.
-			if ($context['template_data']['personal'] && !$recipient_id)
 				logAction('add_warn_template', array('template' => $_POST['template_title']));
 				logAction('add_warn_template', array('template' => $_POST['template_title']));
-			// Conversely if they made it personal it's a delete.
-			elseif (!$context['template_data']['personal'] && $recipient_id)
-				logAction('delete_warn_template', array('template' => $_POST['template_title']));
-			// Otherwise just an edit.
-			else
-				logAction('modify_warn_template', array('template' => $_POST['template_title']));
+			}
+
+			// Get out of town...
+			redirectexit('action=moderate;area=warnings;sa=templates');
 		}
 		}
 		else
 		else
 		{
 		{
-			$smcFunc['db_insert']('',
-				'{db_prefix}log_comments',
-				array(
-					'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
-					'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
-				),
-				array(
-					$user_info['id'], $user_info['name'], 'warntpl', $recipient_id,
-					$_POST['template_title'], $_POST['template_body'], time(),
-				),
-				array('id_comment')
-			);
-
-			logAction('add_warn_template', array('template' => $_POST['template_title']));
+			$context['warning_errors'] = array();
+			$context['template_data']['title'] = !empty($_POST['template_title']) ? $_POST['template_title'] : '';
+			$context['template_data']['body'] = !empty($_POST['template_body']) ? $_POST['template_body'] : $txt['mc_warning_template_body_default'];
+			$context['template_data']['personal'] = !empty($_POST['make_personal']);
+			if (empty($_POST['template_title']))
+				$context['warning_errors'][] = $txt['mc_warning_template_error_no_title'];
+			if (empty($_POST['template_body']))
+				$context['warning_errors'][] = $txt['mc_warning_template_error_no_body'];
 		}
 		}
-
-		// Get out of town...
-		redirectexit('action=moderate;area=warnings;sa=templates');
 	}
 	}
 
 
 	createToken('mod-wt');
 	createToken('mod-wt');

+ 33 - 14
Sources/PersonalMessage.php

@@ -41,7 +41,7 @@ function MessageMain()
 		fatal_lang_error('wireless_error_notyet', false);
 		fatal_lang_error('wireless_error_notyet', false);
 	elseif (WIRELESS)
 	elseif (WIRELESS)
 		$context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
 		$context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
-	else
+	elseif (!isset($_REQUEST['xml']))
 		loadTemplate('PersonalMessage');
 		loadTemplate('PersonalMessage');
 
 
 	// Load up the members maximum message capacity.
 	// Load up the members maximum message capacity.
@@ -203,7 +203,8 @@ function MessageMain()
 		MessageFolder();
 		MessageFolder();
 	else
 	else
 	{
 	{
-		messageIndexBar($_REQUEST['sa']);
+		if (!isset($_REQUEST['xml']))
+			messageIndexBar($_REQUEST['sa']);
 		$subActions[$_REQUEST['sa']]();
 		$subActions[$_REQUEST['sa']]();
 	}
 	}
 }
 }
@@ -347,7 +348,7 @@ function messageIndexBar($area)
 	$context['menu_item_selected'] = $pm_include_data['current_area'];
 	$context['menu_item_selected'] = $pm_include_data['current_area'];
 
 
 	// obExit will know what to do!
 	// obExit will know what to do!
-	if (!WIRELESS)
+	if (!WIRELESS && !isset($_REQUEST['xml']))
 		$context['template_layers'][] = 'pm';
 		$context['template_layers'][] = 'pm';
 }
 }
 
 
@@ -1770,6 +1771,7 @@ function MessagePost()
 		'labels' => array(
 		'labels' => array(
 			'post_button' => $txt['send_message'],
 			'post_button' => $txt['send_message'],
 		),
 		),
+		'preview_type' => 2,
 	);
 	);
 	create_control_richedit($editorOptions);
 	create_control_richedit($editorOptions);
 
 
@@ -1804,10 +1806,13 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 	global $txt, $context, $scripturl, $modSettings;
 	global $txt, $context, $scripturl, $modSettings;
 	global $smcFunc, $user_info, $sourcedir;
 	global $smcFunc, $user_info, $sourcedir;
 
 
-	$context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send';
+	if (!isset($_REQUEST['xml']))
+		$context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send';
 
 
-	if (!WIRELESS)
+	if (!WIRELESS && !isset($_REQUEST['xml']))
 		$context['sub_template'] = 'send';
 		$context['sub_template'] = 'send';
+	elseif (isset($_REQUEST['xml']))
+		$context['sub_template'] = 'pm';
 
 
 	$context['page_title'] = $txt['send_message'];
 	$context['page_title'] = $txt['send_message'];
 
 
@@ -1868,7 +1873,12 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 			)
 			)
 		);
 		);
 		if ($smcFunc['db_num_rows']($request) == 0)
 		if ($smcFunc['db_num_rows']($request) == 0)
-			fatal_lang_error('pm_not_yours', false);
+		{
+			if (!isset($_REQUEST['xml']))
+				fatal_lang_error('pm_not_yours', false);
+			else
+				$error_types[] = 'pm_not_yours';
+		}
 		$row_quoted = $smcFunc['db_fetch_assoc']($request);
 		$row_quoted = $smcFunc['db_fetch_assoc']($request);
 		$smcFunc['db_free_result']($request);
 		$smcFunc['db_free_result']($request);
 
 
@@ -1905,7 +1915,10 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 	
 	
 	$context['post_error'] = array(
 	$context['post_error'] = array(
 		'messages' => array(),
 		'messages' => array(),
+		// @todo error handling: maybe fatal errors can be error_type => serious
+		'error_type' => '',
 	);
 	);
+
 	foreach ($error_types as $error_type)
 	foreach ($error_types as $error_type)
 	{
 	{
 		$context['post_error'][$error_type] = true;
 		$context['post_error'][$error_type] = true;
@@ -1932,6 +1945,7 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 		'labels' => array(
 		'labels' => array(
 			'post_button' => $txt['send_message'],
 			'post_button' => $txt['send_message'],
 		),
 		),
+		'preview_type' => 2,
 	);
 	);
 	create_control_richedit($editorOptions);
 	create_control_richedit($editorOptions);
 
 
@@ -1940,7 +1954,7 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 
 
 	// Check whether we need to show the code again.
 	// Check whether we need to show the code again.
 	$context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
 	$context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
-	if ($context['require_verification'])
+	if ($context['require_verification'] && !isset($_REQUEST['xml']))
 	{
 	{
 		require_once($sourcedir . '/Subs-Editor.php');
 		require_once($sourcedir . '/Subs-Editor.php');
 		$verificationOptions = array(
 		$verificationOptions = array(
@@ -1976,6 +1990,9 @@ function MessagePost2()
 	// Extract out the spam settings - it saves database space!
 	// Extract out the spam settings - it saves database space!
 	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
 	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
 
 
+	// Initialize the errors we're about to make.
+	$post_errors = array();
+
 	// Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
 	// Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
 	if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
 	if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
 	{
 	{
@@ -1995,7 +2012,12 @@ function MessagePost2()
 		$smcFunc['db_free_result']($request);
 		$smcFunc['db_free_result']($request);
 
 
 		if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
 		if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
-			fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
+		{
+			if (!isset($_REQUEST['xml']))
+				fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
+			else
+				$post_errors[] = 'pm_too_many_per_hour';
+		}
 	}
 	}
 
 
 	// If we came from WYSIWYG then turn it back into BBC regardless.
 	// If we came from WYSIWYG then turn it back into BBC regardless.
@@ -2011,11 +2033,8 @@ function MessagePost2()
 		$_REQUEST['message'] = $_POST['message'];
 		$_REQUEST['message'] = $_POST['message'];
 	}
 	}
 
 
-	// Initialize the errors we're about to make.
-	$post_errors = array();
-
 	// If your session timed out, show an error, but do allow to re-submit.
 	// If your session timed out, show an error, but do allow to re-submit.
-	if (checkSession('post', '', false) != '')
+	if (!isset($_REQUEST['xml']) && checkSession('post', '', false) != '')
 		$post_errors[] = 'session_timeout';
 		$post_errors[] = 'session_timeout';
 
 
 	$_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
 	$_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
@@ -2135,7 +2154,7 @@ function MessagePost2()
 	}
 	}
 
 
 	// Wrong verification code?
 	// Wrong verification code?
-	if (!$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'])
+	if (!$user_info['is_admin'] && !isset($_REQUEST['xml']) && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'])
 	{
 	{
 		require_once($sourcedir . '/Subs-Editor.php');
 		require_once($sourcedir . '/Subs-Editor.php');
 		$verificationOptions = array(
 		$verificationOptions = array(
@@ -2150,7 +2169,7 @@ function MessagePost2()
 	}
 	}
 
 
 	// If they did, give a chance to make ammends.
 	// If they did, give a chance to make ammends.
-	if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview']))
+	if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview']) && !isset($_REQUEST['xml']))
 		return messagePostError($post_errors, $namedRecipientList, $recipientList);
 		return messagePostError($post_errors, $namedRecipientList, $recipientList);
 
 
 	// Want to take a second glance before you send?
 	// Want to take a second glance before you send?

+ 3 - 1
Sources/Post.php

@@ -2602,7 +2602,7 @@ function notifyMembersBoard(&$topicData)
 				continue;
 				continue;
 		}
 		}
 
 
-		$langloaded = loadLanguage('EmailTemplates', empty($rowmember['lngfile']) || empty($modSettings['userLanguage']) ? $language : $rowmember['lngfile'], false);
+		$langloaded = loadLanguage('index', empty($rowmember['lngfile']) || empty($modSettings['userLanguage']) ? $language : $rowmember['lngfile'], false);
 
 
 		// Now loop through all the notifications to send for this board.
 		// Now loop through all the notifications to send for this board.
 		if (empty($boards[$rowmember['id_board']]))
 		if (empty($boards[$rowmember['id_board']]))
@@ -2650,6 +2650,8 @@ function notifyMembersBoard(&$topicData)
 	}
 	}
 	$smcFunc['db_free_result']($members);
 	$smcFunc['db_free_result']($members);
 
 
+	loadLanguage('index', $user_info['language']);
+
 	// Sent!
 	// Sent!
 	$smcFunc['db_query']('', '
 	$smcFunc['db_query']('', '
 		UPDATE {db_prefix}log_notify
 		UPDATE {db_prefix}log_notify

+ 39 - 9
Sources/Profile-Actions.php

@@ -79,6 +79,10 @@ function issueWarning($memID)
 	if (empty($modSettings['warning_enable']) || ($context['user']['is_owner'] && !$cur_profile['warning']) || !allowedTo('issue_warning'))
 	if (empty($modSettings['warning_enable']) || ($context['user']['is_owner'] && !$cur_profile['warning']) || !allowedTo('issue_warning'))
 		fatal_lang_error('no_access', false);
 		fatal_lang_error('no_access', false);
 
 
+	// Get the base (errors related) stuff done.
+	loadLanguage('Errors');
+	$context['custom_error_title'] = $txt['profile_warning_errors_occured'];
+
 	// Make sure things which are disabled stay disabled.
 	// Make sure things which are disabled stay disabled.
 	$modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
 	$modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
 	$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
 	$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
@@ -212,15 +216,6 @@ function issueWarning($memID)
 		}
 		}
 		else
 		else
 		{
 		{
-			// Get the base stuff done.
-			loadLanguage('Errors');
-			$context['custom_error_title'] = $txt['profile_warning_errors_occured'];
-
-			// Fill in the suite of errors.
-			$context['post_errors'] = array();
-			foreach ($issueErrors as $error)
-				$context['post_errors'][] = $txt[$error];
-
 			// Try to remember some bits.
 			// Try to remember some bits.
 			$context['warning_data'] = array(
 			$context['warning_data'] = array(
 				'reason' => $_POST['warn_reason'],
 				'reason' => $_POST['warn_reason'],
@@ -234,6 +229,41 @@ function issueWarning($memID)
 		$context['member']['warning'] = $_POST['warning_level'];
 		$context['member']['warning'] = $_POST['warning_level'];
 	}
 	}
 
 
+	if (isset($_POST['preview']))
+	{
+		$warning_body = !empty($_POST['warn_body']) ? trim(censorText($_POST['warn_body'])) : '';
+		$context['preview_subject'] = !empty($_POST['warn_sub']) ? trim($smcFunc['htmlspecialchars']($_POST['warn_sub'])) : '';
+		if (empty($_POST['warn_sub']) || empty($_POST['warn_body']))
+			$issueErrors[] = 'warning_notify_blank';
+
+		if (!empty($_POST['warn_body']))
+		{
+			require_once($sourcedir . '/Subs-Post.php');
+
+			preparsecode($warning_body);
+			$warning_body = parse_bbc($warning_body, true);
+		}
+
+		// Try to remember some bits.
+		$context['warning_data'] = array(
+			'reason' => $_POST['warn_reason'],
+			'notify' => !empty($_POST['warn_notify']),
+			'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '',
+			'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : '',
+			'body_preview' => $warning_body,
+		);
+// 		print_r($context['warning_data']);die();
+	}
+
+	if (!empty($issueErrors))
+	{
+		// Fill in the suite of errors.
+		$context['post_errors'] = array();
+		foreach ($issueErrors as $error)
+			$context['post_errors'][] = $txt[$error];
+	}
+
+
 	$context['page_title'] = $txt['profile_issue_warning'];
 	$context['page_title'] = $txt['profile_issue_warning'];
 
 
 	// Let's use a generic list to get all the current warnings
 	// Let's use a generic list to get all the current warnings

+ 32 - 10
Sources/Profile-Modify.php

@@ -799,7 +799,7 @@ function saveProfileFields()
 	// Cycle through the profile fields working out what to do!
 	// Cycle through the profile fields working out what to do!
 	foreach ($profile_fields as $key => $field)
 	foreach ($profile_fields as $key => $field)
 	{
 	{
-		if (!isset($_POST[$key]) || !empty($field['is_dummy']))
+		if (!isset($_POST[$key]) || !empty($field['is_dummy']) || (isset($_POST['preview']) && $key == 'signature'))
 			continue;
 			continue;
 
 
 		// What gets updated?
 		// What gets updated?
@@ -1584,6 +1584,7 @@ function forumProfile($memID)
 
 
 	$context['sub_template'] = 'edit_options';
 	$context['sub_template'] = 'edit_options';
 	$context['page_desc'] = $txt['forumProfile_info'];
 	$context['page_desc'] = $txt['forumProfile_info'];
+	$context['show_preview_button'] = true;
 
 
 	setupProfileContext(
 	setupProfileContext(
 		array(
 		array(
@@ -2387,7 +2388,7 @@ function profileLoadGroups()
  */
  */
 function profileLoadSignatureData()
 function profileLoadSignatureData()
 {
 {
-	global $modSettings, $context, $txt, $cur_profile, $smcFunc;
+	global $modSettings, $context, $txt, $cur_profile, $smcFunc, $memberContext;
 
 
 	// Signature limits.
 	// Signature limits.
 	list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
 	list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
@@ -2415,7 +2416,27 @@ function profileLoadSignatureData()
 
 
 	$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
 	$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
 
 
-	$context['member']['signature'] = empty($cur_profile['signature']) ? '' : str_replace(array('<br />', '<', '>', '"', '\''), array("\n", '&lt;', '&gt;', '&quot;', '&#039;'), $cur_profile['signature']);
+	if (empty($context['do_preview']))
+		$context['member']['signature'] = empty($cur_profile['signature']) ? '' : str_replace(array('<br />', '<', '>', '"', '\''), array("\n", '&lt;', '&gt;', '&quot;', '&#039;'), $cur_profile['signature']);
+	else
+	{
+		$signature = !empty($_POST['signature']) ? $_POST['signature'] : '';
+		$validation = profileValidateSignature($signature);
+		if (empty($context['post_errors']))
+		{
+			loadLanguage('Errors');
+			$context['post_errors'] = array();
+		}
+		$context['post_errors'][] = 'signature_not_yet_saved';
+		if ($validation !== true && $validation !== false)
+			$context['post_errors'][] = $validation;
+
+		censorText($context['member']['signature']);
+		$context['member']['current_signature'] = $context['member']['signature'];
+		censorText($signature);
+		$context['member']['signature_preview'] = parse_bbc($signature, true, 'sig' . $memberContext[$context['id_member']]);
+		$context['member']['signature'] = $_POST['signature'];
+	}
 
 
 	return true;
 	return true;
 }
 }
@@ -2856,13 +2877,6 @@ function profileValidateSignature(&$value)
 		$disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
 		$disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
 
 
 		$unparsed_signature = strtr(un_htmlspecialchars($value), array("\r" => '', '&#039' => '\''));
 		$unparsed_signature = strtr(un_htmlspecialchars($value), array("\r" => '', '&#039' => '\''));
-		// Too long?
-		if (!empty($sig_limits[1]) && $smcFunc['strlen']($unparsed_signature) > $sig_limits[1])
-		{
-			$_POST['signature'] = trim(htmlspecialchars($smcFunc['substr']($unparsed_signature, 0, $sig_limits[1]), ENT_QUOTES));
-			$txt['profile_error_signature_max_length'] = sprintf($txt['profile_error_signature_max_length'], $sig_limits[1]);
-			return 'signature_max_length';
-		}
 		// Too many lines?
 		// Too many lines?
 		if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2])
 		if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2])
 		{
 		{
@@ -3007,6 +3021,14 @@ function profileValidateSignature(&$value)
 	}
 	}
 
 
 	preparsecode($value);
 	preparsecode($value);
+	// Too long?
+	if (!allowedTo('admin_forum') && !empty($sig_limits[1]) && $smcFunc['strlen'](str_replace('<br />', "\n", $value)) > $sig_limits[1])
+	{
+		$_POST['signature'] = trim(htmlspecialchars(str_replace('<br />', "\n", $value), ENT_QUOTES));
+		$txt['profile_error_signature_max_length'] = sprintf($txt['profile_error_signature_max_length'], $sig_limits[1]);
+		return 'signature_max_length';
+	}
+
 	return true;
 	return true;
 }
 }
 
 

+ 3 - 1
Sources/Profile.php

@@ -394,6 +394,8 @@ function ModifyProfile($post_errors = array())
 
 
 	// Before we go any further, let's work on the area we've said is valid. Note this is done here just in case we ever compromise the menu function in error!
 	// Before we go any further, let's work on the area we've said is valid. Note this is done here just in case we ever compromise the menu function in error!
 	$context['completed_save'] = false;
 	$context['completed_save'] = false;
+	$context['do_preview'] = isset($_REQUEST['preview']);
+
 	$security_checks = array();
 	$security_checks = array();
 	$found_area = false;
 	$found_area = false;
 	foreach ($profile_areas as $section_id => $section)
 	foreach ($profile_areas as $section_id => $section)
@@ -645,7 +647,7 @@ function ModifyProfile($post_errors = array())
 			$context['modify_error'][$error_type] = true;
 			$context['modify_error'][$error_type] = true;
 	}
 	}
 	// If it's you then we should redirect upon save.
 	// If it's you then we should redirect upon save.
-	elseif (!empty($profile_vars) && $context['user']['is_owner'])
+	elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview'])
 		redirectexit('action=profile;area=' . $current_area . ';updated');
 		redirectexit('action=profile;area=' . $current_area . ';updated');
 	elseif (!empty($force_redirect))
 	elseif (!empty($force_redirect))
 		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);
 		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);

+ 20 - 11
Sources/Subs.php

@@ -498,10 +498,11 @@ function updateSettings($changeArray, $update = false, $debug = false)
  * @param int $max_value
  * @param int $max_value
  * @param int $num_per_page
  * @param int $num_per_page
  * @param bool $flexible_start = false
  * @param bool $flexible_start = false
+ * @param bool $show_prevnext = true
  */
  */
-function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flexible_start = false)
+function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flexible_start = false, $show_prevnext = true)
 {
 {
-	global $modSettings, $context;
+	global $modSettings, $context, $txt;
 
 
 	// Save whether $start was less than 0 or not.
 	// Save whether $start was less than 0 or not.
 	$start = (int) $start;
 	$start = (int) $start;
@@ -546,17 +547,21 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 		// If they didn't enter an odd value, pretend they did.
 		// If they didn't enter an odd value, pretend they did.
 		$PageContiguous = (int) ($modSettings['compactTopicPagesContiguous'] - ($modSettings['compactTopicPagesContiguous'] % 2)) / 2;
 		$PageContiguous = (int) ($modSettings['compactTopicPagesContiguous'] - ($modSettings['compactTopicPagesContiguous'] % 2)) / 2;
 
 
-		// Show the first page. (>1< ... 6 7 [8] 9 10 ... 15)
-		if ($start > $num_per_page * $PageContiguous)
-			$pageindex = sprintf($base_link, 0, '1');
+		// Show the "prev page" link. (>prev page< 1 ... 6 7 [8] 9 10 ... 15 next page)
+		if (!empty($start) && $show_prevnext)
+			$pageindex = sprintf($base_link, $start - $num_per_page, '<span class="previous_page">&#171; ' . $txt['prev'] . '</span>');
 		else
 		else
 			$pageindex = '';
 			$pageindex = '';
 
 
-		// Show the ... after the first page.  (1 >...< 6 7 [8] 9 10 ... 15)
+		// Show the first page. (prev page >1< ... 6 7 [8] 9 10 ... 15)
+		if ($start > $num_per_page * $PageContiguous)
+			$pageindex .= sprintf($base_link, 0, '1');
+
+		// Show the ... after the first page.  (prev page 1 >...< 6 7 [8] 9 10 ... 15 next page)
 		if ($start > $num_per_page * ($PageContiguous + 1))
 		if ($start > $num_per_page * ($PageContiguous + 1))
 			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . $num_per_page . ', ' . ($start - $num_per_page * $PageContiguous) . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor = \'pointer\';"> ... </span>';
 			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . $num_per_page . ', ' . ($start - $num_per_page * $PageContiguous) . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor = \'pointer\';"> ... </span>';
 
 
-		// Show the pages before the current one. (1 ... >6 7< [8] 9 10 ... 15)
+		// Show the pages before the current one. (prev page 1 ... >6 7< [8] 9 10 ... 15 next page)
 		for ($nCont = $PageContiguous; $nCont >= 1; $nCont--)
 		for ($nCont = $PageContiguous; $nCont >= 1; $nCont--)
 			if ($start >= $num_per_page * $nCont)
 			if ($start >= $num_per_page * $nCont)
 			{
 			{
@@ -564,13 +569,13 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 				$pageindex.= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1);
 				$pageindex.= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1);
 			}
 			}
 
 
-		// Show the current page. (1 ... 6 7 >[8]< 9 10 ... 15)
+		// Show the current page. (prev page 1 ... 6 7 >[8]< 9 10 ... 15 next page)
 		if (!$start_invalid)
 		if (!$start_invalid)
 			$pageindex .= '[<strong>' . ($start / $num_per_page + 1) . '</strong>] ';
 			$pageindex .= '[<strong>' . ($start / $num_per_page + 1) . '</strong>] ';
 		else
 		else
 			$pageindex .= sprintf($base_link, $start, $start / $num_per_page + 1);
 			$pageindex .= sprintf($base_link, $start, $start / $num_per_page + 1);
 
 
-		// Show the pages after the current one... (1 ... 6 7 [8] >9 10< ... 15)
+		// Show the pages after the current one... (prev page 1 ... 6 7 [8] >9 10< ... 15 next page)
 		$tmpMaxPages = (int) (($max_value - 1) / $num_per_page) * $num_per_page;
 		$tmpMaxPages = (int) (($max_value - 1) / $num_per_page) * $num_per_page;
 		for ($nCont = 1; $nCont <= $PageContiguous; $nCont++)
 		for ($nCont = 1; $nCont <= $PageContiguous; $nCont++)
 			if ($start + $num_per_page * $nCont <= $tmpMaxPages)
 			if ($start + $num_per_page * $nCont <= $tmpMaxPages)
@@ -579,13 +584,17 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 				$pageindex .= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1);
 				$pageindex .= sprintf($base_link, $tmpStart, $tmpStart / $num_per_page + 1);
 			}
 			}
 
 
-		// Show the '...' part near the end. (1 ... 6 7 [8] 9 10 >...< 15)
+		// Show the '...' part near the end. (prev page 1 ... 6 7 [8] 9 10 >...< 15 next page)
 		if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages)
 		if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages)
 			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . ($start + $num_per_page * ($PageContiguous + 1)) . ', ' . $tmpMaxPages . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor=\'pointer\';"> ... </span>';
 			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . ($start + $num_per_page * ($PageContiguous + 1)) . ', ' . $tmpMaxPages . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor=\'pointer\';"> ... </span>';
 
 
-		// Show the last number in the list. (1 ... 6 7 [8] 9 10 ... >15<)
+		// Show the last number in the list. (prev page 1 ... 6 7 [8] 9 10 ... >15<  next page)
 		if ($start + $num_per_page * $PageContiguous < $tmpMaxPages)
 		if ($start + $num_per_page * $PageContiguous < $tmpMaxPages)
 			$pageindex .= sprintf($base_link, $tmpMaxPages, $tmpMaxPages / $num_per_page + 1);
 			$pageindex .= sprintf($base_link, $tmpMaxPages, $tmpMaxPages / $num_per_page + 1);
+
+		// Show the "next page" link. (prev page 1 ... 6 7 [8] 9 10 ... 15 >next page<)
+		if ($start != $tmpMaxPages && $show_prevnext)
+			$pageindex .= sprintf($base_link, $start + $num_per_page, '<span class="next_page">' . $txt['next'] . ' &#187;</span>');
 	}
 	}
 
 
 	return $pageindex;
 	return $pageindex;

+ 216 - 0
Sources/Xml.php

@@ -31,6 +31,9 @@ function XMLhttpMain()
 		'corefeatures' => array(
 		'corefeatures' => array(
 			'function' => 'EnableCoreFeatures',
 			'function' => 'EnableCoreFeatures',
 		),
 		),
+		'previews' => array(
+			'function' => 'RetrievePreview',
+		),
 	);
 	);
 	if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']]))
 	if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']]))
 		fatal_lang_error('no_access', false);
 		fatal_lang_error('no_access', false);
@@ -161,4 +164,217 @@ function EnableCoreFeatures()
 		),
 		),
 	);
 	);
 }
 }
+
+function RetrievePreview()
+{
+	global $context;
+
+	$items = array(
+		'newspreview',
+		'newsletterpreview',
+		'sig_preview',
+		'warning_preview',
+	);
+
+	$context['sub_template'] = 'generic_xml';
+
+	if (!isset($_POST['item']) || !in_array($_POST['item'], $items))
+		return false;
+
+	$_POST['item']();
+}
+
+function newspreview()
+{
+	global $context, $sourcedir, $smcFunc;
+
+	require_once($sourcedir . '/Subs-Post.php');
+
+	$errors = array();
+	$news = !isset($_POST['news'])? '' : $smcFunc['htmlspecialchars']($_POST['news'], ENT_QUOTES);
+	if (empty($news))
+		$errors[] = array('value' => 'no_news');
+	else
+		preparsecode($news);
+
+	$context['xml_data'] = array(
+		'news' => array(
+			'identifier' => 'parsedNews',
+			'children' => array(
+				array(
+					'value' => parse_bbc($news),
+				),
+			),
+		),
+		'errors' => array(
+			'identifier' => 'error',
+			'children' => $errors
+		),
+	);
+}
+function newsletterpreview()
+{
+	global $context, $sourcedir, $smcFunc, $txt;
+
+	require_once($sourcedir . '/Subs-Post.php');
+	require_once($sourcedir . '/ManageNews.php');
+	loadLanguage('Errors');
+
+	$context['post_error']['messages'] = array();
+	$context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0;
+	$context['send_html'] = !empty($_POST['send_html']) ? 1 : 0;
+
+	if (empty($_POST['subject']))
+		$context['post_error']['messages'][] = $txt['error_no_subject'];
+	if (empty($_POST['message']))
+		$context['post_error']['messages'][] = $txt['error_no_message'];
+
+	prepareMailingForPreview();
+
+	$context['sub_template'] = 'pm';
+}
+
+function sig_preview()
+{
+	global $context, $sourcedir, $smcFunc, $txt, $user_info;
+
+	require_once($sourcedir . '/Profile-Modify.php');
+	loadLanguage('Profile');
+	loadLanguage('Errors');
+
+	$user = isset($_POST['user']) ? (int) $_POST['user'] : 0;
+	$is_owner = $user == $user_info['id'];
+
+	// @todo Temporary
+	// Borrowed from loadAttachmentContext in Display.php
+	$can_change = $is_owner ? allowedTo(array('profile_extra_any', 'profile_extra_own')) : allowedTo('profile_extra_any');
+
+	$errors = array();
+	if (!empty($user) && $can_change)
+	{
+		$request = $smcFunc['db_query']('', '
+			SELECT signature
+			FROM {db_prefix}members
+			WHERE id_member = {int:id_member}
+			LIMIT 1',
+			array(
+				'id_member' => $user,
+			)
+		);
+		list($current_signature) = $smcFunc['db_fetch_row']($request);
+		$smcFunc['db_free_result']($request);
+		censorText($current_signature);
+		$current_signature = parse_bbc($current_signature, true, 'sig' . $user);
+
+		$preview_signature = !empty($_POST['signature']) ? $_POST['signature'] : '';
+		$validation = profileValidateSignature($preview_signature);
+
+		if ($validation !== true && $validation !== false)
+			$errors[] = array('value' => $txt['profile_error_' . $validation], 'attributes' => array('type' => 'error'));
+
+		censorText($preview_signature);
+		$preview_signature = parse_bbc($preview_signature, true, 'sig' . $user);
+	}
+	elseif (!$can_change)
+	{
+		if ($is_owner)
+			$errors[] = array('value' => $txt['cannot_profile_extra_own'], 'attributes' => array('type' => 'error'));
+		else
+			$errors[] = array('value' => $txt['cannot_profile_extra_any'], 'attributes' => array('type' => 'error'));
+	}
+	else
+		$errors[] = array('value' => $txt['no_user_selected'], 'attributes' => array('type' => 'error'));
+
+	$context['xml_data']['signatures'] = array(
+			'identifier' => 'signature',
+			'children' => array()
+		);
+	if (isset($current_signature))
+		$context['xml_data']['signatures']['children'][] = array(
+					'value' => $current_signature,
+					'attributes' => array('type' => 'current'),
+				);
+	if (isset($preview_signature))
+		$context['xml_data']['signatures']['children'][] = array(
+					'value' => $preview_signature,
+					'attributes' => array('type' => 'preview'),
+				);
+	if (!empty($errors))
+		$context['xml_data']['errors'] = array(
+			'identifier' => 'error',
+			'children' => array_merge(
+				array(
+					array(
+						'value' => $txt['profile_errors_occurred'],
+						'attributes' => array('type' => 'errors_occurred'),
+					),
+				),
+				$errors
+			),
+		);
+}
+
+function warning_preview()
+{
+	global $context, $sourcedir, $smcFunc, $txt, $user_info, $scripturl, $mbname;
+
+	require_once($sourcedir . '/Subs-Post.php');
+	loadLanguage('Errors');
+	loadLanguage('ModerationCenter');
+
+	$user = isset($_POST['user']) ? (int) $_POST['user'] : 0;
+
+	$context['post_error']['messages'] = array();
+	if (allowedTo('issue_warning'))
+	{
+		$warning_body = !empty($_POST['body']) ? trim(censorText($_POST['body'])) : '';
+		$context['preview_subject'] = !empty($_POST['title']) ? trim($smcFunc['htmlspecialchars']($_POST['title'])) : '';
+		if (isset($_POST['issuing']))
+		{
+			if (empty($_POST['title']) || empty($_POST['body']))
+				$context['post_error']['messages'][] = $txt['warning_notify_blank'];
+		}
+		else
+		{
+			if (empty($_POST['title']))
+				$context['post_error']['messages'][] = $txt['mc_warning_template_error_no_title'];
+			if (empty($_POST['body']))
+				$context['post_error']['messages'][] = $txt['mc_warning_template_error_no_body'];
+			// Add in few replacements.
+			/**
+			* These are the defaults:
+			* - {MEMBER} - Member Name. => current user for review
+			* - {MESSAGE} - Link to Offending Post. (If Applicable) => not applicable here, so not replaced
+			* - {FORUMNAME} - Forum Name.
+			* - {SCRIPTURL} - Web address of forum.
+			* - {REGARDS} - Standard email sign-off.
+			*/
+			$find = array(
+				'{MEMBER}',
+				'{FORUMNAME}',
+				'{SCRIPTURL}',
+				'{REGARDS}',
+			);
+			$replace = array(
+				$user_info['name'],
+				$mbname,
+				$scripturl,
+				$txt['regards_team'],
+			);
+			$warning_body = str_replace($find, $replace, $warning_body);
+		}
+
+		if (!empty($_POST['body']))
+		{
+			preparsecode($warning_body);
+			$warning_body = parse_bbc($warning_body, true);
+		}
+		$context['preview_message'] = $warning_body;
+	}
+	else
+		$context['post_error']['messages'][] = array('value' => $txt['cannot_issue_warning'], 'attributes' => array('type' => 'error'));
+
+	$context['sub_template'] = 'pm';
+}
+
 ?>
 ?>

+ 180 - 12
Themes/default/ManageNews.template.php

@@ -261,9 +261,27 @@ function template_email_members_compose()
 {
 {
 	global $context, $settings, $options, $txt, $scripturl;
 	global $context, $settings, $options, $txt, $scripturl;
 
 
+	echo '
+		<div id="preview_section"', isset($context['preview_message']) ? '' : ' style="display: none;"', '>
+			<div class="cat_bar">
+				<h3 class="catbg">
+					<span id="preview_subject">', empty($context['preview_subject']) ? '' : $context['preview_subject'], '</span>
+				</h3>
+			</div>
+			<div class="windowbg">
+				<span class="topslice"><span></span></span>
+				<div class="content">
+					<div class="post" id="preview_body">
+						', empty($context['preview_message']) ? '<br />' : $context['preview_message'], '
+					</div>
+				</div>
+				<span class="botslice"><span></span></span>
+			</div>
+		</div><br />';
+
 	echo '
 	echo '
 	<div id="admincenter">
 	<div id="admincenter">
-		<form action="', $scripturl, '?action=admin;area=news;sa=mailingsend" method="post" accept-charset="', $context['character_set'], '">
+		<form name="newsmodify" action="', $scripturl, '?action=admin;area=news;sa=mailingsend" method="post" accept-charset="', $context['character_set'], '">
 			<div class="cat_bar">
 			<div class="cat_bar">
 				<h3 class="catbg">
 				<h3 class="catbg">
 					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['admin_newsletters'], '
 					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['admin_newsletters'], '
@@ -275,20 +293,47 @@ function template_email_members_compose()
 			<div class="windowbg">
 			<div class="windowbg">
 				<span class="topslice"><span></span></span>
 				<span class="topslice"><span></span></span>
 				<div class="content">
 				<div class="content">
-					<p>
-						<input type="text" name="subject" size="60" value="', $context['default_subject'], '" class="input_text" />
-					</p>
-					<p>
-						<textarea cols="70" rows="9" name="message" class="editor">', $context['default_message'], '</textarea>
-					</p>
+				<div class="', empty($context['error_type']) || $context['error_type'] != 'serious' ? 'noticebox' : 'errorbox', '"', empty($context['post_error']['messages']) ? ' style="display: none"' : '', ' id="errors">
+					<dl>
+						<dt>
+							<strong id="error_serious">', $txt['error_while_submitting'] , '</strong>
+						</dt>
+						<dd class="error" id="error_list">
+							', empty($context['post_error']['messages']) ? '' : implode('<br />', $context['post_error']['messages']), '
+						</dd>
+					</dl>
+				</div>
+				<dl id="post_header">
+					<dt class="clear_left">
+						<span', (isset($context['post_error']['no_subject']) ? ' class="error"' : ''), ' id="caption_subject">', $txt['subject'], ':</span>
+					</dt>
+					<dd id="pm_subject">
+						<input type="text" name="subject" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="60" maxlength="60"',isset($context['post_error']['no_subject']) ? ' class="error"' : ' class="input_text"', '/>
+					</dd>
+				</dl><hr class="clear" />
+				<div id="bbcBox_message"></div>';
+
+	// What about smileys?
+	if (!empty($context['smileys']['postform']) || !empty($context['smileys']['popup']))
+		echo '
+				<div id="smileyBox_message"></div>';
+
+	// Show BBC buttons, smileys and textbox.
+	echo '
+				', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message');
+					
+					echo '
 					<ul class="reset">
 					<ul class="reset">
-						<li><label for="send_pm"><input type="checkbox" name="send_pm" id="send_pm" class="input_check" onclick="if (this.checked && ', $context['total_emails'], ' != 0 && !confirm(\'', $txt['admin_news_cannot_pm_emails_js'], '\')) return false; this.form.parse_html.disabled = this.checked; this.form.send_html.disabled = this.checked; " /> ', $txt['email_as_pms'], '</label></li>
-						<li><label for="send_html"><input type="checkbox" name="send_html" id="send_html" class="input_check" onclick="this.form.parse_html.disabled = !this.checked;" /> ', $txt['email_as_html'], '</label></li>
+						<li><label for="send_pm"><input type="checkbox" name="send_pm" id="send_pm" ', !empty($context['send_pm']) ? 'checked="checked"' : '', 'class="input_check" onclick="checkboxes_status(this);" /> ', $txt['email_as_pms'], '</label></li>
+						<li><label for="send_html"><input type="checkbox" name="send_html" id="send_html" ', !empty($context['send_html']) ? 'checked="checked"' : '', 'class="input_check" onclick="checkboxes_status(this);" /> ', $txt['email_as_html'], '</label></li>
 						<li><label for="parse_html"><input type="checkbox" name="parse_html" id="parse_html" checked="checked" disabled="disabled" class="input_check" /> ', $txt['email_parsed_html'], '</label></li>
 						<li><label for="parse_html"><input type="checkbox" name="parse_html" id="parse_html" checked="checked" disabled="disabled" class="input_check" /> ', $txt['email_parsed_html'], '</label></li>
 					</ul>
 					</ul>
-					<p>
-						<input type="submit" value="', $txt['sendtopic_send'], '" class="button_submit" />
-					</p>
+				<p id="shortcuts" class="smalltext">
+					', isBrowser('is_firefox') ? $txt['shortcuts_firefox'] : $txt['shortcuts'], '
+				</p>
+				<p id="post_confirm_strip" class="righttext">
+					', template_control_richedit_buttons($context['post_box_name']), '
+				</p>
 				</div>
 				</div>
 				<span class="botslice"><span></span></span>
 				<span class="botslice"><span></span></span>
 			</div>
 			</div>
@@ -302,6 +347,129 @@ function template_email_members_compose()
 			<input type="hidden" name="', $key, '" value="', implode(($key == 'emails' ? ';' : ','), $values), '" />';
 			<input type="hidden" name="', $key, '" value="', implode(($key == 'emails' ? ';' : ','), $values), '" />';
 
 
 	echo '
 	echo '
+		<script type="text/javascript"><!-- // --><![CDATA[';
+	// The functions used to preview a posts without loading a new page.
+	echo '
+			var txt_preview_title = "', $txt['preview_title'], '";
+			var txt_preview_fetch = "', $txt['preview_fetch'], '";
+			function previewPost()
+			{';
+	if (isBrowser('is_firefox'))
+		echo '
+				// Firefox doesn\'t render <marquee> that have been put it using javascript
+				if (document.forms.newsmodify.elements[', JavaScriptEscape($context['post_box_name']), '].value.indexOf(\'[move]\') != -1)
+				{
+					return submitThisOnce(document.forms.newsmodify);
+				}';
+	echo '
+				if (window.XMLHttpRequest)
+				{
+					// Opera didn\'t support setRequestHeader() before 8.01.
+					// @todo Remove support for old browsers
+					if (\'opera\' in window)
+					{
+						var test = new XMLHttpRequest();
+						if (!(\'setRequestHeader\' in test))
+							return submitThisOnce(document.forms.newsmodify);
+					}
+					// @todo Currently not sending poll options and option checkboxes.
+					var x = new Array();
+					var textFields = [\'subject\', ', JavaScriptEscape($context['post_box_name']), '];
+					var checkboxFields = [\'send_html\', \'send_pm\'];
+
+					for (var i = 0, n = textFields.length; i < n; i++)
+						if (textFields[i] in document.forms.newsmodify)
+						{
+							// Handle the WYSIWYG editor.
+							if (textFields[i] == ', JavaScriptEscape($context['post_box_name']), ' && ', JavaScriptEscape('oEditorHandle_' . $context['post_box_name']), ' in window && oEditorHandle_', $context['post_box_name'], '.bRichTextEnabled)
+								x[x.length] = \'message_mode=1&\' + textFields[i] + \'=\' + oEditorHandle_', $context['post_box_name'], '.getText(false).replace(/&#/g, \'&#38;#\').php_to8bit().php_urlencode();
+							else
+								x[x.length] = textFields[i] + \'=\' + document.forms.newsmodify[textFields[i]].value.replace(/&#/g, \'&#38;#\').php_to8bit().php_urlencode();
+						}
+					for (var i = 0, n = checkboxFields.length; i < n; i++)
+						if (checkboxFields[i] in document.forms.newsmodify && document.forms.newsmodify.elements[checkboxFields[i]].checked)
+							x[x.length] = checkboxFields[i] + \'=\' + document.forms.newsmodify.elements[checkboxFields[i]].value;
+
+					x[x.length] = \'item=newsletterpreview\';
+
+					sendXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=xmlhttp;sa=previews;xml\', x.join(\'&\'), onDocSent);
+
+					document.getElementById(\'preview_section\').style.display = \'\';
+					setInnerHTML(document.getElementById(\'preview_subject\'), txt_preview_title);
+					setInnerHTML(document.getElementById(\'preview_body\'), txt_preview_fetch);
+
+					return false;
+				}
+				else
+					return submitThisOnce(document.forms.newsmodify);
+			}
+			function onDocSent(XMLDoc)
+			{
+				if (!XMLDoc)
+				{
+					document.forms.newsmodify.preview.onclick = new function ()
+					{
+						return true;
+					}
+					document.forms.newsmodify.preview.click();
+				}
+
+				// Show the preview section.
+				var preview = XMLDoc.getElementsByTagName(\'smf\')[0].getElementsByTagName(\'preview\')[0];
+				setInnerHTML(document.getElementById(\'preview_subject\'), preview.getElementsByTagName(\'subject\')[0].firstChild.nodeValue);
+
+				var bodyText = \'\';
+				for (var i = 0, n = preview.getElementsByTagName(\'body\')[0].childNodes.length; i < n; i++)
+					bodyText += preview.getElementsByTagName(\'body\')[0].childNodes[i].nodeValue;
+
+				setInnerHTML(document.getElementById(\'preview_body\'), bodyText);
+				document.getElementById(\'preview_body\').className = \'post\';
+
+				// Show a list of errors (if any).
+				var errors = XMLDoc.getElementsByTagName(\'smf\')[0].getElementsByTagName(\'errors\')[0];
+				var errorList = new Array();
+				for (var i = 0, numErrors = errors.getElementsByTagName(\'error\').length; i < numErrors; i++)
+					errorList[errorList.length] = errors.getElementsByTagName(\'error\')[i].firstChild.nodeValue;
+				document.getElementById(\'errors\').style.display = numErrors == 0 ? \'none\' : \'\';
+				setInnerHTML(document.getElementById(\'error_list\'), numErrors == 0 ? \'\' : errorList.join(\'<br />\'));
+
+				// Adjust the color of captions if the given data is erroneous.
+				var captions = errors.getElementsByTagName(\'caption\');
+				for (var i = 0, numCaptions = errors.getElementsByTagName(\'caption\').length; i < numCaptions; i++)
+					if (document.getElementById(\'caption_\' + captions[i].getAttribute(\'name\')))
+						document.getElementById(\'caption_\' + captions[i].getAttribute(\'name\')).className = captions[i].getAttribute(\'class\');
+
+				if (errors.getElementsByTagName(\'post_error\').length == 1)
+					document.forms.newsmodify.', $context['post_box_name'], '.style.border = \'1px solid red\';
+				else if (document.forms.newsmodify.', $context['post_box_name'], '.style.borderColor == \'red\' || document.forms.newsmodify.', $context['post_box_name'], '.style.borderColor == \'red red red red\')
+				{
+					if (\'runtimeStyle\' in document.forms.newsmodify.', $context['post_box_name'], ')
+						document.forms.newsmodify.', $context['post_box_name'], '.style.borderColor = \'\';
+					else
+						document.forms.newsmodify.', $context['post_box_name'], '.style.border = null;
+				}
+				location.hash = \'#\' + \'preview_section\';
+			}';
+
+	echo '
+		// ]]></script>';
+
+	echo '
+		<script type="text/javascript"><!-- // --><![CDATA[
+			function checkboxes_status (item)
+			{
+				if (item.id == \'send_html\')
+					document.getElementById(\'parse_html\').disabled = !document.getElementById(\'parse_html\').disabled;
+				if (item.id == \'send_pm\')
+				{
+					if (!document.getElementById(\'send_html\').checked)
+						document.getElementById(\'parse_html\').disabled = true;
+					else
+						document.getElementById(\'parse_html\').disabled = false;
+					document.getElementById(\'send_html\').disabled = !document.getElementById(\'send_html\').disabled;
+				}
+			}
+		// ]]></script>
 		</form>
 		</form>
 	</div>
 	</div>
 	<br class="clear" />';
 	<br class="clear" />';

+ 60 - 1
Themes/default/ModerationCenter.template.php

@@ -823,6 +823,26 @@ function template_warn_template()
 			<div class="windowbg">
 			<div class="windowbg">
 				<span class="topslice"><span></span></span>
 				<span class="topslice"><span></span></span>
 				<div class="content">
 				<div class="content">
+					<div class="errorbox"', empty($context['warning_errors']) ? ' style="display: none"' : '', ' id="errors">
+						<dl>
+							<dt>
+								<strong id="error_serious">', $txt['error_while_submitting'] , '</strong>
+							</dt>
+							<dd class="error" id="error_list">
+								', empty($context['warning_errors']) ? '' : implode('<br />', $context['warning_errors']), '
+							</dd>
+						</dl>
+					</div>
+					<div id="box_preview"', !empty($context['template_preview']) ? '' : ' style="display:none"', '>
+						<dl class="settings">
+							<dt>
+								<strong>', $txt['preview'] , '</strong>
+							</dt>
+							<dd id="template_preview">
+								', !empty($context['template_preview']) ? $context['template_preview'] : '', '
+							</dd>
+						</dl>
+					</div>
 					<dl class="settings">
 					<dl class="settings">
 						<dt>
 						<dt>
 							<strong><label for="template_title">', $txt['mc_warning_template_title'], '</label>:</strong>
 							<strong><label for="template_title">', $txt['mc_warning_template_title'], '</label>:</strong>
@@ -851,6 +871,7 @@ function template_warn_template()
 
 
 	echo '
 	echo '
 					<hr class="hrcolor" />
 					<hr class="hrcolor" />
+					<input type="submit" name="preview" id="preview_button" value="', $txt['preview'], '" class="button_submit" />
 					<input type="submit" name="save" value="', $context['page_title'], '" class="button_submit" />
 					<input type="submit" name="save" value="', $context['page_title'], '" class="button_submit" />
 					<br class="clear_right" />
 					<br class="clear_right" />
 				</div>
 				</div>
@@ -860,7 +881,45 @@ function template_warn_template()
 			<input type="hidden" name="', $context['mod-wt_token_var'], '" value="', $context['mod-wt_token'], '" />
 			<input type="hidden" name="', $context['mod-wt_token_var'], '" value="', $context['mod-wt_token'], '" />
 		</form>
 		</form>
 	</div>
 	</div>
-	<br class="clear" />';
+	<br class="clear" />
+	<script type="text/javascript"><!-- // --><![CDATA[
+		$(document).ready(function() {
+			$("#preview_button").click(function() {
+				return ajax_getTemplatePreview();
+			});
+		});
+
+		function ajax_getTemplatePreview ()
+		{
+			$.ajax({
+				type: "POST",
+				url: "' . $scripturl . '?action=xmlhttp;sa=previews;xml",
+				data: {item: "warning_preview", title: $("#template_title").val(), body: $("#template_body").val(), user: $(\'input[name="u"]\').attr("value")},
+				context: document.body,
+				success: function(request){
+					$("#box_preview").css({display:""});
+					$("#template_preview").html($(request).find(\'body\').text());
+					if ($(request).find("error").text() != \'\')
+					{
+						$("#errors").css({display:""});
+						var errors_html = \'\';
+						var errors = $(request).find(\'error\').each(function() {
+							errors_html += $(this).text() + \'<br />\';
+						});
+
+						$(document).find("#error_list").html(errors_html);
+					}
+					else
+					{
+						$("#errors").css({display:"none"});
+						$("#error_list").html(\'\');
+					}
+				return false;
+				},
+			});
+			return false;
+		}
+	// ]]></script>';
 }
 }
 
 
 ?>
 ?>

+ 131 - 19
Themes/default/PersonalMessage.template.php

@@ -10,6 +10,7 @@
  * @version 2.1 Alpha 1
  * @version 2.1 Alpha 1
  */
  */
 
 
+
 // This is the main sidebar for the personal messages section.
 // This is the main sidebar for the personal messages section.
 function template_pm_above()
 function template_pm_above()
 {
 {
@@ -951,19 +952,23 @@ function template_send()
 	}
 	}
 
 
 	// Show the preview of the personal message.
 	// Show the preview of the personal message.
-	if (isset($context['preview_message']))
 	echo '
 	echo '
-		<div class="cat_bar">
-			<h3 class="catbg">', $context['preview_subject'], '</h3>
-		</div>
-		<div class="windowbg">
-			<span class="topslice"><span></span></span>
-			<div class="content">
-				', $context['preview_message'], '
+		<div id="preview_section"', isset($context['preview_message']) ? '' : ' style="display: none;"', '>
+			<div class="cat_bar">
+				<h3 class="catbg">
+					<span id="preview_subject">', empty($context['preview_subject']) ? '' : $context['preview_subject'], '</span>
+				</h3>
 			</div>
 			</div>
-			<span class="botslice"><span></span></span>
-		</div>
-		<br />';
+			<div class="windowbg">
+				<span class="topslice"><span></span></span>
+				<div class="content">
+					<div class="post" id="preview_body">
+						', empty($context['preview_message']) ? '<br />' : $context['preview_message'], '
+					</div>
+				</div>
+				<span class="botslice"><span></span></span>
+			</div>
+		</div><br />';
 
 
 	// Main message editing box.
 	// Main message editing box.
 	echo '
 	echo '
@@ -980,20 +985,17 @@ function template_send()
 			<div class="roundframe"><br class="clear" />';
 			<div class="roundframe"><br class="clear" />';
 
 
 	// If there were errors for sending the PM, show them.
 	// If there were errors for sending the PM, show them.
-	if (!empty($context['post_error']['messages']))
-	{
-		echo '
+	echo '
 				<div class="', empty($context['error_type']) || $context['error_type'] != 'serious' ? 'noticebox' : 'errorbox', '"', empty($context['post_error']['messages']) ? ' style="display: none"' : '', ' id="errors">
 				<div class="', empty($context['error_type']) || $context['error_type'] != 'serious' ? 'noticebox' : 'errorbox', '"', empty($context['post_error']['messages']) ? ' style="display: none"' : '', ' id="errors">
 					<dl>
 					<dl>
 						<dt>
 						<dt>
-							<strong id="error_serious">', $txt['pm_error_while_submitting'] , '</strong>
+							<strong id="error_serious">', $txt['error_while_submitting'] , '</strong>
 						</dt>
 						</dt>
 						<dd class="error" id="error_list">
 						<dd class="error" id="error_list">
 							', empty($context['post_error']['messages']) ? '' : implode('<br />', $context['post_error']['messages']), '
 							', empty($context['post_error']['messages']) ? '' : implode('<br />', $context['post_error']['messages']), '
 						</dd>
 						</dd>
 					</dl>
 					</dl>
 				</div>';
 				</div>';
-	}
 
 
 	echo '
 	echo '
 				<dl id="post_header">';
 				<dl id="post_header">';
@@ -1001,7 +1003,7 @@ function template_send()
 	// To and bcc. Include a button to search for members.
 	// To and bcc. Include a button to search for members.
 	echo '
 	echo '
 					<dt>
 					<dt>
-						<span', (isset($context['post_error']['no_to']) || isset($context['post_error']['bad_to']) ? ' class="error"' : ''), '>', $txt['pm_to'], ':</span>
+						<span', (isset($context['post_error']['no_to']) || isset($context['post_error']['bad_to']) ? ' class="error"' : ''), ' id="caption_to">', $txt['pm_to'], ':</span>
 					</dt>';
 					</dt>';
 
 
 	// Autosuggest will be added by the JavaScript later on.
 	// Autosuggest will be added by the JavaScript later on.
@@ -1023,7 +1025,7 @@ function template_send()
 	// This BCC row will be hidden by default if JavaScript is enabled.
 	// This BCC row will be hidden by default if JavaScript is enabled.
 	echo '
 	echo '
 					<dt  class="clear_left" id="bcc_div">
 					<dt  class="clear_left" id="bcc_div">
-						<span', (isset($context['post_error']['no_to']) || isset($context['post_error']['bad_bcc']) ? ' class="error"' : ''), '>', $txt['pm_bcc'], ':</span>
+						<span', (isset($context['post_error']['no_to']) || isset($context['post_error']['bad_bcc']) ? ' class="error"' : ''), ' id="caption_bbc">', $txt['pm_bcc'], ':</span>
 					</dt>
 					</dt>
 					<dd id="bcc_div2">
 					<dd id="bcc_div2">
 						<input type="text" name="bcc" id="bcc_control" value="', $context['bcc_value'], '" tabindex="', $context['tabindex']++, '" size="40" style="width: 130px;" class="input_text" />
 						<input type="text" name="bcc" id="bcc_control" value="', $context['bcc_value'], '" tabindex="', $context['tabindex']++, '" size="40" style="width: 130px;" class="input_text" />
@@ -1033,7 +1035,7 @@ function template_send()
 	// The subject of the PM.
 	// The subject of the PM.
 	echo '
 	echo '
 					<dt class="clear_left">
 					<dt class="clear_left">
-						<span', (isset($context['post_error']['no_subject']) ? ' class="error"' : ''), '>', $txt['subject'], ':</span>
+						<span', (isset($context['post_error']['no_subject']) ? ' class="error"' : ''), ' id="caption_subject">', $txt['subject'], ':</span>
 					</dt>
 					</dt>
 					<dd id="pm_subject">
 					<dd id="pm_subject">
 						<input type="text" name="subject" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="60" maxlength="60"',isset($context['post_error']['no_subject']) ? ' class="error"' : ' class="input_text"', '/>
 						<input type="text" name="subject" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="60" maxlength="60"',isset($context['post_error']['no_subject']) ? ' class="error"' : ' class="input_text"', '/>
@@ -1090,6 +1092,116 @@ function template_send()
 		</div>
 		</div>
 	</form>';
 	</form>';
 
 
+	echo '
+		<script type="text/javascript"><!-- // --><![CDATA[';
+	// The functions used to preview a personal message without loading a new page.
+	echo '
+			var txt_preview_title = "', $txt['preview_title'], '";
+			var txt_preview_fetch = "', $txt['preview_fetch'], '";
+			function previewPost()
+			{';
+	if (isBrowser('is_firefox'))
+		echo '
+				// Firefox doesn\'t render <marquee> that have been put it using javascript
+				if (document.forms.postmodify.elements[', JavaScriptEscape($context['post_box_name']), '].value.indexOf(\'[move]\') != -1)
+				{
+					return submitThisOnce(document.forms.postmodify);
+				}';
+	echo '
+				if (window.XMLHttpRequest)
+				{
+					// Opera didn\'t support setRequestHeader() before 8.01.
+					// @todo Remove support for old browsers
+					if (\'opera\' in window)
+					{
+						var test = new XMLHttpRequest();
+						if (!(\'setRequestHeader\' in test))
+							return submitThisOnce(document.forms.postmodify);
+					}
+					// @todo Currently not sending poll options and option checkboxes.
+					var x = new Array();
+					var textFields = [\'subject\', ', JavaScriptEscape($context['post_box_name']), ', \'to\', \'bcc\'];
+					var numericFields = [\'recipient_to[]\', \'recipient_bcc[]\'];
+					var checkboxFields = [\'outbox\'];
+
+					for (var i = 0, n = textFields.length; i < n; i++)
+						if (textFields[i] in document.forms.postmodify)
+						{
+							// Handle the WYSIWYG editor.
+							if (textFields[i] == ', JavaScriptEscape($context['post_box_name']), ' && ', JavaScriptEscape('oEditorHandle_' . $context['post_box_name']), ' in window && oEditorHandle_', $context['post_box_name'], '.bRichTextEnabled)
+								x[x.length] = \'message_mode=1&\' + textFields[i] + \'=\' + oEditorHandle_', $context['post_box_name'], '.getText(false).replace(/&#/g, \'&#38;#\').php_to8bit().php_urlencode();
+							else
+								x[x.length] = textFields[i] + \'=\' + document.forms.postmodify[textFields[i]].value.replace(/&#/g, \'&#38;#\').php_to8bit().php_urlencode();
+						}
+					for (var i = 0, n = numericFields.length; i < n; i++)
+						if (numericFields[i] in document.forms.postmodify && \'value\' in document.forms.postmodify[numericFields[i]])
+							x[x.length] = numericFields[i] + \'=\' + parseInt(document.forms.postmodify.elements[numericFields[i]].value);
+					for (var i = 0, n = checkboxFields.length; i < n; i++)
+						if (checkboxFields[i] in document.forms.postmodify && document.forms.postmodify.elements[checkboxFields[i]].checked)
+							x[x.length] = checkboxFields[i] + \'=\' + document.forms.postmodify.elements[checkboxFields[i]].value;
+
+					sendXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=pm;sa=send2;preview;xml\', x.join(\'&\'), onDocSent);
+
+					document.getElementById(\'preview_section\').style.display = \'\';
+					setInnerHTML(document.getElementById(\'preview_subject\'), txt_preview_title);
+					setInnerHTML(document.getElementById(\'preview_body\'), txt_preview_fetch);
+
+					return false;
+				}
+				else
+					return submitThisOnce(document.forms.postmodify);
+			}
+			function onDocSent(XMLDoc)
+			{
+				if (!XMLDoc)
+				{
+					document.forms.postmodify.preview.onclick = new function ()
+					{
+						return true;
+					}
+					document.forms.postmodify.preview.click();
+				}
+
+				// Show the preview section.
+				var preview = XMLDoc.getElementsByTagName(\'smf\')[0].getElementsByTagName(\'preview\')[0];
+				setInnerHTML(document.getElementById(\'preview_subject\'), preview.getElementsByTagName(\'subject\')[0].firstChild.nodeValue);
+
+				var bodyText = \'\';
+				for (var i = 0, n = preview.getElementsByTagName(\'body\')[0].childNodes.length; i < n; i++)
+					bodyText += preview.getElementsByTagName(\'body\')[0].childNodes[i].nodeValue;
+
+				setInnerHTML(document.getElementById(\'preview_body\'), bodyText);
+				document.getElementById(\'preview_body\').className = \'post\';
+
+				// Show a list of errors (if any).
+				var errors = XMLDoc.getElementsByTagName(\'smf\')[0].getElementsByTagName(\'errors\')[0];
+				var errorList = new Array();
+				for (var i = 0, numErrors = errors.getElementsByTagName(\'error\').length; i < numErrors; i++)
+					errorList[errorList.length] = errors.getElementsByTagName(\'error\')[i].firstChild.nodeValue;
+				document.getElementById(\'errors\').style.display = numErrors == 0 ? \'none\' : \'\';
+				setInnerHTML(document.getElementById(\'error_list\'), numErrors == 0 ? \'\' : errorList.join(\'<br />\'));
+
+				// Adjust the color of captions if the given data is erroneous.
+				var captions = errors.getElementsByTagName(\'caption\');
+				for (var i = 0, numCaptions = errors.getElementsByTagName(\'caption\').length; i < numCaptions; i++)
+					if (document.getElementById(\'caption_\' + captions[i].getAttribute(\'name\')))
+						document.getElementById(\'caption_\' + captions[i].getAttribute(\'name\')).className = captions[i].getAttribute(\'class\');
+
+				if (errors.getElementsByTagName(\'post_error\').length == 1)
+					document.forms.postmodify.', $context['post_box_name'], '.style.border = \'1px solid red\';
+				else if (document.forms.postmodify.', $context['post_box_name'], '.style.borderColor == \'red\' || document.forms.postmodify.', $context['post_box_name'], '.style.borderColor == \'red red red red\')
+				{
+					if (\'runtimeStyle\' in document.forms.postmodify.', $context['post_box_name'], ')
+						document.forms.postmodify.', $context['post_box_name'], '.style.borderColor = \'\';
+					else
+						document.forms.postmodify.', $context['post_box_name'], '.style.border = null;
+				}
+				location.hash = \'#\' + \'preview_section\';
+			}';
+
+	echo '
+		// ]]></script>';
+
 	// Show the message you're replying to.
 	// Show the message you're replying to.
 	if ($context['reply'])
 	if ($context['reply'])
 		echo '
 		echo '

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

@@ -720,6 +720,8 @@ function template_main()
 					}
 					}
 				}
 				}
 
 
+				location.hash = \'#\' + \'preview_section\';
+
 				if (typeof(smf_codeFix) != \'undefined\')
 				if (typeof(smf_codeFix) != \'undefined\')
 					smf_codeFix();
 					smf_codeFix();
 			}';
 			}';

+ 149 - 16
Themes/default/Profile.template.php

@@ -26,8 +26,7 @@ function template_profile_above()
 	// ]]></script>';
 	// ]]></script>';
 
 
 	// If an error occurred while trying to save previously, give the user a clue!
 	// If an error occurred while trying to save previously, give the user a clue!
-	if (!empty($context['post_errors']))
-		echo '
+	echo '
 					', template_error_message();
 					', template_error_message();
 
 
 	// If the profile was update successfully, let the user know this.
 	// If the profile was update successfully, let the user know this.
@@ -1181,7 +1180,7 @@ function template_edit_options()
 
 
 	// The main header!
 	// The main header!
 	echo '
 	echo '
-		<form action="', (!empty($context['profile_custom_submit_url']) ? $context['profile_custom_submit_url'] : $scripturl . '?action=profile;area=' . $context['menu_item_selected'] . ';u=' . $context['id_member'] . ';save'), '" method="post" accept-charset="', $context['character_set'], '" name="creator" id="creator" enctype="multipart/form-data" onsubmit="return checkProfileSubmit();">
+		<form action="', (!empty($context['profile_custom_submit_url']) ? $context['profile_custom_submit_url'] : $scripturl . '?action=profile;area=' . $context['menu_item_selected'] . ';u=' . $context['id_member']), '" method="post" accept-charset="', $context['character_set'], '" name="creator" id="creator" enctype="multipart/form-data" onsubmit="return checkProfileSubmit();">
 			<div class="cat_bar">
 			<div class="cat_bar">
 				<h3 class="catbg">
 				<h3 class="catbg">
 					<span class="ie6_header floatleft"><img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />';
 					<span class="ie6_header floatleft"><img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />';
@@ -1365,10 +1364,10 @@ function template_edit_options()
 	// The button shouldn't say "Change profile" unless we're changing the profile...
 	// The button shouldn't say "Change profile" unless we're changing the profile...
 	if (!empty($context['submit_button_text']))
 	if (!empty($context['submit_button_text']))
 		echo '
 		echo '
-					<input type="submit" value="', $context['submit_button_text'], '" class="button_submit" />';
+					<input type="submit" name="save" value="', $context['submit_button_text'], '" class="button_submit" />';
 	else
 	else
 		echo '
 		echo '
-					<input type="submit" value="', $txt['change_profile'], '" class="button_submit" />';
+					<input type="submit" name="save" value="', $txt['change_profile'], '" class="button_submit" />';
 
 
 	if (!empty($context['token_check']))
 	if (!empty($context['token_check']))
 		echo '
 		echo '
@@ -2201,6 +2200,7 @@ function template_issueWarning()
 			document.getElementById(\'warn_body\').disabled = disable;
 			document.getElementById(\'warn_body\').disabled = disable;
 			document.getElementById(\'warn_temp\').disabled = disable;
 			document.getElementById(\'warn_temp\').disabled = disable;
 			document.getElementById(\'new_template_link\').style.display = disable ? \'none\' : \'\';
 			document.getElementById(\'new_template_link\').style.display = disable ? \'none\' : \'\';
+			document.getElementById(\'preview_button\').style.display = disable ? \'none\' : \'\';
 		}
 		}
 
 
 		function changeWarnLevel(amount)
 		function changeWarnLevel(amount)
@@ -2305,6 +2305,17 @@ function template_issueWarning()
 					</dd>
 					</dd>
 				</dl>
 				</dl>
 				<hr />
 				<hr />
+				<div id="box_preview"', !empty($context['warning_data']['body_preview']) ? '' : ' style="display:none"', '>
+					<dl class="settings">
+						<dt>
+							<strong>', $txt['preview'] , '</strong>
+						</dt>
+						<dd id="body_preview">
+							', !empty($context['warning_data']['body_preview']) ? $context['warning_data']['body_preview'] : '', '
+						</dd>
+					</dl>
+					<hr />
+				</div>
 				<dl class="settings">
 				<dl class="settings">
 					<dt>
 					<dt>
 						<strong><label for="warn_notify">', $txt['profile_warning_notify'], ':</label></strong>
 						<strong><label for="warn_notify">', $txt['profile_warning_notify'], ':</label></strong>
@@ -2342,18 +2353,70 @@ function template_issueWarning()
 
 
 	if (!empty($context['token_check']))
 	if (!empty($context['token_check']))
 		echo '
 		echo '
-				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+					<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
 
 
 	echo '
 	echo '
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<input type="submit" name="preview" id="preview_button" value="', $txt['preview'], '" class="button_submit" />
 					<input type="submit" name="save" value="', $context['user']['is_owner'] ? $txt['change_profile'] : $txt['profile_warning_issue'], '" class="button_submit" />
 					<input type="submit" name="save" value="', $context['user']['is_owner'] ? $txt['change_profile'] : $txt['profile_warning_issue'], '" class="button_submit" />
 				</div>
 				</div>
 				<br class="clear" />
 				<br class="clear" />
 			</div>
 			</div>
 			<span class="botslice"><span></span></span>
 			<span class="botslice"><span></span></span>
 		</div>
 		</div>
-	</form>
-	<br />';
+	</form>';
+
+	// Previous warnings?
+	echo '
+		<br />
+		<div class="cat_bar">
+			<h3 class="catbg">
+				', $txt['profile_warning_previous'], '
+			</h3>
+		</div>
+		<table border="0" width="100%" cellspacing="0" cellpadding="5" class="table_grid">
+			<thead>
+				<tr class="titlebg lefttext">
+					<th class="first_th" scope="col" width="20%">', $txt['profile_warning_previous_issued'], '</th>
+					<th scope="col" width="30%">', $txt['profile_warning_previous_time'], '</th>
+					<th scope="col">', $txt['profile_warning_previous_reason'], '</th>
+					<th class="last_th" scope="col" width="6%">', $txt['profile_warning_previous_level'], '</th>
+				</tr>
+			</thead>
+			<tbody>';
+
+	// Print the warnings.
+	$alternate = 0;
+	foreach ($context['previous_warnings'] as $warning)
+	{
+		$alternate = !$alternate;
+		echo '
+				<tr class="', $alternate ? 'windowbg' : 'windowbg2', '">
+					<td class="smalltext">', $warning['issuer']['link'], '</td>
+					<td class="smalltext">', $warning['time'], '</td>
+					<td class="smalltext">
+						<div class="floatleft">
+							', $warning['reason'], '
+						</div>';
+
+		if (!empty($warning['id_notice']))
+			echo '
+						<div class="floatright">
+							<a href="', $scripturl, '?action=moderate;area=notice;nid=', $warning['id_notice'], '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=400,height=250\');return false;" target="_blank" class="new_win" title="', $txt['profile_warning_previous_notice'], '"><img src="', $settings['images_url'], '/filter.gif" alt="" /></a>
+						</div>';
+		echo '
+					</td>
+					<td class="smalltext">', $warning['counter'], '</td>
+				</tr>';
+	}
+
+	if (empty($context['previous_warnings']))
+		echo '
+				<tr class="windowbg2">
+					<td align="center" colspan="4">
+						', $txt['profile_warning_previous_none'], '
+					</td>
+				</tr>';
 
 
 	template_show_list('view_warnings');
 	template_show_list('view_warnings');
 
 
@@ -2361,11 +2424,49 @@ function template_issueWarning()
 	echo '
 	echo '
 	<script type="text/javascript"><!-- // --><![CDATA[
 	<script type="text/javascript"><!-- // --><![CDATA[
 		document.getElementById(\'warndiv1\').style.display = "";
 		document.getElementById(\'warndiv1\').style.display = "";
+		document.getElementById(\'preview_button\').style.display = "none";
 		document.getElementById(\'warndiv2\').style.display = "none";';
 		document.getElementById(\'warndiv2\').style.display = "none";';
 
 
 	if (!$context['user']['is_owner'])
 	if (!$context['user']['is_owner'])
 		echo '
 		echo '
-		modifyWarnNotify();';
+		modifyWarnNotify();
+		$(document).ready(function() {
+			$("#preview_button").click(function() {
+				return ajax_getTemplatePreview();
+			});
+		});
+
+		function ajax_getTemplatePreview ()
+		{
+			$.ajax({
+				type: "POST",
+				url: "' . $scripturl . '?action=xmlhttp;sa=previews;xml",
+				data: {item: "warning_preview", title: $("#warn_sub").val(), body: $("#warn_body").val(), issuing: true},
+				context: document.body,
+				success: function(request){
+					$("#box_preview").css({display:""});
+					$("#body_preview").html($(request).find(\'body\').text());
+					if ($(request).find("error").text() != \'\')
+					{
+						$("#profile_error").css({display:""});
+						var errors_html = \'<span>\' + $("#profile_error").find("span").html() + \'</span>\' + \'<ul class="list_errors" class="reset">\';
+						var errors = $(request).find(\'error\').each(function() {
+							errors_html += \'<li>\' + $(this).text() + \'</li>\';
+						});
+						errors_html += \'</ul>\';
+
+						$("#profile_error").html(errors_html);
+					}
+					else
+					{
+						$("#profile_error").css({display:"none"});
+						$("#error_list").html(\'\');
+					}
+				return false;
+				},
+			});
+			return false;
+		}';
 
 
 	echo '
 	echo '
 	// ]]></script>';
 	// ]]></script>';
@@ -2499,9 +2600,13 @@ function template_error_message()
 	global $context, $txt;
 	global $context, $txt;
 
 
 	echo '
 	echo '
-		<div class="errorbox">
-			<span>', !empty($context['custom_error_title']) ? $context['custom_error_title'] : $txt['profile_errors_occurred'], '</span>
-			<ul class="reset">';
+		<div class="errorbox" ', empty($context['post_errors']) ? 'style="display:none" ' : '', 'id="profile_error">';
+
+	if (!empty($context['post_errors']))
+	{
+		echo '
+			<span>', !empty($context['custom_error_title']) ? $context['custom_error_title'] : $txt['profile_errors_occurred'], ':</span>
+			<ul id="list_errors">';
 
 
 		// Cycle through each error and display an error message.
 		// Cycle through each error and display an error message.
 		foreach ($context['post_errors'] as $error)
 		foreach ($context['post_errors'] as $error)
@@ -2509,7 +2614,10 @@ function template_error_message()
 				<li>', isset($txt['profile_error_' . $error]) ? $txt['profile_error_' . $error] : $error, '</li>';
 				<li>', isset($txt['profile_error_' . $error]) ? $txt['profile_error_' . $error] : $error, '</li>';
 
 
 		echo '
 		echo '
-			</ul>
+			</ul>';
+	}
+
+	echo '
 		</div>';
 		</div>';
 }
 }
 
 
@@ -2580,7 +2688,22 @@ function template_profile_birthdate()
 // Show the signature editing box?
 // Show the signature editing box?
 function template_profile_signature_modify()
 function template_profile_signature_modify()
 {
 {
-	global $txt, $context, $settings;
+	global $txt, $context, $settings, $scripturl;
+
+	echo '
+							<dt id="current_signature"', !isset($context['member']['current_signature']) ? ' style="display:none"' : '', '>
+								<strong>', $txt['current_signature'], ':</strong>
+							</dt>
+							<dd id="current_signature_display"', !isset($context['member']['current_signature']) ? ' style="display:none"' : '', '>
+								', isset($context['member']['current_signature']) ? $context['member']['current_signature'] : '', '<hr />
+							</dd>';
+	echo '
+							<dt id="preview_signature"', !isset($context['member']['signature_preview']) ? ' style="display:none"' : '', '>
+								<strong>', $txt['signature_preview'], ':</strong>
+							</dt>
+							<dd id="preview_signature_display"', !isset($context['member']['signature_preview']) ? ' style="display:none"' : '', '>
+								', isset($context['member']['signature_preview']) ? $context['member']['signature_preview'] : '', '<hr />
+							</dd>';
 
 
 	echo '
 	echo '
 							<dt>
 							<dt>
@@ -2595,13 +2718,17 @@ function template_profile_signature_modify()
 		echo '
 		echo '
 							</dt>
 							</dt>
 							<dd>
 							<dd>
-								<textarea class="editor" onkeyup="calcCharLeft();" name="signature" rows="5" cols="50" style="min-width: 50%; max-width: 99%;">', $context['member']['signature'], '</textarea><br />';
+								<textarea class="editor" onkeyup="calcCharLeft();" id="signature" name="signature" rows="5" cols="50" style="min-width: 50%; max-width: 99%;">', $context['member']['signature'], '</textarea><br />';
 
 
 	// If there is a limit at all!
 	// If there is a limit at all!
 	if (!empty($context['signature_limits']['max_length']))
 	if (!empty($context['signature_limits']['max_length']))
 		echo '
 		echo '
 								<span class="smalltext">', sprintf($txt['max_sig_characters'], $context['signature_limits']['max_length']), ' <span id="signatureLeft">', $context['signature_limits']['max_length'], '</span></span><br />';
 								<span class="smalltext">', sprintf($txt['max_sig_characters'], $context['signature_limits']['max_length']), ' <span id="signatureLeft">', $context['signature_limits']['max_length'], '</span></span><br />';
 
 
+	if (!empty($context['show_preview_button']))
+		echo '
+						<input type="submit" name="preview" id="preview_button" value="', $txt['preview_signature'], '" class="button_submit" />';
+
 	if ($context['signature_warning'])
 	if ($context['signature_warning'])
 		echo '
 		echo '
 								<span class="smalltext">', $context['signature_warning'], '</span>';
 								<span class="smalltext">', $context['signature_warning'], '</span>';
@@ -2615,7 +2742,13 @@ function template_profile_signature_modify()
 	echo '
 	echo '
 								<script type="text/javascript"><!-- // --><![CDATA[
 								<script type="text/javascript"><!-- // --><![CDATA[
 									var maxLength = ', $context['signature_limits']['max_length'], ';
 									var maxLength = ', $context['signature_limits']['max_length'], ';
-									addLoadEvent(tick);
+
+									$(document).ready(function() {
+										calcCharLeft();
+										$("#preview_button").click(function() {
+											return ajax_getSignaturePreview(true);
+										});
+									});
 								// ]]></script>
 								// ]]></script>
 							</dd>';
 							</dd>';
 }
 }

+ 29 - 0
Themes/default/Xml.template.php

@@ -132,6 +132,35 @@ function template_post()
 </smf>';
 </smf>';
 }
 }
 
 
+function template_pm()
+{
+	global $context, $settings, $options, $txt;
+
+	// @todo something could be removed...otherwise it can be merged again with template_post
+	echo '<', '?xml version="1.0" encoding="', $context['character_set'], '"?', '>
+<smf>
+	<preview>
+		<subject><![CDATA[', $context['preview_subject'], ']]></subject>
+		<body><![CDATA[', $context['preview_message'], ']]></body>
+	</preview>
+	<errors serious="', empty($context['error_type']) || $context['error_type'] != 'serious' ? '0' : '1', '">';
+	if (!empty($context['post_error']['messages']))
+		foreach ($context['post_error']['messages'] as $message)
+			echo '
+		<error><![CDATA[', cleanXml($message), ']]></error>';
+
+	echo '
+		<caption name="to" class="', isset($context['post_error']['no_to']) ? 'error' : '', '" />
+		<caption name="bbc" class="', isset($context['post_error']['no_bbc']) ? 'error' : '', '" />
+		<caption name="subject" class="', isset($context['post_error']['no_subject']) ? 'error' : '', '" />
+		<caption name="question" class="', isset($context['post_error']['no_question']) ? 'error' : '', '" />', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? '
+		<post_error />' : '', '
+	</errors>';
+
+	echo '
+</smf>';
+}
+
 function template_stats()
 function template_stats()
 {
 {
 	global $context, $settings, $options, $txt, $modSettings;
 	global $context, $settings, $options, $txt, $modSettings;

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

@@ -136,6 +136,7 @@ $txt['member_id'] = 'ID';
 $txt['unknown'] = 'unknown';
 $txt['unknown'] = 'unknown';
 $txt['security_wrong'] = 'Administration login attempt!' . "\n" . 'Referer: %1$s' . "\n" . 'User agent: %2$s' . "\n" . 'IP: %3$s';
 $txt['security_wrong'] = 'Administration login attempt!' . "\n" . 'Referer: %1$s' . "\n" . 'User agent: %2$s' . "\n" . 'IP: %3$s';
 
 
+$txt['email_preview_warning'] = 'The preview is not 100% accurate. In order to preserve the functionality of the page only the basic html tags are represented';
 $txt['email_as_html'] = 'Send in HTML format.  (with this you can put normal HTML in the email.)';
 $txt['email_as_html'] = 'Send in HTML format.  (with this you can put normal HTML in the email.)';
 $txt['email_parsed_html'] = 'Add &lt;br /&gt;s and &amp;nbsp;s to this message.';
 $txt['email_parsed_html'] = 'Add &lt;br /&gt;s and &amp;nbsp;s to this message.';
 $txt['email_variables'] = 'In this message you can use a few &quot;variables&quot;.  Click <a href="' . $scripturl . '?action=helpadmin;help=emailmembers" onclick="return reqWin(this.href);" class="help">here</a> for more information.';
 $txt['email_variables'] = 'In this message you can use a few &quot;variables&quot;.  Click <a href="' . $scripturl . '?action=helpadmin;help=emailmembers" onclick="return reqWin(this.href);" class="help">here</a> for more information.';
@@ -446,6 +447,7 @@ $txt['news_title'] = 'News and Newsletters';
 $txt['news_settings_desc'] = 'Here you can change the settings and permissions related to news and newsletters.';
 $txt['news_settings_desc'] = 'Here you can change the settings and permissions related to news and newsletters.';
 $txt['news_settings_submit'] = 'Save';
 $txt['news_settings_submit'] = 'Save';
 $txt['news_mailing_desc'] = 'From this menu you can send messages to all members who\'ve registered and entered their email addresses. You may edit the distribution list, or send messages to all. Useful for important update/news information.';
 $txt['news_mailing_desc'] = 'From this menu you can send messages to all members who\'ve registered and entered their email addresses. You may edit the distribution list, or send messages to all. Useful for important update/news information.';
+$txt['news_error_no_news'] = 'Nothing written';
 $txt['groups_edit_news'] = 'Groups allowed to edit news items';
 $txt['groups_edit_news'] = 'Groups allowed to edit news items';
 $txt['groups_send_mail'] = 'Groups allowed to send out forum newsletters';
 $txt['groups_send_mail'] = 'Groups allowed to send out forum newsletters';
 $txt['xmlnews_enable'] = 'Enable XML/RSS news';
 $txt['xmlnews_enable'] = 'Enable XML/RSS news';

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

@@ -177,6 +177,7 @@ $txt['invalid_hostname'] = 'Invalid host name / host name range.<br />Example of
 $txt['invalid_ip'] = 'Invalid IP / IP range.<br />Example of a valid IP address: 127.0.0.1<br />Example of a valid IP range: 127.0.0-20.*';
 $txt['invalid_ip'] = 'Invalid IP / IP range.<br />Example of a valid IP address: 127.0.0.1<br />Example of a valid IP range: 127.0.0-20.*';
 $txt['invalid_tracking_ip'] = 'Invalid IP / IP range.<br />Example of a valid IP address: 127.0.0.1<br />Example of a valid IP range: 127.0.0.*';
 $txt['invalid_tracking_ip'] = 'Invalid IP / IP range.<br />Example of a valid IP address: 127.0.0.1<br />Example of a valid IP range: 127.0.0.*';
 $txt['invalid_username'] = 'Member name not found';
 $txt['invalid_username'] = 'Member name not found';
+$txt['no_user_selected'] = 'Member not found';
 $txt['no_ban_admin'] = 'You may not ban an admin - You must demote them first!';
 $txt['no_ban_admin'] = 'You may not ban an admin - You must demote them first!';
 $txt['no_bantype_selected'] = 'No ban type was selected';
 $txt['no_bantype_selected'] = 'No ban type was selected';
 $txt['ban_not_found'] = 'Ban not found';
 $txt['ban_not_found'] = 'Ban not found';
@@ -316,6 +317,7 @@ $txt['profile_error_password_restricted_words'] = 'Your password must not contai
 $txt['profile_error_password_chars'] = 'Your password must contain a mix of upper and lower case letters, as well as digits.';
 $txt['profile_error_password_chars'] = 'Your password must contain a mix of upper and lower case letters, as well as digits.';
 $txt['profile_error_already_requested_group'] = 'You already have an outstanding request for this group!';
 $txt['profile_error_already_requested_group'] = 'You already have an outstanding request for this group!';
 $txt['profile_error_openid_in_use'] = 'Another user is already using that OpenID authentication URL';
 $txt['profile_error_openid_in_use'] = 'Another user is already using that OpenID authentication URL';
+$txt['profile_error_signature_not_yet_saved'] = 'The signature is not yet saved!';
 
 
 $txt['mysql_error_space'] = ' - check database storage space or contact the server administrator.';
 $txt['mysql_error_space'] = ' - check database storage space or contact the server administrator.';
 
 

+ 2 - 1
Themes/default/languages/ModerationCenter.english.php

@@ -125,7 +125,8 @@ $txt['mc_warning_template_body_desc'] = 'The content of the notification message
 $txt['mc_warning_template_body_default'] = '{MEMBER},' . "\n\n" . 'You have received a warning for inappropriate activity. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}';
 $txt['mc_warning_template_body_default'] = '{MEMBER},' . "\n\n" . 'You have received a warning for inappropriate activity. Please cease these activities and abide by the forum rules otherwise we will take further action.' . "\n\n" . '{REGARDS}';
 $txt['mc_warning_template_personal'] = 'Personal Template';
 $txt['mc_warning_template_personal'] = 'Personal Template';
 $txt['mc_warning_template_personal_desc'] = 'If you select this option only you will be able to see, edit and use this template. If not selected all moderators will be able to use this template.';
 $txt['mc_warning_template_personal_desc'] = 'If you select this option only you will be able to see, edit and use this template. If not selected all moderators will be able to use this template.';
-$txt['mc_warning_template_error_empty'] = 'You must set both a title and notification body.';
+$txt['mc_warning_template_error_no_title'] = 'You must set title.';
+$txt['mc_warning_template_error_no_body'] = 'You must set a notification body.';
 
 
 $txt['mc_settings'] = 'Change Settings';
 $txt['mc_settings'] = 'Change Settings';
 $txt['mc_prefs_title'] = 'Moderation Preferences';
 $txt['mc_prefs_title'] = 'Moderation Preferences';

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

@@ -9,7 +9,6 @@ $txt['message_icon'] = 'Message icon';
 $txt['subject_not_filled'] = 'The Subject field was not filled out. It is required.';
 $txt['subject_not_filled'] = 'The Subject field was not filled out. It is required.';
 $txt['message_body_not_filled'] = 'The Message Body was not filled out. It is required.';
 $txt['message_body_not_filled'] = 'The Message Body was not filled out. It is required.';
 // Use numeric entities in the below string.
 // Use numeric entities in the below string.
-$txt['regards_team'] = "Regards,\nThe " . $context['forum_name'] . ' Team.';
 $txt['add_bbc'] = 'Add BBC tags';
 $txt['add_bbc'] = 'Add BBC tags';
 $txt['bold'] = 'Bold';
 $txt['bold'] = 'Bold';
 $txt['italic'] = 'Italicized';
 $txt['italic'] = 'Italicized';

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

@@ -9,6 +9,9 @@ $txt['website_url'] = 'Website URL';
 $txt['signature'] = 'Signature';
 $txt['signature'] = 'Signature';
 $txt['profile_posts'] = 'Posts';
 $txt['profile_posts'] = 'Posts';
 $txt['change_profile'] = 'Change profile';
 $txt['change_profile'] = 'Change profile';
+$txt['preview_signature'] = 'Preview signature';
+$txt['current_signature'] = 'Current signature';
+$txt['signature_preview'] = 'Signature preview';
 $txt['delete_user'] = 'Delete user';
 $txt['delete_user'] = 'Delete user';
 $txt['current_status'] = 'Current Status:';
 $txt['current_status'] = 'Current Status:';
 $txt['personal_text'] = 'Personal Text';
 $txt['personal_text'] = 'Personal Text';

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

@@ -95,6 +95,8 @@ $txt['message_index'] = 'Message Index';
 $txt['news'] = 'News';
 $txt['news'] = 'News';
 $txt['home'] = 'Home';
 $txt['home'] = 'Home';
 $txt['page'] = 'Page';
 $txt['page'] = 'Page';
+$txt['prev'] = 'previous';
+$txt['next'] = 'next';
 
 
 $txt['lock_unlock'] = 'Lock/Unlock Topic';
 $txt['lock_unlock'] = 'Lock/Unlock Topic';
 $txt['post'] = 'Post';
 $txt['post'] = 'Post';
@@ -112,7 +114,7 @@ $txt['notify'] = 'Notify';
 $txt['unnotify'] = 'Unnotify';
 $txt['unnotify'] = 'Unnotify';
 $txt['notify_request'] = 'Do you want a notification email if someone replies to this topic?';
 $txt['notify_request'] = 'Do you want a notification email if someone replies to this topic?';
 // Use numeric entities in the below string.
 // Use numeric entities in the below string.
-$txt['regards_team'] = 'Regards,' . "\n" . 'The ' . $context['forum_name'] . ' Team.';
+$txt['regards_team'] = "Regards,\nThe " . $context['forum_name'] . ' Team.';
 $txt['notify_replies'] = 'Notify of replies';
 $txt['notify_replies'] = 'Notify of replies';
 $txt['move_topic'] = 'Move Topic';
 $txt['move_topic'] = 'Move Topic';
 $txt['move_to'] = 'Move to';
 $txt['move_to'] = 'Move to';

+ 54 - 15
Themes/default/scripts/script.js

@@ -1563,20 +1563,10 @@ function smfSetLatestPackages()
 	tempOldOnload();
 	tempOldOnload();
 }
 }
 
 
-function tick()
-{
-	if (typeof(document.forms.creator) != "undefined")
-	{
-		calcCharLeft();
-		setTimeout("tick()", 1000);
-	}
-	else
-		setTimeout("tick()", 800);
-}
-
 function calcCharLeft()
 function calcCharLeft()
 {
 {
 	var oldSignature = "", currentSignature = document.forms.creator.signature.value;
 	var oldSignature = "", currentSignature = document.forms.creator.signature.value;
+	var currentChars = 0;
 
 
 	if (!document.getElementById("signatureLeft"))
 	if (!document.getElementById("signatureLeft"))
 		return;
 		return;
@@ -1585,12 +1575,61 @@ function calcCharLeft()
 	{
 	{
 		oldSignature = currentSignature;
 		oldSignature = currentSignature;
 
 
-		if (currentSignature.replace(/\r/, "").length > maxLength)
-			document.forms.creator.signature.value = currentSignature.replace(/\r/, "").substring(0, maxLength);
-		currentSignature = document.forms.creator.signature.value.replace(/\r/, "");
+		var currentChars = currentSignature.replace(/\r/, "").length;
+		if (is_opera)
+			currentChars = currentSignature.replace(/\r/g, "").length;
+
+		ajax_getSignaturePreview(false);
+		if (currentChars > maxLength)
+			document.getElementById("signatureLeft").className = "error";
+		else
+			document.getElementById("signatureLeft").className = "";
 	}
 	}
 
 
-	setInnerHTML(document.getElementById("signatureLeft"), maxLength - currentSignature.length);
+	setInnerHTML(document.getElementById("signatureLeft"), maxLength - currentChars);
+}
+
+function ajax_getSignaturePreview (showPreview)
+{
+	showPreview = (typeof showPreview == 'undefined') ? false : showPreview;
+	$.ajax({
+		type: "POST",
+		url: smf_scripturl + "?action=xmlhttp;sa=previews;xml",
+		data: {item: "sig_preview", signature: $("#signature").val(), user: $('input[name="u"]').attr("value")},
+		context: document.body,
+		success: function(request){
+			if (showPreview)
+			{
+				var signatures = new Array("current", "preview");
+				for (var i = 0; i < signatures.length; i++)
+				{
+					$("#" + signatures[i] + "_signature").css({display:""});
+					$("#" + signatures[i] + "_signature_display").css({display:""}).html($(request).find('[type="' + signatures[i] + '"]').text() + '<hr />');
+				}
+			}
+
+			if ($(request).find("error").text() != '')
+			{
+				if (!$("#profile_error").is(":visible"))
+					$("#profile_error").css({display: "", position: "fixed", top: 0, left: 0, width: "100%"});
+				var errors = $(request).find('[type="error"]');
+				var errors_html = '<span>' + $(request).find('[type="errors_occurred"]').text() + '</span><ul class="reset">';
+
+				for (var i = 0; i < errors.length; i++)
+					errors_html += '<li>' + $(errors).text() + '</li>';
+
+				errors_html += '</ul>';
+				$(document).find("#profile_error").html(errors_html);
+			}
+			else
+			{
+				$("#profile_error").css({display:"none"});
+				$("#profile_error").html('');
+			}
+		return false;
+		},
+	});
+	return false;
 }
 }
 
 
 function changeSel(selected)
 function changeSel(selected)