Browse Source

Merge branch 'master' of https://github.com/SimpleMachines/SMF2.1

emanuele 12 years ago
parent
commit
359a39ad24
100 changed files with 2196 additions and 939 deletions
  1. 11 3
      Sources/Admin.php
  2. 3 3
      Sources/BoardIndex.php
  3. 10 10
      Sources/Calendar.php
  4. 13 10
      Sources/Class-BrowserDetect.php
  5. 14 15
      Sources/Class-CurlFetchWeb.php
  6. 1 1
      Sources/DbPackages-mysql.php
  7. 8 6
      Sources/Display.php
  8. 884 0
      Sources/Drafts.php
  9. 7 7
      Sources/DumpDatabase.php
  10. 1 1
      Sources/Help.php
  11. 51 45
      Sources/Load.php
  12. 11 1
      Sources/LogInOut.php
  13. 1 1
      Sources/ManageAttachments.php
  14. 1 1
      Sources/ManageCalendar.php
  15. 4 4
      Sources/ManageErrors.php
  16. 46 12
      Sources/ManageMaintenance.php
  17. 11 14
      Sources/ManageMembers.php
  18. 2 2
      Sources/ManageNews.php
  19. 6 0
      Sources/ManagePermissions.php
  20. 2 2
      Sources/ManagePosts.php
  21. 2 2
      Sources/ManageSearch.php
  22. 3 3
      Sources/ManageSearchEngines.php
  23. 17 18
      Sources/ManageServer.php
  24. 27 2
      Sources/ManageSettings.php
  25. 1 1
      Sources/ManageSmileys.php
  26. 10 10
      Sources/Memberlist.php
  27. 30 3
      Sources/MessageIndex.php
  28. 2 2
      Sources/ModerationCenter.php
  29. 3 3
      Sources/News.php
  30. 6 6
      Sources/Packages.php
  31. 67 11
      Sources/PersonalMessage.php
  32. 3 3
      Sources/Poll.php
  33. 52 7
      Sources/Post.php
  34. 2 2
      Sources/PostModeration.php
  35. 3 3
      Sources/Profile-Modify.php
  36. 20 20
      Sources/Profile-View.php
  37. 11 1
      Sources/Profile.php
  38. 2 2
      Sources/QueryString.php
  39. 5 5
      Sources/Recent.php
  40. 1 1
      Sources/Register.php
  41. 1 1
      Sources/Reminder.php
  42. 3 3
      Sources/RemoveTopic.php
  43. 12 12
      Sources/Reports.php
  44. 47 7
      Sources/ScheduledTasks.php
  45. 5 5
      Sources/SearchAPI-Custom.php
  46. 20 2
      Sources/Security.php
  47. 7 7
      Sources/SplitTopics.php
  48. 4 4
      Sources/Stats.php
  49. 16 16
      Sources/Subs-Admin.php
  50. 31 3
      Sources/Subs-BoardIndex.php
  51. 1 1
      Sources/Subs-Boards.php
  52. 7 7
      Sources/Subs-Calendar.php
  53. 7 7
      Sources/Subs-Categories.php
  54. 2 2
      Sources/Subs-Db-mysql.php
  55. 23 25
      Sources/Subs-Editor.php
  56. 6 6
      Sources/Subs-Graphics.php
  57. 10 1
      Sources/Subs-Members.php
  58. 2 2
      Sources/Subs-MembersOnline.php
  59. 1 1
      Sources/Subs-Menu.php
  60. 1 1
      Sources/Subs-OpenID.php
  61. 1 1
      Sources/Subs-Package.php
  62. 9 6
      Sources/Subs-Post.php
  63. 2 2
      Sources/Subs-Sound.php
  64. 82 92
      Sources/Subs.php
  65. 1 1
      Sources/Themes.php
  66. 7 6
      Sources/Who.php
  67. 3 3
      Sources/Xml.php
  68. 10 28
      Themes/default/Admin.template.php
  69. 7 7
      Themes/default/BoardIndex.template.php
  70. 2 2
      Themes/default/Calendar.template.php
  71. 29 7
      Themes/default/Display.template.php
  72. 4 4
      Themes/default/Errors.template.php
  73. 61 3
      Themes/default/GenericControls.template.php
  74. 8 32
      Themes/default/GenericList.template.php
  75. 4 4
      Themes/default/GenericMenu.template.php
  76. 18 6
      Themes/default/ManageMaintenance.template.php
  77. 3 3
      Themes/default/ManageMembergroups.template.php
  78. 1 1
      Themes/default/ManageMembers.template.php
  79. 2 2
      Themes/default/ManageNews.template.php
  80. 1 2
      Themes/default/ManagePermissions.template.php
  81. 2 2
      Themes/default/Memberlist.template.php
  82. 5 5
      Themes/default/Packages.template.php
  83. 125 5
      Themes/default/PersonalMessage.template.php
  84. 65 4
      Themes/default/Post.template.php
  85. 112 12
      Themes/default/Profile.template.php
  86. 1 1
      Themes/default/Reminder.template.php
  87. 5 5
      Themes/default/Themes.template.php
  88. 8 8
      Themes/default/Who.template.php
  89. 0 29
      Themes/default/css/editor.css
  90. 0 40
      Themes/default/css/editor_ie.css
  91. 0 170
      Themes/default/css/ie6.css
  92. 0 86
      Themes/default/css/ie7.css
  93. BIN
      Themes/default/images/admin/feature_dr.png
  94. BIN
      Themes/default/images/theme/frame_repeat.png
  95. BIN
      Themes/default/images/theme/main_block.png
  96. BIN
      Themes/default/images/theme/menu_gfx.png
  97. 8 4
      Themes/default/index.template.php
  98. 7 0
      Themes/default/languages/Admin.english.php
  99. 41 0
      Themes/default/languages/Drafts.english.php
  100. 2 0
      Themes/default/languages/Errors.english.php

+ 11 - 3
Sources/Admin.php

@@ -30,7 +30,7 @@ function AdminMain()
 	// Load the language and templates....
 	loadLanguage('Admin');
 	loadTemplate('Admin', 'admin');
-	loadJavascriptFile('admin.js?alp21', array('default_theme' => true));
+	loadJavascriptFile('admin.js?alp21', array('default_theme' => true), 'admin.js');
 
 	// No indexing evil stuff.
 	$context['robot_no_index'] = true;
@@ -227,6 +227,14 @@ function AdminMain()
 						'topics' => array($txt['manageposts_topic_settings']),
 					),
 				),
+				'managedrafts' => array(
+					'label' => $txt['manage_drafts'],
+					'file' => 'Drafts.php',
+					'function' => 'ModifyDraftSettings',
+					'icon' => 'logs.png',
+					'permission' => array('admin_forum'),
+					'enabled' => in_array('dr', $context['admin_features']),
+				),
 				'managecalendar' => array(
 					'label' => $txt['manage_calendar'],
 					'file' => 'ManageCalendar.php',
@@ -995,7 +1003,7 @@ function AdminEndSession()
 		if (strpos($key, '-admin') !== false)
 			unset($_SESSION['token'][$key]);
 
-	redirectexit('?action=admin');
+	redirectexit('action=admin');
 }
 
-?>
+?>

+ 3 - 3
Sources/BoardIndex.php

@@ -108,14 +108,14 @@ function BoardIndex()
 		$context['show_calendar'] = false;
 
 	$context['page_title'] = sprintf($txt['forum_index'], $context['forum_name']);
-	
+
 	// Mark read button
 	$context['mark_read_button'] = array(
 		'markread' => array('text' => 'mark_as_read', 'image' => 'markread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=all;' . $context['session_var'] . '=' . $context['session_id']),
 	);
-	
+
 	// Allow mods to add additional buttons here
-	call_integration_hook('integrate_mark_read_button'); 
+	call_integration_hook('integrate_mark_read_button');
 }
 
 /**

+ 10 - 10
Sources/Calendar.php

@@ -144,12 +144,12 @@ function CalendarMain()
 			'url' => $scripturl . '?action=calendar;viewweek;year=' . $context['current_year'] . ';month=' . $context['current_month'] . ';day=' . $context['current_day'],
 			'name' => $txt['calendar_week'] . ' ' . $context['calendar_grid_main']['week_number']
 		);
-		
+
 	// Build the calendar button array.
 	$context['calendar_buttons'] = array(
 		'post_event' => array('test' => 'can_post', 'text' => 'calendar_post_event', 'image' => 'calendarpe.png', 'lang' => true, 'url' => $scripturl . '?action=calendar;sa=post;month=' . $context['current_month'] . ';year=' . $context['current_year'] . ';' . $context['session_var'] . '=' . $context['session_id']),
 	);
-	
+
 	// Allow mods to add additional buttons here
 	call_integration_hook('integrate_calendar_buttons');
 }
@@ -234,7 +234,7 @@ function CalendarPost()
 				list ($id_board, $id_topic) = $smcFunc['db_fetch_row']($request);
 				$smcFunc['db_free_result']($request);
 			}
-			
+
 			$eventOptions = array(
 				'title' => substr($_REQUEST['evtitle'], 0, 60),
 				'span' => empty($modSettings['cal_allowspan']) || empty($_POST['span']) || $_POST['span'] == 1 || empty($modSettings['cal_maxspan']) || $_POST['span'] > $modSettings['cal_maxspan'] ? 0 : min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1),
@@ -342,7 +342,7 @@ function iCalDownload()
 	// You can't export if the calendar export feature is off.
 	if (empty($modSettings['cal_export']))
 		fatal_lang_error('calendar_export_off', false);
-	
+
 	// Goes without saying that this is required.
 	if (!isset($_REQUEST['eventid']))
 		fatal_lang_error('no_access', false);
@@ -352,7 +352,7 @@ function iCalDownload()
 
 	// Load up the event in question and check it exists.
 	$event = getEventProperties($_REQUEST['eventid']);
-	
+
 	if ($event === false)
 		fatal_lang_error('no_access', false);
 
@@ -387,15 +387,15 @@ function iCalDownload()
 	$filecontents .= 'ORGANIZER;CN="' . $event['realname'] . '":MAILTO:' . $webmaster_email . "\n";
 	$filecontents .= 'DTSTAMP:' . $datestamp . "\n";
 	$filecontents .= 'DTSTART;VALUE=DATE:' . $datestart . "\n";
-	
+
 	// more than one day
 	if ($event['span'] > 1)
 		$filecontents .= 'DTEND;VALUE=DATE:' . $dateend . "\n";
-	
+
 	// event has changed? advance the sequence for this UID
 	if ($event['sequence'] > 0)
 		$filecontents .= 'SEQUENCE:' . $event['sequence'] . "\n";
-	
+
 	$filecontents .= 'SUMMARY:' . implode('', $title);
 	$filecontents .= 'UID:' . $event['eventid'] . '@' . str_replace(' ', '-', $mbname) . "\n";
 	$filecontents .= 'END:VEVENT' . "\n";
@@ -407,7 +407,7 @@ function iCalDownload()
 		@ob_start('ob_gzhandler');
 	else
 		ob_start();
-	
+
 	// Send the file headers
 	header('Pragma: ');
 	header('Cache-Control: no-cache');
@@ -420,7 +420,7 @@ function iCalDownload()
 	header('Content-Disposition: attachment; filename="' . $event['title'] . '.ics"');
 	if (empty($modSettings['enableCompressedOutput']))
 		header('Content-Length: ' . $smcFunc['strlen']($filecontents));
-	
+
 	// This is a calendar item!
 	header('Content-Type: text/calendar');
 

+ 13 - 10
Sources/Class-BrowserDetect.php

@@ -6,13 +6,13 @@
  * - Detects the following browsers
  * - Opera, Webkit, Firefox, Web_tv, Konqueror, IE, Gecko
  * - Webkit variants: Chrome, iphone, blackberry, android, safari, ipad, ipod
- * - Opera Versions: 6, 7, 8, 9, 10 and mobile mini and mobi
+ * - Opera Versions: 6, 7, 8 ... 10 ... and mobile mini and mobi
  * - Firefox Versions: 1, 2, 3 .... 11 ...
  * - Chrome Versions: 1 ... 18 ...
- * - IE Versions: 4, 5, 5.5, 6, 7, 8, 9, 10 mobile and Mac
- * - Nokia 
+ * - IE Versions: 4, 5, 5.5, 6, 7, 8, 9, 10 ... mobile and Mac
+ * - Nokia
  */
- 
+
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
@@ -53,7 +53,7 @@ class browser_detector
 		// Old friend, old frenemy
 		elseif ($this->isIe())
 			$this->setupIe();
-		
+
 		// Just a few mobile checks
 		$this->isOperaMini();
 		$this->isOperaMobi();
@@ -122,7 +122,7 @@ class browser_detector
 	function isFirefox()
 	{
 		if (!isset($this->_browsers['is_firefox']))
-			$this->_browsers['is_firefox'] = preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/~', $_SERVER['HTTP_USER_AGENT']) === 1;
+			$this->_browsers['is_firefox'] = preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/~', $_SERVER['HTTP_USER_AGENT']) === 1 && $this->isGecko();
 		return $this->_browsers['is_firefox'];
 	}
 
@@ -319,18 +319,21 @@ class browser_detector
 			$context['browser_body_id'] = 'mobile';
 		else
 		{
+			// add in any specific detection conversions here if you want a special body id e.g. 'is_opera9' => 'opera9'
 			$browser_priority = array(
 				'is_ie6' => 'ie6',
 				'is_ie7' => 'ie7',
+				'is_ie8' => 'ie8',
+				'is_ie9' => 'ie9',
+				'is_ie10' => 'ie10',
 				'is_ie' => 'ie',
-				'is_firefox3' => 'firefox3',
-				'is_firefox4' => 'firefox4',
 				'is_firefox' => 'firefox',
 				'is_chrome' => 'chrome',
 				'is_safari' => 'safari',
-				'is_opera8' => 'opera8',
-				'is_opera9' => 'opera9',
 				'is_opera10' => 'opera10',
+				'is_opera11' => 'opera11',
+				'is_opera12' => 'opera12',
+				'is_opera' => 'opera',
 				'is_konqueror' => 'konqueror',
 			);
 

+ 14 - 15
Sources/Class-CurlFetchWeb.php

@@ -18,7 +18,6 @@
  *  - $fetch_data('http://www.simplemachines.org', array('user' => 'name', 'password' => 'password')); // post to a page
  *  - $fetch_data('http://www.simplemachines.org', parameter1&parameter2&parameter3); // post to a page
  *
- *
  * Get the data
  *  - $fetch_data->result('body'); // just the page content
  *  - $fetch_data->result(); // an array of results, body, header, http result codes
@@ -67,7 +66,7 @@ class curl_fetch_web_data
 	}
 
 	/**
-	* Main calling function, 
+	* Main calling function,
 	*  - will request the page data from a given $url
 	*  - optionally will post data to the page form if post data is supplied
 	*  - passed arrays will be converted to a post string joined with &'s
@@ -121,12 +120,12 @@ class curl_fetch_web_data
 		curl_exec($cr);
 
 		// Get what was returned
-		$curl_info		= curl_getinfo($cr);
-		$curl_content	= curl_multi_getcontent($cr);
-		$url			= $curl_info['url']; // Last effective URL
-		$http_code		= $curl_info['http_code']; // Last HTTP code
-		$body			= (!curl_error($cr)) ? substr($curl_content, $curl_info['header_size']) : false;
-		$error			= (curl_error($cr)) ? curl_error($cr) : false;
+		$curl_info = curl_getinfo($cr);
+		$curl_content = curl_multi_getcontent($cr);
+		$url = $curl_info['url']; // Last effective URL
+		$http_code = $curl_info['http_code']; // Last HTTP code
+		$body = (!curl_error($cr)) ? substr($curl_content, $curl_info['header_size']) : false;
+		$error = (curl_error($cr)) ? curl_error($cr) : false;
 
 		// close this request
 		curl_close($cr);
@@ -164,9 +163,9 @@ class curl_fetch_web_data
 
 		// redirect headers are often incomplete or relative so we need to make sure they are fully qualified
 		$new_url_parse['scheme'] = isset($new_url_parse['scheme']) ? $new_url_parse['scheme'] : $last_url_parse['scheme'];
-		$new_url_parse['host']   = isset($new_url_parse['host']) ? $new_url_parse['host'] : $last_url_parse['host'];
-		$new_url_parse['path']   = isset($new_url_parse['path']) ? $new_url_parse['path'] : $last_url_parse['path'];
-		$new_url_parse['query']  = isset($new_url_parse['query']) ? $new_url_parse['query'] : '';
+		$new_url_parse['host'] = isset($new_url_parse['host']) ? $new_url_parse['host'] : $last_url_parse['host'];
+		$new_url_parse['path'] = isset($new_url_parse['path']) ? $new_url_parse['path'] : $last_url_parse['path'];
+		$new_url_parse['query'] = isset($new_url_parse['query']) ? $new_url_parse['query'] : '';
 
 		// Build the new URL that was in the http header
 		return $new_url_parse['scheme'] . '://' . $new_url_parse['host'] . $new_url_parse['path'] . (!empty($new_url_parse['query']) ? '?' . $new_url_parse['query'] : '');
@@ -193,7 +192,7 @@ class curl_fetch_web_data
 
 	/**
 	* Will return all results from all loops (redirects)
-	*  - Can be call as ->result_raw(x) where x is a specific loop results.
+	*  - Can be called as ->result_raw(x) where x is a specific loop results.
 	*  - Call as ->result_raw() for everything.
 	*
 	* @param type $response_number
@@ -223,16 +222,16 @@ class curl_fetch_web_data
 		if (is_array($post_data))
 		{
 			$postvars = array();
-			
+
 			// build the post data, drop ones with leading @'s since those can be used to send files, we don't support that.
 			foreach ($post_data as $name => $value)
 				$postvars[] = $name . '=' . urlencode($value[0] == '@' ? '' : $value);
-			
+
 			return implode('&', $postvars);
 		}
 		else
 			return $post_data;
-		
+
 	}
 
 	/**

+ 1 - 1
Sources/DbPackages-mysql.php

@@ -621,7 +621,7 @@ function smf_db_list_indexes($table_name, $detail = false, $parameters = array()
 function smf_db_create_query_column($column)
 {
 	global $smcFunc;
-	
+
 	// Auto increment is easy here!
 	if (!empty($column['auto']))
 	{

+ 8 - 6
Sources/Display.php

@@ -774,7 +774,7 @@ function Display()
 		);
 
 		// Allow mods to add additional buttons here
-		call_integration_hook('integrate_display_poll');
+		call_integration_hook('integrate_poll_buttons');
 	}
 
 	// Calculate the fastest way to get the messages!
@@ -1080,6 +1080,12 @@ function Display()
 	$context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
 	$context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
 
+	// Check if the draft functions are enabled and that they have permission to use them (for quick reply.)
+	$context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply'];
+	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
+	if (!empty($context['drafts_save']))
+		loadLanguage('Drafts');
+
 	// Wireless shows a "more" if you can do anything special.
 	if (WIRELESS && WIRELESS_PROTOCOL != 'wap')
 	{
@@ -1424,12 +1430,8 @@ function Download()
 	header('Connection: close');
 	header('ETag: ' . $eTag);
 
-	// IE 6 just doesn't play nice. As dirty as this seems, it works.
-	if (isBrowser('ie6') && isset($_REQUEST['image']))
-		unset($_REQUEST['image']);
-
 	// Make sure the mime type warrants an inline display.
-	elseif (isset($_REQUEST['image']) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0)
+	if (isset($_REQUEST['image']) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0)
 		unset($_REQUEST['image']);
 
 	// Does this have a mime type?

+ 884 - 0
Sources/Drafts.php

@@ -0,0 +1,884 @@
+<?php
+
+/**
+ * This file contains all the functions that allow for the saving,
+ * retrieving, deleting and settings for the drafts function.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2011 Simple Machines
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('Hacking attempt...');
+
+loadLanguage('Drafts');
+
+/**
+ * Saves a post draft in the user_drafts table
+ * The core draft feature must be enabled, as well as the post draft option
+ * Determines if this is a new or an existing draft
+ *
+ * @return boolean
+ */
+function SaveDraft(&$post_errors)
+{
+	global $txt, $context, $user_info, $smcFunc, $modSettings, $board;
+
+	// can you be, should you be ... here?
+	if (empty($modSettings['drafts_enabled']) || empty($modSettings['drafts_post_enabled']) || !allowedTo('post_draft') || !isset($_POST['save_draft']) || !isset($_POST['id_draft']))
+		return false;
+
+	// read in what they sent us, if anything
+	$id_draft = (int) $_POST['id_draft'];
+	$draft_info = ReadDraft($id_draft);
+
+	// prepare any data from the form
+	$topic_id = empty($_REQUEST['topic']) ? 0 : (int) $_REQUEST['topic'];
+	$draft['icon'] = empty($_POST['icon']) ? 'xx' : preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']);
+	$draft['smileys_enabled'] = isset($_POST['ns']) ? (int) $_POST['ns'] : 0;
+	$draft['locked'] = isset($_POST['lock']) ? (int) $_POST['lock'] : 0;
+	$draft['sticky'] = isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : 0;
+	$draft['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
+	$draft['body'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
+
+	// message and subject still need a bit more work
+	preparsecode($draft['body']);
+	if ($smcFunc['strlen']($draft['subject']) > 100)
+		$draft['subject'] = $smcFunc['substr']($draft['subject'], 0, 100);
+
+	// Modifying an existing draft, like hitting the save draft button or autosave enabled?
+	if (!empty($id_draft) && !empty($draft_info) && $draft_info['id_member'] == $user_info['id'])
+	{
+		$smcFunc['db_query']('', '
+			UPDATE {db_prefix}user_drafts
+			SET
+				id_topic = {int:id_topic},
+				id_board = {int:id_board},
+				poster_time = {int:poster_time},
+				subject = {string:subject},
+				smileys_enabled = {int:smileys_enabled},
+				body = {string:body},
+				icon = {string:icon},
+				locked = {int:locked},
+				is_sticky = {int:is_sticky}
+			WHERE id_draft = {int:id_draft}',
+			array (
+				'id_topic' => $topic_id,
+				'id_board' => $board,
+				'poster_time' => time(),
+				'subject' => $draft['subject'],
+				'smileys_enabled' => (int) $draft['smileys_enabled'],
+				'body' => $draft['body'],
+				'icon' => $draft['icon'],
+				'locked' => $draft['locked'],
+				'is_sticky' => $draft['sticky'],
+				'id_draft' => $id_draft,
+			)
+		);
+
+		// some items to return to the form
+		$context['draft_saved'] = true;
+		$context['id_draft'] = $id_draft;
+
+		// cleanup
+		unset($_POST['save_draft']);
+	}
+	// otherwise creating a new draft
+	else
+	{
+		$smcFunc['db_insert']('',
+			'{db_prefix}user_drafts',
+			array(
+				'id_topic' => 'int',
+				'id_board' => 'int',
+				'type' => 'int',
+				'poster_time' => 'int',
+				'id_member' => 'int',
+				'subject' => 'string-255',
+				'smileys_enabled' => 'int',
+				'body' => (!empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] > 65534 ? 'string-' . $modSettings['max_messageLength'] : 'string-65534'),
+				'icon' => 'string-16',
+				'locked' => 'int',
+				'is_sticky' => 'int'
+			),
+			array(
+				$topic_id,
+				$board,
+				0,
+				time(),
+				$user_info['id'],
+				$draft['subject'],
+				$draft['smileys_enabled'],
+				$draft['body'],
+				$draft['icon'],
+				$draft['locked'],
+				$draft['sticky']
+			),
+			array(
+				'id_draft'
+			)
+		);
+
+		// get the id of the new draft
+		$id_draft = $smcFunc['db_insert_id']('{db_prefix}user_drafts', 'id_draft');
+
+		// everything go as expected?
+		if (!empty($id_draft))
+		{
+			$context['draft_saved'] = true;
+			$context['id_draft'] = $id_draft;
+		}
+		else
+			$post_errors[] = 'draft_not_saved';
+
+		// cleanup
+		unset($_POST['save_draft']);
+	}
+
+	// if we were called from the autosave function, send something back
+	if (!empty($id_draft) && isset($_REQUEST['xml']) && (!in_array('session_timeout', $post_errors)))
+		XmlDraft($id_draft);
+
+	return true;
+}
+
+/**
+ * Saves a PM draft in the user_drafts table
+ * The core draft feature must be enable, as well as the pm draft option
+ * Determines if this is a new or and update to an existing draft
+ *
+ * @global type $context
+ * @global type $user_info
+ * @global type $smcFunc
+ * @global type $modSettings
+ * @param string $post_errors
+ * @param type $recipientList
+ * @return boolean
+ */
+function SavePMDraft(&$post_errors, $recipientList)
+{
+	global $context, $user_info, $smcFunc, $modSettings;
+
+	// PM survey says ... can you stay or must you go
+	if (empty($modSettings['drafts_enabled']) || empty($modSettings['drafts_pm_enabled']) || !allowedTo('pm_draft') || !isset($_POST['save_draft']))
+		return false;
+
+	// read in what you sent us
+	$id_pm_draft = (int) $_POST['id_pm_draft'];
+	$draft_info = ReadDraft($id_pm_draft, 1);
+
+	// determine who this is being sent to
+	if (isset($_REQUEST['xml']))
+	{
+		$recipientList['to'] = isset($_POST['recipient_to']) ? explode(',', $_POST['recipient_to']) : array();
+		$recipientList['bcc'] = isset($_POST['recipient_bcc']) ? explode(',', $_POST['recipient_bcc']) : array();
+	}
+	elseif (!empty($draft_info['to_list']) && empty($recipientList))
+		$recipientList = unserialize($draft_info['to_list']);
+
+	// prepare the data we got from the form
+	$reply_id = empty($_POST['replied_to']) ? 0 : (int) $_POST['replied_to'];
+	$outbox = empty($_POST['outbox']) ? 0 : 1;
+	$draft['body'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
+	$draft['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
+
+	// message and subject still need a bit more massaging
+	preparsecode($draft['body']);
+	if ($smcFunc['strlen']($draft['subject']) > 100)
+		$draft['subject'] = $smcFunc['substr']($draft['subject'], 0, 100);
+
+	// Modifying an existing PM draft?
+	if (!empty($id_pm_draft) && !empty($draft_info) && $draft_info['id_member'] == $user_info['id'])
+	{
+		$smcFunc['db_query']('', '
+			UPDATE {db_prefix}user_drafts
+			SET id_reply = {int:id_reply},
+				type = {int:type},
+				poster_time = {int:poster_time},
+				subject = {string:subject},
+				body = {string:body},
+				to_list = {string:to_list},
+				outbox = {int:outbox}
+			WHERE id_draft = {int:id_pm_draft}
+			LIMIT 1',
+			array(
+				'id_reply' => $reply_id,
+				'type' => 1,
+				'poster_time' =>  time(),
+				'subject' =>  $draft['subject'],
+				'body' => $draft['body'],
+				'id_pm_draft' => $id_pm_draft,
+				'to_list' => serialize($recipientList),
+				'outbox' => $outbox,
+			)
+		);
+
+		// some items to return to the form
+		$context['draft_saved'] = true;
+		$context['id_pm_draft'] = $id_pm_draft;
+	}
+	// otherwise creating a new PM draft.
+	else
+	{
+		$smcFunc['db_insert']('',
+			'{db_prefix}user_drafts',
+			array(
+				'id_reply' => 'int',
+				'type' => 'int',
+				'poster_time' => 'int',
+				'id_member' => 'int',
+				'subject' => 'string-255',
+				'body' => 'string-65534',
+				'to_list' => 'string-255',
+				'outbox' => 'int',
+			),
+			array(
+				$reply_id,
+				1,
+				time(),
+				$user_info['id'],
+				$draft['subject'],
+				$draft['body'],
+				serialize($recipientList),
+				$outbox,
+			),
+			array(
+				'id_draft'
+			)
+		);
+
+		// get the new id
+		$id_pm_draft = $smcFunc['db_insert_id']('{db_prefix}user_drafts', 'id_draft');
+
+		// everything go as expected, if not toss an error
+		if (!empty($id_pm_draft))
+		{
+			$context['draft_saved'] = true;
+			$context['id_pm_draft'] = $id_pm_draft;
+		}
+		else
+			$post_errors[] = 'draft_not_saved';
+	}
+
+	// if we were called from the autosave function, send something back
+	if (!empty($id_pm_draft) && isset($_REQUEST['xml']) && !in_array('session_timeout', $post_errors))
+		XmlDraft($id_pm_draft);
+
+	return;
+}
+
+/**
+ * Reads a draft in from the user_drafts table
+ * Only loads the draft of a given type 0 for post, 1 for pm draft
+ * validates that the draft is the users draft
+ * Optionally loads the draft in to context or superglobal for loading in to the form
+ *
+ * @param type $id_draft - draft to load
+ * @param type $type - type of draft
+ * @param type $check - validate the user
+ * @param type $load - load it for use in a form
+ * @return boolean
+ */
+function ReadDraft($id_draft, $type = 0, $check = true, $load = false)
+{
+	global $context, $user_info, $smcFunc, $modSettings;
+
+	// always clean to be sure
+	$id_draft = (int) $id_draft;
+	$type = (int) $type;
+
+	// nothing to read, nothing to do
+	if (empty($id_draft))
+		return false;
+
+	// load in this draft from the DB
+	$request = $smcFunc['db_query']('', '
+		SELECT *
+		FROM {db_prefix}user_drafts
+		WHERE id_draft = {int:id_draft}' . ($check ? '
+			AND id_member = {int:id_member}' : '') . '
+			AND type = {int:type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : '') . '
+		LIMIT 1',
+		array(
+			'id_member' => $user_info['id'],
+			'id_draft' => $id_draft,
+			'type' => $type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+
+	// no results?
+	if (!$smcFunc['db_num_rows']($request))
+		return false;
+
+	// load up the data
+	$draft_info = $smcFunc['db_fetch_assoc']($request);
+	$smcFunc['db_free_result']($request);
+
+	// Load it up for the templates as well
+	$recipients = array();
+	if (!empty($load))
+	{
+		if ($type === 0)
+		{
+			// a standard post draft?
+			$context['sticky'] = !empty($draft_info['is_sticky']) ? $draft_info['is_sticky'] : '';
+			$context['locked'] = !empty($draft_info['locked']) ? $draft_info['locked'] : '';
+			$context['use_smileys'] = !empty($draft_info['smileys_enabled']) ? true : false;
+			$context['icon'] = !empty($draft_info['icon']) ? $draft_info['icon'] : 'xx';
+			$context['message'] = !empty($draft_info['body']) ? str_replace('<br />', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : '';
+			$context['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : '';
+			$context['board'] = !empty($draft_info['board_id']) ? $draft_info['id_board'] : '';
+			$context['id_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0;
+		}
+		elseif ($type === 1)
+		{
+			// one of those pm drafts? then set it up like we have an error
+			$_REQUEST['outbox'] = !empty($draft_info['outbox']);
+			$_REQUEST['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : '';
+			$_REQUEST['message'] = !empty($draft_info['body']) ? str_replace('<br />', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : '';
+			$_REQUEST['replied_to'] = !empty($draft_info['id_reply']) ? $draft_info['id_reply'] : 0;
+			$context['id_pm_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0;
+			$recipients = unserialize($draft_info['to_list']);
+
+			// make sure we only have integers in this array
+			$recipients['to'] = array_map('intval', $recipients['to']);
+			$recipients['bcc'] = array_map('intval', $recipients['bcc']);
+
+			// pretend we messed up to populate the pm message form
+			messagePostError(array(), array(), $recipients);
+			return true;
+		}
+	}
+
+	return $draft_info;
+}
+
+/**
+ * Deletes one or many drafts from the DB
+ * Validates the drafts are from the user
+ * is supplied an array of drafts will attempt to remove all of them
+ *
+ * @param type $id_draft
+ * @param type $check
+ * @return boolean
+ */
+function DeleteDraft($id_draft, $check = true)
+{
+	global $user_info, $smcFunc;
+
+	// Only a single draft.
+	if (is_numeric($id_draft))
+		$id_draft = array($id_draft);
+
+	// can't delete nothing
+	if (empty($id_draft) || ($check && empty($user_info['id'])))
+		return false;
+
+	$smcFunc['db_query']('', '
+		DELETE FROM {db_prefix}user_drafts
+		WHERE draft_id IN ({array_int:draft_id})', ($check ? '
+			AND  id_member = {int:id_member}' : ''), '
+		LIMIT 1',
+		array (
+			'draft_id' => $id_draft,
+			'id_member' => empty($user_info['id']) ? -1 : $user_info['id'],
+		)
+	);
+}
+
+/**
+ * Loads in a group of drafts for the user of a given type (0/posts, 1/pm's)
+ * loads a specific draft for forum use if selected.
+ * Used in the posting screens to allow draft selection
+ * WIll load a draft if selected is supplied via post
+ *
+ * @param type $member_id
+ * @param type $topic
+ * @param type $draft_type
+ * @return boolean
+ */
+function ShowDrafts($member_id, $topic = false, $draft_type = 0)
+{
+	global $smcFunc, $scripturl, $context, $txt, $modSettings;
+
+	// Permissions
+	if (($draft_type === 0 && empty($context['drafts_save'])) || ($draft_type === 1 && empty($context['drafts_pm_save'])) || empty($member_id))
+		return false;
+
+	$context['drafts'] = array();
+
+	// has a specific draft has been selected?  Load it up if there is not a message already in the editor
+	if (isset($_REQUEST['id_draft']) && empty($_POST['subject']) && empty($_POST['message']))
+		ReadDraft((int) $_REQUEST['id_draft'], $draft_type, true, true);
+
+	// load the drafts this user has available
+	$request = $smcFunc['db_query']('', '
+		SELECT *
+		FROM {db_prefix}user_drafts
+		WHERE id_member = {int:id_member}' . ((!empty($topic) && empty($draft_type)) ? '
+			AND id_topic = {int:id_topic}' : (!empty($topic) ? '
+			AND id_reply = {int:id_topic}' : '')) . '
+			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : '') . '
+		ORDER BY poster_time DESC',
+		array(
+			'id_member' => $member_id,
+			'id_topic' => (int) $topic,
+			'draft_type' => $draft_type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+
+	// add them to the draft array for display
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		// Post drafts
+		if ($draft_type === 0)
+			$context['drafts'][] = array(
+				'subject' => censorText(shorten_subject(stripslashes($row['subject']), 24)),
+				'poster_time' => timeformat($row['poster_time']),
+				'link' => '<a href="' . $scripturl . '?action=post;board=' . $row['id_board'] . ';' . (!empty($row['id_topic']) ? 'topic='. $row['id_topic'] .'.0;' : '') . 'id_draft=' . $row['id_draft'] . '">' . $row['subject'] . '</a>',
+			);
+		// PM drafts
+		elseif ($draft_type === 1)
+			$context['drafts'][] = array(
+				'subject' => censorText(shorten_subject(stripslashes($row['subject']), 24)),
+				'poster_time' => timeformat($row['poster_time']),
+				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;id_draft=' . $row['id_draft'] . '">' . (!empty($row['subject']) ? $row['subject'] : $txt['drafts_none']) . '</a>',
+			);
+	}
+	$smcFunc['db_free_result']($request);
+}
+
+/**
+ * Returns an xml response to an autosave ajax request
+ * provides the id of the draft saved and the time it was saved
+ *
+ * @param type $id_draft
+ */
+function XmlDraft($id_draft)
+{
+	global $txt, $context;
+
+	header('Content-Type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
+
+	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?>
+	<drafts>
+		<draft id="', $id_draft, '"><![CDATA[', $txt['draft_saved_on'], ': ', timeformat(time()), ']]></draft>
+	</drafts>';
+
+	obExit(false);
+}
+
+/**
+ * Show all drafts of a given type by the current user
+ * Uses the showdraft template
+ * Allows for the deleting and loading/editing of drafts
+ *
+ * @param type $memID
+ * @param type $draft_type
+ */
+function showProfileDrafts($memID, $draft_type = 0)
+{
+	global $txt, $user_info, $scripturl, $modSettings, $context, $smcFunc;
+
+	// Some initial context.
+	$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
+	$context['current_member'] = $memID;
+
+	// If just deleting a draft, do it and then redirect back.
+	if (!empty($_REQUEST['delete']))
+	{
+		checkSession('get');
+		$id_delete = (int) $_REQUEST['delete'];
+
+		$smcFunc['db_query']('', '
+			DELETE FROM {db_prefix}user_drafts
+			WHERE id_draft = {int:id_draft}
+				AND id_member = {int:id_member}
+				AND type = {int:draft_type}
+			LIMIT 1',
+			array(
+				'id_draft' => $id_delete,
+				'id_member' => $memID,
+				'draft_type' => $draft_type,
+			)
+		);
+
+		redirectexit('action=profile;u=' . $memID . ';area=showdrafts;start=' . $context['start']);
+	}
+
+	// Default to 10.
+	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
+		$_REQUEST['viewscount'] = '10';
+
+	// Get the count of applicable drafts on the boards they can (still) see ...
+	// @todo .. should we just let them see their drafts even if they have lost board access ?
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(id_draft)
+		FROM {db_prefix}user_drafts AS ud
+			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board})
+		WHERE id_member = {int:id_member}
+			AND type={int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : ''),
+		array(
+			'id_member' => $memID,
+			'draft_type' => $draft_type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+	list ($msgCount) = $smcFunc['db_fetch_row']($request);
+	$smcFunc['db_free_result']($request);
+
+	$maxIndex = (int) $modSettings['defaultMaxMessages'];
+
+	// Make sure the starting place makes sense and construct our friend the page index.
+	$context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showdrafts', $context['start'], $msgCount, $maxIndex);
+	$context['current_page'] = $context['start'] / $maxIndex;
+
+	// Reverse the query if we're past 50% of the pages for better performance.
+	$start = $context['start'];
+	$reverse = $_REQUEST['start'] > $msgCount / 2;
+	if ($reverse)
+	{
+		$maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages'];
+		$start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages'];
+	}
+
+	// Find this user's drafts for the boards they can access
+	// @todo ... do we want to do this?  If they were able to create a draft, do we remove thier access to said draft if they loose
+	//           access to the board or if the topic moves to a board they can not see?
+	$request = $smcFunc['db_query']('', '
+		SELECT
+			b.id_board, b.name AS bname,
+			ud.id_member, ud.id_draft, ud.body, ud.smileys_enabled, ud.subject, ud.poster_time, ud.icon, ud.id_topic, ud.locked, ud.is_sticky
+		FROM {db_prefix}user_drafts AS ud
+			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board})
+		WHERE ud.id_member = {int:current_member}
+			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : '') . '
+		ORDER BY ud.id_draft ' . ($reverse ? 'ASC' : 'DESC') . '
+		LIMIT ' . $start . ', ' . $maxIndex,
+		array(
+			'current_member' => $memID,
+			'draft_type' => $draft_type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+
+	// Start counting at the number of the first message displayed.
+	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
+	$context['posts'] = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		// Censor....
+		if (empty($row['body']))
+			$row['body'] = '';
+
+		$row['subject'] = $smcFunc['htmltrim']($row['subject']);
+		if (empty($row['subject']))
+			$row['subject'] = $txt['no_subject'];
+
+		censorText($row['body']);
+		censorText($row['subject']);
+
+		// BBC-ilize the message.
+		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], 'draft' . $row['id_draft']);
+
+		// And the array...
+		$context['drafts'][$counter += $reverse ? -1 : 1] = array(
+			'body' => $row['body'],
+			'counter' => $counter,
+			'alternate' => $counter % 2,
+			'board' => array(
+				'name' => $row['bname'],
+				'id' => $row['id_board']
+			),
+			'topic' => array(
+				'id' => $row['id_topic'],
+				'link' => empty($row['id']) ? $row['subject'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>',
+			),
+			'subject' => $row['subject'],
+			'time' => timeformat($row['poster_time']),
+			'timestamp' => forum_time(true, $row['poster_time']),
+			'icon' => $row['icon'],
+			'id_draft' => $row['id_draft'],
+			'locked' => $row['locked'],
+			'sticky' => $row['is_sticky'],
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// All drafts were retrieved in reverse order, get them right again.
+	if ($reverse)
+		$context['drafts'] = array_reverse($context['drafts'], true);
+
+	$context['sub_template'] = 'showDrafts';
+}
+
+/**
+ * Show all PM drafts of the current user
+ * Uses the showpmdraft template
+ * Allows for the deleting and loading/editing of drafts
+ *
+ * @param type $memID
+ * @param type $draft_type
+ */
+function showPMDrafts($memID = -1)
+{
+	global $txt, $user_info, $scripturl, $modSettings, $context, $smcFunc;
+
+	// init
+	$draft_type = 1;
+
+	// If just deleting a draft, do it and then redirect back.
+	if (!empty($_REQUEST['delete']))
+	{
+		checkSession('get');
+		$id_delete = (int) $_REQUEST['delete'];
+		$start = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
+
+		$smcFunc['db_query']('', '
+			DELETE FROM {db_prefix}user_drafts
+			WHERE id_draft = {int:id_draft}
+				AND id_member = {int:id_member}
+				AND type = {int:draft_type}
+			LIMIT 1',
+			array(
+				'id_draft' => $id_delete,
+				'id_member' => $memID,
+				'draft_type' => $draft_type,
+			)
+		);
+
+		// now redirect back to the list
+		redirectexit('action=pm;sa=showpmdrafts;start=' . $start);
+	}
+
+	// perhaps a draft was selected for editing? if so pass this off
+	if (!empty($_REQUEST['id_draft']) && !empty($context['drafts_pm_save']) && $memID == $user_info['id'])
+	{
+		checkSession('get');
+		$draft_id = (int) $_REQUEST['id_draft'];
+		redirectexit('action=pm;sa=send;id_draft=' . $draft_id);
+	}
+
+	// Default to 10.
+	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
+		$_REQUEST['viewscount'] = '10';
+
+	// Get the count of applicable drafts
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(id_draft)
+		FROM {db_prefix}user_drafts AS ud
+		WHERE id_member = {int:id_member}
+			AND type={int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : ''),
+		array(
+			'id_member' => $memID,
+			'draft_type' => $draft_type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+	list ($msgCount) = $smcFunc['db_fetch_row']($request);
+	$smcFunc['db_free_result']($request);
+
+	$maxIndex = (int) $modSettings['defaultMaxMessages'];
+
+	// Make sure the starting place makes sense and construct our friend the page index.
+	$context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=showpmdrafts', $context['start'], $msgCount, $maxIndex);
+	$context['current_page'] = $context['start'] / $maxIndex;
+
+	// Reverse the query if we're past 50% of the total for better performance.
+	$start = $context['start'];
+	$reverse = $_REQUEST['start'] > $msgCount / 2;
+	if ($reverse)
+	{
+		$maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages'];
+		$start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages'];
+	}
+
+	// Load in this user's PM drafts
+	$request = $smcFunc['db_query']('', '
+		SELECT
+			ud.id_member, ud.id_draft, ud.body, ud.subject, ud.poster_time, ud.outbox, ud.id_reply, ud.to_list
+		FROM {db_prefix}user_drafts AS ud
+		WHERE ud.id_member = {int:current_member}
+			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
+			AND poster_time > {int:time}' : '') . '
+		ORDER BY ud.id_draft ' . ($reverse ? 'ASC' : 'DESC') . '
+		LIMIT ' . $start . ', ' . $maxIndex,
+		array(
+			'current_member' => $memID,
+			'draft_type' => $draft_type,
+			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
+		)
+	);
+
+	// Start counting at the number of the first message displayed.
+	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
+	$context['posts'] = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		// Censor....
+		if (empty($row['body']))
+			$row['body'] = '';
+
+		$row['subject'] = $smcFunc['htmltrim']($row['subject']);
+		if (empty($row['subject']))
+			$row['subject'] = $txt['no_subject'];
+
+		censorText($row['body']);
+		censorText($row['subject']);
+
+		// BBC-ilize the message.
+		$row['body'] = parse_bbc($row['body'], true, 'draft' . $row['id_draft']);
+
+		// Have they provide who this will go to?
+		$recipients = array(
+			'to' => array(),
+			'bcc' => array(),
+		);
+		$recipient_ids = (!empty($row['to_list'])) ? unserialize($row['to_list']) : array();
+
+		// @todo ... this is a bit ugly since it runs an extra query for every message, do we want this?
+		// at least its only for draft PM's and only the user can see them ... so not heavily used .. still
+		if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc']))
+		{
+			$recipient_ids['to'] = array_map('intval', $recipient_ids['to']);
+			$recipient_ids['bcc'] = array_map('intval', $recipient_ids['bcc']);
+			$allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']);
+
+			$request_2 = $smcFunc['db_query']('', '
+				SELECT id_member, real_name
+				FROM {db_prefix}members
+				WHERE id_member IN ({array_int:member_list})',
+				array(
+					'member_list' => $allRecipients,
+				)
+			);
+			while ($result = $smcFunc['db_fetch_assoc']($request_2))
+			{
+				$recipientType = in_array($result['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to';
+				$recipients[$recipientType][] = $result['real_name'];
+			}
+			$smcFunc['db_free_result']($request_2);
+		}
+
+		// Add the items to the array for template use
+		$context['drafts'][$counter += $reverse ? -1 : 1] = array(
+			'body' => $row['body'],
+			'counter' => $counter,
+			'alternate' => $counter % 2,
+			'subject' => $row['subject'],
+			'time' => timeformat($row['poster_time']),
+			'timestamp' => forum_time(true, $row['poster_time']),
+			'id_draft' => $row['id_draft'],
+			'recipients' => $recipients,
+			'age' => floor((time() - $row['poster_time']) / 86400),
+			'remaining' => (!empty($modSettings['drafts_keep_days']) ? floor($modSettings['drafts_keep_days'] - ((time() - $row['poster_time']) / 86400)) : 0),
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// if the drafts were retrieved in reverse order, then put them in the right order again.
+	if ($reverse)
+		$context['drafts'] = array_reverse($context['drafts'], true);
+
+	// off to the template we go
+	$context['page_title'] = $txt['drafts'];
+	$context['sub_template'] = 'showPMDrafts';
+	$context['linktree'][] = array(
+		'url' => $scripturl . '?action=pm;sa=showpmdrafts',
+		'name' => $txt['drafts'],
+	);
+}
+
+/**
+ * Modify any setting related to drafts.
+ * Requires the admin_forum permission.
+ * Accessed from ?action=admin;area=managedrafts
+ *
+ * @param bool $return_config = false
+ * @uses Admin template, edit_topic_settings sub-template.
+ */
+function ModifyDraftSettings($return_config = false)
+{
+	global $context, $txt, $sourcedir, $scripturl;
+
+	isAllowedTo('admin_forum');
+
+	// Here are all the draft settings, a bit lite for now, but we can add more :P
+	$config_vars = array(
+			// Draft settings ...
+			array('check', 'drafts_post_enabled'),
+			array('check', 'drafts_pm_enabled'),
+			array('check', 'drafts_show_saved_enabled', 'subtext' => $txt['drafts_show_saved_enabled_subnote']),
+			array('int', 'drafts_keep_days', 'postinput' => $txt['days_word'], 'subtext' => $txt['drafts_keep_days_subnote']),
+			'',
+			array('check', 'drafts_autosave_enabled', 'subtext' => $txt['drafts_autosave_enabled_subnote']),
+			array('int', 'drafts_autosave_frequency', 'postinput' => $txt['manageposts_seconds'], 'subtext' => $txt['drafts_autosave_frequency_subnote']),
+	);
+
+	if ($return_config)
+		return $config_vars;
+
+	// Get the settings template ready.
+	require_once($sourcedir . '/ManageServer.php');
+
+	// Setup the template.
+	$context['page_title'] = $txt['managedrafts_settings'];
+	$context['sub_template'] = 'show_settings';
+
+	// Saving them ?
+	if (isset($_GET['save']))
+	{
+		checkSession();
+
+		// Protect them from themselves.
+		$_POST['drafts_autosave_frequency'] = $_POST['drafts_autosave_frequency'] < 30 ? 30 : $_POST['drafts_autosave_frequency'];
+		saveDBSettings($config_vars);
+		redirectexit('action=admin;area=managedrafts');
+	}
+
+	// some javascript to enable / disable the frequency input box
+	$context['settings_post_javascript'] = '
+		var autosave = document.getElementById(\'drafts_autosave_enabled\');
+		mod_addEvent(autosave, \'change\', toggle);
+		toggle();
+
+		function mod_addEvent(control, ev, fn)
+		{
+			if (control.addEventListener)
+			{
+				control.addEventListener(ev, fn, false);
+			}
+			else if (control.attachEvent)
+			{
+				control.attachEvent(\'on\'+ev, fn);
+			}
+		}
+		function toggle()
+		{
+			var select_elem = document.getElementById(\'drafts_autosave_frequency\');
+			select_elem.disabled = !autosave.checked;
+		}
+	';
+
+	// Final settings...
+	$context['post_url'] = $scripturl . '?action=admin;area=managedrafts;save';
+	$context['settings_title'] = $txt['managedrafts_settings'];
+
+	// Prepare the settings...
+	prepareDBSettingContext($config_vars);
+}
+
+?>

+ 7 - 7
Sources/DumpDatabase.php

@@ -50,7 +50,7 @@ function DumpDatabase2()
 	@set_time_limit(600);
 	$time_limit = ini_get('max_execution_time');
 	$start_time = time();
-	
+
 	// @todo ... fail on not getting the requested memory?
 	setMemoryLimit('256M');
 	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / 4;
@@ -113,7 +113,7 @@ function DumpDatabase2()
 	$crlf = "\r\n";
 
 	// SQL Dump Header.
-	$db_chunks = 
+	$db_chunks =
 		'-- ==========================================================' . $crlf .
 		'--' . $crlf .
 		'-- Database dump of tables in `' . $db_name . '`' . $crlf .
@@ -141,7 +141,7 @@ function DumpDatabase2()
 		// Are we dumping the structures?
 		if (isset($_REQUEST['struct']))
 		{
-			$db_chunks .= 
+			$db_chunks .=
 				$crlf .
 				'--' . $crlf .
 				'-- Table structure for table `' . $tableName . '`' . $crlf .
@@ -177,7 +177,7 @@ function DumpDatabase2()
 
 			if ($first_round)
 			{
-				$db_chunks .= 
+				$db_chunks .=
 					$crlf .
 					'--' . $crlf .
 					'-- Dumping data in `' . $tableName . '`' . $crlf .
@@ -185,7 +185,7 @@ function DumpDatabase2()
 					$crlf;
 				$first_round = false;
 			}
-			$db_chunks .= 
+			$db_chunks .=
 				$get_rows;
 			$current_used_memory += $smcFunc['strlen']($db_chunks);
 
@@ -205,11 +205,11 @@ function DumpDatabase2()
 
 		// No rows to get - skip it.
 		if ($close_table)
-			$db_backup .= 
+			$db_backup .=
 			'-- --------------------------------------------------------' . $crlf;
 	}
 
-	$db_backup .= 
+	$db_backup .=
 		$crlf .
 		'-- Done' . $crlf;
 

+ 1 - 1
Sources/Help.php

@@ -84,7 +84,7 @@ function ShowAdminHelp()
 		loadLanguage('ManagePermissions');
 
 	loadTemplate('Help');
-	
+
 	// Allow mods to load their own language file here
  	call_integration_hook('integrate_helpadmin');
 

+ 51 - 45
Sources/Load.php

@@ -940,7 +940,7 @@ function loadMemberData($users, $is_name = false, $set = 'normal')
 
 	// Allow mods to easily add to the selected member data
 	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables));
-	
+
 	if (!empty($users))
 	{
 		// Load the member's data.
@@ -1645,7 +1645,7 @@ function loadTheme($id_theme = 0, $initialize = true)
 		// If not a user variant, select the default.
 		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
 			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
-	
+
 		// Do this to keep things easier in the templates.
 		$context['theme_variant'] = '_' . $context['theme_variant'];
 		$context['theme_variant_url'] = $context['theme_variant'] . '/';
@@ -1675,10 +1675,6 @@ function loadTheme($id_theme = 0, $initialize = true)
 
 	$context['tabindex'] = 1;
 
-	// Fix font size with HTML 4.01, etc.
-	if (isset($settings['doctype']))
-		$context['browser']['needs_size_fix'] |= $settings['doctype'] == 'html' && isBrowser('ie6');
-
 	// Compatibility.
 	if (!isset($settings['theme_version']))
 		$modSettings['memberCount'] = $modSettings['totalMembers'];
@@ -1703,23 +1699,23 @@ function loadTheme($id_theme = 0, $initialize = true)
 		'ajax_notification_cancel_text' => JavaScriptEscape($txt['modify_cancel']),
 		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
 	);
-	
+
 	// Add the JQuery library to the list of files to load.
 	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
 		loadJavascriptFile('https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', array(), 'jquery');
 	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
 		loadJavascriptFile('jquery-1.7.1.min.js', array('default_theme' => true), 'jquery');
-	// Auto load, eh? template_javascript() will take care of the inline half of this.
+	// Auto loading? template_javascript() will take care of the local half of this.
 	else
 		loadJavascriptFile('https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', array(), 'jquery');
-	
+
 	// Queue our JQuery plugins!
-	loadJavascriptFile('smf_jquery_plugins.js', array('default_theme' => true), 'jquery_plugins');
-	
+	loadJavascriptFile('smf_jquery_plugins.js?alp21', array('default_theme' => true), 'jquery_plugins');
+
 	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
-	loadJavascriptFile('script.js', array('default_theme' => true), 'smf_scripts');
-	loadJavascriptFile('theme.js', array(), 'theme_scripts');
-	
+	loadJavascriptFile('script.js?alp21', array('default_theme' => true), 'smf_scripts');
+	loadJavascriptFile('theme.js?alp21', array(), 'theme_scripts');
+
 	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
 	if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
 	{
@@ -1785,7 +1781,7 @@ function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
 {
 	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
 
-	// Do any style sheets first (reroute to the new function to do this!)
+	// Do any style sheets first, cause we're easy with those.
 	if (!empty($style_sheets))
 	{
 		if (!is_array($style_sheets))
@@ -1889,28 +1885,32 @@ function loadSubTemplate($sub_template_name, $fatal = false)
 /**
  * Add a CSS file for output later
  *
- * @param string $filename
- * @param array $options
+ * Options are the following:
  * 	- local (true/false): define if the file is local
  * 	- default_theme (true/false): force use of default theme url
  * 	- force_current (true/false): if this is false, we will attempt to load the file from the default theme if not found in the current theme
+ *  - validate (true/false): if true script will validate the local file exists
+ *
+ * @param string $filename
+ * @param array $options
  * @param string $id
  */
 function loadCSSFile($filename, $options = array(), $id = '')
 {
 	global $settings, $context;
 
-	$theme = !empty($options['default_theme']) ? 'default_theme' : 'theme';
 	$options['force_current'] = !empty($options['force_current']) ? $options['force_current'] : false;
+	$theme = !empty($options['default_theme']) ? 'default_theme' : 'theme';
+	$id = empty($id) ? basename($filename, '.css?alp21') : $id;
 
 	// Is this a local file?
 	if (strpos($filename, 'http') === false || !empty($options['local']))
 	{
-		// Make sure it exists, too!
-		if(!file_exists($settings[$theme . '_dir'] . '/css/' . $filename))
+		// Are we validating the the file exists?
+		if (!empty($options['validate']) && !file_exists($settings[$theme . '_dir'] . '/css/' . $filename))
 		{
 			// Maybe the default theme has it?
-			if($theme == 'theme' && file_exists($settings['default_theme_dir'] . '/' . $filename) && !$options['force_current'])
+			if ($theme === 'theme' && !$options['force_current'] && file_exists($settings['default_theme_dir'] . '/' . $filename))
 				$filename = $settings['default_theme_url'] . '/css/' . $filename;
 			else
 				$filename = false;
@@ -1919,37 +1919,43 @@ function loadCSSFile($filename, $options = array(), $id = '')
 			$filename = $settings[$theme . '_url'] . '/css/' . $filename;
 	}
 
-	if(!empty($filename))
-		$context['css_files'][(empty($id) ? basename($filename) : $id)] = array('filename' => $filename, 'options' => $options);
+	// Add it to the array for use in the template
+	if (!empty($filename))
+		$context['css_files'][$id] = array('filename' => $filename, 'options' => $options);
 }
 
 /**
  * Add a Javascript file for output later
  *
- * @param string $filename
- * @param array $options, possible parameters:
+ * Options are the following:
  * 	- local (true/false): define if the file is local
  * 	- default_theme (true/false): force use of default theme url
- * 	- force_current (true/false): if this is false, we will attempt to load the file from the default theme if not found in the current theme
  * 	- defer (true/false): define if the file should load in <head> or before the closing <html> tag
+ * 	- force_current (true/false): if this is false, we will attempt to load the file from the
+ *    default theme if not found in the current theme
  *	- async (true/false): if the script should be loaded asynchronously (HTML5)
+ *  - validate (true/false): if true script will validate the local file exists
+ *
+ * @param string $filename
+ * @param array $options
  * @param string $id
  */
 function loadJavascriptFile($filename, $options = array(), $id = '')
 {
 	global $settings, $context;
 
-	$theme = !empty($options['default_theme']) ? 'default_theme' : 'theme';
 	$options['force_current'] = !empty($options['force_current']) ? $options['force_current'] : false;
+	$theme = !empty($options['default_theme']) ? 'default_theme' : 'theme';
+	$id = empty($id) ? basename($filename, '.js?alp21') : $id;
 
 	// Is this a local file?
 	if (strpos($filename, 'http') === false || !empty($options['local']))
 	{
-		// Make sure it exists, too!
-		if(!file_exists($settings[$theme . '_dir'] . '/scripts/' . $filename))
+		// Are we validating it exists on disk?
+		if (!empty($options['validate']) && !file_exists($settings[$theme . '_dir'] . '/scripts/' . $filename))
 		{
-			// Maybe the default theme has it?
-			if($theme == 'theme' && file_exists($settings['default_theme_dir'] . '/' . $filename) && !$options['force_current'])
+			// can't find it in this theme, how about the default?
+			if ($theme === 'theme' && !$options['force_current'] && file_exists($settings['default_theme_dir'] . '/' . $filename))
 				$filename = $settings['default_theme_url'] . '/scripts/' . $filename;
 			else
 				$filename = false;
@@ -1958,8 +1964,9 @@ function loadJavascriptFile($filename, $options = array(), $id = '')
 			$filename = $settings[$theme . '_url'] . '/scripts/' . $filename;
 	}
 
-	if(!empty($filename))
-		$context['javascript_files'][(empty($id) ? basename($filename) : $id)] = array('filename' => $filename, 'options' => $options);
+	// Add it to the array for use in the template
+	if (!empty($filename))
+		$context['javascript_files'][$id] = array('filename' => $filename, 'options' => $options);
 }
 
 /**
@@ -1974,15 +1981,16 @@ function addJavascriptVar($key, $value, $escape = false)
 {
 	global $context;
 
-	if(!empty($key) && !empty($value))
-		$context['javascript_vars'][$key] = $escape ? JavaScriptEscape($value) : $value;
+	if (!empty($key) && !empty($value))
+		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
 }
 
 /**
  * Add a block of inline Javascript code to be executed later
- * Only use this if you have to, generally external JS files are better, but for very small scripts
- * or for scripts that require help from PHP/whatever, this can be useful.
- * Do note that all code added with this function is added to the same <script> tag so do make sure your JS is clean!
+ *
+ * - only use this if you have to, generally external JS files are better, but for very small scripts
+ *   or for scripts that require help from PHP/whatever, this can be useful.
+ * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
  *
  * @param string $javascript
  * @param bool $defer = false, define if the script should load in <head> or before the closing <html> tag
@@ -1991,8 +1999,8 @@ function addInlineJavascript($javascript, $defer = false)
 {
 	global $context;
 
-	if(!empty($javascript))
-		$context['javascript_inline'][$defer ? 'defer' : 'standard'][] = $javascript;
+	if (!empty($javascript))
+		$context['javascript_inline'][(!empty($defer) ? 'defer' : 'standard')][] = $javascript;
 }
 
 /**
@@ -2430,9 +2438,7 @@ function template_include($filename, $once = false)
 				$data2 = preg_split('~\<br( /)?\>~', $data2);
 
 				// Fix the PHP code stuff...
-				if (isBrowser('ie4') || isBrowser('ie5') || isBrowser('ie5.5'))
-					$data2 = str_replace("\t", '<pre style="display: inline;">' . "\t" . '</pre>', $data2);
-				elseif (!isBrowser('gecko'))
+				if (!isBrowser('gecko'))
 					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
 				else
 					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
@@ -2589,8 +2595,8 @@ function cache_quick_get($key, $file, $function, $params, $level = 1)
 /**
  * Puts value in the cache under key for ttl seconds.
  *
- * - It may "miss" so shouldn't be depended on 
- * - Uses the cahce engine chosen in the ACP and saved in settings.php
+ * - It may "miss" so shouldn't be depended on
+ * - Uses the cache engine chosen in the ACP and saved in settings.php
  * - It supports:
  *     Turck MMCache: http://turck-mmcache.sourceforge.net/index_old.html#api
  *     Xcache: http://xcache.lighttpd.net/wiki/XcacheApi

+ 11 - 1
Sources/LogInOut.php

@@ -115,6 +115,11 @@ function Login2()
 		// Some whitelisting for login_url...
 		if (empty($_SESSION['login_url']))
 			redirectexit();
+		elseif (!empty($_SESSION['login_url']) && (strpos('http://', $_SESSION['login_url']) === false && strpos('https://', $_SESSION['login_url']) === false))
+		{
+			unset ($_SESSION['login_url']);
+			redirectexit();
+		}
 		else
 		{
 			// Best not to clutter the session data too much...
@@ -607,6 +612,11 @@ function Logout($internal = false, $redirect = true)
 	{
 		if (empty($_SESSION['logout_url']))
 			redirectexit('', $context['server']['needs_login_fix']);
+		elseif (!empty($_SESSION['logout_url']) && (strpos('http://', $_SESSION['logout_url']) === false && strpos('https://', $_SESSION['logout_url']) === false))
+		{
+			unset ($_SESSION['logout_url']);
+			redirectexit();
+		}
 		else
 		{
 			$temp = $_SESSION['logout_url'];
@@ -740,4 +750,4 @@ function validatePasswordFlood($id_member, $password_flood_value = false, $was_c
 
 }
 
-?>
+?>

+ 1 - 1
Sources/ManageAttachments.php

@@ -78,7 +78,7 @@ function ManageAttachments()
 /**
  * Allows to show/change attachment settings.
  * This is the default sub-action of the 'Attachments and Avatars' center.
- * Called by index.php?action=admin;area=manageattachments;sa=attachements.
+ * Called by index.php?action=admin;area=manageattachments;sa=attachments.
  *
  * @param bool $return_config = false
  * @uses 'attachments' sub template.

+ 1 - 1
Sources/ManageCalendar.php

@@ -162,7 +162,7 @@ function ModifyHolidays()
 			array(
 				'position' => 'below_table_data',
 				'value' => '
-					
+
 					<input type="submit" name="delete" value="' . $txt['quickmod_delete_selected'] . '" class="button_submit" />
 					<a class="button_link" href="' . $scripturl . '?action=admin;area=managecalendar;sa=editholiday" style="margin: 0 1em">' . $txt['holidays_add'] . '</a>',
 				'style' => 'text-align: right;',

+ 4 - 4
Sources/ManageErrors.php

@@ -148,7 +148,7 @@ function ViewErrorLog()
 				'file' => $row['file'],
 				'line' => $row['line'],
 				'href' => $scripturl . '?action=admin;area=logs;sa=errorlog;file=' . base64_encode($row['file']) . ';line=' . $row['line'],
-				'link' => $linkfile ? '<a href="' . $scripturl . '?action=admin;area=logs;sa=errorlog;file=' . base64_encode($row['file']) . ';line=' . $row['line'] . '" onclick="return reqWin(this.href, 600, 400, false);">' . $row['file'] . '</a>' : $row['file'],
+				'link' => $linkfile ? '<a href="' . $scripturl . '?action=admin;area=logs;sa=errorlog;file=' . base64_encode($row['file']) . ';line=' . $row['line'] . '" onclick="return reqWin(this.href, 600, 480, false);">' . $row['file'] . '</a>' : $row['file'],
 				'search' => base64_encode($row['file']),
 			);
 		}
@@ -324,9 +324,9 @@ function deleteErrors()
 /**
  * View a file specified in $_REQUEST['file'], with php highlighting on it
  * Preconditions:
- * file must be readable,
- * full file path must be base64 encoded,
- * user must have admin_forum permission.
+ *  - file must be readable,
+ *  - full file path must be base64 encoded,
+ *  - user must have admin_forum permission.
  * The line number number is specified by $_REQUEST['line']...
  * The function will try to get the 20 lines before and after the specified line.
  */

+ 46 - 12
Sources/ManageMaintenance.php

@@ -82,6 +82,7 @@ function ManageMaintenance()
 			'activities' => array(
 				'massmove' => 'MaintainMassMoveTopics',
 				'pruneold' => 'MaintainRemoveOldPosts',
+				'olddrafts' => 'MaintainRemoveOldDrafts',
 			),
 		),
 		'destroy' => array(
@@ -163,7 +164,7 @@ function MaintainDatabase()
 	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / (1024 * 1024);
 	// Zip limit is set to more or less 1/4th the size of the available memory * 1500
 	// 1500 is an estimate of the number of messages that generates a database of 1 MB (yeah I know IT'S AN ESTIMATION!!!)
-	// Why that? Because the only reliable zip package is the one sent out the first time, 
+	// Why that? Because the only reliable zip package is the one sent out the first time,
 	// so when the backup takes 1/5th (just to stay on the safe side) of the memory available
 	$zip_limit = $memory_limit * 1500 / 5;
 	// Here is more tricky: it depends on many factors, but the main idea is that
@@ -243,7 +244,7 @@ function MaintainMembers()
 		);
 	}
 	$smcFunc['db_free_result']($result);
-	
+
 	if (isset($_GET['done']) && $_GET['done'] == 'recountposts')
 		$context['maintenance_finished'] = $txt['maintain_recountposts'];
 }
@@ -1191,7 +1192,7 @@ function AdminBoardRecount()
 
 	isAllowedTo('admin_forum');
 	checkSession('request');
-	
+
 	// validate the request or the loop
 	if (!isset($_REQUEST['step']))
 		validateToken('admin-maint');
@@ -1881,6 +1882,39 @@ function MaintainRemoveOldPosts()
 	RemoveOldTopics2();
 }
 
+/**
+ * Removing old drafts
+ */
+function MaintainRemoveOldDrafts()
+{
+	global $sourcedir, $smcFunc;
+
+	validateToken('admin-maint');
+
+	$drafts = array();
+
+	// Find all of the old drafts
+	$request = $smcFunc['db_query']('', '
+		SELECT id_draft
+		FROM {db_prefix}user_drafts
+		WHERE poster_time <= {int:poster_time_old}',
+		array(
+			'poster_time_old' => time() - (86400 * $_POST['draftdays']),
+		)
+	);
+
+	while ($row = $smcFunc['db_fetch_row']($request))
+		$drafts[] = $row[0];
+	$smcFunc['db_free_result']($request);
+
+	// If we have old one, remove them
+	if (count($drafts) > 0)
+	{
+		require_once($sourcedir . '/Drafts.php');
+		DeleteDraft($drafts, false);
+	}
+}
+
 /**
  * Moves topics from one board to another.
  *
@@ -1992,7 +2026,7 @@ function MaintainMassMoveTopics()
 /**
  * Recalculate all members post counts
  * it requires the admin_forum permission.
- * 
+ *
  * - recounts all posts for members found in the message table
  * - updates the members post count record in the members talbe
  * - honors the boards post count flag
@@ -2017,7 +2051,7 @@ function MaintainRecountPosts()
 	$context['continue_countdown'] = 3;
 	$context['continue_get_data'] = '';
 	$context['sub_template'] = 'not_done';
-	
+
 	// init
 	$increment = 200;
 	$_REQUEST['start'] = !isset($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
@@ -2029,7 +2063,7 @@ function MaintainRecountPosts()
 	if (!isset($_SESSION['total_members']))
 	{
 		validateToken('admin-maint');
-		
+
 		$request = $smcFunc['db_query']('', '
 			SELECT COUNT(DISTINCT m.id_member)
 			FROM ({db_prefix}messages AS m, {db_prefix}boards AS b)
@@ -2087,7 +2121,7 @@ function MaintainRecountPosts()
 		$_REQUEST['start'] += $increment;
 		$context['continue_get_data'] = '?action=admin;area=maintain;sa=members;activity=recountposts;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id'];
 		$context['continue_percent'] = round(100 * $_REQUEST['start'] / $_SESSION['total_members']);
-		
+
 		createToken('admin-recountposts');
 		$context['continue_post_data'] = '<input type="hidden" name="' . $context['admin-recountposts_token_var'] . '" value="' . $context['admin-recountposts_token'] . '" />';
 
@@ -2095,7 +2129,7 @@ function MaintainRecountPosts()
 			apache_reset_timeout();
 		return;
 	}
-	
+
 	// final steps ... made more difficult since we don't yet support sub-selects on joins
 	// place all members who have posts in the message table in a temp table
 	$createTemporary = $smcFunc['db_query']('', '
@@ -2103,7 +2137,7 @@ function MaintainRecountPosts()
 			id_member mediumint(8) unsigned NOT NULL default {string:string_zero},
 			PRIMARY KEY (id_member)
 		)
-		SELECT m.id_member 
+		SELECT m.id_member
 		FROM ({db_prefix}messages AS m,{db_prefix}boards AS b)
 		WHERE m.id_member != {int:zero}
 			AND b.count_posts = {int:zero}
@@ -2117,7 +2151,7 @@ function MaintainRecountPosts()
 		)
 	) !== false;
 
-	if ($createTemporary) 
+	if ($createTemporary)
 	{
 		// outer join the members table on the temporary table finding the members that have a post count but no posts in the message table
 		$request = $smcFunc['db_query']('', '
@@ -2125,7 +2159,7 @@ function MaintainRecountPosts()
 			FROM {db_prefix}members AS mem
 			LEFT OUTER JOIN {db_prefix}tmp_maint_recountposts AS res
 			ON res.id_member = mem.id_member
-			WHERE res.id_member IS null 
+			WHERE res.id_member IS null
 				AND mem.posts != {int:zero}',
 			array(
 				'zero' => 0,
@@ -2147,7 +2181,7 @@ function MaintainRecountPosts()
 		}
 		$smcFunc['db_free_result']($request);
 	}
-	
+
 	// all done
 	unset($_SESSION['total_members']);
 	$context['maintenance_finished'] = $txt['maintain_recountposts'];

+ 11 - 14
Sources/ManageMembers.php

@@ -602,7 +602,7 @@ function ViewMemberlist()
 		),
 	);
 
-	// Without not enough permissions, don't show 'delete members' checkboxes.
+	// Without enough permissions, don't show 'delete members' checkboxes.
 	if (!allowedTo('profile_remove_any'))
 		unset($listOptions['cols']['check'], $listOptions['form'], $listOptions['additional_rows']);
 
@@ -963,15 +963,13 @@ function MembersAwaitingActivation()
 			array(
 				'position' => 'below_table_data',
 				'value' => '
-					<div class="floatleft">
-						[<a href="' . $scripturl . '?action=admin;area=viewmembers;sa=browse;showdupes=' . ($context['show_duplicates'] ? 0 : 1) . ';type=' . $context['browse_type'] . (!empty($context['show_filter']) ? ';filter=' . $context['current_filter'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . ($context['show_duplicates'] ? $txt['dont_check_for_duplicate'] : $txt['check_for_duplicate']) . '</a>]
-					</div>
-					<div class="floatright">
-						<select name="todo" onchange="onSelectChange();">
-							' . $allowed_actions . '
-						</select>
-						<noscript><input type="submit" value="' . $txt['go'] . '" class="button_submit" /></noscript>
-					</div>',
+					[<a href="' . $scripturl . '?action=admin;area=viewmembers;sa=browse;showdupes=' . ($context['show_duplicates'] ? 0 : 1) . ';type=' . $context['browse_type'] . (!empty($context['show_filter']) ? ';filter=' . $context['current_filter'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . ($context['show_duplicates'] ? $txt['dont_check_for_duplicate'] : $txt['check_for_duplicate']) . '</a>]
+					<select name="todo" onchange="onSelectChange();">
+						' . $allowed_actions . '
+					</select>
+					<noscript><input type="submit" value="' . $txt['go'] . '" class="button_submit" /><br class="clear_right"></noscript>
+				',
+				'class' => 'floatright',
 			),
 		),
 	);
@@ -999,9 +997,9 @@ function MembersAwaitingActivation()
 			</select>
 			<noscript><input type="submit" value="' . $txt['go'] . '" name="filter" class="button_submit" /></noscript>';
 		$listOptions['additional_rows'][] = array(
-			'position' => 'above_column_headers',
+			'position' => 'top_of_list',
 			'value' => $filterOptions,
-			'style' => 'text-align: center;',
+			'class' => 'righttext',
 		);
 	}
 
@@ -1010,8 +1008,7 @@ function MembersAwaitingActivation()
 		$listOptions['additional_rows'][] = array(
 			'position' => 'above_column_headers',
 			'value' => '<strong>' . $txt['admin_browse_filter_show'] . ':</strong> ' . $context['available_filters'][0]['desc'],
-			'class' => 'smalltext',
-			'style' => 'text-align: left;',
+			'class' => 'smalltext floatright',
 		);
 
 	// Now that we have all the options, create the list.

+ 2 - 2
Sources/ManageNews.php

@@ -201,7 +201,7 @@ function EditNews()
 				<span id="moreNewsItems_link" class="floatleft" style="display: none;">
 					<a class="button_link" href="javascript:void(0);" onclick="addNewsItem(); return false;">' . $txt['editnews_clickadd'] . '</a>
 				</span>
-				<input type="submit" name="save_items" value="' . $txt['save'] . '" class="button_submit" /> 
+				<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" />',
 			),
 		),
@@ -209,7 +209,7 @@ function EditNews()
 					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];

+ 6 - 0
Sources/ManagePermissions.php

@@ -1463,6 +1463,8 @@ function loadAllPermissions($loadType = 'classic')
 			'disable_censor' => array(false, 'general', 'disable_censor'),
 			'pm_read' => array(false, 'pm', 'use_pm_system'),
 			'pm_send' => array(false, 'pm', 'use_pm_system'),
+			'pm_draft' => array(false, 'pm', 'use_pm_system'),
+			'pm_autosave_draft' => array(false, 'pm', 'use_pm_system'),
 			'send_email_to_members' => array(false, 'pm', 'use_pm_system'),
 			'calendar_view' => array(false, 'calendar', 'view_basic_info'),
 			'calendar_post' => array(false, 'calendar', 'post_calendar'),
@@ -1492,6 +1494,8 @@ function loadAllPermissions($loadType = 'classic')
 			'moderate_board' => array(false, 'general_board', 'moderate'),
 			'approve_posts' => array(false, 'general_board', 'moderate'),
 			'post_new' => array(false, 'topic', 'make_posts'),
+			'post_draft' => array(false, 'topic', 'make_posts'),
+			'post_autosave_draft' => array(false, 'topic', 'make_posts'),
 			'post_unapproved_topics' => array(false, 'topic', 'make_unapproved_posts'),
 			'post_unapproved_replies' => array(true, 'topic', 'make_unapproved_posts', 'make_unapproved_posts'),
 			'post_reply' => array(true, 'topic', 'make_posts', 'make_posts'),
@@ -2249,6 +2253,8 @@ function loadIllegalGuestPermissions()
 		'modify_replies',
 		'send_mail',
 		'approve_posts',
+		'post_draft',
+		'post_autosave_draft',
 	);
 
 	call_integration_hook('integrate_load_illegal_guest_permissions');

+ 2 - 2
Sources/ManagePosts.php

@@ -45,7 +45,7 @@ function ManagePostSettings()
 
 	$context['page_title'] = $txt['manageposts_title'];
 
-	// Tabs for browsing the different ban functions.
+	// Tabs for browsing the different post functions.
 	$context[$context['admin_menu_name']]['tab_data'] = array(
 		'title' => $txt['manageposts_title'],
 		'help' => 'posts_and_topics',
@@ -232,7 +232,7 @@ function ModifyPostSettings($return_config = false)
 				fatal_lang_error('convert_to_mediumtext', false, array($scripturl . '?action=admin;area=maintain;sa=database'));
 
 		}
-		
+
 		// If we're changing the post preview length let's check its valid
 		if (!empty($_POST['preview_characters']))
 			$_POST['preview_characters'] = (int) min(max(0, $_POST['preview_characters']), 512);

+ 2 - 2
Sources/ManageSearch.php

@@ -542,7 +542,7 @@ function CreateMessageIndex()
 		);
 		$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
 		$context['step'] = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : 0;
-		
+
 		// admin timeouts are painful when building these long indexes
 		if ($_SESSION['admin_time'] + 3300 < time() && $context['step'] >= 1)
 			$_SESSION['admin_time'] = time();
@@ -722,7 +722,7 @@ function CreateMessageIndex()
 			$context['percentage'] = 80 + round($context['start'] / $index_properties[$context['index_settings']['bytes_per_word']]['max_size'], 3) * 20;
 		}
 	}
-	
+
 	// Step 3: remove words not distinctive enough.
 	if ($context['step'] === 3)
 	{

+ 3 - 3
Sources/ManageSearchEngines.php

@@ -459,7 +459,7 @@ function EditSpider()
 
 /**
  * Do we think the current user is a spider?
- * 
+ *
  * @todo Should this not be... you know... in a different file?
  * @return int
  */
@@ -538,7 +538,7 @@ function SpiderCheck()
 
 /**
  * Log the spider presence online.
- * 
+ *
  * @todo Different file?
  */
 function logSpider()
@@ -798,7 +798,7 @@ function SpiderLogs()
 
 /**
  * Callback function for createList()
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort

+ 17 - 18
Sources/ManageServer.php

@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Contains all the functionality required to be able to edit the core server 
+ * Contains all the functionality required to be able to edit the core server
  * settings. This includes anything from which an error may result in the forum
  * destroying itself in a firey fury.
  *
@@ -104,7 +104,7 @@ function ModifySettings()
 	// By default we're editing the core settings
 	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'general';
 	$context['sub_action'] = $_REQUEST['sa'];
-	
+
 	// Any messages to speak of?
 	$context['settings_message'] = (isset($_REQUEST['msg']) && isset($txt[$_REQUEST['msg']])) ? $txt[$_REQUEST['msg']] : '';
 
@@ -323,7 +323,7 @@ function ModifyCookieSettings($return_config = false)
 function ModifyCacheSettings($return_config = false)
 {
 	global $context, $scripturl, $txt, $helptxt, $cache_enable;
-	
+
 	// Detect all available optimizers
 	$detected = array();
 	if (function_exists('eaccelerator_put'))
@@ -338,13 +338,13 @@ function ModifyCacheSettings($return_config = false)
 		$detected['memcached'] = $txt['memcached_cache'];
 	if (function_exists('xcache_set'))
 		$detected['xcache'] = $txt['xcache_cache'];
-		
+
 	// set a message to show what, if anything, we found
 	if (empty($detected))
 		$txt['cache_settings_message'] = $txt['detected_no_caching'];
 	else
 		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
-	
+
 	// This is always an option
 	$detected['smf'] = $txt['default_cache'];
 
@@ -357,7 +357,7 @@ function ModifyCacheSettings($return_config = false)
 		array('cache_memcached', $txt['cache_memcached'], 'file', 'text', $txt['cache_memcached'], 'cache_memcached'),
 		array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir'),
 	);
-	
+
 	// some javascript to enable / disable certain settings if the option is not selected
 	$context['settings_post_javascript'] = '
 		var cache_type = document.getElementById(\'cache_accelerator\');
@@ -368,8 +368,8 @@ function ModifyCacheSettings($return_config = false)
 		{
 			if (control.addEventListener)
 			{
-				control.addEventListener(ev, fn, false); 
-			} 
+				control.addEventListener(ev, fn, false);
+			}
 			else if (control.attachEvent)
 			{
 				control.attachEvent(\'on\'+ev, fn);
@@ -395,14 +395,14 @@ function ModifyCacheSettings($return_config = false)
 		call_integration_hook('integrate_save_cache_settings');
 
 		saveSettings($config_vars);
-		
+
 		// we need to save the $cache_enable to $modSettings as well
 		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
 
 		// exit so we reload our new settings on the page
 		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
 	}
-	
+
 	// if its off, allow them to clear it as well
 	// @todo why only when its off ?
 	if (empty($cache_enable))
@@ -507,7 +507,7 @@ function ModifyLoadBalancingSettings($return_config = false)
 		saveDBSettings($config_vars);
 		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
 	}
-	
+
 	createToken('admin-ssc');
 	createToken('admin-dbsc');
 	prepareDBSettingContext($config_vars);
@@ -570,7 +570,7 @@ function prepareServerSettingsContext(&$config_vars)
 				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
 				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
 			);
-			
+
 			// If this is a select box handle any data.
 			if (!empty($config_var[4]) && is_array($config_var[4]))
 			{
@@ -794,15 +794,15 @@ function saveSettings(&$config_vars)
 		'cookiename',
 		'webmaster_email',
 		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
-		'boarddir', 'sourcedir', 
+		'boarddir', 'sourcedir',
 		'cachedir', 'cache_accelerator', 'cache_memcached',
 	);
-	
+
 	// All the numeric variables.
 	$config_ints = array(
 		'cache_enable',
 	);
-	
+
 	// All the checkboxes.
 	$config_bools = array(
 		'db_persist', 'db_error_send',
@@ -944,7 +944,7 @@ function saveDBSettings(&$config_vars)
 function ShowPHPinfoSettings()
 {
 	global $context, $txt;
-	
+
 	$info_lines = array();
 	$category = $txt['phpinfo_settings'];
 
@@ -956,8 +956,7 @@ function ShowPHPinfoSettings()
 	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
 	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
 	ob_end_clean();
-	
-	// remove things that could be considered sensative
+	// remove things that could be considered sensitive
 	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
 
 	// put all of it into an array

+ 27 - 2
Sources/ManageSettings.php

@@ -225,6 +225,31 @@ function ModifyCoreFeatures($return_config = false)
 					return array();
 			'),
 		),
+		// dr = drafts
+		'dr' => array(
+			'url' => 'action=admin;area=managedrafts',
+			'settings' => array(
+				'drafts_enabled' => 1,
+				'drafts_post_enabled' => 2,
+				'drafts_pm_enabled' => 2,
+				'drafts_autosave_enabled' => 2,
+				'drafts_show_saved_enabled' => 2,
+			),
+			'setting_callback' => create_function('$value', '
+				global $smcFunc, $sourcedir;
+
+				// Set the correct disabled value for the scheduled task.
+				$smcFunc[\'db_query\'](\'\', \'
+					UPDATE {db_prefix}scheduled_tasks
+					SET disabled = {int:disabled}
+					WHERE task = {string:task}\',
+					array(
+						\'disabled\' => $value ? 0 : 1,
+						\'task\' => \'remove_old_drafts\',
+					)
+				);
+			'),
+		),
 		// k = karma.
 		'k' => array(
 			'url' => 'action=admin;area=featuresettings;sa=karma',
@@ -491,7 +516,7 @@ function ModifyBasicSettings($return_config = false)
 		'',
 			// Option-ish things... miscellaneous sorta.
 			array('check', 'allow_disableAnnounce'),
-			array('check', 'disallow_sendBody'),	
+			array('check', 'disallow_sendBody'),
 	);
 
 	// Get all the time zones.
@@ -1736,7 +1761,7 @@ function EditCustomProfiles()
 		// Regex you say?  Do a very basic test to see if the pattern is valid
 		if (!empty($_POST['regex']) && @preg_match($_POST['regex'], 'dummy') === false)
 			redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=regex_error');
-			
+
 		$_POST['field_name'] = $smcFunc['htmlspecialchars']($_POST['field_name']);
 		$_POST['field_desc'] = $smcFunc['htmlspecialchars']($_POST['field_desc']);
 

+ 1 - 1
Sources/ManageSmileys.php

@@ -207,7 +207,7 @@ function EditSmileySets()
 			$set_paths = explode(',', $modSettings['smiley_sets_known']);
 			$set_names = explode("\n", $modSettings['smiley_sets_names']);
 			foreach ($_POST['smiley_set'] as $id => $val)
-			{	
+			{
 				if (isset($set_paths[$id], $set_names[$id]) && !empty($id))
 					unset($set_paths[$id], $set_names[$id]);
 			}

+ 10 - 10
Sources/Memberlist.php

@@ -150,7 +150,7 @@ function Memberlist()
 			),
 		)
 	);
-	
+
 	$context['colspan'] = 0;
 	$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
 	foreach ($context['columns'] as $key => $column)
@@ -175,15 +175,15 @@ function Memberlist()
 
 	$context['can_send_pm'] = allowedTo('pm_send');
 	$context['can_send_email'] = allowedTo('send_email_to_members');
-	
+
 	// Build the memberlist button array.
 	$context['memberlist_buttons'] = array(
 		'view_all_members' => array('text' => 'view_all_members', 'image' => 'mlist.png', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=all', 'active'=> true),
 		'mlist_search' => array('text' => 'mlist_search', 'image' => 'mlist.png', 'lang' => true, 'url' => $scripturl . '?action=mlist' . ';sa=search'),
 	);
-	
+
 	// Allow mods to add additional buttons here
-	call_integration_hook('integrate_memberlist_buttons');	
+	call_integration_hook('integrate_memberlist_buttons');
 
 	// Jump to the sub action.
 	if (isset($subActions[$context['listing_by']]))
@@ -443,7 +443,7 @@ function MLSearch()
 		// No fields?  Use default...
 		if (empty($_POST['fields']))
 			$_POST['fields'] = array('name');
-			
+
 		// Set defaults for how the results are sorted
 		if (!isset($_REQUEST['sort']) || !isset($context['columns'][$_REQUEST['sort']]))
 			$_REQUEST['sort'] = 'real_name';
@@ -455,18 +455,18 @@ function MLSearch()
 
 			if ((!isset($_REQUEST['desc']) && $col == $_REQUEST['sort']) || ($col != $_REQUEST['sort'] && !empty($column_details['default_sort_rev'])))
 				$context['columns'][$col]['href'] .= ';desc';
-				
+
 			if (isset($_POST['search']) && isset($_POST['fields']))
 				$context['columns'][$col]['href'] .= ';search=' . $_POST['search'] . ';fields=' . implode(',', $_POST['fields']);
 
 			$context['columns'][$col]['link'] = '<a href="' . $context['columns'][$col]['href'] . '" rel="nofollow">' . $context['columns'][$col]['label'] . '</a>';
 			$context['columns'][$col]['selected'] = $_REQUEST['sort'] == $col;
 		}
-	
+
 		// set up some things for use in the template
 		$context['page_title'] = $txt['mlist_search'];
 		$context['can_moderate_forum'] = allowedTo('moderate_forum');
-		$context['sort_direction'] = !isset($_REQUEST['desc']) ? 'up' : 'down';	
+		$context['sort_direction'] = !isset($_REQUEST['desc']) ? 'up' : 'down';
 		$context['sort_by'] = $_REQUEST['sort'];
 
 		$query_parameters = array(
@@ -502,7 +502,7 @@ function MLSearch()
 
 		$customJoin = array();
 		$customCount = 10;
-		
+
 		// Any custom fields to search for - these being tricky?
 		foreach ($_POST['fields'] as $field)
 		{
@@ -574,7 +574,7 @@ function MLSearch()
 		'url' => $scripturl . '?action=mlist;sa=search',
 		'name' => &$context['page_title']
 	);
-	
+
 	// Highlight the correct button, too!
 	unset($context['memberlist_buttons']['view_all_members']['active']);
 	$context['memberlist_buttons']['mlist_search']['active'] = true;

+ 30 - 3
Sources/MessageIndex.php

@@ -361,20 +361,22 @@ function MessageIndex()
 				' . ($user_info['is_guest'] ? '0' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from,
 				t.id_last_msg, t.approved, t.unapproved_posts, t.id_redirect_topic, ml.poster_time AS last_poster_time,
 				ml.id_msg_modified, ml.subject AS last_subject, ml.icon AS last_icon,
-				ml.poster_name AS last_member_name, ml.id_member AS last_id_member,
+				ml.poster_name AS last_member_name, ml.id_member AS last_id_member, ' . (!empty($settings['avatars_on_indexes']) ? 'meml.avatar,' : '') . '
 				IFNULL(meml.real_name, ml.poster_name) AS last_display_name, t.id_first_msg,
 				mf.poster_time AS first_poster_time, mf.subject AS first_subject, mf.icon AS first_icon,
 				mf.poster_name AS first_member_name, mf.id_member AS first_id_member,
 				IFNULL(memf.real_name, mf.poster_name) AS first_display_name, ' . (!empty($modSettings['preview_characters']) ? '
 				SUBSTRING(ml.body, 1, ' . ($modSettings['preview_characters'] + 256) . ') AS last_body,
-				SUBSTRING(mf.body, 1, ' . ($modSettings['preview_characters'] + 256) . ') AS first_body,' : '') . 'ml.smileys_enabled AS last_smileys, mf.smileys_enabled AS first_smileys
+				SUBSTRING(mf.body, 1, ' . ($modSettings['preview_characters'] + 256) . ') AS first_body,' : '') . 'ml.smileys_enabled AS last_smileys, mf.smileys_enabled AS first_smileys' . (!empty($settings['avatars_on_indexes']) ? ',
+				IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type' : '') . '
 			FROM {db_prefix}topics AS t
 				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
 				INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
 				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
 				LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)' . ($user_info['is_guest'] ? '' : '
 				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
-				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})'). '
+				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . (!empty($settings['avatars_on_indexes']) ? '
+				LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = ml.id_member)' : '') . '
 			WHERE ' . ($pre_query ? 't.id_topic IN ({array_int:topic_list})' : 't.id_board = {int:current_board}') . (!$modSettings['postmod_active'] || $context['can_approve_posts'] ? '' : '
 				AND (t.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR t.id_member_started = {int:current_member}') . ')') . '
 			ORDER BY ' . ($pre_query ? 'FIND_IN_SET(t.id_topic, {string:find_set_topics})' : (!empty($modSettings['enableStickyTopics']) ? 'is_sticky' . ($fake_ascending ? '' : ' DESC') . ', ' : '') . $_REQUEST['sort'] . ($ascending ? '' : ' DESC')) . '
@@ -472,6 +474,24 @@ function MessageIndex()
 					$context['icon_sources'][$row['last_icon']] = 'images_url';
 			}
 
+			if (!empty($settings['avatars_on_indexes']))
+			{
+				// Allow themers to show the latest poster's avatar along with the topic
+				if(!empty($row['avatar']))
+				{
+					if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize')
+					{
+						$avatar_width = !empty($modSettings['avatar_max_width_external']) ? ' width="' . $modSettings['avatar_max_width_external'] . '"' : '';
+						$avatar_height = !empty($modSettings['avatar_max_height_external']) ? ' height="' . $modSettings['avatar_max_height_external'] . '"' : '';
+					}
+					else
+					{
+						$avatar_width = '';
+						$avatar_height = '';
+					}
+				}
+			}
+
 			// 'Print' the topic info.
 			$context['topics'][$row['id_topic']] = array(
 				'id' => $row['id_topic'],
@@ -530,6 +550,13 @@ function MessageIndex()
 				'approved' => $row['approved'],
 				'unapproved_posts' => $row['unapproved_posts'],
 			);
+			if (!empty($settings['avatars_on_indexes']))
+				$context['topics'][$row['id_topic']]['last_post']['member']['avatar'] = array(
+					'name' => $row['avatar'],
+					'image' => $row['avatar'] == '' ? ($row['id_attach'] > 0 ? '<img class="avatar" src="' . (empty($row['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row['filename']) . '" alt="" />' : '') : (stristr($row['avatar'], 'http://') ? '<img class="avatar" src="' . $row['avatar'] . '"' . $avatar_width . $avatar_height . ' alt="" />' : '<img class="avatar" src="' . $modSettings['avatar_url'] . '/' . htmlspecialchars($row['avatar']) . '" alt="" />'),
+					'href' => $row['avatar'] == '' ? ($row['id_attach'] > 0 ? (empty($row['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row['filename']) : '') : (stristr($row['avatar'], 'http://') ? $row['avatar'] : $modSettings['avatar_url'] . '/' . $row['avatar']),
+					'url' => $row['avatar'] == '' ? '' : (stristr($row['avatar'], 'http://') ? $row['avatar'] : $modSettings['avatar_url'] . '/' . $row['avatar'])
+				);
 
 			determineTopicClass($context['topics'][$row['id_topic']]);
 		}

+ 2 - 2
Sources/ModerationCenter.php

@@ -2164,7 +2164,7 @@ function ModEndSession()
 		if (strpos($key, '-mod') !== false)
 			unset($_SESSION['token'][$key]);
 
-	redirectexit('?action=moderate');
+	redirectexit('action=moderate');
 }
 
-?>
+?>

+ 3 - 3
Sources/News.php

@@ -170,7 +170,7 @@ function ShowXmlFeed()
 	// Show in rss or proprietary format?
 	$xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf', 'webslice')) ? $_GET['type'] : 'smf';
 
-	// @todo Birthdays? 
+	// @todo Birthdays?
 
 	// List all the different types of data they can pull.
 	$subActions = array(
@@ -179,10 +179,10 @@ function ShowXmlFeed()
 		'members' => array('getXmlMembers', 'member'),
 		'profile' => array('getXmlProfile', null),
 	);
-	
+
 	// Easy adding of sub actions
  	call_integration_hook('integrate_xmlfeeds', array(&$subActions));
-	
+
 	if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']]))
 		$_GET['sa'] = 'recent';
 

+ 6 - 6
Sources/Packages.php

@@ -625,7 +625,7 @@ function PackageInstallTest()
 				// Is the action already stated?
 				$theme_action = !empty($action['theme_action']) && in_array($action['theme_action'], array('no', 'yes', 'auto')) ? $action['theme_action'] : 'auto';
 				$action['unparsed_destination'] = $action['unparsed_filename'];
-				
+
 				// If it's not auto do we think we have something we can act upon?
 				if ($theme_action != 'auto' && !in_array($matches[1], array('languagedir', 'languages_dir', 'imagesdir', 'themedir')))
 					$theme_action = '';
@@ -689,7 +689,7 @@ function PackageInstallTest()
 				if (isset($theme_data['theme_dir']) && $id != 1)
 				{
 					$real_path = $theme_data['theme_dir'] . $path;
-					
+
 					// Confirm that we don't already have this dealt with by another entry.
 					if (!in_array(strtolower(strtr($real_path, array('\\' => '/'))), $themeFinds['other_themes']))
 					{
@@ -701,7 +701,7 @@ function PackageInstallTest()
 								$temp = dirname($temp);
 							$chmod_files[] = $temp;
 						}
-						
+
 						if ($action_data['type'] == 'require-dir' && !is_writable($real_path) && (file_exists($real_path) || !is_writable(dirname($real_path))))
 							$chmod_files[] = $real_path;
 
@@ -1649,7 +1649,7 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 					if ($packageInfo['can_install'] === false && $install->exists('@for') && empty($_SESSION['version_emulate']))
 					{
 						$reset = true;
-						
+
 						// Get the highest install version that is available from the package
 						foreach ($installs as $install)
 						{
@@ -1689,12 +1689,12 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 							break;
 						}
 					}
-					
+
 					// no uninstall found for this version, lets see if one exists for another
 					if ($packageInfo['can_uninstall'] === false && $uninstall->exists('@for') && empty($_SESSION['version_emulate']))
 					{
 						$reset = true;
-						
+
 						// Get the highest install version that is available from the package
 						foreach ($uninstalls as $uninstall)
 						{

+ 67 - 11
Sources/PersonalMessage.php

@@ -35,7 +35,7 @@ function MessageMain()
 	// This file contains the basic functions for sending a PM.
 	require_once($sourcedir . '/Subs-Post.php');
 
-	loadLanguage('PersonalMessage');
+	loadLanguage('PersonalMessage+Drafts');
 
 	if (WIRELESS && WIRELESS_PROTOCOL == 'wap')
 		fatal_lang_error('wireless_error_notyet', false);
@@ -197,6 +197,7 @@ function MessageMain()
 		'send' => 'MessagePost',
 		'send2' => 'MessagePost2',
 		'settings' => 'MessageSettings',
+		'showpmdrafts' => 'MessageDrafts',
 	);
 
 	if (!isset($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
@@ -210,7 +211,7 @@ function MessageMain()
 }
 
 /**
- * A sidebar to easily access different areas of the section
+ * A menu to easily access different areas of the PM section
  *
  * @param string $area
  */
@@ -235,6 +236,12 @@ function messageIndexBar($area)
 					'label' => $txt['sent_items'],
 					'custom_url' => $scripturl . '?action=pm;f=sent',
 				),
+				'drafts' => array(
+					'label' => $txt['drafts_show'],
+					'custom_url' => $scripturl . '?action=pm;sa=showpmdrafts',
+					'permission' => allowedTo('pm_draft'),
+					'enabled' => !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_pm_enabled']),
+				),
 			),
 		),
 		'labels' => array(
@@ -340,14 +347,19 @@ function messageIndexBar($area)
 	$pm_include_data = createMenu($pm_areas, $menuOptions);
 	unset($pm_areas);
 
+	// No menu means no access.
+	if (!$pm_include_data && (!$user_info['is_guest'] || validateSession()))
+		fatal_lang_error('no_access', false);
+
 	// Make a note of the Unique ID for this menu.
 	$context['pm_menu_id'] = $context['max_menu_id'];
 	$context['pm_menu_name'] = 'menu_data_' . $context['pm_menu_id'];
 
 	// Set the selected item.
-	$context['menu_item_selected'] = $pm_include_data['current_area'];
+	$current_area = $pm_include_data['current_area'];
+	$context['menu_item_selected'] = $current_area;
 
-	// obExit will know what to do!
+	// Set the template for this area and add the profile layer.
 	if (!WIRELESS && !isset($_REQUEST['xml']))
 		$context['template_layers'][] = 'pm';
 }
@@ -843,7 +855,7 @@ function MessageFolder()
 		elseif (!empty($context['current_pm']))
 			markMessages($display_pms, $context['current_label_id']);
 	}
-	
+
 	// Build the conversation button array.
 	if ($context['display_mode'] == 2)
 	{
@@ -851,7 +863,7 @@ function MessageFolder()
 			'reply' => array('text' => 'reply_to_all', 'image' => 'reply.png', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=send;f=' . $context['folder'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';pmsg=' . $context['current_pm'] . ';u=all', 'active' => true),
 			'delete' => array('text' => 'delete_conversation', 'image' => 'delete.png', 'lang' => true, 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"'),
 		);
-		
+
 		// Allow mods to add additional buttons here
 		call_integration_hook('integrate_conversation_buttons');
 	}
@@ -1771,6 +1783,18 @@ function MessagePost()
 
 	$modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
 
+	// Are PM drafts enabled?
+	$context['drafts_pm_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_pm_enabled']) && allowedTo('pm_draft');
+	$context['drafts_autosave'] = !empty($context['drafts_pm_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('pm_autosave_draft');
+
+	// Generate a list of drafts that they can load in to the editor
+	if (!empty($context['drafts_pm_save']))
+	{
+		require_once($sourcedir . '/Drafts.php');
+		$pm_seed = isset($_REQUEST['pmsg']) ? $_REQUEST['pmsg'] : (isset($_REQUEST['quote']) ? $_REQUEST['quote'] : 0);
+		ShowDrafts($user_info['id'], $pm_seed, 1);
+	}
+
 	// Needed for the WYSIWYG editor.
 	require_once($sourcedir . '/Subs-Editor.php');
 
@@ -1806,6 +1830,28 @@ function MessagePost()
 	checkSubmitOnce('register');
 }
 
+/**
+ * This function allows the user to view their PM drafts
+ */
+function MessageDrafts()
+{
+	global $context, $sourcedir, $user_info, $modSettings;
+
+	// Set draft capability
+	$context['drafts_pm_save'] = !empty($modSettings['drafts_pm_enabled']) && allowedTo('pm_draft');
+	$context['drafts_autosave'] = !empty($context['drafts_pm_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('pm_autosave_draft');
+
+	// validate with loadMemberData()
+	$memberResult = loadMemberData($user_info['id'], false);
+	if (!is_array($memberResult))
+		fatal_lang_error('not_a_user', false);
+	list ($memID) = $memberResult;
+
+	// drafts is where the functions reside
+	require_once($sourcedir . '/Drafts.php');
+	showPMDrafts($memID);
+}
+
 /**
  * An error in the message...
  *
@@ -1922,9 +1968,9 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 
 	// Set each of the errors for the template.
 	loadLanguage('Errors');
-	
+
 	$context['error_type'] = 'minor';
-	
+
 	$context['post_error'] = array(
 		'messages' => array(),
 		// @todo error handling: maybe fatal errors can be error_type => serious
@@ -1940,12 +1986,16 @@ function messagePostError($error_types, $named_recipients, $recipient_ids = arra
 				$txt['error_' . $error_type] = sprintf($txt['error_' . $error_type], $modSettings['max_messageLength']);
 			$context['post_error']['messages'][] = $txt['error_' . $error_type];
 		}
-		
+
 		// If it's not a minor error flag it as such.
 		if (!in_array($error_type, array('new_reply', 'not_approved', 'new_replies', 'old_topic', 'need_qr_verification', 'no_subject')))
 			$context['error_type'] = 'serious';
 	}
 
+	// Need to reset draft capability once again
+	$context['drafts_pm_save'] = !empty($modSettings['drafts_pm_enabled']) && allowedTo('pm_draft');
+	$context['drafts_autosave'] = !empty($context['drafts_pm_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('pm_autosave_draft');
+
 	// We need to load the editor once more.
 	require_once($sourcedir . '/Subs-Editor.php');
 
@@ -2162,9 +2212,7 @@ function MessagePost2()
 		$context['require_verification'] = create_control_verification($verificationOptions, true);
 
 		if (is_array($context['require_verification']))
-		{
 			$post_errors = array_merge($post_errors, $context['require_verification']);
-		}
 	}
 
 	// If they did, give a chance to make ammends.
@@ -2207,6 +2255,14 @@ function MessagePost2()
 		return messagePostError(array(), $namedRecipientList, $recipientList);
 	}
 
+	// Want to save this as a draft and think about it some more?
+	if (!empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_pm_enabled']) && isset($_POST['save_draft']))
+	{
+		require_once($sourcedir . '/Drafts.php');
+		SavePMDraft($post_errors, $recipientList);
+		return messagePostError($post_errors, $namedRecipientList, $recipientList);
+	}
+
 	// Before we send the PM, let's make sure we don't have an abuse of numbers.
 	elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum')))
 	{

+ 3 - 3
Sources/Poll.php

@@ -213,7 +213,7 @@ function Vote()
 		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
 		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
 	}
-	
+
 	// Maybe let a social networking mod log this, or something?
 	call_integration_hook('integrate_poll_vote', array(&$row['id_poll'], &$pollOptions));
 
@@ -882,7 +882,7 @@ function EditPoll2()
 			)
 		);
 	}
-	
+
 	call_integration_hook('integrate_poll_add_edit', array($bcinfo['id_poll'], $isEdit));
 
 	// Off we go.
@@ -976,7 +976,7 @@ function RemovePoll()
 			'no_poll' => 0,
 		)
 	);
-	
+
 	// A mod might have logged this (social network?), so let them remove, it too
 	call_integration_hook('integrate_poll_remove', array(&$pollID));
 

+ 52 - 7
Sources/Post.php

@@ -19,6 +19,7 @@ if (!defined('SMF'))
 
 /**
  * handles showing the post screen, loading the post to be modified, and loading any post quoted.
+ *
  * - additionally handles previews of posts.
  * - @uses the Post template and language file, main sub template.
  * - allows wireless access using the protocol_post sub template.
@@ -464,7 +465,7 @@ function Post($post_errors = array())
 		}
 
 		// Only show the preview stuff if they hit Preview.
-		if ($really_previewing == true || isset($_REQUEST['xml']))
+		if (($really_previewing == true || isset($_REQUEST['xml'])) && !isset($_POST['id_draft']))
 		{
 			// Set up the preview message and subject and censor them...
 			$context['preview_message'] = $form_message;
@@ -638,9 +639,8 @@ function Post($post_errors = array())
 			)
 		);
 		// The message they were trying to edit was most likely deleted.
-		//@todo Change this error message?
 		if ($smcFunc['db_num_rows']($request) == 0)
-			fatal_lang_error('no_board', false);
+			fatal_lang_error('no_message', false);
 		$row = $smcFunc['db_fetch_assoc']($request);
 
 		$attachment_stuff = array($row);
@@ -1051,6 +1051,17 @@ function Post($post_errors = array())
 	$context['subject'] = addcslashes($form_subject, '"');
 	$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
 
+	// Are post drafts enabled?
+	$context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
+	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
+
+	// Build a list of drafts that they can load in to the editor
+	if (!empty($context['drafts_save']))
+	{
+		require_once($sourcedir . '/Drafts.php');
+		ShowDrafts($user_info['id'], $topic);
+	}
+
 	// Needed for the editor and message icons.
 	require_once($sourcedir . '/Subs-Editor.php');
 
@@ -1159,7 +1170,8 @@ function Post($post_errors = array())
 }
 
 /**
- * actually posts or saves the message composed with Post().
+ * Posts or saves the message composed with Post().
+ *
  * requires various permissions depending on the action.
  * handles attachment, post, and calendar saving.
  * sends off notifications, and allows for announcements and moderation.
@@ -1229,6 +1241,10 @@ function Post2()
 	require_once($sourcedir . '/Subs-Post.php');
 	loadLanguage('Post');
 
+	// Drafts enabled?
+	if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft']))
+		require_once($sourcedir . '/Drafts.php');
+
 	// First check to see if they are trying to delete any current attachments.
 	if (isset($_POST['attach_del']))
 	{
@@ -1528,6 +1544,13 @@ function Post2()
 		if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
 			unset($_POST['sticky']);
 
+		// If drafts are enabled, then pass this off
+		if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft']))
+		{
+			SaveDraft($post_errors);
+			return Post();
+		}
+
 		// If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error.
 		if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
 		{
@@ -1566,6 +1589,13 @@ function Post2()
 		if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky')))
 			unset($_POST['sticky']);
 
+		// Saving your new topic as a draft first?
+		if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft']))
+		{
+			SaveDraft($post_errors);
+			return Post();
+		}
+
 		$posterIsGuest = $user_info['is_guest'];
 	}
 	// Modifying an existing message?
@@ -1642,6 +1672,13 @@ function Post2()
 				$moderationAction = true;
 		}
 
+		// If drafts are enabled, then lets send this off to save
+		if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft']))
+		{
+			SaveDraft($post_errors);
+			return Post();
+		}
+
 		$posterIsGuest = empty($row['id_member']);
 
 		// Can they approve it?
@@ -1947,7 +1984,7 @@ function Post2()
 			$pollOptions,
 			array('id_poll', 'id_choice')
 		);
-		
+
 		call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
 	}
 	else
@@ -2229,6 +2266,7 @@ function Post2()
 
 /**
  * handle the announce topic function (action=announce).
+ *
  * checks the topic announcement permissions and loads the announcement template.
  * requires the announce_topic permission.
  * uses the ManageMembers template and Post language file.
@@ -2261,6 +2299,7 @@ function AnnounceTopic()
 
 /**
  * Allow a user to chose the membergroups to send the announcement to.
+ *
  * lets the user select the membergroups that will receive the topic announcement.
  */
 function AnnouncementSelectMembergroup()
@@ -2339,6 +2378,7 @@ function AnnouncementSelectMembergroup()
 
 /**
  * Send the announcement in chunks.
+ *
  * splits the members to be sent a topic announcement into chunks.
  * composes notification messages in all languages needed.
  * does the actual sending of the topic announcements in chunks.
@@ -2465,8 +2505,8 @@ function AnnouncementSend()
 }
 
 /**
- * notifies members who have requested notification for new topics
- * * posted on a board of said posts.
+ * Notifies members who have requested notification for new topics posted on a board of said posts.
+ *
  * receives data on the topics to send out notifications to by the passed in array.
  * only sends notifications to those who can *currently* see the topic (it doesn't matter if they could when they requested notification.)
  * loads the Post language file multiple times for each language if the userLanguage setting is set.
@@ -2624,6 +2664,7 @@ function notifyMembersBoard(&$topicData)
 
 /**
  * Get the topic for display purposes.
+ *
  * gets a summary of the most recent posts in a topic.
  * depends on the topicSummaryPosts setting.
  * if you are editing a post, only shows posts previous to that post.
@@ -2786,6 +2827,10 @@ function QuoteFast()
 		);
 }
 
+/**
+ * Used to edit the body or subject of a message inline
+ * called from action=jsmodify from script and topic js
+ */
 function JavaScriptModify()
 {
 	global $sourcedir, $modSettings, $board, $topic, $txt;

+ 2 - 2
Sources/PostModeration.php

@@ -522,7 +522,7 @@ function UnapprovedAttachments()
 /**
  * Callback function for UnapprovedAttachments
  * retrieve all the attachments waiting for approval the approver can approve
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -602,7 +602,7 @@ function list_getUnapprovedAttachments($start, $items_per_page, $sort, $approve_
 /**
  * Callback function for UnapprovedAttachments
  * count all the attachments waiting for approval the approver can approve
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort

+ 3 - 3
Sources/Profile-Modify.php

@@ -1361,7 +1361,7 @@ function editBuddies($memID)
 	if (isset($_GET['remove']))
 	{
 		checkSession('get');
-		
+
 		call_integration_hook('integrate_remove_buddy', array($memID));
 
 		// Heh, I'm lazy, do it the easy way...
@@ -1392,7 +1392,7 @@ function editBuddies($memID)
 			if (strlen($new_buddies[$k]) == 0 || in_array($new_buddies[$k], array($user_profile[$memID]['member_name'], $user_profile[$memID]['real_name'])))
 				unset($new_buddies[$k]);
 		}
-		
+
 		call_integration_hook('integrate_add_buddies', array($memID, &$new_buddies));
 
 		if (!empty($new_buddies))
@@ -1456,7 +1456,7 @@ function editBuddies($memID)
 		loadMemberContext($buddy);
 		$context['buddies'][$buddy] = $memberContext[$buddy];
 	}
-	
+
 	call_integration_hook('integrate_view_buddies', array($memID));
 }
 

+ 20 - 20
Sources/Profile-View.php

@@ -184,7 +184,7 @@ function summary($memID)
 /**
  * Show all posts by the current user
  * @todo This function needs to be split up properly.
- * 
+ *
  * @param int $memID id_member
  */
 function showPosts($memID)
@@ -510,7 +510,7 @@ function showPosts($memID)
 
 /**
  * Show all the attachments of a user.
- * 
+ *
  * @param int $memID id_member
  */
 function showAttachments($memID)
@@ -520,7 +520,7 @@ function showAttachments($memID)
 
 	// OBEY permissions!
 	$boardsAllowed = boardsAllowedTo('view_attachments');
-	
+
 	// Make sure we can't actually see anything...
 	if (empty($boardsAllowed))
 		$boardsAllowed = array(-1);
@@ -699,7 +699,7 @@ function list_getNumAttachments($boardsAllowed, $memID)
 
 /**
  * Gets the user stats for display
- * 
+ *
  * @param int $memID id_member
  */
 function statPanel($memID)
@@ -884,14 +884,14 @@ function statPanel($memID)
 
 	// Put it in the right order.
 	ksort($context['posts_by_time']);
-	
+
 	// Custom stats (just add a template_layer to add it to the template!)
  	call_integration_hook('integrate_profile_stats', array($memID));
 }
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function tracking($memID)
@@ -937,7 +937,7 @@ function tracking($memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function trackActivity($memID)
@@ -1158,7 +1158,7 @@ function trackActivity($memID)
 
 /**
  * Get the number of user errors
- * 
+ *
  * @param string $where
  * @param array $where_vars = array()
  * @return string number of user errors
@@ -1182,7 +1182,7 @@ function list_getUserErrorCount($where, $where_vars = array())
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -1225,7 +1225,7 @@ function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars
 
 /**
  * @todo needs a description
- * 
+ *
  * @param string $where
  * @param array $where_vars
  * @return string count of messages matching the IP
@@ -1250,7 +1250,7 @@ function list_getIPMessageCount($where, $where_vars = array())
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -1299,7 +1299,7 @@ function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID = 0 id_member
  */
 function TrackIP($memID = 0)
@@ -1582,7 +1582,7 @@ function TrackIP($memID = 0)
 
 /**
  * Tracks a users logins.
- * 
+ *
  * @param int $memID = 0 id_member
  */
 function TrackLogins($memID = 0)
@@ -1661,7 +1661,7 @@ function TrackLogins($memID = 0)
 
 /**
  * Callback for trackLogins for counting history.
- * 
+ *
  * @param string $where
  * @param array $where_vars
  * @return string count of messages matching the IP
@@ -1687,7 +1687,7 @@ function list_getLoginCount($where, $where_vars = array())
 
 /**
  * Callback for trackLogins data.
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -1722,7 +1722,7 @@ function list_getLogins($start, $items_per_page, $sort, $where, $where_vars = ar
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function trackEdits($memID)
@@ -1823,7 +1823,7 @@ function trackEdits($memID)
 
 /**
  * How many edits?
- * 
+ *
  * @param int $memID id_member
  * @return string number of profile edits
  */
@@ -1850,7 +1850,7 @@ function list_getProfileEditCount($memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -1938,7 +1938,7 @@ function list_getProfileEdits($start, $items_per_page, $sort, $memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function showPermissions($memID)
@@ -2120,7 +2120,7 @@ function showPermissions($memID)
 
 /**
  * View a members warnings?
- * 
+ *
  * @param int $memID id_member
  */
 function viewWarning($memID)

+ 11 - 1
Sources/Profile.php

@@ -30,7 +30,7 @@ function ModifyProfile($post_errors = array())
 
 	// Don't reload this as we may have processed error strings.
 	if (empty($post_errors))
-		loadLanguage('Profile');
+		loadLanguage('Profile+Drafts');
 	loadTemplate('Profile');
 
 	require_once($sourcedir . '/Subs-Menu.php');
@@ -116,6 +116,16 @@ function ModifyProfile($post_errors = array())
 						'any' => 'profile_view_any',
 					),
 				),
+				'showdrafts' => array(
+					'label' => $txt['drafts_show'],
+					'file' => 'Drafts.php',
+					'function' => 'showProfileDrafts',
+					'enabled' => !empty($modSettings['drafts_enabled']) && $context['user']['is_owner'],
+					'permission' => array(
+						'own' => 'profile_view_own',
+						'any' =>  array(),
+					),
+				),
 				'permissions' => array(
 					'label' => $txt['showPermissions'],
 					'file' => 'Profile-View.php',

+ 2 - 2
Sources/QueryString.php

@@ -308,7 +308,7 @@ function isValidIPv6($ip)
 /**
  * Converts IPv6s to numbers.  This makes ban checks much easier.
  * @param string $ip ip address to be converted
- * @return array 
+ * @return array
  */
 function convertIPv6toInts($ip)
 {
@@ -373,7 +373,7 @@ function expandIPv6($addr, $strict_check = true)
 	// Save this incase of repeated use.
 	$converted[$addr] = $result;
 
-	// Quick check to make sure the length is as expected. 
+	// Quick check to make sure the length is as expected.
 	if (!$strict_check || strlen($result) == 39)
 		return $result;
 	else

+ 5 - 5
Sources/Recent.php

@@ -432,7 +432,7 @@ function UnreadTopics()
 		header('HTTP/1.1 403 Forbidden');
 		die;
 	}
-	
+
 	$context['showCheckboxes'] = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read'];
 
 	$context['showing_all_topics'] = isset($_GET['all']);
@@ -1318,7 +1318,7 @@ function UnreadTopics()
 
 	$context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
 	$context['topics_to_mark'] = implode('-', $topic_ids);
-	
+
 	if ($settings['show_mark_read'])
 	{
 		// Build the recent button array.
@@ -1335,9 +1335,9 @@ function UnreadTopics()
 					'lang' => true,
 					'url' => 'javascript:document.quickModForm.submit();',
 				);
-				
+
 			if (!empty($context['topics']) && !$context['showing_all_topics'])
-				$context['recent_buttons']['readall'] = array('text' => 'unread_topics_all', 'image' => 'markreadall.png', 'lang' => true, 'url' => $scripturl . '?action=unread;all' . $context['querystring_board_limits'], 'active' => true);	
+				$context['recent_buttons']['readall'] = array('text' => 'unread_topics_all', 'image' => 'markreadall.png', 'lang' => true, 'url' => $scripturl . '?action=unread;all' . $context['querystring_board_limits'], 'active' => true);
 		}
 		elseif (!$is_topics && isset($context['topics_to_mark']))
 		{
@@ -1357,7 +1357,7 @@ function UnreadTopics()
 		// Allow mods to add additional buttons here
 		call_integration_hook('integrate_recent_buttons');
 	}
-	
+
 	// Allow helpdesks and bug trackers and what not to add their own unread data (just add a template_layer to show custom stuff in the template!)
  	call_integration_hook('integrate_unread_list');
 }

+ 1 - 1
Sources/Register.php

@@ -105,7 +105,7 @@ function Register($reg_errors = array())
 			$context['agreement'] = parse_bbc(file_get_contents($boarddir . '/agreement.txt'), true, 'agreement');
 		else
 			$context['agreement'] = '';
-		
+
 		// Nothing to show, lets disable registration and inform the admin of this error
 		if (empty($context['agreement']))
 		{

+ 1 - 1
Sources/Reminder.php

@@ -70,7 +70,7 @@ function RemindPick()
 	// You must enter a username/email address.
 	if (empty($where))
 		fatal_lang_error('username_no_exist', false);
-		
+
 	// Make sure we are not being slammed
 	spamProtection('remind');
 

+ 3 - 3
Sources/RemoveTopic.php

@@ -232,7 +232,7 @@ function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = fal
 	// Only a single topic.
 	if (is_numeric($topics))
 		$topics = array($topics);
-		
+
 	// Decrease the post counts.
 	if ($decreasePostCount)
 	{
@@ -533,7 +533,7 @@ function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = fal
 			'topics' => $topics,
 		)
 	);
-	
+
 	// Maybe there's a mod that wants to delete topic related data of its own
  	call_integration_hook('integrate_remove_topics', array($topics));
 
@@ -962,7 +962,7 @@ function removeMessage($message, $decreasePostCount = true)
 			'id_msg' => $message,
 		);
 		removeAttachments($attachmentQuery);
-		
+
 		// Allow mods to remove message related data of their own (likes, maybe?)
 		call_integration_hook('integrate_remove_message', array($message));
 	}

+ 12 - 12
Sources/Reports.php

@@ -6,11 +6,11 @@
  * core report generation is done in two areas. Firstly, a report "generator"
  * will fill context with relevant data. Secondly, the choice of sub-template
  * will determine how this data is shown to the user
- * 
+ *
  * Functions ending with "Report" are responsible for generating data for reporting.
  * They are all called from ReportsMain.
  * Never access the context directly, but use the data handling functions to do so.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -103,16 +103,16 @@ function ReportsMain()
 
 	// Make the page title more descriptive.
 	$context['page_title'] .= ' - ' . (isset($txt['gr_type_' . $context['report_type']]) ? $txt['gr_type_' . $context['report_type']] : $context['report_type']);
-	
+
 	// Build the reports button array.
 	$context['report_buttons'] = array(
 		'generate_reports' => array('text' => 'generate_reports', 'image' => 'print.png', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports', 'active' => true),
 		'print' => array('text' => 'print', 'image' => 'print.png', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports;rt=' . $context['report_type']. ';st=print', 'custom' => 'target="_blank"'),
 	);
-	
+
 	// Allow mods to add additional buttons here
-	call_integration_hook('integrate_report_buttons');	
-	
+	call_integration_hook('integrate_report_buttons');
+
 	// Now generate the data.
 	$context['report_types'][$context['report_type']]['function']();
 
@@ -785,7 +785,7 @@ function StaffReport()
  * context, ready for filling using addData().
  * Fills the context variable current_table with the ID of the table created.
  * Keeps track of the current table count using context variable table_count.
- * 
+ *
  * @param string $title = '' Title to be displayed with this data table.
  * @param string $default_value = '' Value to be displayed if a key is missing from a row.
  * @param string $shading = 'all' Should the left, top or both (all) parts of the table beshaded?
@@ -839,7 +839,7 @@ function newTable($title = '', $default_value = '', $shading = 'all', $width_nor
  * if any key in the incoming data begins with '#sep#', the function
  * will add a separator accross the table at this point.
  * once the incoming data has been sanitized, it is added to the table.
- * 
+ *
  * @param array $inc_data
  * @param int $custom_table = null
  */
@@ -902,10 +902,10 @@ function addData($inc_data, $custom_table = null)
 
 /**
  * Add a separator row, only really used when adding data by rows.
- * 
+ *
  * @param string $title = ''
  * @param string $custom_table = null
- * 
+ *
  * @return bool returns false if there are no tables
  */
 function addSeparator($title = '', $custom_table = null)
@@ -966,7 +966,7 @@ function finishTables()
 
 /**
  * Set the keys in use by the tables - these ensure entries MUST exist if the data isn't sent.
- * 
+ *
  * sets the current set of "keys" expected in each data array passed to
  * addData. It also sets the way we are adding data to the data table.
  * method specifies whether the data passed to addData represents a new
@@ -975,7 +975,7 @@ function finishTables()
  * addData().
  * if reverse is set to true, then the values of the variable "keys"
  * are used as oppossed to the keys(!
- * 
+ *
  * @param string $method = 'rows' rows or cols
  * @param array $keys = array()
  * @param bool $reverse = false

+ 47 - 7
Sources/ScheduledTasks.php

@@ -2,7 +2,7 @@
 
 /**
  * This file is automatically called and handles all manner of scheduled things.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -1685,16 +1685,16 @@ function scheduled_remove_temp_attachments()
 /**
  * Check for move topic notices that have past their best by date
  */
-function scheduled_remove_topic_redirect() 
+function scheduled_remove_topic_redirect()
 {
 	global $smcFunc, $sourcedir;
-	
+
 	// init
 	$topics = array();
-	
+
 	// We will need this for lanaguage files
 	loadEssentialThemeData();
-	
+
 	// Find all of the old MOVE topic notices that were set to expire
 	$request = $smcFunc['db_query']('', '
 		SELECT id_topic
@@ -1705,11 +1705,11 @@ function scheduled_remove_topic_redirect()
 			'redirect_expires' => time(),
 		)
 	);
-	
+
 	while ($row = $smcFunc['db_fetch_row']($request))
 		$topics[] = $row[0];
 	$smcFunc['db_free_result']($request);
-	
+
 	// Zap, your gone
 	if (count($topics) > 0)
 	{
@@ -1720,4 +1720,44 @@ function scheduled_remove_topic_redirect()
 	return true;
 }
 
+/**
+ * Check for old drafts and remove them
+ */
+function scheduled_remove_old_drafts()
+{
+	global $smcFunc, $sourcedir, $modSettings;
+
+	if (empty($modSettings['drafts_keep_days']))
+		return true;
+
+	// init
+	$drafts= array();
+
+	// We need this for lanaguage items
+	loadEssentialThemeData();
+
+	// Find all of the old drafts
+	$request = $smcFunc['db_query']('', '
+		SELECT id_draft
+		FROM {db_prefix}user_drafts
+		WHERE poster_time <= {int:poster_time_old}',
+		array(
+			'poster_time_old' => time() - (86400 * $modSettings['drafts_keep_days']),
+		)
+	);
+
+	while ($row = $smcFunc['db_fetch_row']($request))
+		$drafts[] = $row[0];
+	$smcFunc['db_free_result']($request);
+
+	// If we have old one, remove them
+	if (count($drafts) > 0)
+	{
+		require_once($sourcedir . '/Drafts.php');
+		DeleteDraft($drafts, false);
+	}
+
+	return true;
+}
+
 ?>

+ 5 - 5
Sources/SearchAPI-Custom.php

@@ -210,7 +210,7 @@ class custom_search
 
 		return $ignoreRequest;
 	}
-	
+
 	/**
 	 * After a post is made, we update the search index database.
 	 */
@@ -239,21 +239,21 @@ class custom_search
 	public function postModified($msgOptions, $topicOptions, $posterOptions)
 	{
 		global $modSettings, $smcFunc;
-		
+
 		if (isset($msgOptions['body']))
 		{
 			$customIndexSettings = unserialize($modSettings['search_custom_index_config']);
 			$stopwords = empty($modSettings['search_stopwords']) ? array() : explode(',', $modSettings['search_stopwords']);
 			$old_body = isset($msgOptions['old_body']) ? $msgOptions['old_body'] : '';
-			
+
 			// create thew new and old index
 			$old_index = text2words($old_body, $customIndexSettings['bytes_per_word'], true);
 			$new_index = text2words($msgOptions['body'], $customIndexSettings['bytes_per_word'], true);
-			
+
 			// Calculate the words to be added and removed from the index.
 			$removed_words = array_diff(array_diff($old_index, $new_index), $stopwords);
 			$inserted_words = array_diff(array_diff($new_index, $old_index), $stopwords);
-			
+
 			// Delete the removed words AND the added ones to avoid key constraints.
 			if (!empty($removed_words))
 			{

+ 20 - 2
Sources/Security.php

@@ -22,6 +22,7 @@ if (!defined('SMF'))
  * Makes sure the user is who they claim to be by requiring a password to be typed in every hour.
  * Is turned on and off by the securityDisable setting.
  * Uses the adminLogin() function of Subs-Auth.php if they need to login, which saves all request (post and get) data.
+ *
  * @param string $type = admin
  */
 function validateSession($type = 'admin')
@@ -97,6 +98,7 @@ function validateSession($type = 'admin')
  * Require a user who is logged in. (not a guest.)
  * Checks if the user is currently a guest, and if so asks them to login with a message telling them why.
  * Message is what to tell them when asking them to login.
+ *
  * @param string $message = ''
  */
 function is_not_guest($message = '')
@@ -162,6 +164,7 @@ function is_not_guest($message = '')
  * Checks if the user is banned, and if so dies with an error.
  * Caches this information for optimization purposes.
  * Forces a recheck if force_check is true.
+ *
  * @param bool $forceCheck = false
  */
 function is_not_banned($forceCheck = false)
@@ -491,6 +494,7 @@ function banPermissions()
  * Log a ban in the database.
  * Log the current user in the ban logs.
  * Increment the hit counters for the specified ban ID's (if any.)
+ *
  * @param array $ban_ids = array()
  * @param string $email = null
  */
@@ -525,6 +529,7 @@ function log_ban($ban_ids = array(), $email = null)
  * Checks if a given email address might be banned.
  * Check if a given email is banned.
  * Performs an immediate ban if the turns turns out positive.
+ *
  * @param string $email
  * @param string $restriction
  * @param string $error
@@ -594,6 +599,7 @@ function isBannedEmail($email, $restriction, $error)
  * Depends on the disableCheckUA setting, which is usually missing.
  * Will check GET, POST, or REQUEST depending on the passed type.
  * Also optionally checks the referring action if passed. (note that the referring action must be by GET.)
+ *
  * @param string $type = 'post' (post, get, request)
  * @param string $from_action = ''
  * @param bool $is_fatal = true
@@ -707,6 +713,7 @@ function checkSession($type = 'post', $from_action = '', $is_fatal = true)
 
 /**
  * Check if a specific confirm parameter was given.
+ *
  * @param string $action
  */
 function checkConfirm($action)
@@ -727,6 +734,7 @@ function checkConfirm($action)
 
 /**
  * Lets give you a token of our appreciation.
+ *
  * @param string $action
  * @param string $type = 'post'
  * @return array
@@ -819,7 +827,10 @@ function validateToken($action, $type = 'post', $reset = true)
 }
 
 /**
- * Clean up a little.
+ * Removes old unused tokens from session
+ * defaults to 3 hours before a token is considered expired
+ * if $complete = true will remove all tokens
+ *
  * @param bool $complete = false
  */
 function cleanTokens($complete = false)
@@ -887,6 +898,7 @@ function checkSubmitOnce($action, $is_fatal = true)
  * checks whether the user is allowed to do permission. (ie. post_new.)
  * If boards is specified, checks those boards instead of the current one.
  * Always returns true if the user is an administrator.
+ *
  * @param string $permission
  * @param array $boards = null
  * @return bool if the user can do the permission
@@ -961,6 +973,7 @@ function allowedTo($permission, $boards = null)
  * Checks the passed boards or current board for the permission.
  * If they are not, it loads the Errors language file and shows an error using $txt['cannot_' . $permission].
  * If they are a guest and cannot do it, this calls is_not_guest().
+ *
  * @param string $permission
  * @param array $boards = null
  */
@@ -1020,6 +1033,7 @@ function isAllowedTo($permission, $boards = null)
  * Returns an array with only a 0 in it if the user has permission to do this on every board.
  * Returns an empty array if he or she cannot do this on any board.
  * If check_access is true will also make sure the group has proper access to that board.
+ *
  * @param array $permissions
  * @param bool $check_access = true
  * @param bool $simple = true
@@ -1110,6 +1124,7 @@ function boardsAllowedTo($permissions, $check_access = true, $simple = true)
  *  'no_through_forum': don't show the email address, but do allow
  *    things to be mailed using the built-in forum mailer.
  *  'no': keep the email address hidden.
+ *
  * @param bool $userProfile_hideEmail
  * @param int $userProfile_id
  * @return string (yes, yes_permission_override, no_through_forum, no)
@@ -1201,6 +1216,7 @@ function spamProtection($error_type)
 
 /**
  * A generic function to create a pair of index.php and .htaccess files in a directory
+ *
  * @param string $path, the (absolute) directory path
  * @param boolean $attachments, if the directory is an attachments directory or not
  * @return true on success, error string if anything fails
@@ -1271,7 +1287,9 @@ else
 }
 
 /**
- * Another helper function that put together the
+ * Helper function that puts together a ban query for a given ip
+ * builds the query for ipv6, ipv4 or 255.255.255.255 depending on whats supplied
+ *
  * @param string $fullip An IP address either IPv6 or not
  * @return string A SQL condition
  */

+ 7 - 7
Sources/SplitTopics.php

@@ -2,7 +2,7 @@
 
 /**
  * Handle merging and splitting of topics
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -11,7 +11,7 @@
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
- * 
+ *
  * Original module by Mach8 - We'll never forget you.
  */
 
@@ -181,11 +181,11 @@ function SplitExecute()
 /**
  * allows the user to select the messages to be split.
  * is accessed with ?action=splittopics;sa=selectTopics.
- * uses 'select' sub template of the SplitTopics template or (for 
+ * uses 'select' sub template of the SplitTopics template or (for
  * XMLhttp) the 'split' sub template of the Xml template.
  * supports XMLhttp for adding/removing a message to the selection.
  * uses a session variable to store the selected topics.
- * shows two independent page indexes for both the selected and 
+ * shows two independent page indexes for both the selected and
  * not-selected messages (;topic=1.x;start2=y).
  */
 function SplitSelectTopics()
@@ -965,12 +965,12 @@ function MergeIndex()
 
 /**
  * set merge options and do the actual merge of two or more topics.
- * 
+ *
  * the merge options screen:
  * * shows topics to be merged and allows to set some merge options.
  * * is accessed by ?action=mergetopics;sa=options.and can also internally be called by QuickModeration() (Subs-Boards.php).
  * * uses 'merge_extra_options' sub template of the SplitTopics template.
- * 
+ *
  * the actual merge:
  * * is accessed with ?action=mergetopics;sa=execute.
  * * updates the statistics to reflect the merge.
@@ -1309,7 +1309,7 @@ function MergeExecute($topics = array())
 	while ($row = $smcFunc['db_fetch_row']($request))
 		$affected_msgs[] = $row[0];
 	$smcFunc['db_free_result']($request);
-	
+
 	// Assign the first topic ID to be the merged topic.
 	$id_topic = min($topics);
 

+ 4 - 4
Sources/Stats.php

@@ -2,7 +2,7 @@
 
 /**
  * Provide a display for forum statistics
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -18,7 +18,7 @@ if (!defined('SMF'))
 
 /**
  * Display some useful/interesting board statistics.
- * 
+ *
  * gets all the statistics in order and puts them in.
  * uses the Stats template and language file. (and main sub template.)
  * requires the view_stats permission.
@@ -627,7 +627,7 @@ function DisplayStats()
 		return;
 
 	getDailyStats(implode(' OR ', $condition_text), $condition_params);
-	
+
 	// Custom stats (just add a template_layer to add it to the template!)
  	call_integration_hook('integrate_forum_stats');
 }
@@ -670,7 +670,7 @@ function getDailyStats($condition_string, $condition_parameters = array())
  * only returns anything if stats was enabled during installation.
  * can also be accessed by the admin, to show what stats sm.org collects.
  * does not return any data directly to sm.org, instead starts a new request for security.
- * 
+ *
  * @link http://www.simplemachines.org/about/stats.php for more info.
  */
 function SMStats()

+ 16 - 16
Sources/Subs-Admin.php

@@ -73,7 +73,7 @@ function getServerVersions($checkFor)
 		$versions['memcache'] = array('title' => 'Memcached', 'version' => empty($memcached) ? '???' : memcache_get_version($memcached));
 	if (in_array('xcache', $checkFor) && function_exists('xcache_set'))
 		$versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION);
-	
+
 	if (in_array('php', $checkFor))
 		$versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION, 'more' => '?action=admin;area=serversettings;sa=phpinfo');
 
@@ -86,8 +86,8 @@ function getServerVersions($checkFor)
 /**
  * Search through source, theme and language files to determine their version.
  * Get detailed version information about the physical SMF files on the server.
- * 
- * - the input parameter allows to set whether to include SSI.php and whether 
+ *
+ * - the input parameter allows to set whether to include SSI.php and whether
  *   the results should be sorted.
  * - returns an array containing information on source files, templates and
  *   language files found in the default theme directory (grouped by language).
@@ -231,7 +231,7 @@ function getFileVersions(&$versionOptions)
  *
  * The most important function in this file for mod makers happens to be the
  * updateSettingsFile() function, but it shouldn't be used often anyway.
- * 
+ *
  * - updates the Settings.php file with the changes supplied in config_vars.
  * - expects config_vars to be an associative array, with the keys as the
  *   variable names in Settings.php, and the values the variable values.
@@ -240,7 +240,7 @@ function getFileVersions(&$versionOptions)
  * - writes nothing if the resulting file would be less than 10 lines
  *   in length (sanity check for read lock.)
  * - check for changes to db_last_error and passes those off to a separate handler
- * - attempts to create a backup file and will use it should the writing of the 
+ * - attempts to create a backup file and will use it should the writing of the
  *   new settings file fail
  *
  * @param array $config_vars
@@ -248,7 +248,7 @@ function getFileVersions(&$versionOptions)
 function updateSettingsFile($config_vars)
 {
 	global $boarddir, $cachedir, $context;
-	
+
 	// Updating the db_last_error, then don't mess around with Settings.php
 	if (count($config_vars) === 1 && isset($config_vars['db_last_error']))
 	{
@@ -261,7 +261,7 @@ function updateSettingsFile($config_vars)
 
 	// Load the settings file.
 	$settingsArray = trim(file_get_contents($boarddir . '/Settings.php'));
-	
+
 	// Break it up based on \r or \n, and then clean out extra characters.
 	if (strpos($settingsArray, "\n") !== false)
 		$settingsArray = explode("\n", $settingsArray);
@@ -322,11 +322,11 @@ function updateSettingsFile($config_vars)
 			$settingsArray[$end++] = '';
 		else
 			$end++;
-		
+
 		// Add in any newly defined vars that were passed
 		foreach ($config_vars as $var => $val)
 			$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
-		
+
 		$settingsArray[$end] = '?' . '>';
 	}
 	else
@@ -344,7 +344,7 @@ function updateSettingsFile($config_vars)
 	// to validate that we even write things on this filesystem.
 	if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
 		$cachedir = $boarddir . '/cache';
-	
+
 	$test_fp = @fopen($cachedir . '/settings_update.tmp', "w+");
 	if ($test_fp)
 	{
@@ -372,7 +372,7 @@ function updateSettingsFile($config_vars)
 		// write out the new
 		$write_settings = implode('', $settingsArray);
 		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
-		
+
 		// survey says ...
 		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
 		{
@@ -387,15 +387,15 @@ function updateSettingsFile($config_vars)
 
 /**
  * Saves the time of the last db error for the error log
- * - Done separately from updateSettingsFile to avoid race conditions 
+ * - Done separately from updateSettingsFile to avoid race conditions
  *   which can occur during a db error
  * - If it fails Settings.php will assume 0
  */
-function updateDbLastError($time) 
+function updateDbLastError($time)
 {
-	global $boarddir; 
-	
-	// Write out the db_last_error file with the error timestamp 
+	global $boarddir;
+
+	// Write out the db_last_error file with the error timestamp
 	file_put_contents($boarddir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = ' . $time . ';' . "\n" . '?' . '>', LOCK_EX);
 	@touch($boarddir . '/' . 'Settings.php');
 }

+ 31 - 3
Sources/Subs-BoardIndex.php

@@ -53,8 +53,9 @@ function getBoardIndex($boardIndexOptions)
 			' . ($user_info['is_guest'] ? ' 1 AS is_read, 0 AS new_from,' : '
 			(IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_read, IFNULL(lb.id_msg, -1) + 1 AS new_from,' . ($boardIndexOptions['include_categories'] ? '
 			c.can_collapse, IFNULL(cc.id_member, 0) AS is_collapsed,' : '')) . '
-			IFNULL(mem.id_member, 0) AS id_member, m.id_msg,
-			IFNULL(mods_mem.id_member, 0) AS id_moderator, mods_mem.real_name AS mod_real_name
+			IFNULL(mem.id_member, 0) AS id_member, mem.avatar, m.id_msg,
+			IFNULL(mods_mem.id_member, 0) AS id_moderator, mods_mem.real_name AS mod_real_name' . (!empty($settings['avatars_on_indexes']) ? ',
+			IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type' : '') . '
 		FROM {db_prefix}boards AS b' . ($boardIndexOptions['include_categories'] ? '
 			LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)' : '') . '
 			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = b.id_last_msg)
@@ -62,7 +63,8 @@ function getBoardIndex($boardIndexOptions)
 			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})' . ($boardIndexOptions['include_categories'] ? '
 			LEFT JOIN {db_prefix}collapsed_categories AS cc ON (cc.id_cat = c.id_cat AND cc.id_member = {int:current_member})' : '')) . '
 			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
-			LEFT JOIN {db_prefix}members AS mods_mem ON (mods_mem.id_member = mods.id_member)
+			LEFT JOIN {db_prefix}members AS mods_mem ON (mods_mem.id_member = mods.id_member)' . (!empty($settings['avatars_on_indexes']) ? '
+			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = m.id_member)' : '') . '
 		WHERE {query_see_board}' . (empty($boardIndexOptions['countChildPosts']) ? (empty($boardIndexOptions['base_level']) ? '' : '
 			AND b.child_level >= {int:child_level}') : '
 			AND b.child_level BETWEEN ' . $boardIndexOptions['base_level'] . ' AND ' . ($boardIndexOptions['base_level'] + 1)),
@@ -228,6 +230,24 @@ function getBoardIndex($boardIndexOptions)
 		else
 			continue;
 
+		if (!empty($settings['avatars_on_indexes']))
+		{
+			// Allow themers to show the latest poster's avatar along with the board
+			if(!empty($row_board['avatar']))
+			{
+				if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize')
+				{
+					$avatar_width = !empty($modSettings['avatar_max_width_external']) ? ' width="' . $modSettings['avatar_max_width_external'] . '"' : '';
+					$avatar_height = !empty($modSettings['avatar_max_height_external']) ? ' height="' . $modSettings['avatar_max_height_external'] . '"' : '';
+				}
+				else
+				{
+					$avatar_width = '';
+					$avatar_height = '';
+				}
+			}
+		}
+
 		// Prepare the subject, and make sure it's not too long.
 		censorText($row_board['subject']);
 		$row_board['short_subject'] = shorten_subject($row_board['subject'], 24);
@@ -247,6 +267,14 @@ function getBoardIndex($boardIndexOptions)
 			'topic' => $row_board['id_topic']
 		);
 
+		if (!empty($settings['avatars_on_indexes']))
+			$this_last_post['member']['avatar'] = array(
+				'name' => $row_board['avatar'],
+				'image' => $row_board['avatar'] == '' ? ($row_board['id_attach'] > 0 ? '<img class="avatar" src="' . (empty($row_board['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row_board['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row_board['filename']) . '" alt="" />' : '') : (stristr($row_board['avatar'], 'http://') ? '<img class="avatar" src="' . $row_board['avatar'] . '"' . $avatar_width . $avatar_height . ' alt="" />' : '<img class="avatar" src="' . $modSettings['avatar_url'] . '/' . htmlspecialchars($row_board['avatar']) . '" alt="" />'),
+				'href' => $row_board['avatar'] == '' ? ($row_board['id_attach'] > 0 ? (empty($row_board['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row_board['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row_board['filename']) : '') : (stristr($row_board['avatar'], 'http://') ? $row_board['avatar'] : $modSettings['avatar_url'] . '/' . $row_board['avatar']),
+				'url' => $row_board['avatar'] == '' ? '' : (stristr($row_board['avatar'], 'http://') ? $row_board['avatar'] : $modSettings['avatar_url'] . '/' . $row_board['avatar'])
+			);
+
 		// Provide the href and link.
 		if ($row_board['subject'] != '')
 		{

+ 1 - 1
Sources/Subs-Boards.php

@@ -3,7 +3,7 @@
 /**
  * This file is mainly concerned with minor tasks relating to boards, such as
  * marking them read, collapsing categories, or quick moderation.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF

+ 7 - 7
Sources/Subs-Calendar.php

@@ -172,7 +172,7 @@ function getEventRange($low_date, $high_date, $use_permissions = true)
 					'start_date' => $row['start_date'],
 					'end_date' => $row['end_date'],
 					'is_last' => false,
-					'id_board' => $row['id_board'],	
+					'id_board' => $row['id_board'],
 					'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
 					'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
 					'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
@@ -885,7 +885,7 @@ function insertEvent(&$eventOptions)
 
 	// Store the just inserted id_event for future reference.
 	$eventOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}calendar', 'id_event');
-	
+
 	call_integration_hook('integrate_insert_event', array($eventOptions));
 
 	// Update the settings to show something calendarish was updated.
@@ -904,7 +904,7 @@ function modifyEvent($event_id, &$eventOptions)
 
 	// Properly sanitize the title.
 	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
-	
+
 	// Scan the start date for validity and get its components.
 	if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3)
 		trigger_error('modifyEvent(): invalid start date format given', E_USER_ERROR);
@@ -916,9 +916,9 @@ function modifyEvent($event_id, &$eventOptions)
 	if (!isset($eventOptions['end_date']))
 		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400);
 
-	
-	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions));		
-		
+
+	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions));
+
 	$smcFunc['db_query']('', '
 		UPDATE {db_prefix}calendar
 		SET
@@ -958,7 +958,7 @@ function removeEvent($event_id)
 			'id_event' => $event_id,
 		)
 	);
-	
+
 	call_integration_hook('integrate_remove_event', array($event_id));
 
 	updateSettings(array(

+ 7 - 7
Sources/Subs-Categories.php

@@ -2,7 +2,7 @@
 
 /**
  * This file contains the functions to add, modify, remove, collapse and expand categories.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -20,7 +20,7 @@ if (!defined('SMF'))
  * Edit the position and properties of a category.
  * general function to modify the settings and position of a category.
  * used by ManageBoards.php to change the settings of a category.
- * 
+ *
  * @param int $category_id
  * @param array $catOptions
  */
@@ -117,7 +117,7 @@ function modifyCategory($category_id, $catOptions)
  * general function to create a new category and set its position.
  * allows (almost) the same options as the modifyCat() function.
  * returns the ID of the newly created category.
- * 
+ *
  * @param int $createCategory
  * @param array $catOptions
  */
@@ -163,14 +163,14 @@ function createCategory($catOptions)
 	return $category_id;
 }
 
-/** 
+/**
  * Remove one or more categories.
  * general function to delete one or more categories.
  * allows to move all boards in the categories to a different category before deleting them.
  * if moveChildrenTo is set to null, all boards inside the given categorieswill be deleted.
  * deletes all information that's associated with the given categories.
  * updates the statistics to reflect the new situation.
- * 
+ *
  * @param array $categories_to_remove
  * @param int $moveChildrenTo = null
  */
@@ -246,12 +246,12 @@ function deleteCategories($categories, $moveBoardsTo = null)
 	reorderBoards();
 }
 
-/** 
+/**
  * Collapse, expand or toggle one or more categories for one or more members.
  * if members is null, the category is collapsed/expanded for all members.
  * allows three changes to the status: 'expand', 'collapse' and 'toggle'.
  * if check_collapsable is set, only category allowed to be collapsed, will be collapsed.
- * 
+ *
  * @param array $categories
  * @param string $new_status
  * @param array $members = null

+ 2 - 2
Sources/Subs-Db-mysql.php

@@ -124,7 +124,7 @@ function smf_db_replacement__callback($matches)
 	global $db_callback, $user_info, $db_prefix;
 
 	list ($values, $connection) = $db_callback;
-	
+
 	// Connection gone???  This should *never* happen at this point, yet it does :'(
 	if (!is_resource($connection))
 		display_db_error();
@@ -390,7 +390,7 @@ function smf_db_query($identifier, $db_string, $db_values = array(), $connection
 		$ret = @mysql_query($db_string, $connection);
 	else
 		$ret = @mysql_unbuffered_query($db_string, $connection);
-		
+
 	if ($ret === false && empty($db_values['db_error_skip']))
 		$ret = smf_db_error($db_string, $connection);
 

+ 23 - 25
Sources/Subs-Editor.php

@@ -3,7 +3,7 @@
 /**
  * This file contains those functions specific to the editing box and is
  * generally used for WYSIWYG type functionality.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -21,7 +21,7 @@ if (!defined('SMF'))
  * !!!Compatibility!!!
  * Since we changed the editor we don't need it any more, but let's keep it if any mod wants to use it
  * Convert only the BBC that can be edited in HTML mode for the editor.
- * 
+ *
  * @param string $text
  * @param boolean $compat_mode if true will convert the text, otherwise not (default false)
  * @return string
@@ -85,9 +85,9 @@ function bbc_to_html($text, $compat_mode = false)
  * !!!Compatibility!!!
  * This is no more needed, but to avoid break mods let's keep it
  * Run it it shouldn't even hurt either, so let's not bother remove it
- * 
+ *
  * The harder one - wysiwyg to BBC!
- * 
+ *
  * @param string $text
  * @return string
  */
@@ -849,9 +849,9 @@ function html_to_bbc($text)
 /**
  * !!!Compatibility!!!
  * This is no more needed, but to avoid break mods let's keep it
- * 
+ *
  * Returns an array of attributes associated with a tag.
- * 
+ *
  * @param string $text
  * @return string
  */
@@ -1332,7 +1332,7 @@ function loadLocale()
 	else
 		@ob_start();
 
-	// If we don't have any locale better avoit broken js
+	// If we don't have any locale better avoid broken js
 	if (empty($txt['lang_locale']))
 		die();
 
@@ -1361,7 +1361,7 @@ function loadLocale()
  *   message icons or a list of custom message icons retrieved from the database.
  * - The board_id is needed for the custom message icons (which can be set for
  *   each board individually).
- * 
+ *
  * @param int $board_id
  * @return array
  */
@@ -1435,7 +1435,7 @@ function getMessageIcons($board_id)
 
 /**
  * Compatibility function - used in 1.1 for showing a post box.
- * 
+ *
  * @param string $msg
  * @return string
  */
@@ -1470,27 +1470,25 @@ function create_control_richedit($editorOptions)
 
 		// This really has some WYSIWYG stuff.
 		loadTemplate('GenericControls', isBrowser('ie') ? 'editor_ie' : 'editor');
-		$context['html_headers'] .= '
-		<script type="text/javascript"><!-- // --><![CDATA[
-			var smf_smileys_url = \'' . $settings['smileys_url'] . '\';
-			var bbc_quote_from = \'' . addcslashes($txt['quote_from'], "'") . '\';
-			var bbc_quote = \'' . addcslashes($txt['quote'], "'") . '\';
-			var bbc_search_on = \'' . addcslashes($txt['search_on'], "'") . '\';
-		// ]]></script>
-		<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/editor.js?alp21"></script>
-		<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/jquery.sceditor.css" type="text/css" media="all" />
-		<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/jquery.sceditor.js"></script>
-		<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/jquery.sceditor.bbcode.js"></script>';
 
+		// Css and JS make the editor go round
+		loadCSSFile( $settings['default_theme_url'] . '/css/jquery.sceditor.css', array());
+		loadJavascriptFile($settings['default_theme_url'] . '/scripts/editor.js?alp21', array(), 'editor.js');
+		loadJavascriptFile($settings['default_theme_url'] . '/scripts/jquery.sceditor.js?alp21', array());
+		loadJavascriptFile($settings['default_theme_url'] . '/scripts/jquery.sceditor.bbcode.js?alp21', array());
+		addInlineJavascript('
+		var smf_smileys_url = \'' . $settings['smileys_url'] . '\';
+		var bbc_quote_from = \'' . addcslashes($txt['quote_from'], "'") . '\';
+		var bbc_quote = \'' . addcslashes($txt['quote'], "'") . '\';
+		var bbc_search_on = \'' . addcslashes($txt['search_on'], "'") . '\';');
+		// editor language file
 		if (!empty($txt['lang_locale']) && $txt['lang_locale'] != 'en_US')
-			$context['html_headers'] .= '
-		<script type="text/javascript" src="' . $scripturl . '?action=loadeditorlocale"></script>';
+			loadJavascriptFile($scripturl . '?action=loadeditorlocale', array(), 'sceditor_language');
 
 		$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
 		if ($context['show_spellchecking'])
 		{
-			$context['html_headers'] .= '
-		<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/spellcheck.js?alp21"></script>';
+			loadJavascriptFile($settings['default_theme_url'] . '/scripts/spellcheck.js?alp21', array());
 
 			// Some hidden information is needed in order to make the spell checking work.
 			if (!isset($_REQUEST['xml']))
@@ -2186,7 +2184,7 @@ function AutoSuggestHandler($checkRegistered = null)
 
 /**
  * Search for a member - by real_name or member_name by default.
- * 
+ *
  * @return string
  */
 function AutoSuggest_Search_Member()

+ 6 - 6
Sources/Subs-Graphics.php

@@ -65,7 +65,7 @@ function downloadAvatar($url, $memID, $max_width, $max_height)
 		array('id_attach')
 	);
 	$attachID = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
-	
+
 	// Retain this globally in case the script wants it.
 	$modSettings['new_avatar_data'] = array(
 		'id' => $attachID,
@@ -282,7 +282,7 @@ function checkImagick()
 function imageMemoryCheck($sizes)
 {
 	global $modSettings;
-	
+
 	// doing the old 'set it and hope' way?
 	if (empty($modSettings['attachment_thumb_memory']))
 	{
@@ -291,10 +291,10 @@ function imageMemoryCheck($sizes)
 	}
 
 	// Determine the memory requirements for this image, note: if you want to use an image formula W x H x bits/8 x channels x Overhead factor
-	// you will need to account for single bit images as GD expands them to an 8 bit and will greatly overun the calculated value.  The 5 is 
+	// you will need to account for single bit images as GD expands them to an 8 bit and will greatly overun the calculated value.  The 5 is
 	// simply a shortcut of 8bpp, 3 channels, 1.66 overhead
 	$needed_memory = ($sizes[0] * $sizes[1] * 5);
-	
+
 	// if we need more, lets try to get it
 	return setMemoryLimit($needed_memory, true);
 }
@@ -359,7 +359,7 @@ function resizeImageFile($source, $destination, $max_width, $max_height, $prefer
 	// We can't get to the file.
 	else
 		$sizes = array(-1, -1, -1);
-		
+
 	// See if we have -or- can get the needed memory for this operation
 	if (checkGD() && !imageMemoryCheck($sizes))
 		return false;
@@ -710,7 +710,7 @@ if (!function_exists('imagecreatefrombmp'))
 				for ($j = 0; $j < $scan_line_size; $x++)
 				{
 					$byte = ord($scan_line{$j++});
-					
+
 					imagesetpixel($dst_img, $x, $y, $palette[(($byte) & 128) != 0]);
 					for ($shift = 1; $shift < 8; $shift++) {
 						if (++$x < $info['width']) imagesetpixel($dst_img, $x, $y, $palette[(($byte << $shift) & 128) != 0]);

+ 10 - 1
Sources/Subs-Members.php

@@ -209,6 +209,15 @@ function deleteMembers($users, $check_not_admin = false)
 		)
 	);
 
+	// Delete any drafts...
+	$smcFunc['db_query']('', '
+		DELETE FROM {db_prefix}user_drafts
+		WHERE id_member IN ({array_int:users})',
+		array(
+			'users' => $users,
+		)
+	);
+
 	// Delete the logs...
 	$smcFunc['db_query']('', '
 		DELETE FROM {db_prefix}log_actions
@@ -1187,7 +1196,7 @@ function reattributePosts($memID, $email = false, $membername = false, $post_cou
 			'memID' => $memID,
 		)
 	);
-	
+
 	// Allow mods with their own post tables to reattribute posts as well :)
  	call_integration_hook('integrate_reattribute_posts', array(&$memID, &$email, &$membername, &$post_count));
 }

+ 2 - 2
Sources/Subs-MembersOnline.php

@@ -2,7 +2,7 @@
 
 /**
  * Handle online users
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -189,7 +189,7 @@ function getMembersOnlineStats($membersOnlineOptions)
 	$membersOnlineStats['num_users_online'] = count($membersOnlineStats['users_online']) + $membersOnlineStats['num_users_hidden'] - (isset($modSettings['show_spider_online']) && $modSettings['show_spider_online'] > 1 ? count($spider_finds) : 0);
 
 	cache_put_data('membersOnlineStats-' . $membersOnlineOptions['sort'], $membersOnlineStats, 240);
-	
+
 	return $membersOnlineStats;
 }
 

+ 1 - 1
Sources/Subs-Menu.php

@@ -2,7 +2,7 @@
 
 /**
  * This file contains a standard way of displaying side/drop down menus for SMF.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF

+ 1 - 1
Sources/Subs-OpenID.php

@@ -517,7 +517,7 @@ function smf_openid_generate_private_key()
 }
 
 /**
- * 
+ *
  * Enter description here ...
  * @param string $openid_url
  * @return bool|array

+ 1 - 1
Sources/Subs-Package.php

@@ -369,7 +369,7 @@ function url_exists($url)
  * - gets this information from Packages/installed.list.
  * - returns the array of data.
  * - default sort order is package_installed time
- * 
+ *
  * @return array
  */
 function loadInstalledPackages()

+ 9 - 6
Sources/Subs-Post.php

@@ -232,6 +232,11 @@ function preparsecode(&$message, $previewing = false)
 			// Close any remaining table tags.
 			foreach ($table_array as $tag)
 				$parts[$i] .= '[/' . $tag . ']';
+
+			// Remove empty bbc from the sections outside the code tags
+			$parts[$i] = preg_replace('~\[[bisu]\]\s*\[/[bisu]\]~', '', $parts[$i]);
+			$parts[$i] = preg_replace('~\[quote\]\s*\[/quote\]~', '', $parts[$i]);
+			$parts[$i] = preg_replace('~\[color=(?:#[\da-fA-F]{3}|#[\da-fA-F]{6}|[A-Za-z]{1,20}|rgb\(\d{1,3}, ?\d{1,3}, ?\d{1,3}\))\]\s*\[/color\]~', '', $parts[$i]);
 		}
 	}
 
@@ -243,10 +248,6 @@ function preparsecode(&$message, $previewing = false)
 
 	// Now let's quickly clean up things that will slow our parser (which are common in posted code.)
 	$message = strtr($message, array('[]' => '&#91;]', '[&#039;' => '&#91;&#039;'));
-
-	// Remove empty bbc.
-	$message = preg_replace('~\[[bisu]\]\s*\[/[bisu]\]~', '', $message);
-	$message = preg_replace('~\[quote\]\s*\[/quote\]~', '', $message);
 }
 
 /**
@@ -827,8 +828,10 @@ function sendpm($recipients, $subject, $message, $store_outbox = false, $from =
 
 	// This is the one that will go in their inbox.
 	$htmlmessage = $smcFunc['htmlspecialchars']($message, ENT_QUOTES);
-	$htmlsubject = $smcFunc['htmlspecialchars']($subject);
 	preparsecode($htmlmessage);
+	$htmlsubject = strtr($smcFunc['htmlspecialchars']($subject), array("\r" => '', "\n" => '', "\t" => ''));
+	if ($smcFunc['strlen']($htmlsubject) > 100)
+		$htmlsubject = $smcFunc['substr']($htmlsubject, 0, 100);
 
 	// Integrated PMs
 	call_integration_hook('integrate_personal_message', array(&$recipients, &$from['username'], &$subject, &$message));
@@ -2420,7 +2423,7 @@ function modifyPost(&$msgOptions, &$topicOptions, &$posterOptions)
 	if (isset($msgOptions['body']))
 	{
 		$messages_columns['body'] = $msgOptions['body'];
-		
+
 		// using a custom search index, then lets get the old message so we can update our index as needed
 		if (!empty($modSettings['search_custom_index_config']))
 		{

+ 2 - 2
Sources/Subs-Sound.php

@@ -4,7 +4,7 @@
  * Handles sound processing. In order to make sure the visual
  * verification is still accessible for all users, a sound clip is being addded
  * that reads the letters that are being shown.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -22,7 +22,7 @@ if (!defined('SMF'))
  * Creates a wave file that spells the letters of $word.
  * Tries the user's language first, and defaults to english.
  * Used by VerificationCode() (Register.php).
- * 
+ *
  * @param string $word
  * @return bool false on failure
  */

+ 82 - 92
Sources/Subs.php

@@ -2,7 +2,7 @@
 
 /**
  * This file has all the main functions in it that relate to, well, everything.
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -18,8 +18,8 @@ if (!defined('SMF'))
 
 /**
  * Update some basic statistics.
- * 
- * 'member' statistic updates the latest member, the total member 
+ *
+ * 'member' statistic updates the latest member, the total member
  *  count, and the number of unapproved members.
  * 'member' also only counts approved members when approval is on, but
  *  is much more efficient with it off.
@@ -31,12 +31,12 @@ if (!defined('SMF'))
  * 'topic' updates the total number of topics, or if parameter1 is true
  *  simply increments them.
  *
- * 'subject' updateds the log_search_subjects in the event of a topic being 
+ * 'subject' updateds the log_search_subjects in the event of a topic being
  *  moved, removed or split.  parameter1 is the topicid, parameter2 is the new subject
- * 
+ *
  * 'postgroups' case updates those members who match condition's
  *  post-based membergroups in the database (restricted by parameter1).
- * 
+ *
  * @param string $type Stat type - can be 'member', 'message', 'topic', 'subject' or 'postgroups'
  * @param mixed $parameter1 = null
  * @param mixed $parameter2 = null
@@ -249,10 +249,10 @@ function updateStats($type, $parameter1 = null, $parameter2 = null)
 
 /**
  * Updates the columns in the members table.
- * Assumes the data has been htmlspecialchar'd. 
+ * Assumes the data has been htmlspecialchar'd.
  * this function should be used whenever member data needs to be
  * updated in place of an UPDATE query.
- * 
+ *
  * id_member is either an int or an array of ints to be updated.
  *
  * data is an associative array of the columns to be updated and their respective values.
@@ -284,6 +284,17 @@ function updateMemberData($members, $data)
 		$parameters['member'] = $members;
 	}
 
+	// Everything is assumed to be a string unless it's in the below.
+	$knownInts = array(
+		'date_registered', 'posts', 'id_group', 'last_login', 'instant_messages', 'unread_messages',
+		'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'pm_receive_from', 'karma_good', 'karma_bad',
+		'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types',
+		'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning',
+	);
+	$knownFloats = array(
+		'time_offset',
+	);
+
 	if (!empty($modSettings['integrate_change_member_data']))
 	{
 		// Only a few member variables are really interesting for integration.
@@ -327,21 +338,10 @@ function updateMemberData($members, $data)
 
 			if (!empty($member_names))
 				foreach ($vars_to_integrate as $var)
-					call_integration_hook('integrate_change_member_data', array($member_names, $var, $data[$var]));
+					call_integration_hook('integrate_change_member_data', array($member_names, $var, $data[$var], $knownInts, $knownFloats));
 		}
 	}
 
-	// Everything is assumed to be a string unless it's in the below.
-	$knownInts = array(
-		'date_registered', 'posts', 'id_group', 'last_login', 'instant_messages', 'unread_messages',
-		'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'pm_receive_from', 'karma_good', 'karma_bad',
-		'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types',
-		'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning',
-	);
-	$knownFloats = array(
-		'time_offset',
-	);
-
 	$setString = '';
 	foreach ($data as $var => $val)
 	{
@@ -405,7 +405,7 @@ function updateMemberData($members, $data)
 
 /**
  * Updates the settings table as well as $modSettings... only does one at a time if $update is true.
- * 
+ *
  * - updates both the settings table and $modSettings array.
  * - all of changeArray's indexes and values are assumed to have escaped apostrophes (')!
  * - if a variable is already set to what you want to change it to, that
@@ -413,7 +413,7 @@ function updateMemberData($members, $data)
  * - When use_update is true, UPDATEs will be used instead of REPLACE.
  * - when use_update is true, the value can be true or false to increment
  *  or decrement it, respectively.
- * 
+ *
  * @param array $changeArray
  * @param bool $update = false
  * @param bool $debug = false
@@ -492,7 +492,7 @@ function updateSettings($changeArray, $update = false, $debug = false)
  *
  * an example is available near the function definition.
  * $pageindex = constructPageIndex($scripturl . '?board=' . $board, $_REQUEST['start'], $num_messages, $maxindex, true);
- * 
+ *
  * @param string $base_url
  * @param int $start
  * @param int $max_value
@@ -633,13 +633,13 @@ function comma_format($number, $override_decimal_count = false)
 
 /**
  * Format a time to make it look purdy.
- * 
+ *
  * - returns a pretty formated version of time based on the user's format in $user_info['time_format'].
  * - applies all necessary time offsets to the timestamp, unless offset_type is set.
  * - if todayMod is set and show_today was not not specified or true, an
  *   alternate format string is used to show the date with something to show it is "today" or "yesterday".
  * - performs localization (more than just strftime would do alone.)
- * 
+ *
  * @param int $log_time
  * @param bool $show_today = true
  * @param string $offset_type = false
@@ -728,7 +728,7 @@ function timeformat($log_time, $show_today = true, $offset_type = false)
  *
  * - removes the base entities (&lt;, &quot;, etc.) from text.
  * - additionally converts &nbsp; and &#039;.
- * 
+ *
  * @param string $string
  * @return the string without entities
  */
@@ -744,12 +744,12 @@ function un_htmlspecialchars($string)
 
 /**
  * Shorten a subject + internationalization concerns.
- * 
+ *
  * - shortens a subject so that it is either shorter than length, or that length plus an ellipsis.
  * - respects internationalization characters and entities as one character.
  * - avoids trailing entities.
  * - returns the shortened string.
- * 
+ *
  * @param string $subject
  * @param int $len
  */
@@ -790,7 +790,7 @@ function forum_time($use_user_offset = true, $timestamp = null)
  * Calculates all the possible permutations (orders) of array.
  * should not be called on huge arrays (bigger than like 10 elements.)
  * returns an array containing each permutation.
- * 
+ *
  * @param array $array
  * @return array
  */
@@ -829,7 +829,7 @@ function permute($array)
  * - applies the fixLongWords magic if the setting is set to on.
  * - uses the cache_id as a unique identifier to facilitate any caching it may do.
  *  -returns the modified message.
- * 
+ *
  * @param string $message
  * @param bool $smileys = true
  * @param string $cache_id = ''
@@ -849,7 +849,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 	// Just in case it wasn't determined yet whether UTF-8 is enabled.
 	if (!isset($context['utf8']))
 		$context['utf8'] = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
-		
+
 	// Clean up any cut/paste issues we may have
 	$message = sanitizeMSCutPaste($message);
 
@@ -880,10 +880,10 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 		$temp_bbc = $bbc_codes;
 		$bbc_codes = array();
 	}
-	
+
 	// Allow mods access before entering the main parse_bbc loop
 	call_integration_hook('integrate_pre_parsebbc', array(&$message, &$smileys, &$cache_id, &$parse_tags));
-	
+
 	// Sift out the bbc for a performance improvement.
 	if (empty($bbc_codes) || $message === false || !empty($parse_tags))
 	{
@@ -1071,12 +1071,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 
 						// Fix the PHP code stuff...
 						$data = str_replace("<pre style=\"display: inline;\">\t</pre>", "\t", implode(\'\', $php_parts));
-
-						// Older browsers are annoying, aren\'t they?
-						if ($context[\'browser\'][\'is_ie4\'] || $context[\'browser\'][\'is_ie5\'] || $context[\'browser\'][\'is_ie5.5\'])
-							$data = str_replace("\t", "<pre style=\"display: inline;\">\t</pre>", $data);
-						else
-							$data = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data);
+						$data = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data);
 
 						// Recent Opera bug requiring temporary fix. &nsbp; is needed before </code> to avoid broken selection.
 						if ($context[\'browser\'][\'is_opera\'])
@@ -1113,12 +1108,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 
 						// Fix the PHP code stuff...
 						$data[0] = str_replace("<pre style=\"display: inline;\">\t</pre>", "\t", implode(\'\', $php_parts));
-
-						// Older browsers are annoying, aren\'t they?
-						if ($context[\'browser\'][\'is_ie4\'] || $context[\'browser\'][\'is_ie5\'] || $context[\'browser\'][\'is_ie5.5\'])
-							$data[0] = str_replace("\t", "<pre style=\"display: inline;\">\t</pre>", $data[0]);
-						else
-							$data[0] = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data[0]);
+						$data[0] = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $data[0]);
 
 						// Recent Opera bug requiring temporary fix. &nsbp; is needed before </code> to avoid broken selection.
 						if ($context[\'browser\'][\'is_opera\'])
@@ -1153,7 +1143,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 				'tag' => 'flash',
 				'type' => 'unparsed_commas_content',
 				'test' => '\d+,\d+\]',
-				'content' => (isBrowser('ie') && !isBrowser('mac_ie') ? '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$2" height="$3"><param name="movie" value="$1" /><param name="play" value="true" /><param name="loop" value="true" /><param name="quality" value="high" /><param name="AllowScriptAccess" value="never" /><embed src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed></object>' : '<embed type="application/x-shockwave-flash" src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed>'),
+				'content' => (isBrowser('ie') ? '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$2" height="$3"><param name="movie" value="$1" /><param name="play" value="true" /><param name="loop" value="true" /><param name="quality" value="high" /><param name="AllowScriptAccess" value="never" /><embed src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed></object>' : '<embed type="application/x-shockwave-flash" src="$1" width="$2" height="$3" play="true" loop="true" quality="high" AllowScriptAccess="never" /><noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed>'),
 				'validate' => create_function('&$tag, &$data, $disabled', '
 					if (isset($disabled[\'url\']))
 						$tag[\'content\'] = \'$1\';
@@ -2390,7 +2380,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 
 	// Allow mods access to what parse_bbc created
 	call_integration_hook('integrate_post_parsebbc', array(&$message, &$smileys, &$cache_id, &$parse_tags));
-	
+
 	// Cache the output if it took some time...
 	if (isset($cache_key, $cache_t) && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.05)
 		cache_put_data($cache_key, $message, 240);
@@ -2418,7 +2408,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
  * These are specifically not parsed in code tags [url=mailto:[email protected]]
  * Caches the smileys from the database or array in memory.
  * Doesn't return anything, but rather modifies message directly.
- * 
+ *
  * @param string &$message
  */
 function parsesmileys(&$message)
@@ -2717,7 +2707,7 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
 function url_image_size($url)
 {
 	global $sourcedir;
-	
+
 	// Make sure it is a proper URL.
 	$url = str_replace(' ', '%20', $url);
 
@@ -2915,8 +2905,6 @@ function setupThemeContext($forceload = false)
 		// If we've upgraded recently, go easy on the passwords.
 		if (!empty($modSettings['disableHashTime']) && ($modSettings['disableHashTime'] == 1 || time() < $modSettings['disableHashTime']))
 			$context['disable_login_hashing'] = true;
-		elseif (isBrowser('ie5') || isBrowser('ie5.5'))
-			$context['disable_login_hashing'] = true;
 	}
 
 	// Setup the main menu items.
@@ -2951,7 +2939,7 @@ function setupThemeContext($forceload = false)
 		var smf_avatarMaxWidth = ' . (int) $modSettings['avatar_max_width_external'] . ';
 		var smf_avatarMaxHeight = ' . (int) $modSettings['avatar_max_height_external'] . ';';
 
-		if (!isBrowser('ie') && !isBrowser('mac_ie'))
+		if (!isBrowser('ie'))
 			$context['html_headers'] .= '
 	window.addEventListener("load", smf_avatarResize, false);';
 		else
@@ -3007,7 +2995,7 @@ function setMemoryLimit($needed, $in_use = false)
 	$memory_used = 0;
 	$memory_current = memoryReturnBytes(ini_get('memory_limit'));
 	$memory_needed = memoryReturnBytes($needed);
-	
+
 	// should we account for how much is currently being used?
 	if ($in_use)
 		$memory_needed += function_exists('memory_get_usage') ? memory_get_usage() : (2 * 1048576);
@@ -3035,12 +3023,12 @@ function memoryReturnBytes($val)
 {
 	if (is_integer($val))
 		return $val;
-	
+
 	// Separate the number from the designator
 	$val = trim($val);
 	$num = intval(substr($val, 0, strlen($val) - 1));
 	$last = strtolower(substr($val, -1));
-	
+
 	// convert to bytes
 	switch ($last)
 	{
@@ -3065,7 +3053,7 @@ function template_rawdata()
 }
 
 /**
- * 
+ *
  */
 function template_header()
 {
@@ -3208,7 +3196,7 @@ function theme_copyright()
 }
 
 /**
- * 
+ *
  */
 function template_footer()
 {
@@ -3232,7 +3220,11 @@ function template_footer()
 }
 
 /**
- * Output the Javascript files (messed up tabbing in this function is to make the HTML source look good)
+ * Output the Javascript files
+ * 	- tabbing in this function is to make the HTML source look good proper
+ *  - if defered is set function will output all JS (source & inline) set to load at page end
+ *
+ * @param bool $do_defered = false
  */
 function template_javascript($do_defered = false)
 {
@@ -3240,8 +3232,8 @@ function template_javascript($do_defered = false)
 
 	// Use this hook to minify/optimize Javascript files and vars
 	call_integration_hook('pre_javascript_output');
-	
-	// Javascript variables.
+
+	// Ouput the declared Javascript variables.
 	if (!empty($context['javascript_vars']) && !$do_defered)
 	{
 		echo '
@@ -3255,47 +3247,45 @@ function template_javascript($do_defered = false)
 	// ]]></script>';
 	}
 
-	// Javascript files
-	foreach ($context['javascript_files'] as $id => $file)
+	// While we have Javascript files to place in the template
+	foreach ($context['javascript_files'] as $id => $js_file)
 	{
-		if ((!$do_defered && empty($file['options']['defer'])) || ($do_defered && !empty($file['options']['defer'])))
+		if ((!$do_defered && empty($js_file['options']['defer'])) || ($do_defered && !empty($js_file['options']['defer'])))
 			echo '
-	<script type="text/javascript" src="', $file['filename'], '" id="', $id,'"' , !empty($file['options']['async']) ? ' async="async"' : '' ,'></script>';
-	
-		// If this was JQuery being loaded and we are set to 'auto' load it, add the inline JS stuff here
-		if($id == 'jquery' && (!isset($modSettings['jquery_source']) || !in_array($modSettings['jquery_source'],array('local', 'cdn'))))
+	<script type="text/javascript" src="', $js_file['filename'], '" id="', $id,'"' , !empty($js_file['options']['async']) ? ' async="async"' : '' ,'></script>';
+
+		// If we are loading JQuery and we are set to 'auto' load, put in our remote success or load local check
+		if ($id == 'jquery' && (!isset($modSettings['jquery_source']) || !in_array($modSettings['jquery_source'],array('local', 'cdn'))))
 		echo '
 	<script type="text/javascript"><!-- // --><![CDATA[
 		window.jQuery || document.write(\'<script src="' . $settings['default_theme_url'] . '/scripts/jquery-1.7.1.min.js"><\/script>\');
 	// ]]></script>';
 
 	}
-	
+
 	// Inline JavaScript - Actually useful some times!
 	if (!empty($context['javascript_inline']))
 	{
-		if(!empty($context['javascript_inline']['defer']) && $do_defered)
+		if (!empty($context['javascript_inline']['defer']) && $do_defered)
 		{
 			echo '
-<script type="text/javascript"><!-- // --><![CDATA[
-	';
+<script type="text/javascript"><!-- // --><![CDATA[';
+
+			foreach ($context['javascript_inline']['defer'] as $js_code)
+				echo $js_code;
 
-			foreach ($context['javascript_inline']['defer'] as $code)
-				echo $code;
-					
 			echo'
 // ]]></script>';
 		}
 
-		if(!empty($context['javascript_inline']['standard']) && !$do_defered)
+		if (!empty($context['javascript_inline']['standard']) && !$do_defered)
 		{
 			echo '
-	<script type="text/javascript"><!-- // --><![CDATA[
-		';
+	<script type="text/javascript"><!-- // --><![CDATA[';
+
+			foreach ($context['javascript_inline']['standard'] as $js_code)
+				echo $js_code;
 
-			foreach ($context['javascript_inline']['standard'] as $code)
-				echo $code;
-					
 			echo'
 	// ]]></script>';
 		}
@@ -3593,7 +3583,7 @@ function text2words($text, $max_chars = 20, $encrypt = false)
 
 /**
  * Creates an image/text button
- * 
+ *
  * @param string $filename
  * @param string $alt
  * @param string $label = ''
@@ -3627,12 +3617,12 @@ function create_button($name, $alt, $label = '', $custom = '', $force_use = fals
  *  - If no type is specified will perfom a complete cache clearing
  * For cache engines that do not distinguish on types, a full cache flush will be done
  *
- * @param string $type = '' 
+ * @param string $type = ''
  */
 function clean_cache($type = '')
 {
 	global $cachedir, $sourcedir, $cache_accelerator, $modSettings, $memcached;
-	
+
 	switch ($cache_accelerator)
 	{
 		case 'memcached':
@@ -3653,11 +3643,11 @@ function clean_cache($type = '')
 			break;
 		case 'eaccelerator':
 			if (function_exists('eaccelerator_clear') && function_exists('eaccelerator_clean') )
-			{ 
+			{
 				// Clean out the already expired items
 				@eaccelerator_clean();
-				
-				// Remove all unused scripts and data from shared memory and disk cache, 
+
+				// Remove all unused scripts and data from shared memory and disk cache,
 				// e.g. all data that isn't used in the current requests.
 				eaccelerator_clear();
 			}
@@ -3689,7 +3679,7 @@ function clean_cache($type = '')
 		case 'xcache':
 			if (function_exists('xcache_clear_cache'))
 			{
-				// 
+				//
 				if ($type === '')
 				{
 					xcache_clear_cache(XC_TYPE_VAR, 0);
@@ -3742,7 +3732,7 @@ function loadClassFile($filename)
 }
 
 /**
- * 
+ *
  */
 function setupMenuContext()
 {
@@ -4184,10 +4174,10 @@ function remove_integration_function($hook, $function)
 function sanitizeMSCutPaste($string)
 {
 	global $context;
-	
+
 	if (empty($string))
 		return $string;
-		
+
 	// UTF-8 occurences of MS special characters
 	$findchars_utf8 = array(
 		"\xe2\80\x9a",	// single low-9 quotation mark
@@ -4200,7 +4190,7 @@ function sanitizeMSCutPaste($string)
 		"\xe2\x80\x93",	// en dash
 		"\xe2\x80\x94",	// em dash
 	);
-	
+
 	// windows 1252 / iso equivalents
 	$findchars_iso = array(
 		chr(130),
@@ -4226,7 +4216,7 @@ function sanitizeMSCutPaste($string)
 		'-',	// &ndash;
 		'--',	// &mdash;
 	);
-	
+
 	if ($context['utf8'])
 		$string = str_replace($findchars_utf8, $replacechars, $string);
 	else

+ 1 - 1
Sources/Themes.php

@@ -1048,7 +1048,7 @@ function PickTheme()
 		// Change a specific member's theme.
 		else
 		{
-			// The forum's default theme is always 0 and we 
+			// The forum's default theme is always 0 and we
 			if (isset($_GET['th']) && $_GET['th'] == 0)
 					$_GET['th'] = $modSettings['theme_guests'];
 

+ 7 - 6
Sources/Who.php

@@ -684,11 +684,12 @@ function Credits($in_admin = false)
 			),
 		),
 	);
-	
+
 	// Give credit to any graphic library's, software library's, plugins etc
 	$context['credits_software_graphics'] = array(
 		'graphics' => array(
 			'<a href="http://p.yusukekamiyamane.com/">Fugue Icons</a> | &copy; 2012 Yusuke Kamiyamane | These icons are licensed under a Creative Commons Attribution 3.0 License',
+			'<a href="http://www.oxygen-icons.org/">Oxygen Icons</a> | These icons are licensed under <a href="http://www.gnu.org/licenses/lgpl-3.0.txt">GNU LGPL 3</a>',
 		),
 		'software' => array(
 			'<a href="http://jquery.org/">JQuery</a> | &copy; John Resig | Licensed under <a href="http://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt">The MIT License (MIT)</a>',
@@ -698,7 +699,7 @@ function Credits($in_admin = false)
 			'<a href="http://wayfarerweb.com/jquery/plugins/animadrag/">animaDrag</a> | &copy; Abel Mohler | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
 		),
 	);
-	
+
 	// support for mods that use the <credits> tag via the package manager
 	$context['credits_modifications'] = array();
 	if (($mods = cache_get_data('mods_credits', 86400)) === null)
@@ -716,7 +717,7 @@ function Credits($in_admin = false)
 				'empty' => '',
 			)
 		);
-		
+
 		while ($row = $smcFunc['db_fetch_assoc']($request))
 		{
 			$credit_info = unserialize($row['credits']);
@@ -733,7 +734,7 @@ function Credits($in_admin = false)
 		cache_put_data('mods_credits', $mods, 86400);
 	}
 	$context['credits_modifications'] = $mods;
-	
+
 	$context['copyrights'] = array(
 		'smf' => sprintf($forum_copyright, $forum_version),
 		/* Modification Authors:  You may add a copyright statement to this array for your mods.
@@ -744,10 +745,10 @@ function Credits($in_admin = false)
 		'mods' => array(
 		),
 	);
-	
+
 	// Support for those that want to use a hook as well
 	call_integration_hook('integrate_credits');
-	
+
 	if (!$in_admin)
 	{
 		loadTemplate('Who');

+ 3 - 3
Sources/Xml.php

@@ -2,7 +2,7 @@
 
 /**
  * Maintains all XML-based interaction (mainly XMLhttp)
- * 
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -35,10 +35,10 @@ function XMLhttpMain()
 			'function' => 'RetrievePreview',
 		),
 	);
-	
+
 	// Easy adding of sub actions
  	call_integration_hook('integrate_xmlhttp', array(&$sub_actions));
-	
+
 	if (!isset($_REQUEST['sa'], $sub_actions[$_REQUEST['sa']]))
 		fatal_lang_error('no_access', false);
 

+ 10 - 28
Themes/default/Admin.template.php

@@ -60,7 +60,7 @@ function template_admin()
 			<div id="live_news" class="floatleft">
 				<div class="cat_bar">
 					<h3 class="catbg">
-						<span class="ie6_header floatleft"><a href="', $scripturl, '?action=helpadmin;help=live_news" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['live'], '</span>
+						<a href="', $scripturl, '?action=helpadmin;help=live_news" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['live'], '
 					</h3>
 				</div>
 				<div class="windowbg nopadding">
@@ -223,7 +223,7 @@ function template_credits()
 
 		// more details for this item, show them a link
 		if ($context['can_admin'] && isset($version['more']))
-			echo 
+			echo
 					' <a href="', $scripturl, $version['more'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['version_check_more'], '</a>';
 		echo '
 					<br />';
@@ -327,12 +327,6 @@ function template_credits()
 	// This sets the latest support stuff.
 	echo '
 		<script type="text/javascript"><!-- // --><![CDATA[
-			function smfSetLatestSupport()
-			{
-				if (window.smfLatestSupport)
-					setInnerHTML(document.getElementById("latestSupport"), window.smfLatestSupport);
-			}
-
 			function smfCurrentVersion()
 			{
 				var smfVer, yourVer;
@@ -348,16 +342,8 @@ function template_credits()
 				var currentVersion = getInnerHTML(yourVer);
 				if (currentVersion != window.smfVersion)
 					setInnerHTML(yourVer, "<span class=\"alert\">" + currentVersion + "</span>");
-			}';
-
-	// IE 4 is rather annoying, this wouldn't be necessary...
-	echo '
-			var fSetupCredits = function ()
-			{
-				smfSetLatestSupport();
-				smfCurrentVersion()
 			}
-			addLoadEvent(fSetupCredits);
+			addLoadEvent(smfCurrentVersion)
 		// ]]></script>';
 }
 
@@ -574,7 +560,6 @@ function template_view_versions()
 	   file categories. (sources, languages, and templates.) */
 	echo '
 		<script type="text/javascript" src="', $scripturl, '?action=viewsmfile;filename=detailed-version.js"></script>
-		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/admin.js?alp21"></script>
 		<script type="text/javascript"><!-- // --><![CDATA[
 			var oViewVersions = new smf_ViewVersions({
 				aKnownLanguages: [
@@ -625,7 +610,6 @@ function template_edit_censored()
 					<div id="moreCensoredWords"></div><div style="margin-top: 1ex; display: none;" id="moreCensoredWords_link">
 						<a class="button_link" style="float: left" href="#;" onclick="addNewWord(); return false;">', $txt['censor_clickadd'], '</a><br />
 					</div>
-					<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/admin.js?alp21"></script>
 					<script type="text/javascript"><!-- // --><![CDATA[
 						document.getElementById("moreCensoredWords_link").style.display = "";
 					// ]]></script>
@@ -739,7 +723,7 @@ function template_show_settings()
 
 	if (!empty($context['settings_insert_above']))
 		echo $context['settings_insert_above'];
-		
+
 	echo '
 	<div id="admincenter">
 		<form id="admin_form_wrapper" action="', $context['post_url'], '" method="post" accept-charset="', $context['character_set'], '"', !empty($context['force_form_onsubmit']) ? ' onsubmit="' . $context['force_form_onsubmit'] . '"' : '', '>';
@@ -976,7 +960,7 @@ function template_show_settings()
 function template_show_custom_profile()
 {
 	global $context, $txt, $settings, $scripturl;
-	
+
 	// Standard fields.
 	template_show_list('standard_profile_fields');
 
@@ -1002,7 +986,7 @@ function template_edit_profile_field()
 	<script type="text/javascript"><!-- // --><![CDATA[
 		var startOptID = ', count($context['field']['options']), ';
 	// ]]></script>';
-	
+
 	// any errors messages to show?
 	if (isset($_GET['msg']))
 	{
@@ -1360,7 +1344,7 @@ function template_core_features()
 						{
 							$(ajax_infobar).attr(\'class\', \'errorbox\');
 							$(ajax_infobar).html(' . JavaScriptEscape($txt['core_settings_generic_error']) . ').slideDown(\'fast\');
-							
+
 						}
 					}
 				});
@@ -1459,8 +1443,6 @@ function template_callback_question_answer_list()
 		<dt id="add_more_question_placeholder" style="display: none;"></dt><dd></dd>
 		<dt id="add_more_link_div" style="display: none;">
 			<a href="#" onclick="addAnotherQuestion(); return false;">&#171; ', $txt['setup_verification_add_more'], ' &#187;</a>
-			<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/admin.js?alp21"></script>
-
 		</dt><dd></dd>';
 
 	// The javascript needs to go at the end but we'll put it in this template for looks.
@@ -1592,7 +1574,7 @@ function template_php_info()
 
 		$alternate = true;
 		$localmaster = true;
-		
+
 		// and for each setting in this category
 		foreach ($php_area as $key => $setting)
 		{
@@ -1610,7 +1592,7 @@ function template_php_info()
 		</tr>';
 					$localmaster = false;
 				}
-					
+
 				echo '
 		<tr>
 			<td align="left" width="33%" class="windowbg', $alternate ? '2' : '', '">', $key, '</td>';
@@ -1632,7 +1614,7 @@ function template_php_info()
 			<td align="left" class="windowbg', $alternate ? '2' : '', '" colspan="2">', $setting, '</td>
 		</tr>';
 			}
-		
+
 			$alternate = !$alternate;
 		}
 		echo '

+ 7 - 7
Themes/default/BoardIndex.template.php

@@ -252,7 +252,7 @@ function template_info_center()
 	<div class="roundframe" id="info_center">
 		<div class="cat_bar">
 			<h3 class="catbg">
-				<img class="icon" id="upshrink_ic" src="', $settings['images_url'], '/collapse.png" alt="*" title="', $txt['upshrink_description'], '" style="display: none;" />
+				<img class="icon" id="upshrink_ic" src="', $settings['images_url'], '/collapse.png" alt="*" title="', $txt['hide'], '" style="display: none;" />
 				<a href="#" id="upshrink_link">', sprintf($txt['info_center_title'], $context['forum_name_html_safe']), '</a>
 			</h3>
 		</div>
@@ -327,21 +327,21 @@ function template_info_center()
 			echo '
 				<p class="inline">
 					<span class="birthday">', $context['calendar_only_today'] ? $txt['birthdays'] : $txt['birthdays_upcoming'], '</span>';
-			// Each member in calendar_birthdays has: id, name (person), age (if they have one set?), is_last. (last in list?), and is_today (birthday is today?) 
+			// Each member in calendar_birthdays has: id, name (person), age (if they have one set?), is_last. (last in list?), and is_today (birthday is today?)
 			foreach ($context['calendar_birthdays'] as $member)
 				echo '
 					<a href="', $scripturl, '?action=profile;u=', $member['id'], '">', $member['is_today'] ? '<strong class="fix_rtl_names">' : '', $member['name'], $member['is_today'] ? '</strong>' : '', isset($member['age']) ? ' (' . $member['age'] . ')' : '', '</a>', $member['is_last'] ? '' : ', ';
 			echo '
 				</p>';
 		}
-		
+
 		// Events like community get-togethers.
 		if (!empty($context['calendar_events']))
 		{
 			echo '
 				<p class="inline">
 					<span class="event">', $context['calendar_only_today'] ? $txt['events'] : $txt['events_upcoming'], '</span> ';
-			
+
 			// Each event in calendar_events should have:
 			//		title, href, is_last, can_edit (are they allowed?), modify_href, and is_today.
 			foreach ($context['calendar_events'] as $event)
@@ -392,7 +392,7 @@ function template_info_center()
 
 	echo $context['show_who'] ? '</a>' : '', '
 
-				&nbsp;-&nbsp;', $txt['most_online_today'], ': <strong>', comma_format($modSettings['mostOnlineToday']), '</strong>&nbsp;-&nbsp; 
+				&nbsp;-&nbsp;', $txt['most_online_today'], ': <strong>', comma_format($modSettings['mostOnlineToday']), '</strong>&nbsp;-&nbsp;
 				', $txt['most_online_ever'], ': ', comma_format($modSettings['mostOnline']), ' (', timeformat($modSettings['mostDate']), ')<br />';
 
 	// Assuming there ARE users online... each user in users_online has an id, username, name, group, href, and link.
@@ -441,9 +441,9 @@ function template_info_center()
 				{
 					sId: \'upshrink_ic\',
 					srcExpanded: smf_images_url + \'/collapse.png\',
-					altExpanded: ', JavaScriptEscape($txt['upshrink_description']), ',
+					altExpanded: ', JavaScriptEscape($txt['hide']), ',
 					srcCollapsed: smf_images_url + \'/expand.png\',
-					altCollapsed: ', JavaScriptEscape($txt['upshrink_description']), '
+					altCollapsed: ', JavaScriptEscape($txt['show']), '
 				}
 			],
 			aSwapLinks: [

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

@@ -374,12 +374,12 @@ function template_show_month_grid($grid_name)
 						if ($event['can_edit'])
 							echo '
 								<a class="modify_event" href="', $event['modify_href'], '"><img src="' . $settings['images_url'] . '/icons/calendar_modify.png" alt="*" title="' . $txt['modify'] . '" /></a>';
-						
+
 						if ($event['can_export'])
 							echo '
 								<a class="modify_event" href="', $event['export_href'], '"><img src="' . $settings['images_url'] . '/icons/calendar_export.png" alt=">" title="' . $txt['save'] . '"/></a>';
 
-								
+
 						echo '
 								', $event['link'], $event['is_last'] ? '' : '<br />';
 					}

+ 29 - 7
Themes/default/Display.template.php

@@ -25,7 +25,7 @@ function template_main()
 
 	// Show the anchor for the top and for the first message. If the first message is new, say so.
 	echo '
-			
+
 			<a id="msg', $context['first_message'], '"></a>', $context['first_new_message'] ? '<a id="new"></a>' : '';
 
 	// Is this topic also a poll?
@@ -405,7 +405,7 @@ function template_main()
 		{
 			if(!empty($modSettings['onlineEnable']))
 				echo '
-								<li class="poster_online"><a href="', $scripturl,'?action=pm;sa=send;u=', $message['member']['id'], '" ', ($message['member']['online']['is_online']) ? 'title="'. $message['member']['name']. ' is online' : $message['member']['name']. ' is offline', '">Send message <img src="'. $message['member']['online']['image_href']. '" alt="" /></a></li>';
+								<li class="poster_online"><a href="', $scripturl,'?action=pm;sa=send;u=', $message['member']['id'], '" title="', ($message['member']['online']['is_online']) ? $message['member']['name']. ' is online' : $message['member']['name']. ' is offline', '">Send message <img src="'. $message['member']['online']['image_href']. '" alt="" /></a></li>';
 			else
 				echo '
 								<li class="poster_online"><a href="', $scripturl,'?action=pm;sa=send;u=', $message['member']['id'], '">Send message</a></li>';
@@ -475,7 +475,7 @@ function template_main()
 			$last_approved_state = 1;
 			$attachments_per_line = 4;
 			$i = 0;
-			
+
 			foreach ($message['attachment'] as $attachment)
 			{
 				// Show a special box for unapproved attachments...
@@ -493,7 +493,7 @@ function template_main()
 					echo '
 									</legend>';
 				}
-				
+
 				echo '
 									<div class="floatleft">';
 
@@ -769,6 +769,15 @@ function template_main()
 			echo '
 								<input type="button" value="', $txt['spell_check'], '" onclick="spellCheck(\'postmodify\', \'message\');" tabindex="', $context['tabindex']++, '" class="button_submit" />';
 
+		if (($context['drafts_save']) && !empty($options['drafts_show_saved_enabled']))
+			echo '
+								<input type="submit" name="save_draft" value="', $txt['draft_save'], '" onclick="return confirm(' . JavaScriptEscape($txt['draft_save_note']) . ') && submitThisOnce(this);" accesskey="d" tabindex="', $context['tabindex']++, '" class="button_submit" />
+								<input type="hidden" id="id_draft" name="id_draft" value="', empty($context['id_draft']) ? 0 : $context['id_draft'], '" />';
+
+		if (!empty($context['drafts_autosave']) && !empty($options['drafts_autosave_enabled']))
+			echo '
+								<div class="clear righttext padding"><span id="throbber" style="display:none"><img src="' . $settings['images_url'] . '/loading_sm.gif" alt="" class="centericon" />&nbsp;</span><span id="draft_lastautosave" ></span></div>';
+
 		echo '
 							</div>
 						</form>
@@ -780,6 +789,21 @@ function template_main()
 		echo '
 		<br class="clear" />';
 
+	if (!empty($context['drafts_autosave']) && !empty($options['drafts_autosave_enabled']))
+		echo '
+			<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
+			<script type="text/javascript"><!-- // --><![CDATA[
+				var oDraftAutoSave = new smf_DraftAutoSave({
+					sSelf: \'oDraftAutoSave\',
+					sLastNote: \'draft_lastautosave\',
+					sLastID: \'id_draft\',
+					sSceditorID: \'', $context['post_box_name']. '\',
+					sType: \'', !empty($options['display_quick_reply']) && $options['display_quick_reply'] > 2 ? 'quick' : 'quick', '\',
+					iBoard: ', (empty($context['current_board']) ? 0 : $context['current_board']), ',
+					iFreq: ', (empty($modSettings['masterAutoSaveDraftsDelay']) ? 60000 : $modSettings['masterAutoSaveDraftsDelay'] * 1000), '
+				});
+			// ]]></script>';
+
 	if ($context['show_spellchecking'])
 		echo '
 			<form action="', $scripturl, '?action=spellcheck" method="post" accept-charset="', $context['character_set'], '" name="spell_form" id="spell_form" target="spellWindow"><input type="hidden" name="spellstring" value="" /></form>
@@ -893,14 +917,12 @@ function template_main()
 					}';
 
 	if (!empty($ignoredMsgs))
-	{
 		echo '
 					ignore_toggles([', implode(', ', $ignoredMsgs), '], ', JavaScriptEscape($txt['show_ignore_user_post']), ');';
-	}
 
 	echo '
 				// ]]></script>';
-	
+
 }
 
 ?>

+ 4 - 4
Themes/default/Errors.template.php

@@ -45,7 +45,7 @@ function template_error_log()
 
 	echo '
 		<form class="generic_list_wrapper" action="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';start=', $context['start'], $context['has_filter'] ? $context['filter']['href'] : '', '" method="post" accept-charset="', $context['character_set'], '">';
-	
+
 	echo '
 			<div class="title_bar clear_right">
 				<h3 class="titlebg">
@@ -157,7 +157,7 @@ function template_error_log()
 			</table>
 			<div class="pagesection floatleft">
 				&nbsp;&nbsp;', $txt['pages'], ': ', $context['page_index'], '
-			</div>';  
+			</div>';
 
 	echo '
 			<div class="floatright" style="margin-top: 1ex">
@@ -169,7 +169,7 @@ function template_error_log()
 	if ($context['sort_direction'] == 'down')
 		echo '
 			<input type="hidden" name="desc" value="1" />';
-		
+
 	echo '
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />';
 
@@ -221,7 +221,7 @@ function template_attachment_errors()
 		</div>
 		<div class="windowbg">
 			<div class="padding">
-				<div class="noticebox" />', 
+				<div class="noticebox" />',
 					$context['error_message'], '
 				</div>
 				<hr class="hrcolor" />

+ 61 - 3
Themes/default/GenericControls.template.php

@@ -34,7 +34,7 @@ function template_control_richedit($editor_id, $smileyContainer = null, $bbcCont
 				$("#', $editor_id, '").sceditorBBCodePlugin({
 					style: "', $settings['default_theme_url'], '/css/jquery.sceditor.default.css",
 					emoticonsCompat: true,
-					supportedWysiwyg: ((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome),',
+					supportedWysiwyg: (is_ie || is_ff || is_opera95up || is_safari || is_chrome),',
 					!empty($editor_context['locale']) ? '
 					locale: \'' . $editor_context['locale'] . '\',' : '', '
 					colors: "black,red,yellow,pink,green,orange,purple,blue,beige,brown,teal,navy,maroon,limegreen,white"';
@@ -99,10 +99,10 @@ function template_control_richedit($editor_id, $smileyContainer = null, $bbcCont
 		echo '
 				});
 				$("#', $editor_id, '").data("sceditor").createPermanentDropDown();
-				$(".sceditor-container").width("100%").height("100%");', 
+				$(".sceditor-container").width("100%").height("100%");',
 				$editor_context['rich_active'] ? '' : '
 				$("#' . $editor_id . '").data("sceditor").setTextMode();', '
-				if (!((is_ie5up && !is_ie50) || is_ff || is_opera95up || is_safari || is_chrome))
+				if (!(is_ie || is_ff || is_opera95up || is_safari || is_chrome))
 				{
 					$("#' . $editor_id . '").data("sceditor").setTextMode();
 					$(".sceditor-button-source").hide();
@@ -141,6 +141,64 @@ function template_control_richedit_buttons($editor_id)
 	if ($context['show_spellchecking'])
 		echo '
 		<input type="button" value="', $txt['spell_check'], '" tabindex="', $context['tabindex']++, '" onclick="oEditorHandle_', $editor_id, '.spellCheckStart();" class="button_submit" />';
+
+	if (!empty($context['drafts_save']))
+	{
+		// Show the save draft button
+		echo '
+		<input type="submit" name="save_draft" value="', $txt['draft_save'], '" tabindex="', $context['tabindex']++, '" onclick="return confirm(' . JavaScriptEscape($txt['draft_save_note']) . ') && submitThisOnce(this);" accesskey="d" class="button_submit" />
+		<input type="hidden" id="id_draft" name="id_draft" value="', empty($context['id_draft']) ? 0 : $context['id_draft'], '" />';
+
+		// Start an instance of the auto saver if its enabled
+		if (!empty($context['drafts_autosave']) && !empty($options['drafts_autosave_enabled']))
+			echo '
+		<br />
+		<span class="righttext padding" style="display: block">
+			<span id="throbber" style="display:none"><img src="' . $settings['images_url'] . '/loading_sm.gif" alt="" class="centericon" />&nbsp;</span>
+			<span id="draft_lastautosave" ></span>
+		</span>
+		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
+		<script type="text/javascript"><!-- // --><![CDATA[
+			var oDraftAutoSave = new smf_DraftAutoSave({
+				sSelf: \'oDraftAutoSave\',
+				sLastNote: \'draft_lastautosave\',
+				sLastID: \'id_draft\',
+				sSceditorID: \'', $editor_id, '\',
+				sType: \'post\',
+				iBoard: ', (empty($context['current_board']) ? 0 : $context['current_board']), ',
+				iFreq: ', (empty($modSettings['drafts_autosave_frequency']) ? 60000 : $modSettings['drafts_autosave_frequency'] * 1000), '
+			});
+		// ]]></script>';
+	}
+
+	if (!empty($context['drafts_pm_save']))
+	{
+		// The PM draft save button
+		echo '
+		<input type="submit" name="save_draft" value="', $txt['draft_save'], '" tabindex="', $context['tabindex']++, '" onclick="submitThisOnce(this);" accesskey="d" class="button_submit" />
+		<input type="hidden" id="id_pm_draft" name="id_pm_draft" value="', empty($context['id_pm_draft']) ? 0 : $context['id_pm_draft'], '" />';
+
+		// Load in the PM autosaver if its enabled and the user wants to use it
+		if (!empty($context['drafts_autosave']) && !empty($options['drafts_autosave_enabled']))
+			echo '
+		<span class="righttext padding" style="display: block">
+			<span id="throbber" style="display:none"><img src="' . $settings['images_url'] . '/loading_sm.gif" alt="" class="centericon" />&nbsp;</span>
+			<span id="draft_lastautosave" ></span>
+		</span>
+		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/drafts.js?alp21"></script>
+		<script type="text/javascript"><!-- // --><![CDATA[
+			var oDraftAutoSave = new smf_DraftAutoSave({
+				sSelf: \'oDraftAutoSave\',
+				sLastNote: \'draft_lastautosave\',
+				sLastID: \'id_pm_draft\',
+				sSceditorID: \'', $editor_id, '\',
+				sType: \'post\',
+				bPM: true,
+				iBoard: 0,
+				iFreq: ', (empty($modSettings['drafts_autosave_frequency']) ? 60000 : $modSettings['drafts_autosave_frequency'] * 1000), '
+			});
+		// ]]></script>';
+	}
 }
 
 // What's this, verification?!

+ 8 - 32
Themes/default/GenericList.template.php

@@ -44,9 +44,6 @@ function template_show_list($list_id = null)
 	if (empty($settings['use_tabs']) && isset($cur_list['list_menu'], $cur_list['list_menu']['show_on']) && ($cur_list['list_menu']['show_on'] == 'both' || $cur_list['list_menu']['show_on'] == 'top'))
 		template_create_list_menu($cur_list['list_menu'], 'top');
 
-	if (isset($cur_list['additional_rows']['top_of_list']))
-		template_additional_rows('top_of_list', $cur_list);
-
 	if (isset($cur_list['additional_rows']['after_title']))
 	{
 		echo '
@@ -56,6 +53,9 @@ function template_show_list($list_id = null)
 			</div>';
 	}
 
+	if (isset($cur_list['additional_rows']['top_of_list']))
+		template_additional_rows('top_of_list', $cur_list);
+
 	if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['above_column_headers']))
 	{
 		echo '
@@ -69,16 +69,8 @@ function template_show_list($list_id = null)
 				</div>';
 
 		if (isset($cur_list['additional_rows']['above_column_headers']))
-		{
-			echo '
-				<div class="floatright">';
-
 			template_additional_rows('above_column_headers', $cur_list);
 
-			echo '
-				</div>';
-		}
-
 		echo '
 			</div>';
 	}
@@ -111,7 +103,7 @@ function template_show_list($list_id = null)
 		echo '
 				</tr>
 			</thead>';
-	} 
+	}
 
 		echo '
 			<tbody>';
@@ -147,7 +139,7 @@ function template_show_list($list_id = null)
 			</tbody>
 			</table>';
 
-	if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['below_table_data']) || isset($cur_list['additional_rows']['bottom_of_list']))
+	if (!empty($cur_list['items_per_page']) || isset($cur_list['additional_rows']['below_table_data']))
 	{
 		echo '
 			<div class="flow_auto">';
@@ -160,31 +152,15 @@ function template_show_list($list_id = null)
 				</div>';
 
 		if (isset($cur_list['additional_rows']['below_table_data']))
-		{
-			echo '
-				<div class="floatright">';
-
 			template_additional_rows('below_table_data', $cur_list);
 
-			echo '
-				</div>';
-		}
-
-		if (isset($cur_list['additional_rows']['bottom_of_list']))
-		{
-			echo '
-				<div class="floatright">';
-
-			template_additional_rows('bottom_of_list', $cur_list);
-
-			echo '
-				</div>';
-		}
-
 		echo '
 			</div>';
 	}
 
+	if (isset($cur_list['additional_rows']['bottom_of_list']))
+		template_additional_rows('bottom_of_list', $cur_list);
+
 	if (isset($cur_list['form']))
 	{
 		foreach ($cur_list['form']['hidden_fields'] as $name => $value)

+ 4 - 4
Themes/default/GenericMenu.template.php

@@ -235,19 +235,19 @@ function template_generic_menu_tabs(&$menu_context)
 		// Has a custom URL defined in the main admin structure?
 		if (isset($tab['url']) && !isset($tab_context['tabs'][$id]['url']))
 			$tab_context['tabs'][$id]['url'] = $tab['url'];
-		
+
 		// Any additional paramaters for the url?
 		if (isset($tab['add_params']) && !isset($tab_context['tabs'][$id]['add_params']))
 			$tab_context['tabs'][$id]['add_params'] = $tab['add_params'];
-		
+
 		// Has it been deemed selected?
 		if (!empty($tab['is_selected']))
 			$tab_context['tabs'][$id]['is_selected'] = true;
-		
+
 		// Does it have its own help?
 		if (!empty($tab['help']))
 			$tab_context['tabs'][$id]['help'] = $tab['help'];
-		
+
 		// Is this the last one?
 		if (!empty($tab['is_last']) && !isset($tab_context['override_last']))
 			$tab_context['tabs'][$id]['is_last'] = true;

+ 18 - 6
Themes/default/ManageMaintenance.template.php

@@ -93,18 +93,15 @@ function template_maintain_database()
 			<h3 class="catbg">', $txt[$context['convert_to'] . '_title'], '</h3>
 		</div>
 		<div class="windowbg">
-			<span class="topslice"><span></span></span>
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertmsgbody" method="post" accept-charset="', $context['character_set'], '">
 					<p>', $txt['mediumtext_introduction'], '</p>',
 					$context['convert_to_suggest'] ? '<p class="infobox">' . $txt['convert_to_suggest_text'] . '</p>' : '', '
-					<hr class="hrcolor" />
 					<input type="submit" name="evaluate_conversion" value="', $txt['maintain_run_now'], '" class="button_submit" /><br class="clear_right" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
 				</form>
 			</div>
-			<span class="botslice"><span></span></span>
 		</div>';
 	}
 
@@ -255,9 +252,10 @@ function template_maintain_members()
 			membersSwap = !membersSwap;
 			var membersForm = document.getElementById(\'membersForm\');
 
+			$("#membersPanel").slideToggle(300);
+
 			document.getElementById("membersIcon").src = smf_images_url + (membersSwap ? "/selected_open.png" : "/selected.png");
 			setInnerHTML(document.getElementById("membersText"), membersSwap ? "', $txt['maintain_members_choose'], '" : "', $txt['maintain_members_all'], '");
-			document.getElementById("membersPanel").style.display = (membersSwap ? "block" : "none");
 
 			for (var i = 0; i < membersForm.length; i++)
 			{
@@ -339,7 +337,7 @@ function template_maintain_members()
 						<input type="checkbox" name="posts" id="posts" checked="checked" class="input_check" />
 						<label for="posts">', $txt['reattribute_increase_posts'], '</label>
 					</p>
-					<input type="submit" id="do_attribute" value="', $txt['reattribute'], '" onclick="if (!checkAttributeValidity()) return false; 
+					<input type="submit" id="do_attribute" value="', $txt['reattribute'], '" onclick="if (!checkAttributeValidity()) return false;
 					return confirm(warningMessage);" class="button_submit" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
@@ -431,7 +429,7 @@ function template_maintain_topics()
 				setInnerHTML(document.getElementById("rotText"), rotSwap ? ', JavaScriptEscape($txt['maintain_old_choose']), ' : ', JavaScriptEscape($txt['maintain_old_all']), ');
 
 				// Toggle panel
-				document.getElementById("rotPanel").style.display = !rotSwap ? "none" : "";
+				$("#rotPanel").slideToggle(300);
 
 				// Toggle checkboxes
 				var rotPanel = document.getElementById(\'rotPanel\');
@@ -513,6 +511,20 @@ function template_maintain_topics()
 				</form>
 			</div>
 		</div>
+
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt['maintain_old_drafts'], '</h3>
+		</div>
+		<div class="windowbg">
+			<div class="content">
+				<form action="', $scripturl, '?action=admin;area=maintain;sa=topics;activity=olddrafts" method="post" accept-charset="', $context['character_set'], '">
+					<p>', $txt['maintain_old_drafts_days'], '&nbsp;<input type="text" name="draftdays" value="', (!empty($modSettings['drafts_keep_days']) ? $modSettings['drafts_keep_days'] : 30), '" size="3" />&nbsp;', $txt['days_word'], '</p>
+					<input type="submit" value="', $txt['maintain_old_remove'], '" onclick="return confirm(\'', $txt['maintain_old_drafts_confirm'], '\');" class="button_submit" />
+					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
+				</form>
+			</div>
+		</div>
 		<div class="cat_bar">
 			<h3 class="catbg">', $txt['move_topics_maintenance'], '</h3>
 		</div>

+ 3 - 3
Themes/default/ManageMembergroups.template.php

@@ -585,19 +585,19 @@ function template_group_members()
 	echo '
 				</tbody>
 			</table>';
-		
+
 	if (!empty($context['group']['assignable']))
 		echo '
 			<div class="floatright">
 				<input type="submit" name="remove" value="', $txt['membergroups_members_remove'], '" class="button_submit " />
 			</div>';
-	
+
 	echo '
 			<div class="pagesection flow_hidden">
 				<div class="floatleft">', $txt['pages'], ': ', $context['page_index'], '</div>
 			</div>
 			<br />';
-	
+
 	if (!empty($context['group']['assignable']))
 	{
 		echo '

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

@@ -303,7 +303,7 @@ function template_admin_browse()
 							</select>
 						</dd>
 					</dl>
-					<input type="submit" value="', $txt['admin_browse_outstanding_go'], '" class="button_submit" />
+					<input type="submit" value="', $txt['admin_browse_outstanding_go'], '" class="button_submit" /><br class="clear_right">
 					<input type="hidden" name="type" value="', $context['browse_type'], '" />
 					<input type="hidden" name="sort" value="', $context['approve_list']['sort']['id'], '" />
 					<input type="hidden" name="start" value="', $context['approve_list']['start'], '" />

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

@@ -18,7 +18,7 @@ function template_email_members()
 	<div id="admincenter">
 		<form action="', $scripturl, '?action=admin;area=news;sa=mailingcompose" method="post" id="admin_newsletters" class="flow_hidden" accept-charset="', $context['character_set'], '">
 			<div class="cat_bar">
-				<h3 class="catbg">', $txt['admin_newsletters'], '</h3>',$bat_poo,'
+				<h3 class="catbg">', $txt['admin_newsletters'], '</h3>
 			</div>
 			<div class="information">
 				', $txt['admin_news_select_recipients'], '
@@ -245,7 +245,7 @@ function template_email_members_compose()
 	// 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" ', !empty($context['send_pm']) ? 'checked="checked"' : '', 'class="input_check" onclick="checkboxes_status(this);" /> ', $txt['email_as_pms'], '</label></li>

+ 1 - 2
Themes/default/ManagePermissions.template.php

@@ -349,9 +349,8 @@ function template_by_board()
 			<input type="submit" name="save_changes" value="', $txt['save'], '" class="button_submit" />';
 	else
 		echo '
-			
 			<a class="button_link" href="', $scripturl, '?action=admin;area=permissions;sa=board;edit;', $context['session_var'], '=', $context['session_id'], '">', $txt['permissions_board_all'], '</a>';
-
+	
 	echo '
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-mpb_token_var'], '" value="', $context['admin-mpb_token'], '" />

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

@@ -123,7 +123,7 @@ function template_main()
 
 		echo '
 				</tr>';
-				
+
 			$alternate = !$alternate;
 		}
 	}
@@ -138,7 +138,7 @@ function template_main()
 			</tbody>
 			</table>
 		</div>';
-		
+
 	// Show the page numbers again. (makes 'em easier to find!)
 	echo '
 		<div class="pagesection">

+ 5 - 5
Themes/default/Packages.template.php

@@ -42,7 +42,7 @@ function template_view_package()
 		echo '
 		<div class="errorbox">
 			', $txt['package_will_fail_title'], '<br />
-			', $txt['package_will_fail_warning'], 
+			', $txt['package_will_fail_warning'],
 			!empty($context['failure_details']) ? '<br /><br /><strong>' . $context['failure_details'] . '</strong>' : '', '
 		</div>';
 	}
@@ -68,7 +68,7 @@ function template_view_package()
 			</div>
 			<br />';
 	}
-	
+
 	// Did they specify a license to display?
 	if (isset($context['package_license']))
 	{
@@ -90,7 +90,7 @@ function template_view_package()
 			</div>
 			<br />';
 	}
-	
+
 	echo '
 		<form action="', $scripturl, '?action=admin;area=packages;sa=', $context['uninstalling'] ? 'uninstall' : 'install', $context['ftp_needed'] ? '' : '2', ';package=', $context['filename'], ';pid=', $context['install_id'], '" onsubmit="submitonce(this);" method="post" accept-charset="', $context['character_set'], '">
 			<div class="cat_bar">
@@ -366,7 +366,7 @@ function template_view_package()
 
 	echo '
 	// ]]></script>';
-	
+
 	// Get the currently selected item from a select list
 	echo '
 	<script type="text/javascript"><!-- // --><![CDATA[
@@ -624,7 +624,7 @@ function template_browse()
 			<input type="hidden" name="area" value="packages" />
 			<input type="hidden" name="sa" value="', $context['sub_action'], '" />
 		</form>';
-	
+
 	echo '
 	</div>
 	<br class="clear" />

+ 125 - 5
Themes/default/PersonalMessage.template.php

@@ -607,23 +607,23 @@ function template_subject_list()
 
 			echo '
 					<option value="" disabled="disabled">', $txt['pm_msg_label_apply'], ':</option>';
-					
+
 			foreach ($context['labels'] as $label)
 			{
 				if ($label['id'] != $context['current_label_id'])
 					echo '
 					<option value="add_', $label['id'], '">&nbsp;', $label['name'], '</option>';
 			}
-			
+
 			echo '
 					<option value="" disabled="disabled">', $txt['pm_msg_label_remove'], ':</option>';
-					
+
 			foreach ($context['labels'] as $label)
 			{
 				echo '
 					<option value="rem_', $label['id'], '">&nbsp;', $label['name'], '</option>';
 			}
-			
+
 			echo '
 				</select>
 				<noscript>
@@ -975,6 +975,12 @@ function template_send()
 					</dl>
 				</div>';
 
+	if (!empty($modSettings['drafts_pm_enabled']))
+		echo '
+				<div id="draft_section" class="infobox"', isset($context['draft_saved']) ? '' : ' style="display: none;"', '>',
+					sprintf($txt['draft_pm_saved'], $scripturl . '?action=pm;sa=showpmdrafts'), '
+				</div>';
+
 	echo '
 				<dl id="post_header">';
 
@@ -1016,7 +1022,7 @@ function template_send()
 						<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"', '/>
+						<input type="text" name="subject" value="', $context['subject'], '" tabindex="', $context['tabindex']++, '" size="80" maxlength="80"',isset($context['post_error']['no_subject']) ? ' class="error"' : ' class="input_text"', '/>
 					</dd>
 				</dl><hr class="clear" />';
 
@@ -1069,6 +1075,30 @@ function template_send()
 		</div>
 	</form>';
 
+	// If the admin enabled the pm drafts feature, show a draft selection box
+	if (!empty($modSettings['drafts_enabled']) && !empty($context['drafts_pm_save']) && !empty($context['drafts']) && !empty($options['drafts_show_saved_enabled']))
+	{
+		echo '
+			<br />
+			<div id="postDraftOptionsHeader" class="title_bar">
+				<h4 class="titlebg">
+					<img id="postDraftExpand" class="panel_toggle" style="display: none;" src="', $settings['images_url'], '/collapse.png" alt="-" /> <strong><a href="#" id="postDraftExpandLink">', $txt['draft_load'], '</a></strong>
+				</h4>
+			</div>
+			<div id="postDraftOptions" class="load_drafts padding">
+				<dl class="settings">
+					<dt><strong>', $txt['subject'], '</strong></dt>
+					<dd><strong>', $txt['draft_saved_on'], '</strong></dd>';
+
+		foreach ($context['drafts'] as $draft)
+			echo '
+					<dt>', $draft['link'], '</dt>
+					<dd>', $draft['poster_time'], '</dd>';
+		echo '
+				</dl>
+			</div>';
+	}
+
 	echo '
 		<script type="text/javascript"><!-- // --><![CDATA[';
 	// The functions used to preview a personal message without loading a new page.
@@ -1176,6 +1206,33 @@ function template_send()
 				location.hash = \'#\' + \'preview_section\';
 			}';
 
+	// Code for showing and hiding drafts
+	if (!empty($context['drafts']))
+		echo '
+			var oSwapDraftOptions = new smc_Toggle({
+				bToggleEnabled: true,
+				bCurrentlyCollapsed: true,
+				aSwappableContainers: [
+					\'postDraftOptions\',
+				],
+				aSwapImages: [
+					{
+						sId: \'postDraftExpand\',
+						srcExpanded: smf_images_url + \'/collapse.png\',
+						altExpanded: \'-\',
+						srcCollapsed: smf_images_url + \'/expand.png\',
+						altCollapsed: \'+\'
+					}
+				],
+				aSwapLinks: [
+					{
+						sId: \'postDraftExpandLink\',
+						msgExpanded: ', JavaScriptEscape($txt['draft_hide']), ',
+						msgCollapsed: ', JavaScriptEscape($txt['draft_load']), '
+					}
+				]
+			});';
+
 	echo '
 		// ]]></script>';
 
@@ -1822,4 +1879,67 @@ function template_add_rule()
 		// ]]></script>';
 }
 
+// Template for showing all the PM drafts of the user.
+function template_showPMDrafts()
+{
+	global $context, $settings, $options, $scripturl, $modSettings, $txt;
+
+	echo '
+		<div class="cat_bar">
+			<h3 class="catbg">
+				<img src="', $settings['images_url'], '/message_sm.png" alt="" class="icon" />
+					', $txt['drafts_show'], '
+			</h3>
+		</div>
+		<div class="pagesection">
+			<span>', $txt['pages'], ': ', $context['page_index'], '</span>
+		</div>';
+
+	// Button shortcuts
+	$edit_button = create_button('modify_inline.png', 'draft_edit', 'draft_edit', 'class="centericon"');
+	$remove_button = create_button('delete.png', 'draft_delete', 'draft_delete', 'class="centericon"');
+
+	// For every draft to be displayed, give it its own div, and show the important details of the draft.
+	foreach ($context['drafts'] as $draft)
+	{
+		echo '
+		<div class="topic">
+			<div class="', $draft['alternate'] == 0 ? 'windowbg2' : 'windowbg', ' core_posts">
+				<div class="content">
+					<div class="counter">', $draft['counter'], '</div>
+					<div class="topic_details">
+						<h5><strong>', $draft['subject'], '</strong>&nbsp;';
+
+		echo '
+						</h5>
+						<span class="smalltext">&#171;&nbsp;<strong>', $txt['draft_saved_on'], ':</strong> ', sprintf($txt['draft_days_ago'], $draft['age']), (!empty($draft['remaining']) ? ', ' . sprintf($txt['draft_retain'], $draft['remaining']) : ''), '&#187;</span><br />
+						<span class="smalltext">&#171;&nbsp;<strong>', $txt['to'], ':</strong> ', implode(', ', $draft['recipients']['to']), '&nbsp;&#187;</span><br />
+						<span class="smalltext">&#171;&nbsp;<strong>', $txt['pm_bcc'], ':</strong> ', implode(', ', $draft['recipients']['bcc']), '&nbsp;&#187;</span>
+					</div>
+					<div class="list_posts">
+						', $draft['body'], '
+					</div>
+
+					<ul class="reset smalltext quickbuttons">
+						<li><a href="', $scripturl, '?action=pm;sa=showpmdrafts;id_draft=', $draft['id_draft'], ';', $context['session_var'], '=', $context['session_id'], '"  class="reply_button"><span>', $txt['draft_edit'], '</span></a></li>
+						<li><a href="', $scripturl, '?action=pm;sa=showpmdrafts;delete=', $draft['id_draft'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(\'', $txt['draft_remove'], '?\');" class="remove_button"><span>', $txt['draft_delete'], '</span></a></li>
+					</ul>
+				</div>
+			</div>
+		</div>';
+	}
+
+	// No drafts? Just show an informative message.
+	if (empty($context['drafts']))
+		echo '
+		<div class="tborder windowbg2 padding centertext">
+			', $txt['draft_none'], '
+		</div>';
+
+	// Show page numbers.
+	echo '
+		<div class="pagesection" style="margin-bottom: 0;">
+			<span>', $txt['pages'], ': ', $context['page_index'], '</span>
+		</div>';
+}
 ?>

+ 65 - 4
Themes/default/Post.template.php

@@ -124,6 +124,12 @@ function template_main()
 						', $txt['topic_locked_no_reply'], '
 					</p>';
 
+	if (!empty($modSettings['drafts_post_enabled']))
+		echo '
+				<div id="draft_section" class="infobox"', isset($context['draft_saved']) ? '' : ' style="display: none;"', '>',
+					sprintf($txt['draft_saved'], $scripturl . '?action=profile;u=' . $context['user']['id'] . ';area=showdrafts'), '
+				</div>';
+
 	// The post header... important stuff
 	echo '
 					<dl id="post_header">';
@@ -172,7 +178,8 @@ function template_main()
 							</select>
 							<img src="', $context['icon_url'], '" name="icons" hspace="15" alt="" />
 						</dd>
-					</dl><hr class="clear" />';
+					</dl>
+					<hr class="clear" />';
 
 	// Are you posting a calendar event?
 	if ($context['make_event'])
@@ -344,6 +351,7 @@ function template_main()
 					</div>';
 	}
 
+	// Show the actual posting area...
 	echo '
 					', template_control_richedit($context['post_box_name'], 'smileyBox_message', 'bbcBox_message');
 
@@ -477,6 +485,30 @@ function template_main()
 					</dl>';
 	}
 
+	// If the admin enabled the drafts feature, show a draft selection box
+	if (!empty($modSettings['drafts_enabled']) && !empty($context['drafts']) && !empty($options['drafts_show_saved_enabled']))
+	{
+		echo '
+			<br />
+			<div id="postDraftOptionsHeader" class="title_bar">
+				<h4 class="titlebg">
+					<img id="postDraftExpand" class="panel_toggle" style="display: none;" src="', $settings['images_url'], '/collapse.png" alt="-" /> <strong><a href="#" id="postDraftExpandLink">', $txt['draft_load'], '</a></strong>
+				</h4>
+			</div>
+			<div id="postDraftOptions" class="load_drafts padding">
+				<dl class="settings">
+					<dt><strong>', $txt['subject'], '</strong></dt>
+					<dd><strong>', $txt['draft_saved_on'], '</strong></dd>';
+
+		foreach ($context['drafts'] as $draft)
+			echo '
+					<dt>', $draft['link'], '</dt>
+					<dd>', $draft['poster_time'], '</dd>';
+		echo '
+				</dl>
+			</div>';
+	}
+
 	// Is visual verification enabled?
 	if ($context['require_verification'])
 	{
@@ -492,8 +524,8 @@ function template_main()
 	// Finally, the submit buttons.
 	echo '
 					<br class="clear_right" />
-					<span class="smalltext" >
-						', isBrowser('is_firefox') ? $txt['shortcuts_firefox'] : $txt['shortcuts'], '
+					<span class="smalltext">
+						', isBrowser('is_firefox') ? ($context['drafts_save'] ? $txt['shortcuts_drafts_firefox'] : $txt['shortcuts_firefox']) : ($context['drafts_save'] ? $txt['shortcuts_drafts'] : $txt['shortcuts']), '
 					</span>
 					<span id="post_confirm_buttons">
 						', template_control_richedit_buttons($context['post_box_name']);
@@ -745,6 +777,33 @@ function template_main()
 				]
 			});';
 
+	// Code for showing and hiding drafts
+	if (!empty($context['drafts']))
+		echo '
+			var oSwapDraftOptions = new smc_Toggle({
+				bToggleEnabled: true,
+				bCurrentlyCollapsed: true,
+				aSwappableContainers: [
+					\'postDraftOptions\',
+				],
+				aSwapImages: [
+					{
+						sId: \'postDraftExpand\',
+						srcExpanded: smf_images_url + \'/collapse.png\',
+						altExpanded: \'-\',
+						srcCollapsed: smf_images_url + \'/expand.png\',
+						altCollapsed: \'+\'
+					}
+				],
+				aSwapLinks: [
+					{
+						sId: \'postDraftExpandLink\',
+						msgExpanded: ', JavaScriptEscape($txt['draft_hide']), ',
+						msgCollapsed: ', JavaScriptEscape($txt['draft_load']), '
+					}
+				]
+			});';
+
 	echo '
 		// ]]></script>';
 
@@ -768,7 +827,9 @@ function template_main()
 			echo '
 				<div class="', $post['alternate'] == 0 ? 'windowbg' : 'windowbg2', ' core_posts">
 				<div class="content" id="msg', $post['id'], '">
-						<h5 class="floatleft"><span>', $txt['posted_by'], '</span>&nbsp;', $post['poster'], '</h5>&nbsp;-&nbsp;', $post['time'];
+					<h5 class="floatleft">
+						<span>', $txt['posted_by'], '</span>&nbsp;', $post['poster'], '
+					</h5>&nbsp;-&nbsp;', $post['time'];
 
 			if ($context['can_quote'])
 			{

+ 112 - 12
Themes/default/Profile.template.php

@@ -104,7 +104,13 @@ function template_summary()
 		echo '
 					<a href="', $scripturl, '?action=pm;sa=send;u=', $context['id_member'], '">', $txt['profile_sendpm_short'], '</a><br />';
 	echo '
-					<a href="', $scripturl, '?action=profile;area=showposts;u=', $context['id_member'], '">', $txt['showPosts'], '</a><br />
+					<a href="', $scripturl, '?action=profile;area=showposts;u=', $context['id_member'], '">', $txt['showPosts'], '</a><br />';
+
+	if ($context['user']['is_owner'] && !empty($modSettings['drafts_enabled']))
+		echo '
+					<a href="', $scripturl, '?action=profile;area=showdrafts;u=', $context['id_member'], '">', $txt['drafts_show'], '</a><br />';
+
+	echo '
 					<a href="', $scripturl, '?action=profile;area=statistics;u=', $context['id_member'], '">', $txt['statPanel'], '</a>
 				</p>';
 
@@ -334,6 +340,9 @@ function template_showPosts()
 			<h3 class="catbg">
 				', (!isset($context['attachments']) && empty($context['is_topics']) ? $txt['showMessages'] : (!empty($context['is_topics']) ? $txt['showTopics'] : $txt['showAttachments'])), ' - ', $context['member']['name'], '
 			</h3>
+		</div>
+		<div class="pagesection">
+			<div class="pagelinks">', $context['page_index'], '</div>
 		</div>';
 
 	// Button shortcuts
@@ -399,15 +408,9 @@ function template_showPosts()
 				</div>';
 
 			echo '
-			</div>
-		</div>';
+				</div>
+			</div>';
 		}
-
-		// Show more page numbers.
-		echo '
-		<div class="pagesection" style="margin-bottom: 0;">
-			<span>', $txt['pages'], ': ', $context['page_index'], '</span>
-		</div>';
 	}
 	else
 		template_show_list('attachments');
@@ -423,8 +426,86 @@ function template_showPosts()
 
 		echo '
 			</tbody>
-		</table>
-	</div>';
+		</table>';
+
+	// Show more page numbers.
+	echo '
+		<div class="pagesection" style="margin-bottom: 0;">
+			<div class="pagelinks">', $context['page_index'], '</div>
+		</div>';
+}
+
+// Template for showing all the drafts of the user.
+function template_showDrafts()
+{
+	global $context, $settings, $options, $scripturl, $modSettings, $txt;
+
+	echo '
+		<div class="cat_bar">
+			<h3 class="catbg">
+				<span class="ie6_header floatleft"><img src="', $settings['images_url'], '/message_sm.png" alt="" class="icon" />
+					', $txt['drafts_show'], ' - ', $context['member']['name'], '
+				</span>
+			</h3>
+		</div>
+		<div class="pagesection" style="margin-bottom: 0;">
+			<div class="pagelinks">', $context['page_index'], '</div>
+		</div>';
+
+	// Button shortcuts
+	$edit_button = create_button('modify_inline.png', 'draft_edit', 'draft_edit', 'class="centericon"');
+	$remove_button = create_button('delete.png', 'draft_delete', 'draft_delete', 'class="centericon"');
+
+	// For every draft to be displayed, give it its own div, and show the important details of the draft.
+	foreach ($context['drafts'] as $draft)
+	{
+		echo '
+		<div class="topic">
+			<div class="', $draft['alternate'] == 0 ? 'windowbg2' : 'windowbg', ' core_posts">
+				<span class="topslice"><span></span></span>
+				<div class="content">
+					<div class="counter">', $draft['counter'], '</div>
+					<div class="topic_details">
+						<h5><strong><a href="', $scripturl, '?board=', $draft['board']['id'], '.0">', $draft['board']['name'], '</a> / ', $draft['topic']['link'], '</strong> &nbsp; &nbsp;';
+
+		if (!empty($draft['sticky']))
+			echo '<img src="', $settings['images_url'], '/icons/quick_sticky.png" alt="', $txt['sticky_topic'], '" title="', $txt['sticky_topic'], '" />';
+
+		if (!empty($draft['locked']))
+			echo '<img src="', $settings['images_url'], '/icons/quick_lock.png" alt="', $txt['locked_topic'], '" title="', $txt['locked_topic'], '" />';
+
+		echo '
+						</h5>
+						<span class="smalltext">&#171;&nbsp;<strong>', $txt['on'], ':</strong> ', $draft['time'], '&nbsp;&#187;</span>
+					</div>
+					<div class="list_posts">
+						', $draft['body'], '
+					</div>
+				</div>
+				<div class="floatright">
+					<ul class="reset smalltext quickbuttons">
+						<li><a href="', $scripturl, '?action=post;', (empty($draft['topic']['id']) ? 'board=' . $draft['board']['id'] : 'topic=' . $draft['topic']['id']), '.0;id_draft=', $draft['id_draft'], '" class="reply_button"><span>', $txt['draft_edit'], '</span></a></li>
+						<li><a href="', $scripturl, '?action=profile;u=', $context['member']['id'], ';area=showdrafts;delete=', $draft['id_draft'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(\'', $txt['draft_remove'], '?\');" class="remove_button"><span>', $txt['draft_delete'], '</span></a></li>
+					</ul>
+				</div>
+				<br class="clear" />
+				<span class="botslice"><span></span></span>
+			</div>
+		</div>';
+	}
+
+	// No drafts? Just show an informative message.
+	if (empty($context['drafts']))
+		echo '
+		<div class="tborder windowbg2 padding centertext">
+			', $txt['draft_none'], '
+		</div>';
+
+	// Show page numbers.
+	echo '
+		<div class="pagesection" style="margin-bottom: 0;">
+			<div class="pagelinks">', $context['page_index'], '</div>
+		</div>';
 }
 
 // Template for showing all the buddies of the current user.
@@ -1577,6 +1658,25 @@ function template_profile_theme_settings()
 								</select>
 							</dd>';
 
+	if (!empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_autosave_enabled']))
+		echo '
+							<dt>
+								<label for="drafts_autosave_enabled">', $txt['drafts_autosave_enabled'], '</label>
+							</dt>
+							<dd>
+								<input type="hidden" name="default_options[drafts_autosave_enabled]" value="0" />
+								<label for="drafts_autosave_enabled"><input type="checkbox" name="default_options[drafts_autosave_enabled]" id="drafts_autosave_enabled" value="1"', !empty($context['member']['options']['drafts_autosave_enabled']) ? ' checked="checked"' : '', ' class="input_check" /></label>
+							</dd>';
+	if (!empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_show_saved_enabled']))
+		echo '
+							<dt>
+								<label for="drafts_show_saved_enabled">', $txt['drafts_show_saved_enabled'], '</label>
+							</dt>
+							<dd>
+								<input type="hidden" name="default_options[drafts_show_saved_enabled]" value="0" />
+								<label for="drafts_show_saved_enabled"><input type="checkbox" name="default_options[drafts_show_saved_enabled]" id="drafts_show_saved_enabled" value="1"', !empty($context['member']['options']['drafts_show_saved_enabled']) ? ' checked="checked"' : '', ' class="input_check" /></label>
+							</dd>';
+
 	echo '
 							<dt>
 								<label for="display_quick_reply">', $txt['display_quick_reply'], '</label>
@@ -2498,7 +2598,7 @@ function template_profile_group_manage()
 							</dt>
 							<dd>
 								<select name="id_group" ', ($context['user']['is_owner'] && $context['member']['group_id'] == 1 ? 'onchange="if (this.value != 1 &amp;&amp; !confirm(\'' . $txt['deadmin_confirm'] . '\')) this.value = 1;"' : ''), '>';
-		
+
 		// Fill the select box with all primary member groups that can be assigned to a member.
 		foreach ($context['member_groups'] as $member_group)
 			if (!empty($member_group['can_be_primary']))

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

@@ -61,7 +61,7 @@ function template_reminder_pick()
 				<br class="clear" />
 			</div>
 		</div>
-		
+
 		<input type="hidden" name="uid" value="', $context['current_member']['id'], '" />
 		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 		<input type="hidden" name="', $context['remind_token_var'], '" value="', $context['remind_token'], '" />

+ 5 - 5
Themes/default/Themes.template.php

@@ -334,7 +334,7 @@ function template_set_options()
 	{
 		echo '
 						<dt ', $context['theme_options_reset'] ? 'style="width:50%"' : '', '>';
-						
+
 		// Show the change option box ?
 		if ($context['theme_options_reset'])
 			echo '
@@ -343,7 +343,7 @@ function template_set_options()
 								<option value="1">', $txt['themeadmin_reset_options_change'], '</option>
 								<option value="2">', $txt['themeadmin_reset_options_default'], '</option>
 							</select>&nbsp;</span>';
-		
+
 		// display checkbox options
 		if ($setting['type'] == 'checkbox')
 		{
@@ -393,12 +393,12 @@ function template_set_options()
 						<dd ', $context['theme_options_reset'] ? 'style="width:40%"' : '', '>
 							<input type="text" name="', !empty($setting['default']) ? 'default_' : '', 'options[', $setting['id'], ']" id="options_', $setting['id'], '" value="', $setting['value'], '"', $setting['type'] == 'number' ? ' size="5"' : '', $context['theme_options_reset'] ? ' disabled="disabled"' : '', ' class="input_text" />';
 		}
-		
-		// end of this defintion 
+
+		// end of this defintion
 		echo '
 						</dd>';
 	}
-	
+
 	// close the option page up
 	echo '
 					</dl>

+ 8 - 8
Themes/default/Who.template.php

@@ -189,7 +189,7 @@ function template_credits()
 			</div>
 		</div>';
 	}
-	
+
 	// Other software and graphics
 	if (!empty($context['credits_software_graphics']))
 	{
@@ -199,26 +199,26 @@ function template_credits()
 		</div>
 		<div class="windowbg">
 			<div class="content">';
-		
+
 		if (!empty($context['credits_software_graphics']['graphics']))
 			echo '
 				<dl>
 					<dt><strong>', $txt['credits_graphics'], '</strong></dt>
 					<dd>', implode('</dd><dd>', $context['credits_software_graphics']['graphics']), '</dd>
 				</dl>';
-		
+
 		if (!empty($context['credits_software_graphics']['software']))
 			echo '
 				<dl>
 					<dt><strong>', $txt['credits_software'], '</strong></dt>
 					<dd>', implode('</dd><dd>', $context['credits_software_graphics']['software']), '</dd>
 				</dl>';
-	
+
 		echo '
 			</div>
 		</div>';
 	}
-	
+
 	// How about Modifications, we all love em
 	if (!empty($context['credits_modifications']))
 	{
@@ -228,13 +228,13 @@ function template_credits()
 		</div>
 		<div class="windowbg">
 			<div class="content">';
-				
+
 		echo '
 				<dl>
 					<dt><strong>', $txt['credits_modifications'], '</strong></dt>
 					<dd>', implode('</dd><dd>', $context['credits_modifications']), '</dd>
 				</dl>';
-		
+
 		echo '
 			</div>
 		</div>';
@@ -254,7 +254,7 @@ function template_credits()
 	echo '
 					</dd>
 				</dl>';
-				
+
 	if (!empty($context['copyrights']['mods']))
 	{
 		echo '

+ 0 - 29
Themes/default/css/editor.css

@@ -1,29 +0,0 @@
-/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */
-.editor {
-	width: 100%;
-	max-width: 100%;
-	min-width: 100%;
-}
-
-.editor, .rich_editor_frame {
-	border: 1px solid #808080;
-	padding: 2px !important;
-	margin: 0;
-}
-
-.rich_editor_frame {
-	background: #fff;
-}
-
-/* The resize handle. */
-.richedit_resize {
-	height: 5px;
-	font-size: 0;
-	background: #eee url(../images/bbc/resize-handle.png) no-repeat 50% 1px;
-	border: 1px solid #ddd;
-	border-top-width: 0;
-	cursor: s-resize;
-	width: 100%;
-	padding: 0 2px;
-}
-div#message_resizer, div.richedit_resize {display: none;}

+ 0 - 40
Themes/default/css/editor_ie.css

@@ -1,40 +0,0 @@
-/* This is the editor's playground (textarea for non-wysiwyg, iframe for wysiwyg). */
-.editor {
-	width: 635px;
-	max-width: 100%;
-	min-width: 100%;
-}
-
-/* This is the IFRAME that holds the editor. */
-.rich_editor_frame {
-	border: 1px solid #808080;
-}
-
-/* This is the WYSIWYG editor */
-.rich_editor {
-	background-color: #fff;
-	color: #000;
-	font-family: verdana;
-	font-size: x-small;
-	border: none;
-}
-
-.rich_editor p {
-	margin: 0;
-}
-
-.rich_editor a img {
-	border: 0;
-}
-
-/* The resize handle. */
-.richedit_resize {
-	height: 5px;
-	font-size: 0;
-	background: #eee url(../images/bbc/resize-handle.png) no-repeat 50% 1px;
-	border: 1px solid #ddd;
-	border-top-width: 0;
-	cursor: s-resize;
-	width: 100%;
-}
-div#message_resizer, div.richedit_resize {display: visible;}

+ 0 - 170
Themes/default/css/ie6.css

@@ -1,170 +0,0 @@
-.codeheader, code.bbc_code {
-	width: 96%;
-	margin: 0 auto;
-}
-code.bbc_code {
-	white-space: normal;
-}
-h3.catbg input.input_check {
-	margin: 0 4px;
-}
-h3.catbg img.icon, h4.titlebg img.icon {
-	margin: 1px 3px 0 0;
-}
-h3.catbg span.ie6_header, h4.catbg span.ie6_header, h3.titlebg span.ie6_header, h4.titlebg span.ie6_header {
-	padding: 10px 0;
-}
-#statistics h4.titlebg span.ie6_header {
-	padding: 0;
-}
-#statistics h4.titlebg span.ie6_header img.icon {
-	padding: 5px 0;
-}
-/* The dropdown menus
-------------------------------------------------------- */
-
-.dropmenu li {
-	width: 1px;
-}
-.dropmenu li a span {
-	white-space: nowrap;
-}
-.dropmenu li a:hover {
-	text-decoration: none;
-}
-.dropmenu li.iehover {
-	z-index: 120;
-}
-
-/* the page section */
-.pagesection {
-	overflow: auto;
-}
-/* the user section needs some attention */
-#main_menu {
-	width: 98%;
-}
-#top_section {
-	height: 65px;
-}
-
-/* the tabled definition lists */
-/* I commented the following out. Not sure why it was there. 
-/* Changing float: left; to float: right; sorts the settings dd class in index.css*/
-/* All the others seem fine too.*/
-/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd {
-	float: none !important;
-	width: auto;
-}*/
-/* generic lists header */
-/* Side paddings must NOT be defined here.*/
-.table_grid thead th {
-	padding-top: 0 !important;
-	padding-bottom: 0 !important;
-}
-
-/* overflow: hidden doesn't help in IE6. */
-h3.titlebg a, h3.titlebg, h4.titlebg, h4.titlebg a {
-	display: inline-block;
-}
-
-#upper_section {
-	display: inline-block;
-}
-
-/* Overrides for the message index template
-------------------------------------------------------- */
-#messageindex table {
-	margin-top: 5px;
-}
-#messageindex table th {
-	border-bottom: 1px solid #fff;
-}
-#topic_icons .description {
-	padding: 2em 1em 1em 1em;
-	overflow: auto;
-}
-
-/* Overrides for the display template
-------------------------------------------------------- */
-#forumposts .postarea {
-	margin-left: 0;
-	margin-right: 0;
-	float: right;
-}
-.keyinfo {
-	padding-bottom: 6px;
-}
-.inner {
-	clear: both;
-}
-.post {
-	word-wrap: break-word;
-}
-.buttonlist ul li {
-	width: 1%;
-	white-space: nowrap;
-}
-#forumposts h3.catbg {
-	clear: both;
-}
-#quickReplyOptions form textarea {
-	width: 98%;
-}
-
-/* Styles for the statistics center.
-------------------------------------------------- */
-#statistics div.content {
-	height: 210px;
-	overflow: hidden;
-}
-#statistics div.top_row {
-	height: 150px;
-}
-
-/* Overrides for the admin template
-------------------------------------------------------- */
-#main_admsection {
-	height: 100%;
-}
-#main_admsection table {
-	width: 99%;
-}
-
-/* Overrides for the profile template
-------------------------------------------------------- */
-#basicinfo h4 {
-	word-wrap: break-word;
-}
-.ignoreboards {
-	margin: 0 1%;
-	padding: 0;
-	width: 45%;
-}
-
-/* Overrides for the personal messages template
-------------------------------------------------------- */
-#personal_messages .postarea {
-	margin-left: 0;
-	margin-right: 0;
-	float: right;
-}
-
-/* Overrides for the admin section of the register template
-------------------------------------------------------- */
-#registration_agreement {
-	width: 99.5%;
-	margin: 0 auto;
-}
-
-#edit_poll ul.poll_main li {
-	padding-left: 0;
-	margin: 0 -2em;
-}
-#postmodify div.roundframe { margin-right: 0;}
-
-/* Overrides for the recent posts template
-------------------------------------------------------- */
-.list_posts {
-	word-wrap: break-word;
-}

+ 0 - 86
Themes/default/css/ie7.css

@@ -1,86 +0,0 @@
-code.bbc_code {
-	white-space: normal;
-}
-h3.catbg input.input_check {
-	margin: 0 4px;
-}
-
-/* The dropdown menus
-------------------------------------------------------- */
-/* the dropmenu - RTL tweak */
-.dropmenu li ul {
-	margin: 0 -50px 0 0;
-}
-/* the hover effects */
-.dropmenu li.iehover {
-	z-index: 120;
-}
-/* the tabled definition lists
-/* I commented the following out. Not sure why it was there. 
-/* Changing float: left; to float: right; sorts the settings dd class in index.css*/
-/* All the others seem fine too.*/
-/*dl.settings dd, #creator dd, dl.stats dd, dl.register_form dd, #poll_options dl.options dd, .login dd {
-	float: none !important;
-	width: auto;
-}*/
-/* generic lists header */
-/* Side paddings must NOT be defined here.*/
-.table_grid thead th {
-	padding-top: 0 !important;
-	padding-bottom: 0 !important;
-}
-
-/* Overrides for the messageindex template
-------------------------------------------------------- */
-#messageindex table {
-	margin-top: 5px;
-}
-#messageindex table th {
-	border-bottom: 1px solid #fff;
-}
-#topic_icons .description {
-	padding: 2em 1em 1em 1em;
-	overflow: auto;
-}
-
-/* Overrides for the display template
-------------------------------------------------------- */
-.post {
-	padding-top: 1em;
-	float: none;
-	word-wrap: break-word;
-}
-#content_section #forumposts div.cat_bar {
-	margin-top: 8px;
-	clear: both;
-}
-#content_section .pagesection {
-	height: 1%;
-}
-#quickReplyOptions form textarea {
-	width: 98%;
-}
-
-/* Overrides for the profile template
-------------------------------------------------------- */
-#basicinfo h4 {
-	word-wrap: break-word;
-}
-
-/* Overrides for the calendar template
-------------------------------------------------- */
-#main_grid table.weeklist h4.titlebg {
-	margin: 2px 0 -4px 0;
-}
-
-/* Overrides for the personal messages template
-------------------------------------------------------- */
-#postmodify dl #pm_to, #postmodify dl #bcc_div2, #postmodify dl #pm_subject {
-	clear:both !important;
-}
-
-/* Overrides for the recent posts template
-------------------------------------------------------- */
-.list_posts {
-	word-wrap: break-word;
-}

BIN
Themes/default/images/admin/feature_dr.png


BIN
Themes/default/images/theme/frame_repeat.png


BIN
Themes/default/images/theme/main_block.png


BIN
Themes/default/images/theme/menu_gfx.png


+ 8 - 4
Themes/default/index.template.php

@@ -70,6 +70,9 @@ function template_init()
 
 	// Set the following variable to true if this theme requires the optional theme strings file to be loaded.
 	$settings['require_theme_strings'] = false;
+
+	// Set the following variable to true is this theme wants to display the avatar of the user that posted the last post on the board index and message index
+	$settings['avatars_on_indexes'] = false;
 }
 
 /**
@@ -118,7 +121,7 @@ function template_html_above()
 
 	// load in any javascript files from mods and themes
 	template_javascript();
-		
+
 	echo '
 	<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '" />
 	<meta name="description" content="', $context['page_title_html_safe'], '" />', !empty($context['meta_keywords']) ? '
@@ -330,7 +333,7 @@ function template_body_above()
 					</div>';
 
 	echo '
-					<hr class="clear" /> 
+					<hr class="clear" />
 				</div>';
 
 	// Show the menu here, according to the menu sub template, followed by the navigation tree.
@@ -388,6 +391,7 @@ function template_html_below()
 {
 	global $context, $settings, $options, $scripturl, $txt, $modSettings;
 
+	// load in any javascipt that could be defered to the end of the page
 	template_javascript(true);
 
 	echo '
@@ -465,7 +469,7 @@ function template_menu()
 		<div id="main_menu">
 			<ul class="dropmenu" id="menu_nav">';
 
-	// Note: Menu markup has been cleaned up to remove unnecessary spans and classes. 
+	// Note: Menu markup has been cleaned up to remove unnecessary spans and classes.
 	foreach ($context['menu_buttons'] as $act => $button)
 	{
 		echo '
@@ -515,7 +519,7 @@ function template_menu()
 
 	// The upshrink image, right-floated. Yes, I know it takes some space from the menu bar.
 	// Menu bar will still accommodate ten buttons on a 1024, with theme set to 90%. That's more than enough.
-	// If anyone is terrified of losing 40px out of the menu bar, set your theme to 92% instead of 90%. :P 
+	// If anyone is terrified of losing 40px out of the menu bar, set your theme to 92% instead of 90%. :P
 	echo '
 				<li style="float: right; position: absolute; top: 0; right: 0;">
 					<img id="upshrink" src="', $settings['images_url'], '/upshrink.png" alt="*" title="', $txt['upshrink_description'], '" style="padding: 4px 9px 3px 9px; display: none;" />

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

@@ -492,6 +492,13 @@ $txt['manageposts_topic_settings'] = 'Topic Settings';
 $txt['manageposts_topic_settings_description'] = 'Here you can set all settings involving topics.';
 $txt['manageposts_topic_settings_submit'] = 'Save';
 
+$txt['managedrafts_settings'] = 'Draft Settings';
+$txt['managedrafts_settings_description'] = 'Here you can set all settings involving drafts.';
+$txt['managedrafts_submit'] = 'Save';
+$txt['manage_drafts'] = 'Drafts';
+$txt['drafts_autosave'] = 'Enable the autosaving of posts as drafts';
+$txt['drafts_autosave_frequency'] = 'How often should drafts be autosaved?';
+
 $txt['removeNestedQuotes'] = 'Remove nested quotes when quoting';
 $txt['enableEmbeddedFlash'] = 'Embed flash into posts';
 $txt['enableEmbeddedFlash_warning'] = 'may be a security risk!';

+ 41 - 0
Themes/default/languages/Drafts.english.php

@@ -0,0 +1,41 @@
+<?php
+// Version: 2.1; Profile
+
+global $scripturl, $context;
+
+// profile
+$txt['drafts_show'] = 'Show Drafts';
+$txt['drafts_autosave_enabled'] = 'Enable the automatic saving of drafts.';
+$txt['drafts_show_saved_enabled'] = 'Enable selection of drafts from posting screens.';
+
+// misc
+$txt['drafts'] = 'Drafts';
+$txt['draft_save'] = 'Save Draft';
+$txt['draft_save_note'] = 'This will save the text of your post, but it will not save attachments, poll or event information.';
+$txt['draft_none'] = 'You have no drafts.';
+$txt['draft_edit'] = 'Edit draft';
+$txt['draft_load'] = 'Load drafts';
+$txt['draft_hide'] = 'Hide drafts';
+$txt['draft_delete'] = 'Delete draft';
+$txt['draft_saved_on'] = 'Draft last saved';
+$txt['draft_days_ago'] = '%s days ago';
+$txt['draft_retain'] = 'this will be retained for %s more days';
+$txt['draft_remove'] = 'Remove this draft';
+$txt['draft_saved'] = 'The contents have been saved as a draft, and will be accessible from the <a href="%1$s">Show Drafts area</a> of your profile.';
+$txt['draft_pm_saved'] = 'The contents have been saved as a draft, and will be accessible from the <a href="%1$s">Show Drafts area</a> of your message center.';
+
+// Admin options
+$txt['drafts_autosave_enabled'] = 'Enable automatic saving of drafts';
+$txt['drafts_autosave_enabled_subnote'] = 'This will automatically save user drafts in the background on a given frequency.  The user must also have the proper permissions';
+$txt['drafts_show_saved_enabled'] = 'Enable the selection of drafts from the posting screen';
+$txt['drafts_show_saved_enabled_subnote'] = 'This will allow the user to select and load appropriate drafts from the posting screen. The user must also have the proper permissions';
+$txt['drafts_keep_days'] = 'Maximum number of days to keep a draft';
+$txt['drafts_keep_days_subnote'] = 'Enter 0 to keep drafts indefinitely';
+$txt['drafts_autosave_frequency'] = 'How often should drafts be autosaved?';
+$txt['drafts_autosave_frequency_subnote'] = 'The minimum allowable value is 30 seconds';
+$txt['drafts_pm_enabled'] = 'Enable the saving of PM drafts';
+$txt['drafts_post_enabled'] = 'Enable the saving of Post drafts';
+$txt['drafts_none'] = 'No Subject';
+$txt['drafts_saved'] = 'Draft was sucessfuly saved';
+
+?>

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

@@ -37,6 +37,7 @@ $txt['feature_disabled'] = 'Sorry, this feature is disabled.';
 $txt['feature_no_exists'] = 'Sorry, this feature doesn\'t exist.';
 $txt['couldnt_connect'] = 'Could not connect to server or could not find file';
 $txt['no_board'] = 'The board you specified doesn\'t exist';
+$txt['no_message'] = 'The message is no longer available';
 $txt['cant_split'] = 'You are not allowed to split topics';
 $txt['cant_merge'] = 'You are not allowed to merge topics';
 $txt['no_topic_id'] = 'You specified an invalid topic ID.';
@@ -252,6 +253,7 @@ $txt['error_wrong_verification_answer'] = 'You did not answer the verification q
 $txt['error_need_verification_code'] = 'Please enter the verification code below to continue to the results.';
 $txt['error_bad_file'] = 'Sorry but the file specified could not be opened: %1$s';
 $txt['error_bad_line'] = 'The line you specified is invalid.';
+$txt['error_draft_not_saved'] = 'There was an error saving the draft';
 
 $txt['smiley_not_found'] = 'Smiley not found.';
 $txt['smiley_has_no_code'] = 'No code for this smiley was given.';

Some files were not shown because too many files changed in this diff