Преглед изворни кода

Merge branch 'previews'

Conflicts:
	Sources/Xml.php
	Themes/default/ManageNews.template.php
	Themes/default/ModerationCenter.template.php
	Themes/default/PersonalMessage.template.php
	Themes/default/Profile.template.php
	Themes/default/languages/Admin.english.php
emanuele пре 12 година
родитељ
комит
7f2ef790d2

+ 261 - 12
Sources/ManageNews.php

@@ -85,7 +85,7 @@ function ManageNews()
  */
 function EditNews()
 {
-	global $txt, $modSettings, $context, $sourcedir, $user_info;
+	global $txt, $modSettings, $context, $sourcedir, $user_info, $scripturl;
 	global $smcFunc;
 
 	require_once($sourcedir . '/Subs-Post.php');
@@ -131,16 +131,157 @@ function EditNews()
 		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.
 	foreach (explode("\n", $modSettings['news']) as $id => $line)
-		$context['admin_current_news'][$id] = array(
+		$admin_current_news[$id] = array(
 			'id' => $id,
 			'unparsed' => un_preparsecode($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');
 }
 
+/**
+ * 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.
  * Called by ?action=admin;area=news;sa=mailingcompose.
@@ -278,7 +476,49 @@ function SelectMailingMembers()
  */
 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!
 	$toClean = array();
@@ -421,13 +661,6 @@ function ComposeMailing()
 	// Clean up the arrays.
 	$context['recipients']['members'] = array_unique($context['recipients']['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 $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.
 	$num_at_once = empty($modSettings['mail_queue']) ? 60 : 1000;
 
@@ -537,6 +776,10 @@ function SendMailing($clean_only = false)
 
 	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
 	$context['subject'] = htmlspecialchars($_POST['subject']);
 	$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.
 	$user_info['time_format'] = $modSettings['time_format'];
 

+ 68 - 57
Sources/ModerationCenter.php

@@ -1974,72 +1974,83 @@ function ModifyWarningTemplate()
 		$_POST['template_title'] = trim($_POST['template_title']);
 
 		// 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']));
-			// 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
 		{
-			$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');

+ 33 - 14
Sources/PersonalMessage.php

@@ -41,7 +41,7 @@ function MessageMain()
 		fatal_lang_error('wireless_error_notyet', false);
 	elseif (WIRELESS)
 		$context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
-	else
+	elseif (!isset($_REQUEST['xml']))
 		loadTemplate('PersonalMessage');
 
 	// Load up the members maximum message capacity.
@@ -203,7 +203,8 @@ function MessageMain()
 		MessageFolder();
 	else
 	{
-		messageIndexBar($_REQUEST['sa']);
+		if (!isset($_REQUEST['xml']))
+			messageIndexBar($_REQUEST['sa']);
 		$subActions[$_REQUEST['sa']]();
 	}
 }
@@ -347,7 +348,7 @@ function messageIndexBar($area)
 	$context['menu_item_selected'] = $pm_include_data['current_area'];
 
 	// obExit will know what to do!
-	if (!WIRELESS)
+	if (!WIRELESS && !isset($_REQUEST['xml']))
 		$context['template_layers'][] = 'pm';
 }
 
@@ -1770,6 +1771,7 @@ function MessagePost()
 		'labels' => array(
 			'post_button' => $txt['send_message'],
 		),
+		'preview_type' => 2,
 	);
 	create_control_richedit($editorOptions);
 
@@ -1804,10 +1806,13 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 	global $txt, $context, $scripturl, $modSettings;
 	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';
+	elseif (isset($_REQUEST['xml']))
+		$context['sub_template'] = 'pm';
 
 	$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)
-			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);
 		$smcFunc['db_free_result']($request);
 
@@ -1905,7 +1915,10 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 	
 	$context['post_error'] = array(
 		'messages' => array(),
+		// @todo error handling: maybe fatal errors can be error_type => serious
+		'error_type' => '',
 	);
+
 	foreach ($error_types as $error_type)
 	{
 		$context['post_error'][$error_type] = true;
@@ -1932,6 +1945,7 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 		'labels' => array(
 			'post_button' => $txt['send_message'],
 		),
+		'preview_type' => 2,
 	);
 	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.
 	$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');
 		$verificationOptions = array(
@@ -1976,6 +1990,9 @@ function MessagePost2()
 	// 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']);
 
+	// 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!
 	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);
 
 		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.
@@ -2011,11 +2033,8 @@ function MessagePost2()
 		$_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 (checkSession('post', '', false) != '')
+	if (!isset($_REQUEST['xml']) && checkSession('post', '', false) != '')
 		$post_errors[] = 'session_timeout';
 
 	$_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
@@ -2135,7 +2154,7 @@ function MessagePost2()
 	}
 
 	// 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');
 		$verificationOptions = array(
@@ -2150,7 +2169,7 @@ function MessagePost2()
 	}
 
 	// 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);
 
 	// Want to take a second glance before you send?

+ 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'))
 		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.
 	$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;
@@ -212,15 +216,6 @@ function issueWarning($memID)
 		}
 		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.
 			$context['warning_data'] = array(
 				'reason' => $_POST['warn_reason'],
@@ -234,6 +229,41 @@ function issueWarning($memID)
 		$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'];
 
 	// 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!
 	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;
 
 		// What gets updated?
@@ -1584,6 +1584,7 @@ function forumProfile($memID)
 
 	$context['sub_template'] = 'edit_options';
 	$context['page_desc'] = $txt['forumProfile_info'];
+	$context['show_preview_button'] = true;
 
 	setupProfileContext(
 		array(
@@ -2387,7 +2388,7 @@ function profileLoadGroups()
  */
 function profileLoadSignatureData()
 {
-	global $modSettings, $context, $txt, $cur_profile, $smcFunc;
+	global $modSettings, $context, $txt, $cur_profile, $smcFunc, $memberContext;
 
 	// Signature limits.
 	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['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;
 }
@@ -2856,13 +2877,6 @@ function profileValidateSignature(&$value)
 		$disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
 
 		$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?
 		if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2])
 		{
@@ -3007,6 +3021,14 @@ function profileValidateSignature(&$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;
 }
 

+ 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!
 	$context['completed_save'] = false;
+	$context['do_preview'] = isset($_REQUEST['preview']);
+
 	$security_checks = array();
 	$found_area = false;
 	foreach ($profile_areas as $section_id => $section)
@@ -645,7 +647,7 @@ function ModifyProfile($post_errors = array())
 			$context['modify_error'][$error_type] = true;
 	}
 	// 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');
 	elseif (!empty($force_redirect))
 		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);

+ 216 - 0
Sources/Xml.php

@@ -31,6 +31,9 @@ function XMLhttpMain()
 		'corefeatures' => array(
 			'function' => 'EnableCoreFeatures',
 		),
+		'previews' => array(
+			'function' => 'RetrievePreview',
+		),
 	);
 	if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']]))
 		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;
 
+	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 '
 	<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">
 				<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'], '
@@ -275,20 +293,47 @@ function template_email_members_compose()
 			<div class="windowbg">
 				<span class="topslice"><span></span></span>
 				<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">
-						<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>
 					</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>
 				<span class="botslice"><span></span></span>
 			</div>
@@ -302,6 +347,129 @@ function template_email_members_compose()
 			<input type="hidden" name="', $key, '" value="', implode(($key == 'emails' ? ';' : ','), $values), '" />';
 
 	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>
 	</div>
 	<br class="clear" />';

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

@@ -823,6 +823,26 @@ function template_warn_template()
 			<div class="windowbg">
 				<span class="topslice"><span></span></span>
 				<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">
 						<dt>
 							<strong><label for="template_title">', $txt['mc_warning_template_title'], '</label>:</strong>
@@ -851,6 +871,7 @@ function template_warn_template()
 
 	echo '
 					<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" />
 					<br class="clear_right" />
 				</div>
@@ -860,7 +881,45 @@ function template_warn_template()
 			<input type="hidden" name="', $context['mod-wt_token_var'], '" value="', $context['mod-wt_token'], '" />
 		</form>
 	</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
  */
 
+
 // This is the main sidebar for the personal messages section.
 function template_pm_above()
 {
@@ -951,19 +952,23 @@ function template_send()
 	}
 
 	// Show the preview of the personal message.
-	if (isset($context['preview_message']))
 	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>
-			<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.
 	echo '
@@ -980,20 +985,17 @@ function template_send()
 			<div class="roundframe"><br class="clear" />';
 
 	// 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">
 					<dl>
 						<dt>
-							<strong id="error_serious">', $txt['pm_error_while_submitting'] , '</strong>
+							<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>';
-	}
 
 	echo '
 				<dl id="post_header">';
@@ -1001,7 +1003,7 @@ function template_send()
 	// To and bcc. Include a button to search for members.
 	echo '
 					<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>';
 
 	// 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.
 	echo '
 					<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>
 					<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" />
@@ -1033,7 +1035,7 @@ function template_send()
 	// The subject of the PM.
 	echo '
 					<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>
 					<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"', '/>
@@ -1090,6 +1092,116 @@ function template_send()
 		</div>
 	</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.
 	if ($context['reply'])
 		echo '

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

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

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

@@ -26,8 +26,7 @@ function template_profile_above()
 	// ]]></script>';
 
 	// If an error occurred while trying to save previously, give the user a clue!
-	if (!empty($context['post_errors']))
-		echo '
+	echo '
 					', template_error_message();
 
 	// If the profile was update successfully, let the user know this.
@@ -1181,7 +1180,7 @@ function template_edit_options()
 
 	// The main header!
 	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">
 				<h3 class="catbg">
 					<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...
 	if (!empty($context['submit_button_text']))
 		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
 		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']))
 		echo '
@@ -2201,6 +2200,7 @@ function template_issueWarning()
 			document.getElementById(\'warn_body\').disabled = disable;
 			document.getElementById(\'warn_temp\').disabled = disable;
 			document.getElementById(\'new_template_link\').style.display = disable ? \'none\' : \'\';
+			document.getElementById(\'preview_button\').style.display = disable ? \'none\' : \'\';
 		}
 
 		function changeWarnLevel(amount)
@@ -2305,6 +2305,17 @@ function template_issueWarning()
 					</dd>
 				</dl>
 				<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">
 					<dt>
 						<strong><label for="warn_notify">', $txt['profile_warning_notify'], ':</label></strong>
@@ -2342,18 +2353,70 @@ function template_issueWarning()
 
 	if (!empty($context['token_check']))
 		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 '
 					<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" />
 				</div>
 				<br class="clear" />
 			</div>
 			<span class="botslice"><span></span></span>
 		</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');
 
@@ -2361,11 +2424,49 @@ function template_issueWarning()
 	echo '
 	<script type="text/javascript"><!-- // --><![CDATA[
 		document.getElementById(\'warndiv1\').style.display = "";
+		document.getElementById(\'preview_button\').style.display = "none";
 		document.getElementById(\'warndiv2\').style.display = "none";';
 
 	if (!$context['user']['is_owner'])
 		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 '
 	// ]]></script>';
@@ -2499,9 +2600,13 @@ function template_error_message()
 	global $context, $txt;
 
 	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.
 		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>';
 
 		echo '
-			</ul>
+			</ul>';
+	}
+
+	echo '
 		</div>';
 }
 
@@ -2580,7 +2688,22 @@ function template_profile_birthdate()
 // Show the signature editing box?
 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 '
 							<dt>
@@ -2595,13 +2718,17 @@ function template_profile_signature_modify()
 		echo '
 							</dt>
 							<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 (!empty($context['signature_limits']['max_length']))
 		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 />';
 
+	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'])
 		echo '
 								<span class="smalltext">', $context['signature_warning'], '</span>';
@@ -2615,7 +2742,13 @@ function template_profile_signature_modify()
 	echo '
 								<script type="text/javascript"><!-- // --><![CDATA[
 									var maxLength = ', $context['signature_limits']['max_length'], ';
-									addLoadEvent(tick);
+
+									$(document).ready(function() {
+										calcCharLeft();
+										$("#preview_button").click(function() {
+											return ajax_getSignaturePreview(true);
+										});
+									});
 								// ]]></script>
 							</dd>';
 }

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

@@ -132,6 +132,35 @@ function template_post()
 </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()
 {
 	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['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_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.';
@@ -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_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_error_no_news'] = 'Nothing written';
 $txt['groups_edit_news'] = 'Groups allowed to edit news items';
 $txt['groups_send_mail'] = 'Groups allowed to send out forum newsletters';
 $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_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['no_user_selected'] = 'Member not found';
 $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['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_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_signature_not_yet_saved'] = 'The signature is not yet saved!';
 
 $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_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_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_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['message_body_not_filled'] = 'The Message Body was not filled out. It is required.';
 // Use numeric entities in the below string.
-$txt['regards_team'] = "Regards,\nThe " . $context['forum_name'] . ' Team.';
 $txt['add_bbc'] = 'Add BBC tags';
 $txt['bold'] = 'Bold';
 $txt['italic'] = 'Italicized';

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

@@ -9,6 +9,9 @@ $txt['website_url'] = 'Website URL';
 $txt['signature'] = 'Signature';
 $txt['profile_posts'] = 'Posts';
 $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['current_status'] = 'Current Status:';
 $txt['personal_text'] = 'Personal Text';

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

@@ -114,7 +114,7 @@ $txt['notify'] = 'Notify';
 $txt['unnotify'] = 'Unnotify';
 $txt['notify_request'] = 'Do you want a notification email if someone replies to this topic?';
 // 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['move_topic'] = 'Move Topic';
 $txt['move_to'] = 'Move to';