Browse Source

Merge branch 'master' into generic_js_load_boards

Conflicts:
	Themes/default/scripts/script.js
emanuele 12 years ago
parent
commit
c6dd6c3fc2
100 changed files with 3578 additions and 1879 deletions
  1. 7 0
      .gitignore
  2. 11 3
      Sources/Admin.php
  3. 3 3
      Sources/BoardIndex.php
  4. 10 10
      Sources/Calendar.php
  5. 13 10
      Sources/Class-BrowserDetect.php
  6. 16 15
      Sources/Class-CurlFetchWeb.php
  7. 1 1
      Sources/DbPackages-mysql.php
  8. 35 17
      Sources/Display.php
  9. 884 0
      Sources/Drafts.php
  10. 7 7
      Sources/DumpDatabase.php
  11. 2 84
      Sources/Groups.php
  12. 1 1
      Sources/Help.php
  13. 145 43
      Sources/Load.php
  14. 19 5
      Sources/LogInOut.php
  15. 3 6
      Sources/ManageAttachments.php
  16. 1 1
      Sources/ManageCalendar.php
  17. 4 4
      Sources/ManageErrors.php
  18. 7 25
      Sources/ManageLanguages.php
  19. 203 13
      Sources/ManageMaintenance.php
  20. 2 2
      Sources/ManageMembergroups.php
  21. 11 14
      Sources/ManageMembers.php
  22. 4 3
      Sources/ManageNews.php
  23. 6 0
      Sources/ManagePermissions.php
  24. 7 28
      Sources/ManagePosts.php
  25. 2 16
      Sources/ManageSearch.php
  26. 3 3
      Sources/ManageSearchEngines.php
  27. 17 18
      Sources/ManageServer.php
  28. 126 57
      Sources/ManageSettings.php
  29. 1 1
      Sources/ManageSmileys.php
  30. 10 10
      Sources/Memberlist.php
  31. 32 5
      Sources/MessageIndex.php
  32. 3 15
      Sources/ModerationCenter.php
  33. 1 1
      Sources/Modlog.php
  34. 3 3
      Sources/News.php
  35. 21 10
      Sources/Packages.php
  36. 67 13
      Sources/PersonalMessage.php
  37. 3 3
      Sources/Poll.php
  38. 87 46
      Sources/Post.php
  39. 4 4
      Sources/PostModeration.php
  40. 23 6
      Sources/Profile-Modify.php
  41. 149 66
      Sources/Profile-View.php
  42. 15 2
      Sources/Profile.php
  43. 2 2
      Sources/QueryString.php
  44. 5 5
      Sources/Recent.php
  45. 1 1
      Sources/Register.php
  46. 1 1
      Sources/Reminder.php
  47. 3 3
      Sources/RemoveTopic.php
  48. 12 12
      Sources/Reports.php
  49. 47 7
      Sources/ScheduledTasks.php
  50. 2 1
      Sources/Search.php
  51. 5 5
      Sources/SearchAPI-Custom.php
  52. 20 2
      Sources/Security.php
  53. 7 7
      Sources/SplitTopics.php
  54. 4 4
      Sources/Stats.php
  55. 16 16
      Sources/Subs-Admin.php
  56. 32 4
      Sources/Subs-BoardIndex.php
  57. 1 1
      Sources/Subs-Boards.php
  58. 7 7
      Sources/Subs-Calendar.php
  59. 7 7
      Sources/Subs-Categories.php
  60. 2 2
      Sources/Subs-Db-mysql.php
  61. 25 25
      Sources/Subs-Editor.php
  62. 6 6
      Sources/Subs-Graphics.php
  63. 14 3
      Sources/Subs-List.php
  64. 10 1
      Sources/Subs-Members.php
  65. 2 2
      Sources/Subs-MembersOnline.php
  66. 1 32
      Sources/Subs-Menu.php
  67. 1 1
      Sources/Subs-OpenID.php
  68. 2 1
      Sources/Subs-Package.php
  69. 9 6
      Sources/Subs-Post.php
  70. 2 2
      Sources/Subs-Sound.php
  71. 182 109
      Sources/Subs.php
  72. 1 1
      Sources/Themes.php
  73. 12 9
      Sources/Who.php
  74. 3 3
      Sources/Xml.php
  75. 0 74
      Themes/READ_ME.txt
  76. 117 180
      Themes/default/Admin.template.php
  77. 32 48
      Themes/default/BoardIndex.template.php
  78. 2 2
      Themes/default/Calendar.template.php
  79. 260 239
      Themes/default/Display.template.php
  80. 43 43
      Themes/default/Errors.template.php
  81. 70 5
      Themes/default/GenericControls.template.php
  82. 22 37
      Themes/default/GenericList.template.php
  83. 15 28
      Themes/default/GenericMenu.template.php
  84. 2 2
      Themes/default/Login.template.php
  85. 1 1
      Themes/default/ManageBans.template.php
  86. 7 18
      Themes/default/ManageBoards.template.php
  87. 15 15
      Themes/default/ManageMail.template.php
  88. 129 83
      Themes/default/ManageMaintenance.template.php
  89. 8 15
      Themes/default/ManageMembergroups.template.php
  90. 5 8
      Themes/default/ManageMembers.template.php
  91. 7 8
      Themes/default/ManageNews.template.php
  92. 5 6
      Themes/default/ManagePermissions.template.php
  93. 12 15
      Themes/default/ManageSearch.template.php
  94. 9 9
      Themes/default/Memberlist.template.php
  95. 37 31
      Themes/default/MessageIndex.template.php
  96. 19 23
      Themes/default/ModerationCenter.template.php
  97. 7 7
      Themes/default/Packages.template.php
  98. 126 6
      Themes/default/PersonalMessage.template.php
  99. 67 11
      Themes/default/Post.template.php
  100. 148 103
      Themes/default/Profile.template.php

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+Settings.php
+Settings_bak.php
+db_last_error.php
+cache/data*.php
+Packages/backups/*
+Packages/temp
+*.*~

+ 11 - 3
Sources/Admin.php

@@ -30,7 +30,7 @@ function AdminMain()
 	// Load the language and templates....
 	loadLanguage('Admin');
 	loadTemplate('Admin', 'admin');
-	loadJavascriptFile('scripts/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',
 			);
 

+ 16 - 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
@@ -87,6 +86,8 @@ class curl_fetch_web_data
 		// set the options and get it
 		$this->set_options();
 		$this->curl_request(str_replace(' ', '%20', $url));
+
+		return $this;
 	}
 
 	/**
@@ -119,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);
@@ -162,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'] : '');
@@ -191,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
@@ -221,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']))
 	{

+ 35 - 17
Sources/Display.php

@@ -143,6 +143,15 @@ function Display()
 		$_SESSION['last_read_topic'] = $topic;
 	}
 
+	$topic_parameters = array(
+		'current_member' => $user_info['id'],
+		'current_topic' => $topic,
+		'current_board' => $board,
+	);
+	$topic_selects = array();
+	$topic_tables = array();
+	call_integration_hook('integrate_display_topic', array(&$topic_selects, &$topic_tables, &$topic_parameters));
+
 	// @todo Why isn't this cached?
 	// @todo if we get id_board in this query and cache it, we can save a query on posting
 	// Get all the important topic info.
@@ -152,17 +161,15 @@ function Display()
 			t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts, t.id_redirect_topic,
 			' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from
 			' . (!empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board ? ', id_previous_board, id_previous_topic' : '') . '
+			' . (!empty($topic_selects) ? implode(',', $topic_selects) : '') . '
 		FROM {db_prefix}topics AS t
 			INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)' . ($user_info['is_guest'] ? '' : '
 			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_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})') . '
+			' . (!empty($topic_tables) ? implode("\n\t", $topic_tables) : '') . '
 		WHERE t.id_topic = {int:current_topic}
 		LIMIT 1',
-		array(
-			'current_member' => $user_info['id'],
-			'current_topic' => $topic,
-			'current_board' => $board,
-		)
+			$topic_parameters
 	);
 	if ($smcFunc['db_num_rows']($request) == 0)
 		fatal_lang_error('not_a_topic', false);
@@ -322,7 +329,7 @@ function Display()
 	}
 
 	// Create a previous next string if the selected theme has it as a selected option.
-	$context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : '';
+	$context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> - <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : '';
 
 	// Check if spellchecking is both enabled and actually working. (for quick reply.)
 	$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
@@ -480,7 +487,7 @@ function Display()
 			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
 
 		// And show it after the board's name.
-		$context['linktree'][count($context['linktree']) - 2]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')';
+		$context['linktree'][count($context['linktree']) - 2]['extra_after'] = '<span class="board_moderators"> (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
 	}
 
 	// Information about the current topic...
@@ -962,6 +969,14 @@ function Display()
 				$attachments[$row['id_msg']][] = $row;
 		}
 
+		$msg_parameters = array(
+			'message_list' => $messages,
+			'new_from' => $topicinfo['new_from'],
+		);
+		$msg_selects = array();
+		$msg_tables = array();
+		call_integration_hook('integrate_query_message', array(&$msg_selects, &$msg_tables, &$msg_parameters));
+
 		// What?  It's not like it *couldn't* be only guests in this topic...
 		if (!empty($posters))
 			loadMemberData($posters);
@@ -970,13 +985,12 @@ function Display()
 				id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, body,
 				smileys_enabled, poster_name, poster_email, approved,
 				id_msg_modified < {int:new_from} AS is_read
+				' . (!empty($msg_selects) ? implode(',', $msg_selects) : '') . '
 			FROM {db_prefix}messages
+				' . (!empty($msg_tables) ? implode("\n\t", $msg_tables) : '') . '
 			WHERE id_msg IN ({array_int:message_list})
 			ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'),
-			array(
-				'message_list' => $messages,
-				'new_from' => $topicinfo['new_from'],
-			)
+			$msg_parameters
 		);
 
 		// Go to the last message if the given time is beyond the time of the last message.
@@ -1066,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')
 	{
@@ -1254,7 +1274,7 @@ function prepareDisplayContext($reset = false)
 		'first_new' => isset($context['start_from']) && $context['start_from'] == $counter,
 		'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($message['id_member'], $context['user']['ignoreusers']),
 		'can_approve' => !$message['approved'] && $context['can_approve'],
-		'can_unapprove' => $message['approved'] && $context['can_approve'],
+		'can_unapprove' => !empty($modSettings['postmod_active']) && $context['can_approve'] && $message['approved'],
 		'can_modify' => (!$context['is_locked'] || allowedTo('moderate_board')) && (allowedTo('modify_any') || (allowedTo('modify_replies') && $context['user']['started']) || (allowedTo('modify_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || !$message['approved'] || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time()))),
 		'can_remove' => allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']) || (allowedTo('delete_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time())),
 		'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member'] == $user_info['id'] && !empty($user_info['id'])),
@@ -1263,6 +1283,8 @@ function prepareDisplayContext($reset = false)
 	// Is this user the message author?
 	$output['is_message_author'] = $message['id_member'] == $user_info['id'];
 
+	call_integration_hook('integrate_prepare_display_context', array(&$output, &$message));
+
 	if (empty($options['view_newest_first']))
 		$counter++;
 	else
@@ -1408,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;
 

+ 2 - 84
Sources/Groups.php

@@ -67,91 +67,8 @@ function Groups()
  */
 function GroupList()
 {
-	global $txt, $scripturl, $user_profile, $user_info, $context, $settings, $modSettings, $smcFunc, $sourcedir;
+	global $txt, $context, $sourcedir;
 
-	// Yep, find the groups...
-	$request = $smcFunc['db_query']('', '
-		SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden,
-			mg.icons, IFNULL(gm.id_member, 0) AS can_moderate
-		FROM {db_prefix}membergroups AS mg
-			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
-		WHERE mg.min_posts = {int:min_posts}
-			AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
-			AND mg.group_type != {int:is_protected}') . '
-		ORDER BY group_name',
-		array(
-			'current_member' => $user_info['id'],
-			'min_posts' => -1,
-			'mod_group' => 3,
-			'is_protected' => 1,
-		)
-	);
-	// This is where we store our groups.
-	$context['groups'] = array();
-	$group_ids = array();
-	$context['can_moderate'] = allowedTo('manage_membergroups');
-	while ($row = $smcFunc['db_fetch_assoc']($request))
-	{
-		// We only list the groups they can see.
-		if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
-			continue;
-
-		$row['icons'] = explode('#', $row['icons']);
-
-		$context['groups'][$row['id_group']] = array(
-			'id' => $row['id_group'],
-			'name' => $row['group_name'],
-			'desc' => $row['description'],
-			'color' => $row['online_color'],
-			'type' => $row['group_type'],
-			'num_members' => 0,
-			'icons' => !empty($row['icons'][0]) && !empty($row['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/' . $row['icons'][1] . '" alt="*" />', $row['icons'][0]) : '',
-		);
-
-		$context['can_moderate'] |= $row['can_moderate'];
-		$group_ids[] = $row['id_group'];
-	}
-	$smcFunc['db_free_result']($request);
-
-	// Count up the members separately...
-	if (!empty($group_ids))
-	{
-		$query = $smcFunc['db_query']('', '
-			SELECT id_group, COUNT(*) AS num_members
-			FROM {db_prefix}members
-			WHERE id_group IN ({array_int:group_list})
-			GROUP BY id_group',
-			array(
-				'group_list' => $group_ids,
-			)
-		);
-		while ($row = $smcFunc['db_fetch_assoc']($query))
-			$context['groups'][$row['id_group']]['num_members'] += $row['num_members'];
-		$smcFunc['db_free_result']($query);
-
-		// Only do additional groups if we can moderate...
-		if ($context['can_moderate'])
-		{
-			$query = $smcFunc['db_query']('', '
-				SELECT mg.id_group, COUNT(*) AS num_members
-				FROM {db_prefix}membergroups AS mg
-					INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen}
-						AND mem.id_group != mg.id_group
-						AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
-				WHERE mg.id_group IN ({array_int:group_list})
-				GROUP BY mg.id_group',
-				array(
-					'group_list' => $group_ids,
-					'blank_screen' => '',
-				)
-			);
-			while ($row = $smcFunc['db_fetch_assoc']($query))
-				$context['groups'][$row['id_group']]['num_members'] += $row['num_members'];
-			$smcFunc['db_free_result']($query);
-		}
-	}
-
-	$context['sub_template'] = 'group_index';
 	$context['page_title'] = $txt['viewing_groups'];
 
 	// Making a list is not hard with this beauty.
@@ -373,6 +290,7 @@ function list_getGroupCount()
  * It allows sorting on several columns.
  * It redirects to itself.
  * @uses ManageMembergroups template, group_members sub template.
+ * @todo: use createList
  */
 function MembergroupMembers()
 {

+ 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');
 

+ 145 - 43
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.
@@ -1517,6 +1517,14 @@ function loadTheme($id_theme = 0, $initialize = true)
 	// Some basic information...
 	if (!isset($context['html_headers']))
 		$context['html_headers'] = '';
+	if(!isset($context['javascript_files']))
+		$context['javascript_files'] = array();
+	if(!isset($context['css_files']))
+		$context['css_files'] = array();
+	if(!isset($context['javascript_inline']))
+		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
+	if(!isset($context['javascript_vars']))
+		$context['javascript_vars'] = array();
 
 	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
 	$context['session_var'] = $_SESSION['session_var'];
@@ -1569,9 +1577,6 @@ function loadTheme($id_theme = 0, $initialize = true)
 		'spellcheck',
 	);
 
-	$context['javascript_files'] = array();
-	$context['css_files'] = array();
-
 	// Wireless mode?  Load up the wireless stuff.
 	if (WIRELESS)
 	{
@@ -1640,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'] . '/';
@@ -1653,7 +1658,6 @@ function loadTheme($id_theme = 0, $initialize = true)
 	// Allow overriding the board wide time/number formats.
 	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
 		$user_info['time_format'] = $txt['time_format'];
-	$txt['number_format'] = empty($txt['number_format']) ? empty($modSettings['number_format']) ? '' : $modSettings['number_format'] : $txt['number_format'];
 
 	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'always')
 	{
@@ -1671,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'];
@@ -1682,6 +1682,39 @@ function loadTheme($id_theme = 0, $initialize = true)
 	// This allows us to change the way things look for the admin.
 	$context['admin_features'] = isset($modSettings['admin_features']) ? explode(',', $modSettings['admin_features']) : array('cd,cp,k,w,rg,ml,pm');
 
+	// Default JS variables for use in every theme
+	$context['javascript_vars'] = array(
+		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
+		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
+		'smf_images_url' => '"' . $settings['images_url'] . '"',
+		'smf_scripturl' => '"' . $scripturl . '"',
+		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
+		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
+		'smf_charset' => '"' . $context['character_set'] . '"',
+		'smf_session_id' => '"' . $context['session_id'] . '"',
+		'smf_session_var' => '"' . $context['session_var'] . '"',
+		'smf_member_id' => $context['user']['id'],
+		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
+		'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 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?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?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())
 	{
@@ -1701,15 +1734,14 @@ function loadTheme($id_theme = 0, $initialize = true)
 			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
 			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
 
-			$context['html_headers'] .= '
-	<script type="text/javascript">
+			addInlineJavascript('
 		function smfAutoTask()
 		{
 			var tempImage = new Image();
 			tempImage.src = smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '";
 		}
-		window.setTimeout("smfAutoTask();", 1);
-	</script>';
+		window.setTimeout("smfAutoTask();", 1);');
+
 		}
 	}
 
@@ -1755,19 +1787,7 @@ function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
 			$style_sheets = array($style_sheets);
 
 		foreach ($style_sheets as $sheet)
-		{
-			// Prevent the style sheet from being included twice.
-			if (strpos($context['html_headers'], 'id="' . $sheet . '_css"') !== false)
-				continue;
-
-			$sheet_path = file_exists($settings['theme_dir']. '/css/' . $sheet . '.css') ? 'theme_url' : (file_exists($settings['default_theme_dir']. '/css/' . $sheet . '.css') ? 'default_theme_url' : '');
-			if ($sheet_path)
-			{
-				$context['html_headers'] .= "\n\t" . '<link rel="stylesheet" type="text/css" id="' . $sheet . '_css" href="' . $settings[$sheet_path] . '/css/' . $sheet . '.css?alp21" />';
-				if ($db_show_debug === true)
-					$context['debug']['sheets'][] = $sheet . ' (' . basename($settings[$sheet_path]) . ')';
-			}
-		}
+			loadCSSFile($sheet . '.css', array(), $sheet);
 	}
 
 	// No template to load?
@@ -1864,38 +1884,122 @@ function loadSubTemplate($sub_template_name, $fatal = false)
 /**
  * Add a CSS file for output later
  *
+ * 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())
+function loadCSSFile($filename, $options = array(), $id = '')
 {
 	global $settings, $context;
 
-	if (strpos($filename, 'http://') === false || !empty($options['local']))
-		$filename = $settings['theme_url'] . '/' . $filename;
+	$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']))
+	{
+		// 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' && !$options['force_current'] && file_exists($settings['default_theme_dir'] . '/' . $filename))
+				$filename = $settings['default_theme_url'] . '/css/' . $filename;
+			else
+				$filename = false;
+		}
+		else
+			$filename = $settings[$theme . '_url'] . '/css/' . $filename;
+	}
 
-	$context['css_files'][$filename] = $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
- * 	- defer (true/false): define if the file should be load in head or before the closing <html> tag
+ * 	- 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())
+function loadJavascriptFile($filename, $options = array(), $id = '')
 {
 	global $settings, $context;
 
-	$theme = !empty($options['default_theme']) ? 'default_theme_url' : 'theme_url';
+	$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']))
-		$filename = $settings[$theme] . '/' . $filename;
+	{
+		// Are we validating it exists on disk?
+		if (!empty($options['validate']) && !file_exists($settings[$theme . '_dir'] . '/scripts/' . $filename))
+		{
+			// 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;
+		}
+		else
+			$filename = $settings[$theme . '_url'] . '/scripts/' . $filename;
+	}
+
+	// Add it to the array for use in the template
+	if (!empty($filename))
+		$context['javascript_files'][$id] = array('filename' => $filename, 'options' => $options);
+}
+
+/**
+ * Add a Javascript variable for output later (for feeding text strings and similar to JS)
+ * Cleaner and easier (for modders) than to use the function below.
+ *
+ * @param string $key
+ * @param string $value
+ * @param bool $escape = false, whether or not to escape the value
+ */
+function addJavascriptVar($key, $value, $escape = false)
+{
+	global $context;
+
+	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.
+ * - 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
+ */
+function addInlineJavascript($javascript, $defer = false)
+{
+	global $context;
 
-	$context['javascript_files'][$filename] = $options;
+	if (!empty($javascript))
+		$context['javascript_inline'][(!empty($defer) ? 'defer' : 'standard')][] = $javascript;
 }
 
 /**
@@ -2333,9 +2437,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);
@@ -2492,8 +2594,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

+ 19 - 5
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...
@@ -292,7 +297,7 @@ function Login2()
 		$other_passwords = array();
 
 		// None of the below cases will be used most of the time (because the salt is normally set.)
-		if ($user_settings['password_salt'] == '')
+		if (!empty($modSettings['enable_password_conversion']) && $user_settings['password_salt'] == '')
 		{
 			// YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all.
 			$other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2));
@@ -318,10 +323,10 @@ function Login2()
 			$other_passwords[] = md5(crypt($_POST['passwrd'], 'CRYPT_MD5'));
 		}
 		// The hash should be 40 if it's SHA-1, so we're safe with more here too.
-		elseif (strlen($user_settings['passwd']) == 32)
+		elseif (!empty($modSettings['enable_password_conversion']) && strlen($user_settings['passwd']) == 32)
 		{
 			// vBulletin 3 style hashing?  Let's welcome them with open arms \o/.
-			$other_passwords[] = md5(md5($_POST['passwrd']) . $user_settings['password_salt']);
+			$other_passwords[] = md5(md5($_POST['passwrd']) . stripslashes($user_settings['password_salt']));
 
 			// Hmm.. p'raps it's Invision 2 style?
 			$other_passwords[] = md5(md5($user_settings['password_salt']) . md5($_POST['passwrd']));
@@ -336,7 +341,8 @@ function Login2()
 			$other_passwords[] = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));
 
 			// BurningBoard3 style of hashing.
-			$other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_POST['passwrd'])));
+			if (!empty($modSettings['enable_password_conversion']))
+				$other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_POST['passwrd'])));
 
 			// Perhaps we converted to UTF-8 and have a valid password being hashed differently.
 			if ($context['character_set'] == 'utf8' && !empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8')
@@ -358,6 +364,9 @@ function Login2()
 			$other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));
 		}
 
+		// Allows mods to easily extend the $other_passwords array
+		call_integration_hook('integrate_other_passwords', array($other_passwords));
+
 		// Whichever encryption it was using, let's make it use SMF's now ;).
 		if (in_array($user_settings['passwd'], $other_passwords))
 		{
@@ -607,6 +616,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 +754,4 @@ function validatePasswordFlood($id_member, $password_flood_value = false, $was_c
 
 }
 
-?>
+?>

+ 3 - 6
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.
@@ -408,11 +408,8 @@ function BrowseFiles()
 					'value' => $txt['downloads'],
 				),
 				'data' => array(
-					'function' => create_function('$rowData','
-						global $txt;
-
-						return comma_format($rowData[\'downloads\']);
-					'),
+					'db' => 'downloads',
+					'comma_format' => true,
 				),
 				'sort' => array(
 					'default' => 'a.downloads',

+ 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.
  */

+ 7 - 25
Sources/ManageLanguages.php

@@ -1050,7 +1050,7 @@ function ModifyLanguage()
 			// Got a new entry?
 			if ($line[0] == '$' && !empty($multiline_cache))
 			{
-				preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches);
+				preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~ms', strtr($multiline_cache, array("\r" => '')), $matches);
 				if (!empty($matches[3]))
 				{
 					$entries[$matches[2]] = array(
@@ -1061,12 +1061,12 @@ function ModifyLanguage()
 					$multiline_cache = '';
 				}
 			}
-			$multiline_cache .= $line . "\n";
+			$multiline_cache .= $line;
 		}
 		// Last entry to add?
 		if ($multiline_cache)
 		{
-			preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches);
+			preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~ms', strtr($multiline_cache, array("\r" => '')), $matches);
 			if (!empty($matches[3]))
 				$entries[$matches[2]] = array(
 					'type' => $matches[1],
@@ -1087,7 +1087,7 @@ function ModifyLanguage()
 				continue;
 
 			// These are arrays that need breaking out.
-			$arrays = array('days', 'days_short', 'months', 'months_titles', 'months_short');
+			$arrays = array('days', 'days_short', 'months', 'months_titles', 'months_short', 'happy_birthday_author', 'karlbenson1_author', 'nite0859_author', 'zwaldowski_author', 'geezmo_author', 'karlbenson2_author');
 			if (in_array($entryKey, $arrays))
 			{
 				// Get off the first bits.
@@ -1193,7 +1193,7 @@ function ModifyLanguage()
 
 			// Save the actual changes.
 			$fp = fopen($current_file, 'w+');
-			fwrite($fp, $file_contents);
+			fwrite($fp, strtr($file_contents, array("\r" => '')));
 			fclose($fp);
 
 			$madeSave = true;
@@ -1316,7 +1316,7 @@ function cleanLangString($string, $to_display = true)
 		}
 
 		// Unhtml then rehtml the whole thing!
-		$new_string = htmlspecialchars(un_htmlspecialchars($new_string));
+		$new_string = $smcFunc['htmlspecialchars'](un_htmlspecialchars($new_string));
 	}
 	else
 	{
@@ -1326,26 +1326,8 @@ function cleanLangString($string, $to_display = true)
 		$in_html = false;
 		for ($i = 0; $i < strlen($string); $i++)
 		{
-			// Handle line breaks!
-			if ($string{$i} == "\n" || $string{$i} == "\t")
-			{
-				// Are we in a string? Is it the right type?
-				if ($in_string == 1)
-				{
-					// Change type!
-					$new_string .= '\' . "\\' . ($string{$i} == "\n" ? 'n' : 't');
-					$in_string = 2;
-				}
-				elseif ($in_string == 2)
-					$new_string .= '\\' . ($string{$i} == "\n" ? 'n' : 't');
-				// Otherwise start one off - joining if required.
-				else
-					$new_string .= ($new_string ? ' . ' : '') . '"\\' . ($string{$i} == "\n" ? 'n' : 't');
-
-				continue;
-			}
 			// We don't do parsed strings apart from for breaks.
-			elseif ($in_string == 2)
+			if ($in_string == 2)
 			{
 				$in_string = 0;
 				$new_string .= '"';

+ 203 - 13
Sources/ManageMaintenance.php

@@ -64,6 +64,7 @@ function ManageMaintenance()
 				'backup' => 'MaintainDownloadBackup',
 				'convertentities' => 'ConvertEntities',
 				'convertutf8' => 'ConvertUtf8',
+				'convertmsgbody' => 'ConvertMsgBody',
 			),
 		),
 		'members' => array(
@@ -81,6 +82,7 @@ function ManageMaintenance()
 			'activities' => array(
 				'massmove' => 'MaintainMassMoveTopics',
 				'pruneold' => 'MaintainRemoveOldPosts',
+				'olddrafts' => 'MaintainRemoveOldDrafts',
 			),
 		),
 		'destroy' => array(
@@ -132,6 +134,19 @@ function MaintainDatabase()
 	$context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']()), '<=');
 	$context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8';
 
+	if ($db_type == 'mysql')
+	{
+		db_extend('packages');
+
+		$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+		foreach ($colData as $column)
+			if ($column['name'] == 'body')
+				$body_type = $column['type'];
+
+		$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+		$context['convert_to_suggest'] = ($body_type != 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536);
+	}
+
 	// Check few things to give advices before make a backup
 	// If safe mod is enable the external tool is *always* the best (and probably the only) solution
 	$context['safe_mode_enable'] = @ini_get('safe_mode');
@@ -149,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
@@ -229,7 +244,7 @@ function MaintainMembers()
 		);
 	}
 	$smcFunc['db_free_result']($result);
-	
+
 	if (isset($_GET['done']) && $_GET['done'] == 'recountposts')
 		$context['maintenance_finished'] = $txt['maintain_recountposts'];
 }
@@ -733,6 +748,148 @@ function ConvertUtf8()
 	redirectexit('action=admin;area=maintain;done=convertutf8');
 }
 
+/**
+ * Convert the column "body" of the table {db_prefix}messages from TEXT to MEDIUMTEXT and vice versa.
+ * It requires the admin_forum permission.
+ * This is needed only for MySQL.
+ * During the convertion from MEDIUMTEXT to TEXT it check if any of the posts exceed the TEXT length and if so it aborts.
+ * This action is linked from the maintenance screen (if it's applicable).
+ * Accessed by ?action=admin;area=maintain;sa=database;activity=convertmsgbody.
+ *
+ * @uses the convert_msgbody sub template of the Admin template.
+ */
+function ConvertMsgBody()
+{
+	global $scripturl, $context, $txt, $language, $db_character_set, $db_type;
+	global $modSettings, $user_info, $sourcedir, $smcFunc, $db_prefix, $time_start;
+
+	// Show me your badge!
+	isAllowedTo('admin_forum');
+
+	if ($db_type != 'mysql')
+		return;
+
+	db_extend('packages');
+
+	$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+	foreach ($colData as $column)
+		if ($column['name'] == 'body')
+			$body_type = $column['type'];
+
+	$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+
+	if ($body_type == 'text' || ($body_type != 'text' && isset($_POST['do_conversion'])))
+	{
+		checkSession();
+		validateToken('admin-maint');
+
+		// Make it longer so we can do their limit.
+		if ($body_type == 'text')
+			$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext'));
+		// Shorten the column so we can have a bit (literally per record) less space occupied
+		else
+			$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text'));
+
+		$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+		foreach ($colData as $column)
+			if ($column['name'] == 'body')
+				$body_type = $column['type'];
+
+		$context['maintenance_finished'] = $txt[$context['convert_to'] . '_title'];
+		$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+		$context['convert_to_suggest'] = ($body_type != 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536);
+
+		return;
+		redirectexit('action=admin;area=maintain;sa=database');
+	}
+	elseif ($body_type != 'text' && (!isset($_POST['do_conversion']) || isset($_POST['cont'])))
+	{
+		checkSession();
+		if (empty($_REQUEST['start']))
+			validateToken('admin-maint');
+		else
+			validateToken('admin-convertMsg');
+
+		$context['page_title'] = $txt['not_done_title'];
+		$context['continue_post_data'] = '';
+		$context['continue_countdown'] = 3;
+		$context['sub_template'] = 'not_done';
+		$increment = 500;
+		$id_msg_exceeding = isset($_POST['id_msg_exceeding']) ? explode(',', $_POST['id_msg_exceeding']) : array();
+
+		$request = $smcFunc['db_query']('', '
+			SELECT COUNT(*) as count
+			FROM {db_prefix}messages',
+			array()
+		);
+		list($max_msgs) = $smcFunc['db_fetch_row']($request);
+		$smcFunc['db_free_result']($request);
+
+		// Try for as much time as possible.
+		@set_time_limit(600);
+
+		while ($_REQUEST['start'] < $max_msgs)
+		{
+			$request = $smcFunc['db_query']('', '
+				SELECT /*!40001 SQL_NO_CACHE */ id_msg
+				FROM {db_prefix}messages
+				WHERE id_msg BETWEEN {int:start} AND {int:start} + {int:increment}
+					AND LENGTH(body) > 65535',
+				array(
+					'start' => $_REQUEST['start'],
+					'increment' => $increment - 1,
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+				$id_msg_exceeding[] = $row['id_msg'];
+			$smcFunc['db_free_result']($request);
+
+			$_REQUEST['start'] += $increment;
+
+			if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3)
+			{
+				createToken('admin-convertMsg');
+				$context['continue_post_data'] = '
+					<input type="hidden" name="' . $context['admin-convertMsg_token_var'] . '" value="' . $context['admin-convertMsg_token'] . '" />
+					<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
+					<input type="hidden" name="id_msg_exceeding" value="' . implode(',', $id_msg_exceeding) . '" />';
+
+				$context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertmsgbody;start=' . $_REQUEST['start'];
+				$context['continue_percent'] = round(100 * $_REQUEST['start'] / $max_msgs);
+
+				return;
+			}
+		}
+		createToken('admin-maint');
+		$context['page_title'] = $txt[$context['convert_to'] . '_title'];
+		$context['sub_template'] = 'convert_msgbody';
+
+		if (!empty($id_msg_exceeding))
+		{
+			if (count($id_msg_exceeding) > 100)
+			{
+				$query_msg = array_slice($id_msg_exceeding, 0, 100);
+				$context['exceeding_messages_morethan'] = sprintf($txt['exceeding_messages_morethan'], count($id_msg_exceeding));
+			}
+			else
+				$query_msg = $id_msg_exceeding;
+
+			$context['exceeding_messages'] = array();
+			$request = $smcFunc['db_query']('', '
+				SELECT id_msg, id_topic, subject
+				FROM {db_prefix}messages
+				WHERE id_msg IN ({array_int:messages})',
+				array(
+					'messages' => $query_msg,
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+				$context['exceeding_messages'][] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>';
+			$smcFunc['db_free_result']($request);
+		}
+	}
+}
+
 /**
  * Converts HTML-entities to their UTF-8 character equivalents.
  * This requires the admin_forum permission.
@@ -1035,7 +1192,7 @@ function AdminBoardRecount()
 
 	isAllowedTo('admin_forum');
 	checkSession('request');
-	
+
 	// validate the request or the loop
 	if (!isset($_REQUEST['step']))
 		validateToken('admin-maint');
@@ -1651,7 +1808,7 @@ function MaintainPurgeInactiveMembers()
 			$where_vars['is_activated'] = 0;
 		}
 		else
-			$where = 'mem.last_login < {int:time_limit}';
+			$where = 'mem.last_login < {int:time_limit} AND (mem.last_login != 0 OR mem.date_registered < {int:time_limit})';
 
 		// Need to get *all* groups then work out which (if any) we avoid.
 		$request = $smcFunc['db_query']('', '
@@ -1725,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.
  *
@@ -1836,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
@@ -1861,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'];
@@ -1873,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)
@@ -1931,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'] . '" />';
 
@@ -1939,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']('', '
@@ -1947,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}
@@ -1961,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']('', '
@@ -1969,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,
@@ -1991,7 +2181,7 @@ function MaintainRecountPosts()
 		}
 		$smcFunc['db_free_result']($request);
 	}
-	
+
 	// all done
 	unset($_SESSION['total_members']);
 	$context['maintenance_finished'] = $txt['maintain_recountposts'];

+ 2 - 2
Sources/ManageMembergroups.php

@@ -115,9 +115,9 @@ function MembergroupIndex()
 
 						// Add a help option for moderator and administrator.
 						if ($rowData[\'id_group\'] == 1)
-							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_administrator" onclick="return reqWin(this.href);">?</a>)\', $scripturl);
+							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_administrator" onclick="return reqOverlayDiv(this.href);">?</a>)\', $scripturl);
 						elseif ($rowData[\'id_group\'] == 3)
-							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_moderator" onclick="return reqWin(this.href);">?</a>)\', $scripturl);
+							$group_name .= sprintf(\' (<a href="%1$s?action=helpadmin;help=membergroup_moderator" onclick="return reqOverlayDiv(this.href);">?</a>)\', $scripturl);
 
 						return $group_name;
 					'),

+ 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.

+ 4 - 3
Sources/ManageNews.php

@@ -151,7 +151,7 @@ function EditNews()
 					'function' => create_function('$news', '
 
 						if (is_numeric($news[\'id\']))
-							return \'<textarea id="data_\' . $news[\'id\'] . \'" rows="3" cols="65" name="news[]" style="\' . (isBrowser(\'is_ie8\') ? \'width: 635px; max-width: 85%; min-width: 85%\' : \'width 100%;margin 0 5em\') . \';">\' . $news[\'unparsed\'] . \'</textarea>
+							return \'<textarea id="data_\' . $news[\'id\'] . \'" rows="3" cols="50" name="news[]" style="\' . (isBrowser(\'is_ie8\') ? \'width: 635px; max-width: 85%; min-width: 85%\' : \'width 100%;margin 0 5em\') . \';">\' . $news[\'unparsed\'] . \'</textarea>
 							<br />
 							<div class="floatleft" id="preview_\' . $news[\'id\'] . \'"></div>\';
 						else
@@ -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];
@@ -688,6 +688,7 @@ function SendMailing($clean_only = false)
 	}
 
 	// How many to send at once? Quantity depends on whether we are queueing or not.
+	// @todo Might need an interface? (used in Post.php too with different limits)
 	$num_at_once = empty($modSettings['mail_queue']) ? 60 : 1000;
 
 	// If by PM's I suggest we half the above number.

+ 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');

+ 7 - 28
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',
@@ -176,7 +176,7 @@ function SetCensor()
  */
 function ModifyPostSettings($return_config = false)
 {
-	global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_prefix;
+	global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_prefix, $db_type;
 
 	// All the settings...
 	$config_vars = array(
@@ -218,8 +218,8 @@ function ModifyPostSettings($return_config = false)
 	{
 		checkSession();
 
-		// If we're changing the message length let's check the column is big enough.
-		if (!empty($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'])
+		// If we're changing the message length (and we are using MySQL) let's check the column is big enough.
+		if (isset($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'] && $db_type == 'mysql')
 		{
 			db_extend('packages');
 
@@ -228,32 +228,11 @@ function ModifyPostSettings($return_config = false)
 				if ($column['name'] == 'body')
 					$body_type = $column['type'];
 
-			$indData = $smcFunc['db_list_indexes']('{db_prefix}messages', true);
-			foreach ($indData as $index)
-				foreach ($index['columns'] as $column)
-					if ($column == 'body' && $index['type'] == 'fulltext')
-						$fulltext = true;
-
 			if (isset($body_type) && ($_POST['max_messageLength'] > 65535 || $_POST['max_messageLength'] == 0) && $body_type == 'text')
-			{
-				// @todo Show an error message?!
-				// MySQL only likes fulltext indexes on text columns... for now?
-				if (!empty($fulltext))
-					$_POST['max_messageLength'] = 65535;
-				else
-				{
-					// Make it longer so we can do their limit.
-					$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext'));
-				}
-			}
-			elseif (isset($body_type) && $_POST['max_messageLength'] <= 65535 && $body_type != 'text')
-			{
-				// @TODO shouldn't we warn that reducing the size of the column something could be lost?
-				// Shorten the column so we can have the benefit of fulltext searching again!
-				$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text'));
-			}
+				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 - 16
Sources/ManageSearch.php

@@ -235,20 +235,6 @@ function EditSearchMethod()
 				$context['fulltext_index'] = array_unique($context['fulltext_index']);
 		}
 
-		$request = $smcFunc['db_query']('', '
-			SHOW COLUMNS
-			FROM {db_prefix}messages',
-			array(
-			)
-		);
-		if ($request !== false)
-		{
-			while ($row = $smcFunc['db_fetch_assoc']($request))
-				if ($row['Field'] == 'body' && $row['Type'] == 'mediumtext')
-					$context['cannot_create_fulltext'] = true;
-			$smcFunc['db_free_result']($request);
-		}
-
 		if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0)
 			$request = $smcFunc['db_query']('', '
 				SHOW TABLE STATUS
@@ -556,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();
@@ -736,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

+ 126 - 57
Sources/ManageSettings.php

@@ -225,6 +225,38 @@ 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\',
+					)
+				);
+			'),
+		),
+		// ih = Integration Hooks Handling.
+		'ih' => array(
+			'url' => 'action=admin;area=modsettings;sa=hooks',
+			'settings' => array(
+				'handlinghooks_enabled' => 1,
+			),
+		),
 		// k = karma.
 		'k' => array(
 			'url' => 'action=admin;area=featuresettings;sa=karma',
@@ -478,7 +510,6 @@ function ModifyBasicSettings($return_config = false)
 		'',
 			// Number formatting, timezones.
 			array('text', 'time_format'),
-			array('select', 'number_format', array('1234.00' => '1234.00', '1,234.00' => '1,234.00', '1.234,00' => '1.234,00', '1 234,00' => '1 234,00', '1234,00' => '1234,00')),
 			array('float', 'time_offset', 'subtext' => $txt['setting_time_offset_note'], 6, 'postinput' => $txt['hours']),
 			'default_timezone' => array('select', 'default_timezone', array()),
 		'',
@@ -492,7 +523,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.
@@ -562,6 +593,7 @@ function ModifyGeneralSecuritySettings($return_config = false)
 		'',
 			// Password strength.
 			array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
+			array('check', 'enable_password_conversion'),
 		'',
 			// Reporting of personal messages?
 			array('check', 'enableReportPM'),
@@ -1737,7 +1769,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']);
 
@@ -2168,34 +2200,41 @@ function list_integration_hooks()
 	global $sourcedir, $scripturl, $context, $txt, $modSettings, $settings;
 
 	$context['filter'] = '';
-	$presentHooks = get_integration_hooks();
-	if (isset($_GET['filter']) && in_array($_GET['filter'], array_keys($presentHooks)))
+	$currentHooks = get_integration_hooks();
+	if (isset($_GET['filter']) && in_array($_GET['filter'], array_keys($currentHooks)))
 		$context['filter'] = ';filter=' . $_GET['filter'];
 
-	if (!empty($_REQUEST['do']) && isset($_REQUEST['hook']) && isset($_REQUEST['function']))
+	if (!empty($modSettings['handlinghooks_enabled']))
 	{
-		checkSession('request');
-		validateToken('admin-hook', 'request');
-
-		if ($_REQUEST['do'] == 'remove')
-			remove_integration_function($_REQUEST['hook'], $_REQUEST['function']);
-		elseif ($_REQUEST['do'] == 'disable')
+		if (!empty($_REQUEST['do']) && isset($_REQUEST['hook']) && isset($_REQUEST['function']))
 		{
-			remove_integration_function($_REQUEST['hook'], $_REQUEST['function']);
-			// It's a hack I know...but I'm way too lazy!!!
-			add_integration_function($_REQUEST['hook'], $_REQUEST['function'] . ']');
-		}
-		elseif ($_REQUEST['do'] == 'enable')
-		{
-			remove_integration_function($_REQUEST['hook'], $_REQUEST['function'] . ']');
-			// It's a hack I know...but I'm way too lazy!!!
-			add_integration_function($_REQUEST['hook'], $_REQUEST['function']);
-		}
+			checkSession('request');
+			validateToken('admin-hook', 'request');
 
-		redirectexit('action=admin;area=modsettings;sa=hooks' . $context['filter']);
-	}
+			if ($_REQUEST['do'] == 'remove')
+				remove_integration_function($_REQUEST['hook'], urldecode($_REQUEST['function']));
+			else
+			{
+				if ($_REQUEST['do'] == 'disable')
+				{
+					// It's a hack I know...but I'm way too lazy!!!
+					$function_remove = $_REQUEST['function'];
+					$function_add = $_REQUEST['function'] . ']';
+				}
+				else
+				{
+					$function_remove = $_REQUEST['function'] . ']';
+					$function_add = $_REQUEST['function'];
+				}
+				$file = !empty($_REQUEST['includedfile']) ? urldecode($_REQUEST['includedfile']) : '';
 
-	createToken('admin-hook', 'request');
+				remove_integration_function($_REQUEST['hook'], $function_remove, $file);
+				add_integration_function($_REQUEST['hook'], $function_add, $file);
+
+				redirectexit('action=admin;area=modsettings;sa=hooks' . $context['filter']);
+			}
+		}
+	}
 
 	$list_options = array(
 		'id' => 'list_integration_hooks',
@@ -2228,7 +2267,14 @@ function list_integration_hooks()
 					'value' => $txt['hooks_field_function_name'],
 				),
 				'data' => array(
-					'db' => 'function_name',
+					'function' => create_function('$data', '
+						global $txt;
+
+						if (!empty($data[\'included_file\']))
+							return $txt[\'hooks_field_function\'] . \': \' . $data[\'real_function\'] . \'<br />\' . $txt[\'hooks_field_included_file\'] . \': \' . $data[\'included_file\'];
+						else
+							return $data[\'real_function\'];
+					'),
 				),
 				'sort' =>  array(
 					'default' => 'function_name',
@@ -2259,7 +2305,7 @@ function list_integration_hooks()
 						$change_status = array(\'before\' => \'\', \'after\' => \'\');
 						if ($data[\'can_be_disabled\'] && $data[\'status\'] != \'deny\')
 						{
-							$change_status[\'before\'] = \'<a href="\' . $scripturl . \'?action=admin;area=modsettings;sa=hooks;do=\' . ($data[\'enabled\'] ? \'disable\' : \'enable\') . \';hook=\' . $data[\'hook_name\'] . \';function=\' . $data[\'function_name\'] . $context[\'filter\'] . \';\' . $context[\'admin-hook_token_var\'] . \'=\' . $context[\'admin-hook_token\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\'] . \'" onclick="return confirm(\' . javaScriptEscape($txt[\'quickmod_confirm\']) . \');">\';
+							$change_status[\'before\'] = \'<a href="\' . $scripturl . \'?action=admin;area=modsettings;sa=hooks;do=\' . ($data[\'enabled\'] ? \'disable\' : \'enable\') . \';hook=\' . $data[\'hook_name\'] . \';function=\' . $data[\'real_function\'] . (!empty($data[\'included_file\']) ? \';includedfile=\' . urlencode($data[\'included_file\']) : \'\') . $context[\'filter\'] . \';\' . $context[\'admin-hook_token_var\'] . \'=\' . $context[\'admin-hook_token\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\'] . \'" onclick="return confirm(\' . javaScriptEscape($txt[\'quickmod_confirm\']) . \');">\';
 							$change_status[\'after\'] = \'</a>\';
 						}
 						return $change_status[\'before\'] . \'<img src="\' . $settings[\'images_url\'] . \'/admin/post_moderation_\' . $data[\'status\'] . \'.png" alt="\' . $data[\'img_text\'] . \'" title="\' . $data[\'img_text\'] . \'" />\' . $change_status[\'after\'];
@@ -2271,28 +2317,6 @@ function list_integration_hooks()
 					'reverse' => 'status DESC',
 				),
 			),
-			'check' => array(
-				'header' => array(
-					'value' => $txt['hooks_button_remove'],
-					'style' => 'width:3%',
-				),
-				'data' => array(
-					'function' => create_function('$data', '
-						global $txt, $settings, $scripturl, $context;
-
-						if (!$data[\'hook_exists\'])
-							return \'
-							<a href="\' . $scripturl . \'?action=admin;area=modsettings;sa=hooks;do=remove;hook=\' . $data[\'hook_name\'] . \';function=\' . $data[\'function_name\'] . $context[\'filter\'] . \';\' . $context[\'admin-hook_token_var\'] . \'=\' . $context[\'admin-hook_token\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\'] . \'" onclick="return confirm(\' . javaScriptEscape($txt[\'quickmod_confirm\']) . \');">
-								<img src="\' . $settings[\'images_url\'] . \'/icons/quick_remove.png" alt="\' . $txt[\'hooks_button_remove\'] . \'" title="\' . $txt[\'hooks_button_remove\'] . \'" />
-							</a>\';
-					'),
-					'class' => 'centertext',
-				),
-			),
-		),
-		'form' => array(
-			'href' => $scripturl . '?action=admin;area=modsettings;sa=hooks' . $context['filter'] . ';' . $context['session_var'] . '=' . $context['session_id'],
-			'name' => 'list_integration_hooks',
 		),
 		'additional_rows' => array(
 			array(
@@ -2308,6 +2332,35 @@ function list_integration_hooks()
 		),
 	);
 
+	if (!empty($modSettings['handlinghooks_enabled']))
+	{
+		createToken('admin-hook', 'request');
+
+		$list_options['columns']['remove'] = array(
+			'header' => array(
+				'value' => $txt['hooks_button_remove'],
+				'style' => 'width:3%',
+			),
+			'data' => array(
+				'function' => create_function('$data', '
+					global $txt, $settings, $scripturl, $context;
+
+					if (!$data[\'hook_exists\'])
+						return \'
+						<a href="\' . $scripturl . \'?action=admin;area=modsettings;sa=hooks;do=remove;hook=\' . $data[\'hook_name\'] . \';function=\' . urlencode($data[\'function_name\']) . $context[\'filter\'] . \';\' . $context[\'admin-hook_token_var\'] . \'=\' . $context[\'admin-hook_token\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\'] . \'" onclick="return confirm(\' . javaScriptEscape($txt[\'quickmod_confirm\']) . \');">
+							<img src="\' . $settings[\'images_url\'] . \'/icons/quick_remove.png" alt="\' . $txt[\'hooks_button_remove\'] . \'" title="\' . $txt[\'hooks_button_remove\'] . \'" />
+						</a>\';
+				'),
+				'class' => 'centertext',
+			),
+		);
+		$list_options['form'] = array(
+			'href' => $scripturl . '?action=admin;area=modsettings;sa=hooks' . $context['filter'] . ';' . $context['session_var'] . '=' . $context['session_id'],
+			'name' => 'list_integration_hooks',
+		);
+	}
+
+
 	require_once($sourcedir . '/Subs-List.php');
 	createList($list_options);
 
@@ -2340,7 +2393,7 @@ function get_files_recursive($dir_path)
 
 function get_integration_hooks_data($start, $per_page, $sort)
 {
-	global $boarddir, $sourcedir, $settings, $txt, $context, $scripturl;
+	global $boarddir, $sourcedir, $settings, $txt, $context, $scripturl, $modSettings;
 
 	$hooks = $temp_hooks = get_integration_hooks();
 	$hooks_data = $temp_data = $hook_status = array();
@@ -2360,7 +2413,15 @@ function get_integration_hooks_data($start, $per_page, $sort)
 				{
 					foreach ($functions as $function_o)
 					{
-						$function = str_replace(']', '', $function_o);
+						$hook_name = str_replace(']', '', $function_o);
+						if (strpos($hook_name, '::') !== false)
+						{
+							$function = explode('::', $hook_name);
+							$function = $function[1];
+						}
+						$function = explode(':', $function);
+						$function = $function[0];
+
 						if (substr($hook, -8) === '_include')
 						{
 							$hook_status[$hook][$function]['exists'] = file_exists(strtr(trim($function), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir'])));
@@ -2368,11 +2429,10 @@ function get_integration_hooks_data($start, $per_page, $sort)
 							$temp_data['include'][basename($function)] = array('hook' => $hook, 'function' => $function);
 							unset($temp_hooks[$hook][$function_o]);
 						}
-						// @TODO replace with a preg_match? (the difference is the space before the open parentheses
-						elseif (strpos($fc, 'function ' . trim($function) . '(') !== false || strpos($fc, 'function ' . trim($function) . ' (') !== false)
+						elseif (strpos(str_replace(' (', '(', $fc), 'function ' . trim($function) . '(') !== false)
 						{
-							$hook_status[$hook][$function]['exists'] = true;
-							$hook_status[$hook][$function]['in_file'] = $file['name'];
+							$hook_status[$hook][$hook_name]['exists'] = true;
+							$hook_status[$hook][$hook_name]['in_file'] = $file['name'];
 							// I want to remember all the functions called within this file (to check later if they are enabled or disabled and decide if the integrare_*_include of that file can be disabled too)
 							$temp_data['function'][$file['name']][] = $function_o;
 							unset($temp_hooks[$hook][$function_o]);
@@ -2440,18 +2500,27 @@ function get_integration_hooks_data($start, $per_page, $sort)
 				$function = str_replace(']', '', $function);
 				$hook_exists = !empty($hook_status[$hook][$function]['exists']);
 				$file_name = isset($hook_status[$hook][$function]['in_file']) ? $hook_status[$hook][$function]['in_file'] : ((substr($hook, -8) === '_include') ? 'zzzzzzzzz' : 'zzzzzzzza');
-				$status = $hook_exists ? ($enabled ? 'a' : 'b') : 'c';
 				$sort[] = $$sort_options[0];
+
+				if (strpos($function, '::') !== false)
+				{
+					$function = explode('::', $function);
+					$function = $function[1];
+				}
+				$exploded = explode(':', $function);
+
 				$temp_data[] = array(
 					'id' => 'hookid_' . $id++,
 					'hook_name' => $hook,
 					'function_name' => $function,
+					'real_function' => $exploded[0],
+					'included_file' => isset($exploded[1]) ? strtr(trim($exploded[1]), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir'])) : '',
 					'file_name' => (isset($hook_status[$hook][$function]['in_file']) ? $hook_status[$hook][$function]['in_file'] : ''),
 					'hook_exists' => $hook_exists,
 					'status' => $hook_exists ? ($enabled ? 'allow' : 'moderate') : 'deny',
 					'img_text' => $txt['hooks_' . ($hook_exists ? ($enabled ? 'active' : 'disabled') : 'missing')],
 					'enabled' => $enabled,
-					'can_be_disabled' => !isset($hook_status[$hook][$function]['enabled']),
+					'can_be_disabled' => !empty($modSettings['handlinghooks_enabled']) && !isset($hook_status[$hook][$function]['enabled']),
 				);
 			}
 		}

+ 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;

+ 32 - 5
Sources/MessageIndex.php

@@ -113,7 +113,7 @@ function MessageIndex()
 		foreach ($board_info['moderators'] as $mod)
 			$context['link_moderators'][] ='<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
 
-		$context['linktree'][count($context['linktree']) - 1]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')';
+		$context['linktree'][count($context['linktree']) - 1]['extra_after'] = '<span class="board_moderators"> (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
 	}
 
 	// Mark current and parent boards as seen.
@@ -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']]);
 		}
@@ -572,7 +599,7 @@ function MessageIndex()
 	if (!empty($options['display_quick_mod']) && !empty($context['topics']))
 	{
 		$context['can_markread'] = $context['user']['is_logged'];
-		$context['can_lopck'] = allowedTo('lock_any');
+		$context['can_lock'] = allowedTo('lock_any');
 		$context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);
 		$context['can_move'] = allowedTo('move_any');
 		$context['can_remove'] = allowedTo('remove_any');

+ 3 - 15
Sources/ModerationCenter.php

@@ -154,18 +154,6 @@ function ModerationMain($dont_call = false)
 		),
 	);
 
-	// Any files to include for moderation?
-	if (!empty($modSettings['integrate_moderate_include']))
-	{
-		$moderate_includes = explode(',', $modSettings['integrate_moderate_include']);
-		foreach ($moderate_includes as $include)
-		{
-			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
-			if (file_exists($include))
-				require_once($include);
-		}
-	}
-
 	// Let them modify admin areas easily.
 	call_integration_hook('integrate_moderate_areas', array(&$moderation_areas));
 
@@ -225,7 +213,7 @@ function ModerationHome()
 	global $txt, $context, $scripturl, $modSettings, $user_info, $user_settings;
 
 	loadTemplate('ModerationCenter');
-	loadJavascriptFile('scripts/admin.js?alp21', array('default_theme' => true));
+	loadJavascriptFile('admin.js?alp21', array('default_theme' => true));
 
 	$context['page_title'] = $txt['moderation_center'];
 	$context['sub_template'] = 'moderation_center';
@@ -2164,7 +2152,7 @@ function ModEndSession()
 		if (strpos($key, '-mod') !== false)
 			unset($_SESSION['token'][$key]);
 
-	redirectexit('?action=moderate');
+	redirectexit('action=moderate');
 }
 
-?>
+?>

+ 1 - 1
Sources/Modlog.php

@@ -156,7 +156,7 @@ function ViewModlog()
 	// This is all the information required for a watched user listing.
 	$listOptions = array(
 		'id' => 'moderation_log_list',
-		'title' => '<a href="' . $scripturl . '?action=helpadmin;help=' . ($context['log_type'] == 3 ? 'adminlog' : 'modlog') . '" onclick="return reqWin(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a> ' . $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log'],
+		'title' => '<a href="' . $scripturl . '?action=helpadmin;help=' . ($context['log_type'] == 3 ? 'adminlog' : 'modlog') . '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a> ' . $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log'],
 		'width' => '100%',
 		'items_per_page' => $context['displaypage'],
 		'no_items_label' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin_log_' : '') . 'no_entries_found'],

+ 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';
 

+ 21 - 10
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;
 
@@ -1011,9 +1011,9 @@ function PackageInstall()
 			elseif ($action['type'] == 'hook' && isset($action['hook'], $action['function']))
 			{
 				if ($action['reverse'])
-					remove_integration_function($action['hook'], $action['function']);
+					remove_integration_function($action['hook'], $action['function'], $action['include_file']);
 				else
-					add_integration_function($action['hook'], $action['function']);
+					add_integration_function($action['hook'], $action['function'], $action['include_file']);
 			}
 			// Only do the database changes on uninstall if requested.
 			elseif ($action['type'] == 'database' && !empty($action['filename']) && (!$context['uninstalling'] || !empty($_POST['do_db_changes'])))
@@ -1571,7 +1571,13 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 	if ($dir = @opendir($boarddir . '/Packages'))
 	{
 		$dirs = array();
-		$sort_id = 1;
+		$sort_id = array(
+			'mod' => 1,
+			'modification' => 1,
+			'avatar' => 1,
+			'language' => 1,
+			'unknown' => 1,
+		);
 		while ($package = readdir($dir))
 		{
 			if ($package == '.' || $package == '..' || $package == 'temp' || (!(is_dir($boarddir . '/Packages/' . $package) && file_exists($boarddir . '/Packages/' . $package . '/package-info.xml')) && substr(strtolower($package), -7) != '.tar.gz' && substr(strtolower($package), -4) != '.tgz' && substr(strtolower($package), -4) != '.zip'))
@@ -1613,7 +1619,7 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 			{
 				$packageInfo['installed_id'] = isset($installed_mods[$packageInfo['id']]) ? $installed_mods[$packageInfo['id']]['id'] : 0;
 
-				$packageInfo['sort_id'] = $sort_id++;
+				$packageInfo['sort_id'] = $sort_id[$packageInfo['type']];
 				$packageInfo['is_installed'] = isset($installed_mods[$packageInfo['id']]);
 				$packageInfo['is_current'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] == $packageInfo['version']);
 				$packageInfo['is_newer'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] > $packageInfo['version']);
@@ -1643,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)
 						{
@@ -1683,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)
 						{
@@ -1701,24 +1707,29 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 				// Modification.
 				if ($packageInfo['type'] == 'modification' || $packageInfo['type'] == 'mod')
 				{
+					$sort_id['modification']++;
+					$sort_id['mod']++;
 					$packages['modification'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_modification'][md5($package)] = $packageInfo;
 				}
 				// Avatar package.
 				elseif ($packageInfo['type'] == 'avatar')
 				{
+					$sort_id[$packageInfo['type']]++;
 					$packages['avatar'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_avatar'][md5($package)] = $packageInfo;
 				}
 				// Language package.
 				elseif ($packageInfo['type'] == 'language')
 				{
+					$sort_id[$packageInfo['type']]++;
 					$packages['language'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_language'][md5($package)] = $packageInfo;
 				}
 				// Other stuff.
 				else
 				{
+					$sort_id['unknown']++;
 					$packages['unknown'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_unknown'][md5($package)] = $packageInfo;
 				}

+ 67 - 13
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(
@@ -332,22 +339,25 @@ function messageIndexBar($area)
 	$menuOptions = array(
 		'current_area' => $area,
 		'disable_url_session_check' => true,
-		'toggle_url' => $current_page . ';togglebar',
-		'toggle_redirect_url' => $current_page,
 	);
 
 	// Actually create the menu!
 	$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 +853,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 +861,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 +1781,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 +1828,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 +1966,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 +1984,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 +2210,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 +2253,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));
 

+ 87 - 46
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.
@@ -48,18 +49,6 @@ function Post($post_errors = array())
 
 	require_once($sourcedir . '/Subs-Post.php');
 
-	// Any files to include for post?
-	if (!isset($post_includes) && !empty($modSettings['integrate_post_include']))
-	{
-		$post_includes = explode(',', $modSettings['integrate_post_include']);
-		foreach ($post_includes as $include)
-		{
-			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
-			if (file_exists($include))
-				require_once($include);
-		}
-	}
-
 	if (isset($_REQUEST['xml']))
 	{
 		$context['sub_template'] = 'post';
@@ -142,6 +131,10 @@ function Post($post_errors = array())
 
 		$context['notify'] = !empty($context['notify']);
 		$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
+
+		// Check whether this is a really old post being bumped...
+		if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
+			$post_errors[] = array('old_topic', array($modSettings['oldTopicDays']));
 	}
 	else
 	{
@@ -175,7 +168,7 @@ function Post($post_errors = array())
 	$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
 
 	// Generally don't show the approval box... (Assume we want things approved)
-	$context['show_approval'] = false;
+	$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
 
 	// An array to hold all the attachments for this topic.
 	$context['current_attachments'] = array();
@@ -353,10 +346,6 @@ function Post($post_errors = array())
 		}
 	}
 
-	// Check whether this is a really old post being bumped...
-	if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
-		$post_errors[] = array('old_topic', array($modSettings['oldTopicDays']));
-
 	// Get a response prefix (like 'Re:') in the default forum language.
 	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
 	{
@@ -464,7 +453,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;
@@ -509,16 +498,18 @@ function Post($post_errors = array())
 					m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
 					IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach,
 					a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
-					m.poster_time
+					m.poster_time, log.id_action
 				FROM {db_prefix}messages AS m
 					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
 					LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
+					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
 				WHERE m.id_msg = {int:id_msg}
 					AND m.id_topic = {int:current_topic}',
 				array(
 					'current_topic' => $topic,
 					'attachment_type' => 0,
 					'id_msg' => $_REQUEST['msg'],
+					'announce_action' => 'announce_topic',
 				)
 			);
 			// The message they were trying to edit was most likely deleted.
@@ -575,6 +566,12 @@ function Post($post_errors = array())
 				$smcFunc['db_free_result']($request);
 			}
 
+			if ($context['can_announce'] && !empty($row['id_action']))
+			{
+				loadLanguage('Errors');
+				$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
+			}
+
 			// Allow moderators to change names....
 			if (allowedTo('moderate_forum') && !empty($topic))
 			{
@@ -615,22 +612,23 @@ function Post($post_errors = array())
 				m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
 				IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach,
 				a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
-				m.poster_time
+				m.poster_time, log.id_action
 			FROM {db_prefix}messages AS m
 				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
 				LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
+					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
 			WHERE m.id_msg = {int:id_msg}
 				AND m.id_topic = {int:current_topic}',
 			array(
 				'current_topic' => $topic,
 				'attachment_type' => 0,
 				'id_msg' => $_REQUEST['msg'],
+				'announce_action' => 'announce_topic',
 			)
 		);
 		// 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);
@@ -653,6 +651,12 @@ function Post($post_errors = array())
 		else
 			isAllowedTo('modify_any');
 
+		if ($context['can_announce'] && !empty($row['id_action']))
+		{
+			loadLanguage('Errors');
+			$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
+		}
+
 		// When was it last modified?
 		if (!empty($row['modified_time']))
 			$context['last_modified'] = timeformat($row['modified_time']);
@@ -1035,6 +1039,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');
 
@@ -1143,7 +1158,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.
@@ -1168,18 +1184,6 @@ function Post2()
 	// No need!
 	$context['robot_no_index'] = true;
 
-	// Any files to include for post?
-	if (!empty($modSettings['integrate_post_include']))
-	{
-		$post_includes = explode(',', $modSettings['integrate_post_include']);
-		foreach ($post_includes as $include)
-		{
-			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
-			if (file_exists($include))
-				require_once($include);
-		}
-	}
-
 	// Previewing? Go back to start.
 	if (isset($_REQUEST['preview']))
 		return Post();
@@ -1209,6 +1213,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']))
 	{
@@ -1508,6 +1516,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'])
 		{
@@ -1546,6 +1561,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?
@@ -1622,6 +1644,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?
@@ -1636,6 +1665,13 @@ function Post2()
 		}
 	}
 
+	// Incase we want to override
+	if (allowedTo('approve_posts'))
+	{
+		$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
+		$approve_has_changed = isset($row['approved']) ? $row['approved'] != $becomesApproved : false;
+	}
+
 	// If the poster is a guest evaluate the legality of name and email.
 	if ($posterIsGuest)
 	{
@@ -1920,7 +1956,7 @@ function Post2()
 			$pollOptions,
 			array('id_poll', 'id_choice')
 		);
-		
+
 		call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
 	}
 	else
@@ -2178,7 +2214,6 @@ function Post2()
 		$context['linktree'][] = array(
 			'url' => $scripturl . '?topic=' . $topic . '.0',
 			'name' => $_POST['subject'],
-			'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : ''
 		);
 
 		if (isset($_REQUEST['msg']))
@@ -2203,6 +2238,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.
@@ -2235,6 +2271,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()
@@ -2313,6 +2350,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.
@@ -2325,9 +2363,6 @@ function AnnouncementSend()
 
 	checkSession();
 
-	// @todo Might need an interface?
-	$chunkSize = empty($modSettings['mail_queue']) ? 50 : 500;
-
 	$context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
 	$groups = array_merge($board_info['groups'], array(1));
 
@@ -2367,26 +2402,27 @@ function AnnouncementSend()
 	$request = $smcFunc['db_query']('', '
 		SELECT mem.id_member, mem.email_address, mem.lngfile
 		FROM {db_prefix}members AS mem
-		WHERE mem.id_member != {int:current_member}' . (!empty($modSettings['allow_disableAnnounce']) ? '
+		WHERE (mem.id_group IN ({array_int:group_list}) OR mem.id_post_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:additional_group_list}, mem.additional_groups) != 0)' . (!empty($modSettings['allow_disableAnnounce']) ? '
 			AND mem.notify_announcements = {int:notify_announcements}' : '') . '
 			AND mem.is_activated = {int:is_activated}
-			AND (mem.id_group IN ({array_int:group_list}) OR mem.id_post_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:additional_group_list}, mem.additional_groups) != 0)
 			AND mem.id_member > {int:start}
 		ORDER BY mem.id_member
-		LIMIT ' . $chunkSize,
+		LIMIT {int:chunk_size}',
 		array(
-			'current_member' => $user_info['id'],
 			'group_list' => $_POST['who'],
 			'notify_announcements' => 1,
 			'is_activated' => 1,
 			'start' => $context['start'],
 			'additional_group_list' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $_POST['who']),
+			// @todo Might need an interface?
+			'chunk_size' => empty($modSettings['mail_queue']) ? 50 : 500,
 		)
 	);
 
 	// All members have received a mail. Go to the next screen.
 	if ($smcFunc['db_num_rows']($request) == 0)
 	{
+		logAction('announce_topic', array('topic' => $topic), 'user');
 		if (!empty($_REQUEST['move']) && allowedTo('move_any'))
 			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
 		elseif (!empty($_REQUEST['goback']))
@@ -2441,8 +2477,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.
@@ -2600,6 +2636,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.
@@ -2762,6 +2799,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;

+ 4 - 4
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
@@ -670,14 +670,14 @@ function ApproveMessage()
 		approveTopics($topic, !$approved);
 
 		if ($starter != $user_info['id'])
-			logAction('approve_topic', array('topic' => $topic, 'subject' => $subject, 'member' => $starter, 'board' => $board));
+			logAction(($approved ? 'un' : '') . 'approve_topic', array('topic' => $topic, 'subject' => $subject, 'member' => $starter, 'board' => $board));
 	}
 	else
 	{
 		approvePosts($_REQUEST['msg'], !$approved);
 
 		if ($poster != $user_info['id'])
-			logAction('approve', array('topic' => $topic, 'subject' => $subject, 'member' => $poster, 'board' => $board));
+			logAction(($approved ? 'un' : '') . 'approve', array('topic' => $topic, 'subject' => $subject, 'member' => $poster, 'board' => $board));
 	}
 
 	redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg']. '#msg' . $_REQUEST['msg']);

+ 23 - 6
Sources/Profile-Modify.php

@@ -82,6 +82,9 @@ function loadProfileFields($force_reload = false)
 			'permission' => 'profile_extra',
 			'input_validate' => create_function('&$value', '
 				$value = strtr($value, \' \', \'+\');
+				if (strlen($value) > 32)
+					return \'aim_too_long\';
+
 				return true;
 			'),
 		),
@@ -433,6 +436,14 @@ function loadProfileFields($force_reload = false)
 			'input_attr' => array('maxlength="50"'),
 			'size' => 50,
 			'permission' => 'profile_extra',
+			'input_validate' => create_function('&$value', '
+				global $smcFunc;
+
+				if ($smcFunc[\'strlen\']($value) > 50)
+					return \'personal_text_too_long\';
+
+				return true;
+			'),
 		),
 		// This does ALL the pm settings
 		'pm_prefs' => array(
@@ -512,7 +523,7 @@ function loadProfileFields($force_reload = false)
 			'label' => $txt['secret_answer'],
 			'subtext' => $txt['secret_desc2'],
 			'size' => 20,
-			'postinput' => '<span class="smalltext" style="margin-left: 4ex;">[<a href="' . $scripturl . '?action=helpadmin;help=secret_why_blank" onclick="return reqWin(this.href);">' . $txt['secret_why_blank'] . '</a>]</span>',
+			'postinput' => '<span class="smalltext" style="margin-left: 4ex;">[<a href="' . $scripturl . '?action=helpadmin;help=secret_why_blank" onclick="return reqOverlayDiv(this.href);">' . $txt['secret_why_blank'] . '</a>]</span>',
 			'value' => '',
 			'permission' => 'profile_identity',
 			'input_validate' => create_function('&$value', '
@@ -635,6 +646,14 @@ function loadProfileFields($force_reload = false)
 			'size' => 50,
 			'permission' => 'profile_title',
 			'enabled' => !empty($modSettings['titlesEnable']),
+			'input_validate' => create_function('&$value', '
+				global $smcFunc;
+
+				if ($smcFunc[\'strlen\'] > 50)
+					return \'user_title_too_long\';
+
+				return true;
+			'),
 		),
 		'website_title' => array(
 			'type' => 'text',
@@ -670,8 +689,6 @@ function loadProfileFields($force_reload = false)
 			'permission' => 'profile_extra',
 		),
 	);
-	
-	call_integration_hook('integrate_profile_fields', array(&$profile_fields));
 
 	call_integration_hook('integrate_load_profile_fields', array($profile_fields));
 
@@ -1344,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...
@@ -1375,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))
@@ -1439,7 +1456,7 @@ function editBuddies($memID)
 		loadMemberContext($buddy);
 		$context['buddies'][$buddy] = $memberContext[$buddy];
 	}
-	
+
 	call_integration_hook('integrate_view_buddies', array($memID));
 }
 

+ 149 - 66
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,14 +520,109 @@ function showAttachments($memID)
 
 	// OBEY permissions!
 	$boardsAllowed = boardsAllowedTo('view_attachments');
-	
+
 	// Make sure we can't actually see anything...
 	if (empty($boardsAllowed))
 		$boardsAllowed = array(-1);
 
-	// Get the total number of attachments they have posted.
+	require_once($sourcedir . '/Subs-List.php');
+
+	// This is all the information required for a group listing.
+	$listOptions = array(
+		'id' => 'attachments',
+		'width' => '100%',
+		'items_per_page' => $modSettings['defaultMaxMessages'],
+		'no_items_label' => $txt['show_attachments_none'],
+		'base_href' => $scripturl . '?action=profile;area=showposts;sa=attach;sort=filename;u=' . $memID,
+		'default_sort_col' => 'filename',
+		'get_items' => array(
+			'function' => 'list_getAttachments',
+			'params' => array(
+				$boardsAllowed,
+				$memID,
+			),
+		),
+		'get_count' => array(
+			'function' => 'list_getNumAttachments',
+			'params' => array(
+				$boardsAllowed,
+				$memID,
+			),
+		),
+		'data_check' => array(
+			'class' => create_function('$data', '
+				return $data[\'approved\'] ? \'\' : \'approvebg\';
+			')
+		),
+		'columns' => array(
+			'filename' => array(
+				'header' => array(
+					'value' => $txt['show_attach_downloads'],
+					'class' => 'lefttext',
+					'style' => 'width: 25%',
+				),
+				'data' => array(
+					'db' => 'filename',
+				),
+				'sort' => array(
+					'default' => 'a.filename',
+					'reverse' => 'a.filename DESC',
+				),
+			),
+			'downloads' => array(
+				'header' => array(
+					'value' => $txt['show_attach_downloads'],
+					'style' => 'width: 12%',
+				),
+				'data' => array(
+					'db' => 'downloads',
+					'comma_format' => true,
+				),
+				'sort' => array(
+					'default' => 'a.downloads',
+					'reverse' => 'a.downloads DESC',
+				),
+			),
+			'subject' => array(
+				'header' => array(
+					'value' => $txt['message'],
+					'class' => 'lefttext',
+					'style' => 'width: 30%',
+				),
+				'data' => array(
+					'db' => 'subject',
+				),
+				'sort' => array(
+					'default' => 'm.subject',
+					'reverse' => 'm.subject DESC',
+				),
+			),
+			'posted' => array(
+				'header' => array(
+					'value' => $txt['show_attach_posted'],
+					'class' => 'lefttext',
+				),
+				'data' => array(
+					'db' => 'posted',
+					'timeformat' => true,
+				),
+			),
+		),
+	);
+
+	// Create the request list.
+	createList($listOptions);
+
+}
+
+function list_getAttachments($start, $items_per_page, $sort, $boardsAllowed, $memID)
+{
+	global $smcFunc, $board, $modSettings, $context;
+
+	// Retrieve some attachments.
 	$request = $smcFunc['db_query']('', '
-		SELECT COUNT(*)
+		SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic,
+			m.id_board, m.poster_time, m.subject, b.name
 		FROM {db_prefix}attachments AS a
 			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
 			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
@@ -536,7 +631,9 @@ function showAttachments($memID)
 			AND m.id_member = {int:current_member}' . (!empty($board) ? '
 			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
 			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
-			AND m.approved = {int:is_approved}'),
+			AND m.approved = {int:is_approved}') . '
+		ORDER BY {raw:sort}
+		LIMIT {int:offset}, {int:limit}',
 		array(
 			'boards_list' => $boardsAllowed,
 			'attachment_type' => 0,
@@ -544,32 +641,38 @@ function showAttachments($memID)
 			'current_member' => $memID,
 			'is_approved' => 1,
 			'board' => $board,
+			'sort' => $sort,
+			'offset' => $start,
+			'limit' => $items_per_page,
 		)
 	);
-	list ($attachCount) = $smcFunc['db_fetch_row']($request);
-	$smcFunc['db_free_result']($request);
-
-	$maxIndex = (int) $modSettings['defaultMaxMessages'];
+	$attachments = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+		$attachments[] = array(
+			'id' => $row['id_attach'],
+			'filename' => $row['filename'],
+			'downloads' => $row['downloads'],
+			'subject' => censorText($row['subject']),
+			'posted' => $row['poster_time'],
+			'msg' => $row['id_msg'],
+			'topic' => $row['id_topic'],
+			'board' => $row['id_board'],
+			'board_name' => $row['name'],
+			'approved' => $row['approved'],
+		);
 
-	// What about ordering?
-	$sortTypes = array(
-		'filename' => 'a.filename',
-		'downloads' => 'a.downloads',
-		'subject' => 'm.subject',
-		'posted' => 'm.poster_time',
-	);
-	$context['sort_order'] = isset($_GET['sort']) && isset($sortTypes[$_GET['sort']]) ? $_GET['sort'] : 'posted';
-	$context['sort_direction'] = isset($_GET['asc']) ? 'up' : 'down';
+	$smcFunc['db_free_result']($request);
 
-	$sort = $sortTypes[$context['sort_order']];
+	return $attachments;
+}
 
-	// Let's get ourselves a lovely page index.
-	$context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts;sa=attach;sort=' . $context['sort_order'] . ($context['sort_direction'] == 'up' ? ';asc' : ''), $context['start'], $attachCount, $maxIndex);
+function list_getNumAttachments($boardsAllowed, $memID)
+{
+	global $board, $smcFunc, $modSettings, $context;
 
-	// Retrieve some attachments.
+	// Get the total number of attachments they have posted.
 	$request = $smcFunc['db_query']('', '
-		SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic,
-			m.id_board, m.poster_time, m.subject, b.name
+		SELECT COUNT(*)
 		FROM {db_prefix}attachments AS a
 			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
 			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
@@ -578,9 +681,7 @@ function showAttachments($memID)
 			AND m.id_member = {int:current_member}' . (!empty($board) ? '
 			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
 			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
-			AND m.approved = {int:is_approved}') . '
-		ORDER BY {raw:sort}
-		LIMIT {int:offset}, {int:limit}',
+			AND m.approved = {int:is_approved}'),
 		array(
 			'boards_list' => $boardsAllowed,
 			'attachment_type' => 0,
@@ -588,35 +689,17 @@ function showAttachments($memID)
 			'current_member' => $memID,
 			'is_approved' => 1,
 			'board' => $board,
-			'sort' => $sort . ' ' . ($context['sort_direction'] == 'down' ? 'DESC' : 'ASC'),
-			'offset' => $context['start'],
-			'limit' => $maxIndex,
 		)
 	);
-	$context['attachments'] = array();
-	while ($row = $smcFunc['db_fetch_assoc']($request))
-	{
-		$row['subject'] = censorText($row['subject']);
-
-		$context['attachments'][] = array(
-			'id' => $row['id_attach'],
-			'filename' => $row['filename'],
-			'downloads' => $row['downloads'],
-			'subject' => $row['subject'],
-			'posted' => timeformat($row['poster_time']),
-			'msg' => $row['id_msg'],
-			'topic' => $row['id_topic'],
-			'board' => $row['id_board'],
-			'board_name' => $row['name'],
-			'approved' => $row['approved'],
-		);
-	}
+	list ($attachCount) = $smcFunc['db_fetch_row']($request);
 	$smcFunc['db_free_result']($request);
+
+	return $attachCount;
 }
 
 /**
  * Gets the user stats for display
- * 
+ *
  * @param int $memID id_member
  */
 function statPanel($memID)
@@ -801,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)
@@ -854,7 +937,7 @@ function tracking($memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function trackActivity($memID)
@@ -1075,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
@@ -1099,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
@@ -1142,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
@@ -1167,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
@@ -1216,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)
@@ -1499,7 +1582,7 @@ function TrackIP($memID = 0)
 
 /**
  * Tracks a users logins.
- * 
+ *
  * @param int $memID = 0 id_member
  */
 function TrackLogins($memID = 0)
@@ -1578,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
@@ -1604,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
@@ -1639,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)
@@ -1740,7 +1823,7 @@ function trackEdits($memID)
 
 /**
  * How many edits?
- * 
+ *
  * @param int $memID id_member
  * @return string number of profile edits
  */
@@ -1767,7 +1850,7 @@ function list_getProfileEditCount($memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $start
  * @param int $items_per_page
  * @param string $sort
@@ -1855,7 +1938,7 @@ function list_getProfileEdits($start, $items_per_page, $sort, $memID)
 
 /**
  * @todo needs a description
- * 
+ *
  * @param int $memID id_member
  */
 function showPermissions($memID)
@@ -2037,7 +2120,7 @@ function showPermissions($memID)
 
 /**
  * View a members warnings?
- * 
+ *
  * @param int $memID id_member
  */
 function viewWarning($memID)

+ 15 - 2
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',
@@ -697,7 +707,7 @@ function loadCustomFields($memID, $area = 'summary')
 	// Load all the relevant fields - and data.
 	$request = $smcFunc['db_query']('', '
 		SELECT
-			col_name, field_name, field_desc, field_type, field_length, field_options,
+			col_name, field_name, field_desc, field_type, show_reg, field_length, field_options,
 			default_value, bbc, enclose, placement
 		FROM {db_prefix}custom_fields
 		WHERE ' . $where,
@@ -706,6 +716,7 @@ function loadCustomFields($memID, $area = 'summary')
 		)
 	);
 	$context['custom_fields'] = array();
+	$context['custom_fields_required'] = false;
 	while ($row = $smcFunc['db_fetch_assoc']($request))
 	{
 		// Shortcut.
@@ -790,7 +801,9 @@ function loadCustomFields($memID, $area = 'summary')
 			'placement' => $row['placement'],
 			'colname' => $row['col_name'],
 			'value' => $value,
+			'show_reg' => $row['show_reg'],
 		);
+		$context['custom_fields_required'] = $context['custom_fields_required'] || $row['show_reg'];
 	}
 	$smcFunc['db_free_result']($request);
 

+ 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;
+}
+
 ?>

+ 2 - 1
Sources/Search.php

@@ -1743,7 +1743,7 @@ function PlushSearch2()
 		if (!empty($posters))
 			loadMemberData(array_unique($posters));
 
-		call_integration_hook('integrate_search_message_list', array($messages, $posters));
+		call_integration_hook('integrate_search_message_list', array($msg_list, $posters));
 
 		// Get the messages out for the callback - select enough that it can be made to look just like Display.
 		$messages_request = $smcFunc['db_query']('', '
@@ -1821,6 +1821,7 @@ function PlushSearch2()
 	$context['get_topics'] = 'prepareSearchContext';
 	$context['can_send_pm'] = allowedTo('pm_send');
 	$context['can_send_email'] = allowedTo('send_email_to_members');
+	$context['can_restore'] = allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board;
 
 	$context['jump_to'] = array(
 		'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),

+ 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');
 }

+ 32 - 4
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)),
@@ -102,7 +104,7 @@ function getBoardIndex($boardIndexOptions)
 					'boards' => array(),
 					'new' => false
 				);
-				$categories[$row_board['id_cat']]['link'] = '<a id="c' . $row_board['id_cat'] . '"></a>' . ($categories[$row_board['id_cat']]['can_collapse'] ? '<a href="' . $categories[$row_board['id_cat']]['collapse_href'] . '">' . $row_board['cat_name'] . '</a>' : $row_board['cat_name']);
+				$categories[$row_board['id_cat']]['link'] = '<a id="c' . $row_board['id_cat'] . '"></a>' . (!$context['user']['is_guest'] ? '<a href="' . $scripturl . '?action=unread;c='. $row_board['id_cat'] . '" title="New posts in ' . $row_board['cat_name'] . '">' . $row_board['cat_name'] . '</a>' : $row_board['cat_name']);
 			}
 
 			// If this board has new posts in it (and isn't the recycle bin!) then the category is new.
@@ -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);
 

+ 25 - 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']))
@@ -2166,6 +2164,8 @@ function AutoSuggestHandler($checkRegistered = null)
 		'versions' => 'SMFVersions',
 	);
 
+	call_integration_hook('integrate_autosuggest', array($searchTypes));
+
 	// If we're just checking the callback function is registered return true or false.
 	if ($checkRegistered != null)
 		return isset($searchTypes[$checkRegistered]) && function_exists('AutoSuggest_Search_' . $checkRegistered);
@@ -2186,7 +2186,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]);

+ 14 - 3
Sources/Subs-List.php

@@ -151,9 +151,9 @@ function createList($listOptions)
 
 			// Allow for basic formatting.
 			if (!empty($column['data']['comma_format']))
-				$cur_data['value'] = comma_format($list_item[$column['data']['comma_format']]);
+				$cur_data['value'] = comma_format($cur_data['value']);
 			elseif (!empty($column['data']['timeformat']))
-				$cur_data['value'] = timeformat($list_item[$column['data']['timeformat']]);
+				$cur_data['value'] = timeformat($cur_data['value']);
 
 			// Set a style class for this column?
 			if (isset($column['data']['class']))
@@ -167,8 +167,19 @@ function createList($listOptions)
 			$cur_row[$column_id] = $cur_data;
 		}
 
+		$list_context['rows'][$item_id]['class'] = '';
+		$list_context['rows'][$item_id]['style'] = '';
+		// Maybe we wat set a custom class for the row based on the data in the row itself
+		if (isset($listOptions['data_check']))
+		{
+			if (isset($listOptions['data_check']['class']))
+				$list_context['rows'][$item_id]['class'] = ' ' . $listOptions['data_check']['class']($list_item);
+			if (isset($listOptions['data_check']['style']))
+				$list_context['rows'][$item_id]['style'] = ' style="' . $listOptions['data_check']['style']($list_item) . '"';
+		}
+
 		// Insert the row into the list.
-		$list_context['rows'][$item_id] = $cur_row;
+		$list_context['rows'][$item_id]['data'] = $cur_row;
 	}
 
 	// The title is currently optional.

+ 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 - 32
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
@@ -26,34 +26,6 @@ function createMenu($menuData, $menuOptions = array())
 {
 	global $context, $settings, $options, $txt, $modSettings, $scripturl, $smcFunc, $user_info, $sourcedir, $options;
 
-	// First are we toggling use of the side bar generally?
-	if (isset($_GET['togglebar']) && !$user_info['is_guest'])
-	{
-		// Save the new dropdown menu state.
-		$smcFunc['db_insert']('replace',
-			'{db_prefix}themes',
-			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
-			array(
-				array(
-					$user_info['id'],
-					$settings['theme_id'],
-					'use_sidebar_menu',
-					empty($options['use_sidebar_menu']) ? '1' : '0',
-				),
-			),
-			array('id_member', 'id_theme', 'variable')
-		);
-
-		// Clear the theme settings cache for this user.
-		$themes = explode(',', $modSettings['knownThemes']);
-		foreach ($themes as $theme)
-			cache_put_data('theme_settings-' . $theme . ':' . $user_info['id'], null, 60);
-
-		// Redirect as this seems to work best.
-		$redirect_url = isset($menuOptions['toggle_redirect_url']) ? $menuOptions['toggle_redirect_url'] : 'action=' . (isset($_GET['action']) ? $_GET['action'] : 'admin') . ';area=' . (isset($_GET['area']) ? $_GET['area'] : 'index') . ';sa=' . (isset($_GET['sa']) ? $_GET['sa'] : 'settings') . (isset($_GET['u']) ? ';u=' . $_GET['u'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'];
-		redirectexit($redirect_url);
-	}
-
 	// Work out where we should get our images from.
 	$context['menu_image_path'] = file_exists($settings['theme_dir'] . '/images/admin/change_menu.png') ? $settings['images_url'] . '/admin' : $settings['default_images_url'] . '/admin';
 
@@ -235,9 +207,6 @@ function createMenu($menuData, $menuOptions = array())
 	// Should we use a custom base url, or use the default?
 	$menu_context['base_url'] = isset($menuOptions['base_url']) ? $menuOptions['base_url'] : $scripturl . '?action=' . $menu_context['current_action'];
 
-	// What about the toggle url?
-	$menu_context['toggle_url'] = isset($menuOptions['toggle_url']) ? $menuOptions['toggle_url'] : $menu_context['base_url'] . (!empty($menu_context['current_area']) ? ';area=' . $menu_context['current_area'] : '') . (!empty($menu_context['current_subsection']) ? ';sa=' . $menu_context['current_subsection'] : '') . $menu_context['extra_parameters'] . ';togglebar';
-
 	// If there are sections quickly goes through all the sections to check if the base menu has an url
 	if (!empty($menu_context['current_section']))
 	{

+ 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

+ 2 - 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()
@@ -1138,6 +1138,7 @@ function parsePackageInfo(&$packageXML, $testing_only = true, $method = 'install
 				'type' => $actionType,
 				'function' => $action->exists('@function') ? $action->fetch('@function') : '',
 				'hook' => $action->exists('@hook') ? $action->fetch('@hook') : $action->fetch('.'),
+				'include_file' => $action->exists('@file') ? $action->fetch('@file') : '',
 				'reverse' => $action->exists('@reverse') && $action->fetch('@reverse') == 'true' ? true : false,
 				'description' => '',
 			);

+ 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
  */

+ 182 - 109
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
@@ -530,17 +530,17 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 	if (empty($modSettings['compactTopicPagesEnable']))
 	{
 		// Show the left arrow.
-		$pageindex = $start == 0 ? ' ' : sprintf($base_link, $start - $num_per_page, '&#171;');
+		$pageindex = $start == 0 ? ' ' : sprintf($base_link, $start - $num_per_page, '<span class="previous_page">' . $txt['prev'] . '</span>');
 
 		// Show all the pages.
 		$display_page = 1;
 		for ($counter = 0; $counter < $max_value; $counter += $num_per_page)
-			$pageindex .= $start == $counter && !$start_invalid ? '<strong>' . $display_page++ . '</strong> ' : sprintf($base_link, $counter, $display_page++);
+			$pageindex .= $start == $counter && !$start_invalid ? '<span class="current_page"><strong>' . $display_page++ . '</strong></span> ' : sprintf($base_link, $counter, $display_page++);
 
 		// Show the right arrow.
 		$display_page = ($start + $num_per_page) > $max_value ? $max_value : ($start + $num_per_page);
 		if ($start != $counter - $max_value && !$start_invalid)
-			$pageindex .= $display_page > $counter - $num_per_page ? ' ' : sprintf($base_link, $display_page, '&#187;');
+			$pageindex .= $display_page > $counter - $num_per_page ? ' ' : sprintf($base_link, $display_page, '<span class="next_page">' . $txt['next'] . '</span>');
 	}
 	else
 	{
@@ -549,7 +549,7 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 
 		// Show the "prev page" link. (>prev page< 1 ... 6 7 [8] 9 10 ... 15 next page)
 		if (!empty($start) && $show_prevnext)
-			$pageindex = sprintf($base_link, $start - $num_per_page, '<span class="previous_page">&#171; ' . $txt['prev'] . '</span>');
+			$pageindex = sprintf($base_link, $start - $num_per_page, '<span class="previous_page">' . $txt['prev'] . '</span>');
 		else
 			$pageindex = '';
 
@@ -559,7 +559,7 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 
 		// Show the ... after the first page.  (prev page 1 >...< 6 7 [8] 9 10 ... 15 next page)
 		if ($start > $num_per_page * ($PageContiguous + 1))
-			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . $num_per_page . ', ' . ($start - $num_per_page * $PageContiguous) . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor = \'pointer\';"> ... </span>';
+			$pageindex .= '<span class="expand_pages" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . $num_per_page . ', ' . ($start - $num_per_page * $PageContiguous) . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor = \'pointer\';"><strong> ... </strong></span>';
 
 		// Show the pages before the current one. (prev page 1 ... >6 7< [8] 9 10 ... 15 next page)
 		for ($nCont = $PageContiguous; $nCont >= 1; $nCont--)
@@ -571,7 +571,7 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 
 		// Show the current page. (prev page 1 ... 6 7 >[8]< 9 10 ... 15 next page)
 		if (!$start_invalid)
-			$pageindex .= '[<strong>' . ($start / $num_per_page + 1) . '</strong>] ';
+			$pageindex .= '<span class="current_page"><strong>' . ($start / $num_per_page + 1) . '</strong></span>';
 		else
 			$pageindex .= sprintf($base_link, $start, $start / $num_per_page + 1);
 
@@ -586,7 +586,7 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 
 		// Show the '...' part near the end. (prev page 1 ... 6 7 [8] 9 10 >...< 15 next page)
 		if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages)
-			$pageindex .= '<span style="font-weight: bold;" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . ($start + $num_per_page * ($PageContiguous + 1)) . ', ' . $tmpMaxPages . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor=\'pointer\';"> ... </span>';
+			$pageindex .= '<span class="expand_pages" onclick="' . htmlspecialchars('expandPages(this, ' . JavaScriptEscape(($flexible_start ? $base_url : strtr($base_url, array('%' => '%%')) . ';start=%1$d')) . ', ' . ($start + $num_per_page * ($PageContiguous + 1)) . ', ' . $tmpMaxPages . ', ' . $num_per_page . ');') . '" onmouseover="this.style.cursor=\'pointer\';"><strong> ... </strong></span>';
 
 		// Show the last number in the list. (prev page 1 ... 6 7 [8] 9 10 ... >15<  next page)
 		if ($start + $num_per_page * $PageContiguous < $tmpMaxPages)
@@ -594,18 +594,18 @@ function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $flex
 
 		// Show the "next page" link. (prev page 1 ... 6 7 [8] 9 10 ... 15 >next page<)
 		if ($start != $tmpMaxPages && $show_prevnext)
-			$pageindex .= sprintf($base_link, $start + $num_per_page, '<span class="next_page">' . $txt['next'] . ' &#187;</span>');
+			$pageindex .= sprintf($base_link, $start + $num_per_page, '<span class="next_page">' . $txt['next'] . '</span>');
 	}
 
 	return $pageindex;
 }
 
 /**
- * - formats a number to display in the style of the admins' choosing.
+ * - Formats a number.
  * - uses the format of number_format to decide how to format the number.
  *   for example, it might display "1 234,50".
  * - caches the formatting data from the setting for optimization.
- * 
+ *
  * @param float $number
  * @param bool $override_decimal_count = false
  */
@@ -614,9 +614,6 @@ function comma_format($number, $override_decimal_count = false)
 	global $txt;
 	static $thousands_separator = null, $decimal_separator = null, $decimal_count = null;
 
-	// @todo Should, perhaps, this just be handled in the language files, and not a mod setting?
-	// (French uses 1 234,00 for example... what about a multilingual forum?)
-
 	// Cache these values...
 	if ($decimal_separator === null)
 	{
@@ -636,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
@@ -731,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
  */
@@ -747,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
  */
@@ -793,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
  */
@@ -832,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 = ''
@@ -852,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);
 
@@ -883,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))
 	{
@@ -1074,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\'])
@@ -1116,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\'])
@@ -1156,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\';
@@ -2393,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);
@@ -2421,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)
@@ -2720,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);
 
@@ -2918,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.
@@ -2934,6 +2919,17 @@ function setupThemeContext($forceload = false)
 	// This is done to allow theme authors to customize it as they want.
 	$context['show_pm_popup'] = $context['user']['popup_messages'] && !empty($options['popup_messages']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != 'pm');
 
+	// 2.1+: Add the PM popup here instead. Theme authors can still override it simply by editing/removing the 'fPmPopup' in the array.
+	if($context['show_pm_popup'])
+		addInlineJavascript('
+		$(document).ready(function(){
+			new smc_Popup({
+				heading: ' . JavaScriptEscape($txt['show_personal_messages_heading']) . ',
+				content: ' . JavaScriptEscape(sprintf($txt['show_personal_messages'], $context['user']['unread_messages'], $scripturl . '?action=pm')) . ',
+				icon: smf_images_url + \'/im_sm_newmsg.png\'
+			});
+		});');
+
 	// Resize avatars the fancy, but non-GD requiring way.
 	if ($modSettings['avatar_action_too_large'] == 'option_js_resize' && (!empty($modSettings['avatar_max_width_external']) || !empty($modSettings['avatar_max_height_external'])))
 	{
@@ -2943,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
@@ -2999,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);
@@ -3027,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)
 	{
@@ -3057,7 +3053,7 @@ function template_rawdata()
 }
 
 /**
- * 
+ *
  */
 function template_header()
 {
@@ -3200,7 +3196,7 @@ function theme_copyright()
 }
 
 /**
- * 
+ *
  */
 function template_footer()
 {
@@ -3225,31 +3221,74 @@ function template_footer()
 
 /**
  * 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)
 {
-	global $context;
+	global $context, $modSettings, $settings;
 
-	// Use this hook to minify/optimize Javascript files
+	// Use this hook to minify/optimize Javascript files and vars
 	call_integration_hook('pre_javascript_output');
 
-	foreach ($context['javascript_files'] as $filename => $options)
-		if ((!$do_defered && empty($options['defer'])) || ($do_defered && !empty($options['defer'])))
-			echo '
-		<script type="text/javascript" src="', $filename, '"></script>';
-
-	if (!empty($context['javascript_vars']))
+	// Ouput the declared Javascript variables.
+	if (!empty($context['javascript_vars']) && !$do_defered)
 	{
 		echo '
-		<script type="text/javascript"><!-- // --><![CDATA[';
+	<script type="text/javascript"><!-- // --><![CDATA[';
 
 		foreach ($context['javascript_vars'] as $key => $value)
 			echo '
-			var ', $key, ' = ', $value;
+		var ', $key, ' = ', $value, ';';
+
+		echo '
+	// ]]></script>';
+	}
+
+	// While we have Javascript files to place in the template
+	foreach ($context['javascript_files'] as $id => $js_file)
+	{
+		if ((!$do_defered && empty($js_file['options']['defer'])) || ($do_defered && !empty($js_file['options']['defer'])))
+			echo '
+	<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>';
+	<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)
+		{
+			echo '
+<script type="text/javascript"><!-- // --><![CDATA[';
+
+			foreach ($context['javascript_inline']['defer'] as $js_code)
+				echo $js_code;
+
+			echo'
+// ]]></script>';
+		}
+
+		if (!empty($context['javascript_inline']['standard']) && !$do_defered)
+		{
+			echo '
+	<script type="text/javascript"><!-- // --><![CDATA[';
 
+			foreach ($context['javascript_inline']['standard'] as $js_code)
+				echo $js_code;
+
+			echo'
+	// ]]></script>';
+		}
 	}
 }
 
@@ -3263,9 +3302,9 @@ function template_css()
 	// Use this hook to minify/optimize CSS files
 	call_integration_hook('pre_css_output');
 
-	foreach ($context['css_files'] as $filename => $options)
+	foreach ($context['css_files'] as $id => $file)
 		echo '
-	<link rel="stylesheet" type="text/css" href="', $filename, '" />';
+	<link rel="stylesheet" type="text/css" href="', $file['filename'], '" id="', $id,'" />';
 }
 
 /**
@@ -3544,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 = ''
@@ -3578,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':
@@ -3604,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();
 			}
@@ -3640,7 +3679,7 @@ function clean_cache($type = '')
 		case 'xcache':
 			if (function_exists('xcache_clear_cache'))
 			{
-				// 
+				//
 				if ($type === '')
 				{
 					xcache_clear_cache(XC_TYPE_VAR, 0);
@@ -3693,7 +3732,7 @@ function loadClassFile($filename)
 }
 
 /**
- * 
+ *
  */
 function setupMenuContext()
 {
@@ -4007,19 +4046,47 @@ function smf_seed_generator()
  */
 function call_integration_hook($hook, $parameters = array())
 {
-	global $modSettings;
+	global $modSettings, $settings, $boarddir, $sourcedir;
 
 	$results = array();
 	if (empty($modSettings[$hook]))
 		return $results;
 
 	$functions = explode(',', $modSettings[$hook]);
-
 	// Loop through each function.
 	foreach ($functions as $function)
 	{
 		$function = trim($function);
-		$call = strpos($function, '::') !== false ? explode('::', $function) : $function;
+		if (strpos($function, '::') !== false)
+		{
+			$call = explode('::', $function);
+			if (strpos($call[1], ':') !== false)
+			{
+				list($func, $file) = explode(':', $call[1]);
+				if (empty($settings['theme_dir']))
+					$absPath = strtr(trim($file), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
+				else
+					$absPath = strtr(trim($file), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
+				if (file_exists($absPath))
+					require_once($absPath);
+				$call = array($call[0], $func);
+			}
+		}
+		else
+		{
+			$call = $function;
+			if (strpos($function, ':') !== false)
+			{
+				list($func, $file) = explode(':', $function);
+				if (empty($settings['theme_dir']))
+					$absPath = strtr(trim($file), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
+				else
+					$absPath = strtr(trim($file), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
+				if (file_exists($absPath))
+					require_once($absPath);
+				$call = $function;
+			}
+		}
 
 		// Is it valid?
 		if (is_callable($call))
@@ -4035,12 +4102,15 @@ function call_integration_hook($hook, $parameters = array())
  *
  * @param string $hook
  * @param string $function
+ * @param string $file
  * @param bool $permanent = true if true, updates the value in settings table
  */
-function add_integration_function($hook, $function, $permanent = true)
+function add_integration_function($hook, $function, $file = '', $permanent = true)
 {
 	global $smcFunc, $modSettings;
 
+	$integration_call = (!empty($file) && $file !== true) ? $function . ':' . $file : $function;
+
 	// Is it going to be permanent?
 	if ($permanent)
 	{
@@ -4058,13 +4128,13 @@ function add_integration_function($hook, $function, $permanent = true)
 		if (!empty($current_functions))
 		{
 			$current_functions = explode(',', $current_functions);
-			if (in_array($function, $current_functions))
+			if (in_array($integration_call, $current_functions))
 				return;
 
-			$permanent_functions = array_merge($current_functions, array($function));
+			$permanent_functions = array_merge($current_functions, array($integration_call));
 		}
 		else
-			$permanent_functions = array($function);
+			$permanent_functions = array($integration_call);
 
 		updateSettings(array($hook => implode(',', $permanent_functions)));
 	}
@@ -4073,10 +4143,10 @@ function add_integration_function($hook, $function, $permanent = true)
 	$functions = empty($modSettings[$hook]) ? array() : explode(',', $modSettings[$hook]);
 
 	// Do nothing, if it's already there.
-	if (in_array($function, $functions))
+	if (in_array($integration_call, $functions))
 		return;
 
-	$functions[] = $function;
+	$functions[] = $integration_call;
 	$modSettings[$hook] = implode(',', $functions);
 }
 
@@ -4087,11 +4157,14 @@ function add_integration_function($hook, $function, $permanent = true)
  *
  * @param string $hook
  * @param string $function
+ * @param string $file
  */
-function remove_integration_function($hook, $function)
+function remove_integration_function($hook, $function, $file = '')
 {
 	global $smcFunc, $modSettings;
 
+	$integration_call = (!empty($file)) ? $function . ':' . $file : $function;
+
 	// Get the permanent functions.
 	$request = $smcFunc['db_query']('', '
 		SELECT value
@@ -4108,18 +4181,18 @@ function remove_integration_function($hook, $function)
 	{
 		$current_functions = explode(',', $current_functions);
 
-		if (in_array($function, $current_functions))
-			updateSettings(array($hook => implode(',', array_diff($current_functions, array($function)))));
+		if (in_array($integration_call, $current_functions))
+			updateSettings(array($hook => implode(',', array_diff($current_functions, array($integration_call)))));
 	}
 
 	// Turn the function list into something usable.
 	$functions = empty($modSettings[$hook]) ? array() : explode(',', $modSettings[$hook]);
 
 	// You can only remove it if it's available.
-	if (!in_array($function, $functions))
+	if (!in_array($integration_call, $functions))
 		return;
 
-	$functions = array_diff($functions, array($function));
+	$functions = array_diff($functions, array($integration_call));
 	$modSettings[$hook] = implode(',', $functions);
 }
 
@@ -4135,10 +4208,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
@@ -4151,7 +4224,7 @@ function sanitizeMSCutPaste($string)
 		"\xe2\x80\x93",	// en dash
 		"\xe2\x80\x94",	// em dash
 	);
-	
+
 	// windows 1252 / iso equivalents
 	$findchars_iso = array(
 		chr(130),
@@ -4177,7 +4250,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'];
 

+ 12 - 9
Sources/Who.php

@@ -684,19 +684,22 @@ 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>',
 			'<a href="http://cherne.net/brian/resources/jquery.hoverIntent.html">hoverIntent</a> | &copy; Brian Cherne | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
 			'<a href="http://users.tpg.com.au/j_birch/plugins/superfish/">Superfish</a> | &copy; Joel Birch | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
+			'<a href="http://www.sceditor.com/">SCEditor</a> | &copy; Sam Clarke | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
+			'<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)
@@ -714,15 +717,15 @@ function Credits($in_admin = false)
 				'empty' => '',
 			)
 		);
-		
+
 		while ($row = $smcFunc['db_fetch_assoc']($request))
 		{
 			$credit_info = unserialize($row['credits']);
 
 			$copyright = empty($credit_info['copyright']) ? '' : $txt['credits_copyright'] . ' &copy; ' . $smcFunc['htmlspecialchars']($credit_info['copyright']);
-			$license = empty($credit_info['license']) ? '' : $txt['credits_license'] . ' ' . $smcFunc['htmlspecialchars']($credit_info['license']);
-			$version = $txt['credits_version'] . $row['version'];
-			$title = (empty($credit_info['title']) ? $row['name'] : $smcFunc['htmlspecialchars']($credit_info['title'])) . ' : ' . $version;
+			$license = empty($credit_info['license']) ? '' : $txt['credits_license'] . ': ' . $smcFunc['htmlspecialchars']($credit_info['license']);
+			$version = $txt['credits_version'] . ' ' . $row['version'];
+			$title = (empty($credit_info['title']) ? $row['name'] : $smcFunc['htmlspecialchars']($credit_info['title'])) . ': ' . $version;
 
 			// build this one out and stash it away
 			$mod_name = empty($credit_info['url']) ? $title : '<a href="' . $credit_info['url'] . '">' . $title . '</a>';
@@ -731,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.
@@ -742,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);
 

+ 0 - 74
Themes/READ_ME.txt

@@ -1,74 +0,0 @@
-
- The Great Readme. Someone might even read it.
- =============================================
- 
- July 18th, 2012. Let's see if we can kill GitHub!
- 
- Most of the stuff I've done for looks is CSS changes only.
- Markup changes were limited to bug fixes, consistency across templates, or useful features.
- The markup changes are as small as possible and should all be bulletproof.
- 
- TEMPLATES:
- ----------
- 
- 1/ index.template.php:			Significant markup revisions. All very good ones.
-								Trust me on this, or at least please discuss it.
-
- 2/ BoardIndex.template.php:	Cleanup and buglet fixes. Some useful tweaks.
-
- 3/ Calendar.template.php:		Cleanup and buglet fixes. Improved looks via CSS.
-
- 4/ GenericMenu.template.php:	Added chosen class to third level for consistency. That's about it IIRC.
-
- 5/ Help.template.php:			Minor cleanup (old spans, etc). Improved looks via CSS.
-
- 6/ MessageIndex.template.php:	Cleanup and buglet fixes. Some useful tweaks.
-
- 7/ Recent.template.php:		Cleanup and buglet fixes. Some useful tweaks.
-
- 8/ Stats.template.php:			Minor cleanup. Improved looks via CSS only.
-								Went to town on statsbar styling. Much better IMO. Polls are the same.
-
- LANGUAGES:
- ----------
- 
- 1/ index.english.php			Chnaged the last post string to something cleaner.
- 
- SCRIPTS:
- --------
-
- 1/ scripts/theme.js:			Removed old stuff that was only for adding :hover pseudo class to IE6.
-
- CSS:
- ----
- 
- 1/ index.css, of course.		Lots of stuff there. Combination of bug fixes, less crap, and better looks.
-
- 2/ admin.css, too.				Just a bit here and there.
- 
- OTHER:
- ------
-
- 1/ Assorted images:			Old big nasty sprites are gone.
-								New smaller ones are much better, and no more hhtp requests than before. YAY!
-
-								Have changed message icons to 18x18 instead of 16x16.
-								They really do need a little extra grunt with the other stuff around them.
-								20x20 was too big. 16x16 is too small in that situation.
-
- PROBLEMS (meh):
- ===============
- 
- 1/	Currently not sure WTF is going on the the drop menu third levels being clipped in IE, Chrome and Opera.
-	Will find a solution, but CSS3 transitions is not it. Tried those, and the results were less than pleasing.
-	I know the problem is related to the js, as it disappears when js is disabled. CSS seems fine, AFAICT.
-
- 2/	Haven't tackled rtl.css yet, but it shouldn't be hard to sort.
- 
- 3/ Oh yeah, the stuff I don't know about yet. You get that. Hey ho.
- 
- ===================================================================
- 
- Later: got rid of all the old spans and ie6 header stuff in the rest of the templates.
- Also ditched the remaining middletext and normaltext classes. Yay! Less crap.
- 

+ 117 - 180
Themes/default/Admin.template.php

@@ -20,7 +20,7 @@ function template_admin()
 	// Welcome message for the admin.
 	echo '
 	<div id="admincenter">
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">';
 
 	if ($context['user']['is_admin'])
@@ -41,14 +41,12 @@ function template_admin()
 	echo $txt['admin_center'], '
 			</h3>
 		</div>
-		<span class="upperframe"><span></span></span>
 		<div class="roundframe">
 			<div id="welcome">
 				<strong>', $txt['hello_guest'], ' ', $context['user']['name'], '!</strong>
 				', sprintf($txt['admin_main_welcome'], $txt['admin_center'], $txt['help'], $txt['help']), '
 			</div>
-		</div>
-		<span class="lowerframe"><span></span></span>';
+		</div>';
 
 	// Is there an update available?
 	echo '
@@ -62,15 +60,13 @@ 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 reqWin(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">
-					<span class="topslice"><span></span></span>
 					<div class="content">
 						<div id="smfAnnouncements">', $txt['lfyi'], '</div>
 					</div>
-					<span class="botslice"><span></span></span>
 				</div>
 			</div>';
 
@@ -83,7 +79,6 @@ function template_admin()
 					</h3>
 				</div>
 				<div class="windowbg nopadding">
-					<span class="topslice"><span></span></span>
 					<div class="content">
 						<div id="version_details">
 							<strong>', $txt['support_versions'], ':</strong><br />
@@ -106,14 +101,12 @@ function template_admin()
 	echo '
 						</div>
 					</div>
-					<span class="botslice"><span></span></span>
 				</div>
 			</div>
 		</div>';
 
 	echo '
-		<div class="windowbg2 clear_right">
-			<span class="topslice"><span></span></span>
+		<div class="windowbg2 quick_tasks">
 			<div class="content">
 				<ul id="quick_tasks" class="flow_hidden">';
 
@@ -128,10 +121,8 @@ function template_admin()
 	echo '
 				</ul>
 			</div>
-			<span class="botslice clear"><span></span></span>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	// The below functions include all the scripts needed from the simplemachines.org site. The language and format are passed for internationalization.
 	if (empty($modSettings['disable_smf_js']))
@@ -204,117 +195,115 @@ function template_credits()
 	echo '
 
 	<div id="admincenter">
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">
-				', $txt['support_title'], '
+				', $txt['support_credits_title'], '
 			</h3>
 		</div>
-		<div class="windowbg">
-			<span class="topslice"><span></span></span>
-			<div class="content">
-				<strong>', $txt['support_versions'], ':</strong><br />
-					', $txt['support_versions_forum'], ':
-				<em id="yourVersion" style="white-space: nowrap;">', $context['forum_version'], '</em>', $context['can_admin'] ? ' <a href="' . $scripturl . '?action=admin;area=maintain;sa=routine;activity=version">' . $txt['version_check_more'] . '</a>' : '', '<br />
-					', $txt['support_versions_current'], ':
-				<em id="smfVersion" style="white-space: nowrap;">??</em><br />';
+		<div id="support_credits">
+			<div class="cat_bar">
+				<h3 class="catbg">
+					', $txt['support_title'], ' <img src="', $settings['images_url'], '/smflogo.png" id="credits_logo" alt="" />
+				</h3>
+			</div>
+			<div class="windowbg">
+				<div class="content">
+					<strong>', $txt['support_versions'], ':</strong><br />
+						', $txt['support_versions_forum'], ':
+					<em id="yourVersion" style="white-space: nowrap;">', $context['forum_version'], '</em>', $context['can_admin'] ? ' <a href="' . $scripturl . '?action=admin;area=maintain;sa=routine;activity=version">' . $txt['version_check_more'] . '</a>' : '', '<br />
+						', $txt['support_versions_current'], ':
+					<em id="smfVersion" style="white-space: nowrap;">??</em><br />';
 
 	// Display all the variables we have server information for.
 	foreach ($context['current_versions'] as $version)
 	{
 		echo '
-					', $version['title'], ':
-				<em>', $version['version'], '</em>';
-		
+						', $version['title'], ':
+					<em>', $version['version'], '</em>';
+
 		// more details for this item, show them a link
 		if ($context['can_admin'] && isset($version['more']))
-			echo 
-				' <a href="', $scripturl, $version['more'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['version_check_more'], '</a>';
+			echo
+					' <a href="', $scripturl, $version['more'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['version_check_more'], '</a>';
 		echo '
-				<br />';
+					<br />';
 	}
 
 	echo '
-			</div>
-			<span class="botslice"><span></span></span>
-		</div>';
+				</div>
+			</div>';
 
 	// Point the admin to common support resources.
 	echo '
-		<div class="cat_bar">
-			<h3 class="catbg">
-				', $txt['support_resources'], '
-			</h3>
-		</div>
-		<div class="windowbg2">
-			<span class="topslice"><span></span></span>
-			<div class="content">
-				<p>', $txt['support_resources_p1'], '</p>
-				<p>', $txt['support_resources_p2'], '</p>
+			<div id="support_resources" class="cat_bar">
+				<h3 class="catbg">
+					', $txt['support_resources'], '
+				</h3>
 			</div>
-			<span class="botslice"><span></span></span>
-		</div>';
+			<div class="windowbg2">
+				<div class="content">
+					<p>', $txt['support_resources_p1'], '</p>
+					<p>', $txt['support_resources_p2'], '</p>
+				</div>
+			</div>';
 
 	// Display latest support questions from simplemachines.org.
 	echo '
-		<div class="cat_bar">
-			<h3 class="catbg">
-				<span class="ie6_header floatleft"><a href="', $scripturl, '?action=helpadmin;help=latest_support" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['support_latest'], '</span>
-			</h3>
-		</div>
-		<div class="windowbg">
-			<span class="topslice"><span></span></span>
-			<div class="content">
-				<div id="latestSupport">', $txt['support_latest_fetch'], '</div>
+			<div class="cat_bar">
+				<h3 class="catbg">
+					<a href="', $scripturl, '?action=helpadmin;help=latest_support" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['support_latest'], '
+				</h3>
 			</div>
-			<span class="botslice"><span></span></span>
-		</div>';
+			<div class="windowbg">
+				<div class="content">
+					<div id="latestSupport">', $txt['support_latest_fetch'], '</div>
+				</div>
+			</div>';
 
 	// The most important part - the credits :P.
 	echo '
-		<div class="cat_bar">
-			<h3 class="catbg">
-				', $txt['admin_credits'], '
-			</h3>
-		</div>
-		<div class="windowbg2">
-			<span class="topslice"><span></span></span>
-			<div class="content">';
+			<div id="credits_sections" class="cat_bar">
+				<h3 class="catbg">
+					', $txt['admin_credits'], '
+				</h3>
+			</div>
+			<div class="windowbg2">
+				<div class="content">';
 
 	foreach ($context['credits'] as $section)
 	{
 		if (isset($section['pretext']))
 			echo '
-				<p>', $section['pretext'], '</p>';
+					<p>', $section['pretext'], '</p><hr />';
 
 		echo '
-				<dl>';
+					<dl>';
 
 		foreach ($section['groups'] as $group)
 		{
 			if (isset($group['title']))
 				echo '
-					<dt>
-						<strong>', $group['title'], ':</strong>
-					</dt>';
+						<dt>
+							<strong>', $group['title'], ':</strong>
+						</dt>';
 
 			echo '
-					<dd>', implode(', ', $group['members']), '</dd>';
+						<dd>', implode(', ', $group['members']), '</dd>';
 		}
 
 		echo '
-				</dl>';
+					</dl>';
 
 		if (isset($section['posttext']))
 			echo '
-				<p>', $section['posttext'], '</p>';
+					<hr /><p>', $section['posttext'], '</p>';
 	}
 
 	echo '
+				</div>
 			</div>
-			<span class="botslice"><span></span></span>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	// This makes all the support information available to the support script...
 	echo '
@@ -338,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;
@@ -359,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>';
 }
 
@@ -381,7 +356,7 @@ function template_view_versions()
 
 	echo '
 	<div id="admincenter">
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">
 				', $txt['admin_version_check'], '
 			</h3>
@@ -577,8 +552,7 @@ function template_view_versions()
 	}
 
 	echo '
-		</div>
-	<br class="clear" />';
+		</div>';
 
 	/* Below is the hefty javascript for this. Upon opening the page it checks the current file versions with ones
 	   held at simplemachines.org and works out if they are up to date.  If they aren't it colors that files number
@@ -586,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: [
@@ -613,13 +586,12 @@ function template_edit_censored()
 	echo '
 	<div id="admincenter">
 		<form action="', $scripturl, '?action=admin;area=postsettings;sa=censor" method="post" accept-charset="', $context['character_set'], '">
-			<div class="cat_bar">
+			<div id="section_header" class="cat_bar">
 				<h3 class="catbg">
 					', $txt['admin_censored_words'], '
 				</h3>
 			</div>
 			<div class="windowbg2">
-				<span class="topslice"><span></span></span>
 				<div class="content">
 					<p>', $txt['admin_censored_where'], '</p>';
 
@@ -638,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>
@@ -657,11 +628,8 @@ function template_edit_censored()
 							<input type="checkbox" name="censorIgnoreCase" value="1" id="censorIgnoreCase_check"', empty($modSettings['censorIgnoreCase']) ? '' : ' checked="checked"', ' class="input_check" />
 						</dd>
 					</dl>
-					<hr class="hrcolor" />
 					<input type="submit" name="save_censor" value="', $txt['save'], '" class="button_submit" />
-					<br class="clear_right" />
 				</div>
-				<span class="botslice"><span></span></span>
 			</div>
 			<br />';
 
@@ -673,21 +641,18 @@ function template_edit_censored()
 				</h3>
 			</div>
 			<div class="windowbg">
-				<span class="topslice"><span></span></span>
 				<div class="content">
 					<p class="centertext">
 						<input type="text" name="censortest" value="', empty($context['censor_test']) ? '' : $context['censor_test'], '" class="input_text" />
 						<input type="submit" value="', $txt['censor_test_save'], '" class="button_submit" />
 					</p>
 				</div>
-				<span class="botslice"><span></span></span>
 			</div>
 
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-censor_token_var'], '" value="', $context['admin-censor_token'], '" />
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Maintenance is a lovely thing, isn't it?
@@ -697,19 +662,17 @@ function template_not_done()
 
 	echo '
 	<div id="admincenter">
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">
 				', $txt['not_done_title'], '
 			</h3>
 		</div>
 		<div class="windowbg">
-			<span class="topslice"><span></span></span>
 			<div class="content">
 				', $txt['not_done_reason'];
 
 	if (!empty($context['continue_percent']))
 		echo '
-				<br /><br />
 				<div class="progress_bar">
 					<div class="full_bar">', $context['continue_percent'], '%</div>
 					<div class="green_percent" style="width: ', $context['continue_percent'], '%;">&nbsp;</div>
@@ -717,24 +680,19 @@ function template_not_done()
 
 	if (!empty($context['substep_enabled']))
 		echo '
-				<br /><br />
-				<div class="progress_bar">
+					<div class="progress_bar">
 					<div class="full_bar">', $context['substep_title'], ' (', $context['substep_continue_percent'], '%)</div>
 					<div class="blue_percent" style="width: ', $context['substep_continue_percent'], '%;">&nbsp;</div>
 				</div>';
 
 	echo '
 				<form action="', $scripturl, $context['continue_get_data'], '" method="post" accept-charset="', $context['character_set'], '" style="margin: 0;" name="autoSubmit" id="autoSubmit">
-					<hr class="hrcolor" />
 					<input type="submit" name="cont" value="', $txt['not_done_continue'], '" class="button_submit" />
 					', $context['continue_post_data'], '
-					<br class="clear_right" />
 				</form>
 			</div>
-			<span class="botslice"><span></span></span>
 		</div>
 	</div>
-	<br class="clear" />
 	<script type="text/javascript"><!-- // --><![CDATA[
 		var countdown = ', $context['continue_countdown'], ';
 		doAutoSubmit();
@@ -765,17 +723,17 @@ function template_show_settings()
 
 	if (!empty($context['settings_insert_above']))
 		echo $context['settings_insert_above'];
-		
+
 	echo '
 	<div id="admincenter">
-		<form action="', $context['post_url'], '" method="post" accept-charset="', $context['character_set'], '"', !empty($context['force_form_onsubmit']) ? ' onsubmit="' . $context['force_form_onsubmit'] . '"' : '', '>';
+		<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'] . '"' : '', '>';
 
 	// Is there a custom title?
 	if (isset($context['settings_title']))
 		echo '
 			<div class="cat_bar">
 				<h3 class="catbg">
-					', $context['settings_title'], '
+					', $context['settings_title'], ' - Bar 1
 				</h3>
 			</div>';
 
@@ -798,7 +756,6 @@ function template_show_settings()
 				echo '
 					</dl>
 				</div>
-				<span class="botslice"><span></span></span>
 			</div>';
 			}
 
@@ -808,8 +765,8 @@ function template_show_settings()
 				echo '
 					<div class="cat_bar">
 						<h3 class="', !empty($config_var['class']) ? $config_var['class'] : 'catbg', '"', !empty($config_var['force_div_id']) ? ' id="' . $config_var['force_div_id'] . '"' : '', '>
-							', ($config_var['help'] ? '<a href="' . $scripturl . '?action=helpadmin;help=' . $config_var['help'] . '" onclick="return reqWin(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" class="icon" alt="' . $txt['help'] . '" /></a>' : ''), '
-							', $config_var['label'], '
+							', ($config_var['help'] ? '<a href="' . $scripturl . '?action=helpadmin;help=' . $config_var['help'] . '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" class="icon" alt="' . $txt['help'] . '" /></a>' : ''), '
+							', $config_var['label'], ' - Bar 2
 						</h3>
 					</div>';
 			}
@@ -831,7 +788,6 @@ function template_show_settings()
 			$is_open = true;
 			echo '
 			<div class="windowbg2">
-				<span class="topslice"><span></span></span>
 				<div class="content">
 					<dl class="settings">';
 		}
@@ -869,7 +825,7 @@ function template_show_settings()
 				// Show the [?] button.
 				if ($config_var['help'])
 					echo '
-							<a id="setting_', $config_var['name'], '" href="', $scripturl, '?action=helpadmin;help=', $config_var['help'], '" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a><span', ($config_var['disabled'] ? ' style="color: #777777;"' : ($config_var['invalid'] ? ' class="error"' : '')), '><label for="', $config_var['name'], '">', $config_var['label'], '</label>', $subtext, ($config_var['type'] == 'password' ? '<br /><em>' . $txt['admin_confirm_password'] . '</em>' : ''), '</span>
+							<a id="setting_', $config_var['name'], '" href="', $scripturl, '?action=helpadmin;help=', $config_var['help'], '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a><span', ($config_var['disabled'] ? ' style="color: #777777;"' : ($config_var['invalid'] ? ' class="error"' : '')), '><label for="', $config_var['name'], '">', $config_var['label'], '</label>', $subtext, ($config_var['type'] == 'password' ? '<br /><em>' . $txt['admin_confirm_password'] . '</em>' : ''), '</span>
 						</dt>';
 				else
 					echo '
@@ -920,7 +876,7 @@ function template_show_settings()
 						foreach ($bbcColumn as $bbcTag)
 							echo '
 										<li class="list_bbc floatleft">
-											<input type="checkbox" name="', $config_var['name'], '_enabledTags[]" id="tag_', $config_var['name'], '_', $bbcTag['tag'], '" value="', $bbcTag['tag'], '"', !in_array($bbcTag['tag'], $context['bbc_sections'][$config_var['name']]['disabled']) ? ' checked="checked"' : '', ' class="input_check" /> <label for="tag_', $config_var['name'], '_', $bbcTag['tag'], '">', $bbcTag['tag'], '</label>', $bbcTag['show_help'] ? ' (<a href="' . $scripturl . '?action=helpadmin;help=tag_' . $bbcTag['tag'] . '" onclick="return reqWin(this.href);">?</a>)' : '', '
+											<input type="checkbox" name="', $config_var['name'], '_enabledTags[]" id="tag_', $config_var['name'], '_', $bbcTag['tag'], '" value="', $bbcTag['tag'], '"', !in_array($bbcTag['tag'], $context['bbc_sections'][$config_var['name']]['disabled']) ? ' checked="checked"' : '', ' class="input_check" /> <label for="tag_', $config_var['name'], '_', $bbcTag['tag'], '">', $bbcTag['tag'], '</label>', $bbcTag['show_help'] ? ' (<a href="' . $scripturl . '?action=helpadmin;help=tag_' . $bbcTag['tag'] . '" onclick="return reqOverlayDiv(this.href);">?</a>)' : '', '
 										</li>';
 					}
 					echo '			</ul>
@@ -964,14 +920,11 @@ function template_show_settings()
 
 	if (empty($context['settings_save_dont_show']))
 		echo '
-					<hr class="hrcolor" />
-					<input type="submit" value="', $txt['save'], '"', (!empty($context['save_disabled']) ? ' disabled="disabled"' : ''), (!empty($context['settings_save_onclick']) ? ' onclick="' . $context['settings_save_onclick'] . '"' : ''), ' class="button_submit" />
-					<br class="clear_right" />';
+					<input type="submit" value="', $txt['save'], '"', (!empty($context['save_disabled']) ? ' disabled="disabled"' : ''), (!empty($context['settings_save_onclick']) ? ' onclick="' . $context['settings_save_onclick'] . '"' : ''), ' class="button_submit" />';
 
 	if ($is_open)
 		echo '
 				</div>
-				<span class="botslice"><span></span></span>
 			</div>';
 
 
@@ -991,8 +944,7 @@ function template_show_settings()
 	echo '
 		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	if (!empty($context['settings_post_javascript']))
 		echo '
@@ -1008,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');
 
@@ -1034,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']))
 	{
@@ -1049,13 +1001,12 @@ function template_edit_profile_field()
 	echo '
 	<div id="admincenter">
 		<form action="', $scripturl, '?action=admin;area=featuresettings;sa=profileedit;fid=', $context['fid'], ';', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
-			<div class="cat_bar">
+			<div id="section_header" class="cat_bar">
 				<h3 class="catbg">
 					', $context['page_title'], '
 				</h3>
 			</div>
 			<div class="windowbg">
-				<span class="topslice"><span></span></span>
 				<div class="content">
 					<fieldset>
 						<legend>', $txt['custom_edit_general'], '</legend>
@@ -1113,7 +1064,7 @@ function template_edit_profile_field()
 								</select>
 							</dd>
 							<dt>
-								<a id="field_show_enclosed" href="', $scripturl, '?action=helpadmin;help=field_show_enclosed" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
+								<a id="field_show_enclosed" href="', $scripturl, '?action=helpadmin;help=field_show_enclosed" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
 								<strong><label for="enclose">', $txt['custom_edit_enclose'], ':</label></strong><br />
 								<span class="smalltext">', $txt['custom_edit_enclose_desc'], '</span>
 							</dt>
@@ -1158,7 +1109,7 @@ function template_edit_profile_field()
 								<input type="checkbox" name="bbc" id="bbc_dd"', $context['field']['bbc'] ? ' checked="checked"' : '', ' class="input_check" />
 							</dd>
 							<dt id="options_dt">
-								<a href="', $scripturl, '?action=helpadmin;help=customoptions" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
+								<a href="', $scripturl, '?action=helpadmin;help=customoptions" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
 								<strong><label for="options_dd">', $txt['custom_edit_options'], ':</label></strong><br />
 								<span class="smalltext">', $txt['custom_edit_options_desc'], '</span>
 							</dt>
@@ -1187,7 +1138,7 @@ function template_edit_profile_field()
 						<legend>', $txt['custom_edit_advanced'], '</legend>
 						<dl class="settings">
 							<dt id="mask_dt">
-								<a id="custom_mask" href="', $scripturl, '?action=helpadmin;help=custom_mask" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
+								<a id="custom_mask" href="', $scripturl, '?action=helpadmin;help=custom_mask" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a>
 								<strong><label for="mask">', $txt['custom_edit_mask'], ':</label></strong><br />
 								<span class="smalltext">', $txt['custom_edit_mask_desc'], '</span>
 							</dt>
@@ -1231,7 +1182,6 @@ function template_edit_profile_field()
 							</dd>
 						</dl>
 					</fieldset>
-					<hr class="hrcolor" />
 						<input type="submit" name="save" value="', $txt['save'], '" class="button_submit" />';
 
 	if ($context['fid'])
@@ -1239,15 +1189,12 @@ function template_edit_profile_field()
 						<input type="submit" name="delete" value="', $txt['delete'], '" onclick="return confirm(\'', $txt['custom_edit_delete_sure'], '\');" class="button_submit" />';
 
 	echo '
-					<br class="clear_right" />
 				</div>
-				<span class="botslice"><span></span></span>
 			</div>
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-ecp_token_var'], '" value="', $context['admin-ecp_token'], '" />
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	// Get the javascript bits right!
 	echo '
@@ -1262,7 +1209,7 @@ function template_admin_search_results()
 	global $context, $txt, $settings, $options, $scripturl;
 
 	echo '
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">
 				<object id="quick_search">
 					<form action="', $scripturl, '?action=admin;area=search" method="post" accept-charset="', $context['character_set'], '" class="floatright">
@@ -1271,11 +1218,10 @@ function template_admin_search_results()
 						<input type="submit" name="search_go" value="', $txt['admin_search_results_again'], '" class="button_submit" />
 					</form>
 				</object>
-				<span class="ie6_header floatleft"><img src="' . $settings['images_url'] . '/buttons/search.png" alt="" />&nbsp;', sprintf($txt['admin_search_results_desc'], $context['search_term']), '</span>
+				<img src="' . $settings['images_url'] . '/buttons/search.png" alt="" />&nbsp;', sprintf($txt['admin_search_results_desc'], $context['search_term']), '
 			</h3>
 		</div>
-	<div class="windowbg nopadding">
-		<span class="topslice"><span></span></span>
+	<div class="windowbg2 generic_list_wrapper">
 		<div class="content">';
 
 	if (empty($context['search_results']))
@@ -1306,7 +1252,7 @@ function template_admin_search_results()
 			else
 			{
 				echo '
-				<li class="windowbg">
+				<li class="windowbg2">
 					<a href="', $result['url'], '"><strong>', $result['name'], '</strong></a> [', isset($txt['admin_search_section_' . $result['type']]) ? $txt['admin_search_section_' . $result['type']] : $result['type'] , ']';
 
 				if ($result['help'])
@@ -1323,9 +1269,7 @@ function template_admin_search_results()
 
 	echo '
 		</div>
-		<span class="botslice"><span></span></span>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Turn on and off certain key features.
@@ -1400,7 +1344,7 @@ function template_core_features()
 						{
 							$(ajax_infobar).attr(\'class\', \'errorbox\');
 							$(ajax_infobar).html(' . JavaScriptEscape($txt['core_settings_generic_error']) . ').slideDown(\'fast\');
-							
+
 						}
 					}
 				});
@@ -1411,7 +1355,7 @@ function template_core_features()
 	if ($context['is_new_install'])
 	{
 		echo '
-			<div class="cat_bar">
+			<div id="section_header" class="cat_bar">
 				<h3 class="catbg">
 					', $txt['core_settings_welcome_msg'], '
 				</h3>
@@ -1422,12 +1366,12 @@ function template_core_features()
 	}
 
 	echo '
-		<form id="core_features" action="', $scripturl, '?action=admin;area=corefeatures" method="post" accept-charset="', $context['character_set'], '">
-			<div class="cat_bar">
+			<div id="section_header" class="cat_bar">
 				<h3 class="catbg">
 					', $txt['core_settings_title'], '
 				</h3>
 			</div>
+		<form id="core_features" action="', $scripturl, '?action=admin;area=corefeatures" method="post" accept-charset="', $context['character_set'], '">
 			<div style="display:none" id="activation_message" class="errorbox"></div>';
 
 	$alternate = true;
@@ -1437,18 +1381,15 @@ function template_core_features()
 	{
 		$num++;
 		echo '
-			<div class="windowbg', $alternate ? '2' : '', '">
-				<span class="topslice"><span></span></span>
-				<div class="content features">
-					<img class="features_image" src="', $feature['image'], '" alt="', $feature['title'], '" />
-					<div class="features_switch" id="js_feature_', $id, '">
-							<label class="core_features_hide" for="feature_', $id, '">', $txt['core_settings_enabled'], '<input class="core_features_status_box" type="checkbox" name="feature_', $id, '" id="feature_', $id, '"', $feature['enabled'] ? ' checked="checked"' : '', ' /></label>
-							<img class="core_features_img ', $feature['state'], '" src="', $settings['images_url'], '/admin/switch_', $feature['state'], '.png" id="switch_', $id, '" style="margin-top: 1.3em;display:none" alt="', $txt['core_settings_switch_' . $feature['state']], '" title="', $txt['core_settings_switch_' . $feature['state']], '" />
-					</div>
-					<h4 id="feature_link_' . $id . '">', ($feature['enabled'] && $feature['url'] ? '<a href="' . $feature['url'] . '">' . $feature['title'] . '</a>' : $feature['title']), '</h4>
-					<p>', $feature['desc'], '</p>
+			<div class="content features">
+				<img class="features_image" src="', $feature['image'], '" alt="', $feature['title'], '" />
+				<div class="features_switch" id="js_feature_', $id, '">
+					<label class="core_features_hide" for="feature_', $id, '">', $txt['core_settings_enabled'], '<input class="core_features_status_box" type="checkbox" name="feature_', $id, '" id="feature_', $id, '"', $feature['enabled'] ? ' checked="checked"' : '', ' /></label>
+					<img class="core_features_img ', $feature['state'], '" src="', $settings['images_url'], '/admin/switch_', $feature['state'], '.png" id="switch_', $id, '" style="margin-top: 1.3em;display:none" alt="', $txt['core_settings_switch_' . $feature['state']], '" title="', $txt['core_settings_switch_' . $feature['state']], '" />
 				</div>
-				<span class="botslice clear_right"><span></span></span>
+				<h4 id="feature_link_' . $id . '">', ($feature['enabled'] && $feature['url'] ? '<a href="' . $feature['url'] . '">' . $feature['title'] . '</a>' : $feature['title']), '</h4>
+				<p>', $feature['desc'], '</p>
+				<hr />
 			</div>';
 
 		$alternate = !$alternate;
@@ -1461,8 +1402,7 @@ function template_core_features()
 				<input id="core_features_submit" type="submit" value="', $txt['save'], '" name="save" class="button_submit" />
 			</div>
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 
@@ -1503,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.
@@ -1521,13 +1459,12 @@ function template_repair_boards()
 
 	echo '
 	<div id="admincenter">
-		<div class="cat_bar">
+		<div id="section_header" class="cat_bar">
 			<h3 class="catbg">',
 				$context['error_search'] ? $txt['errors_list'] : $txt['errors_fixing'] , '
 			</h3>
 		</div>
 		<div class="windowbg">
-			<span class="topslice"><span></span></span>
 			<div class="content">';
 
 	// Are we actually fixing them, or is this just a prompt?
@@ -1587,10 +1524,8 @@ function template_repair_boards()
 
 	echo '
 			</div>
-			<span class="botslice"><span></span></span>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	if (!empty($context['redirect_to_recount']))
 	{
@@ -1620,6 +1555,9 @@ function template_php_info()
 {
 	global $context, $txt;
 
+	echo '
+	<div class="generic_list_wrapper" id="phpinfo">';
+
 	// for each php info area
 	foreach ($context['pinfo'] as $area => $php_area)
 	{
@@ -1636,7 +1574,7 @@ function template_php_info()
 
 		$alternate = true;
 		$localmaster = true;
-		
+
 		// and for each setting in this category
 		foreach ($php_area as $key => $setting)
 		{
@@ -1654,7 +1592,7 @@ function template_php_info()
 		</tr>';
 					$localmaster = false;
 				}
-					
+
 				echo '
 		<tr>
 			<td align="left" width="33%" class="windowbg', $alternate ? '2' : '', '">', $key, '</td>';
@@ -1676,14 +1614,16 @@ function template_php_info()
 			<td align="left" class="windowbg', $alternate ? '2' : '', '" colspan="2">', $setting, '</td>
 		</tr>';
 			}
-		
+
 			$alternate = !$alternate;
 		}
 		echo '
 		</tbody>
-	</table>
-	<br class="clear" />';
+	</table><br />';
 	}
+
+	echo '
+	</div>';
 }
 
 function template_clean_cache_button_above()
@@ -1699,7 +1639,6 @@ function template_clean_cache_button_below()
 		<h3 class="catbg">', $txt['maintain_cache'], '</h3>
 	</div>
 	<div class="windowbg">
-		<span class="topslice"><span></span></span>
 		<div class="content">
 			<form action="', $scripturl, '?action=admin;area=maintain;sa=routine;activity=cleancache" method="post" accept-charset="', $context['character_set'], '">
 				<p>', $txt['maintain_cache_info'], '</p>
@@ -1708,8 +1647,6 @@ function template_clean_cache_button_below()
 				<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
 			</form>
 		</div>
-		<br class="clear" />
-		<span class="botslice"><span></span></span>
 	</div>';
 }
 

+ 32 - 48
Themes/default/BoardIndex.template.php

@@ -108,10 +108,6 @@ function template_main()
 			echo '
 								<a class="collapse" href="', $category['collapse_href'], '" title="' ,$category['is_collapsed'] ? $txt['show'] : $txt['hide'] ,'">', $category['collapse_image'], '</a>';
 
-		if (!$context['user']['is_guest'] && !empty($category['show_unread']))
-			echo '
-								<a class="unreadlink" href="', $scripturl, '?action=unread;c=', $category['id'], '">', $txt['view_unread_category'], '</a>';
-
 		echo '
 								', $category['link'], '
 							</h3>
@@ -174,8 +170,8 @@ function template_main()
 					echo '
 					</td>
 					<td class="windowbg stats">
-						<p>', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], ' <br />
-						', $board['is_redirect'] ? '' : comma_format($board['topics']) . ' ' . $txt['board_topics'], '
+						<p>', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], '
+						', $board['is_redirect'] ? '' : '<br /> '.comma_format($board['topics']) . ' ' . $txt['board_topics'], '
 						</p>
 					</td>
 					<td class="lastpost">';
@@ -227,35 +223,22 @@ function template_main()
 	}
 	echo '
 		</table>
-	</div>';
+	</div>
+		<ul id="posting_icons">';
 
 	if ($context['user']['is_logged'])
-	{
-		echo '
-	<div id="posting_icons" class="floatleft">';
+	echo '
+			<li class="floatleft"><img src="', $settings['images_url'], '/', $context['theme_variant_url'], 'new_some.png" alt="" /> ', $txt['new_posts'], '</li>';
 
-		echo '
-		<ul class="reset">
-			<li class="floatleft"><img src="', $settings['images_url'], '/', $context['theme_variant_url'], 'new_some.png" alt="" /> ', $txt['new_posts'], '</li>
+	echo '
 			<li class="floatleft"><img src="', $settings['images_url'], '/', $context['theme_variant_url'], 'new_none.png" alt="" /> ', $txt['old_posts'], '</li>
 			<li class="floatleft"><img src="', $settings['images_url'], '/', $context['theme_variant_url'], 'new_redirect.png" alt="" /> ', $txt['redirect_board'], '</li>
-		</ul>
-	</div>';
+		</ul>';
 
-		// Show the mark all as read button?
-		if ($settings['show_mark_read'] && !empty($context['categories']))
-			echo '<div class="mark_read">', template_button_strip($context['mark_read_button'], 'right'), '</div>';
-	}
-	else
-	{
-		echo '
-	<div id="posting_icons" class="flow_hidden">
-		<ul class="reset">
-			<li class="floatleft"><img src="', $settings['images_url'], '/new_none.png" alt="" /> ', $txt['old_posts'], '</li>
-			<li class="floatleft"><img src="', $settings['images_url'], '/new_redirect.png" alt="" /> ', $txt['redirect_board'], '</li>
-		</ul>
-	</div>';
-	}
+	// Show the mark all as read button?
+	if ($settings['show_mark_read'] && !empty($context['categories']))
+	echo '
+		<div class="mark_read">', template_button_strip($context['mark_read_button'], 'right'), '</div>';
 
 	template_info_center();
 }
@@ -269,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>
@@ -303,19 +286,21 @@ function template_info_center()
 		elseif (!empty($context['latest_posts']))
 		{
 			echo '
-				<dl id="ic_recentposts">';
+				<table id="ic_recentposts">';
 
 			/* Each post in latest_posts has:
 					board (with an id, name, and link.), topic (the topic's id.), poster (with id, name, and link.),
 					subject, short_subject (shortened with...), time, link, and href. */
 			foreach ($context['latest_posts'] as $post)
 				echo '
-					<dt>
-						<strong>', $post['link'], '</strong> ', $txt['by'], ' ', $post['poster']['link'], ' (', $post['board']['link'], ')
-					</dt>
-					<dd>', $post['time'], '</dd>';
+					<tr>
+						<td class="recentpost"><strong>', $post['link'], '</strong></td>
+						<td class="recentposter">', $txt['by'], ' ', $post['poster']['link'], '</td>
+						<td class="recentboard">(', $post['board']['link'], ')</td>
+						<td class="recenttime">', $post['time'], '</td>
+					</tr>';
 			echo '
-				</dl>';
+				</table>';
 		}
 		echo '
 			</div>';
@@ -342,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)
@@ -373,13 +358,13 @@ function template_info_center()
 		echo '
 			<div class="title_barIC">
 				<h4 class="titlebg">
-					<a href="', $scripturl, '?action=stats"><img class="icon" src="', $settings['images_url'], '/icons/info.png" alt="" />', $txt['forum_stats'], '</a>
+					<a href="', $scripturl, '?action=stats" title="', $txt['more_stats'], '"><img class="icon" src="', $settings['images_url'], '/icons/info.png" alt="" />', $txt['forum_stats'], '</a>
 				</h4>
 			</div>
 			<p class="inline">
 				', $context['common_stats']['boardindex_total_posts'], '', !empty($settings['show_latest_member']) ? ' - '. $txt['latest_member'] . ': <strong> ' . $context['common_stats']['latest_member']['link'] . '</strong>' : '', '<br />
 				', (!empty($context['latest_post']) ? $txt['latest_post'] . ': <strong>&quot;' . $context['latest_post']['link'] . '&quot;</strong>  ( ' . $context['latest_post']['time'] . ' )<br />' : ''), '
-				<a href="', $scripturl, '?action=recent">', $txt['recent_view'], '</a>', $context['show_stats'] ? '&nbsp;&nbsp;&nbsp;<a href="' . $scripturl . '?action=stats">' . $txt['more_stats'] . '</a>' : '', '
+				<a href="', $scripturl, '?action=recent">', $txt['recent_view'], '</a>
 			</p>';
 	}
 
@@ -405,7 +390,10 @@ function template_info_center()
 	if (!empty($bracketList))
 		echo ' (' . implode(', ', $bracketList) . ')';
 
-	echo $context['show_who'] ? '</a>' : '', '<br />';
+	echo $context['show_who'] ? '</a>' : '', '
+
+				&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.
 	if (!empty($context['users_online']))
@@ -420,10 +408,6 @@ function template_info_center()
 	}
 
 	echo '
-			</p>
-			<p class="last">
-				', $txt['most_online_today'], ': <strong>', comma_format($modSettings['mostOnlineToday']), '</strong>.
-				', $txt['most_online_ever'], ': ', comma_format($modSettings['mostOnline']), ' (', timeformat($modSettings['mostDate']), ')
 			</p>';
 
 	// If they are logged in, but statistical information is off... show a personal message bar.
@@ -457,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 />';
 					}

+ 260 - 239
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?
@@ -146,8 +146,11 @@ function template_main()
 	// Show the page index... "Pages: [1]".
 	echo '
 			<div class="pagesection">
-				<div class="nextlinks">', $context['previous_next'], '</div>', template_button_strip($context['normal_buttons'], 'right'), '
-				<div class="pagelinks floatleft">', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . ' &nbsp;&nbsp;<a href="#bot"><strong>' . $txt['go_down'] . '</strong></a>' : '', '</div>
+				', template_button_strip($context['normal_buttons'], 'right'), '
+				', !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '<a href="#bot" class="topbottom floatleft">' . $txt['go_down'] . '</a>' : '', '
+				<div class="pagelinks floatleft">
+					', $context['page_index'], '
+				</div>
 			</div>';
 
 	// Show the topic information - icon, subject, etc.
@@ -155,29 +158,12 @@ function template_main()
 			<div id="forumposts">
 				<div class="cat_bar">
 					<h3 class="catbg">
-						<img src="', $settings['images_url'], '/topic/', $context['class'], '.png" align="bottom" alt="" />
-						<span id="author">', $txt['author'], '</span>
-						', $txt['topic'], ': ', $context['subject'], ' &nbsp;(', $txt['read'], ' ', $context['num_views'], ' ', $txt['times'], ')
+						<img src="', $settings['images_url'], '/topic/', $context['class'], '.png" alt="" />
+						', $txt['topic'], ': ', $context['subject'], '&nbsp;<span>(', $txt['read'], ' ', $context['num_views'], ' ', $txt['times'], ')</span>
+						<span class="nextlinks floatright">', $context['previous_next'], '</span>
 					</h3>
 				</div>';
 
-	if (!empty($settings['display_who_viewing']))
-	{
-		echo '
-				<p id="whoisviewing" class="smalltext">';
-
-		// Show just numbers...?
-		if ($settings['display_who_viewing'] == 1)
-				echo count($context['view_members']), ' ', count($context['view_members']) == 1 ? $txt['who_member'] : $txt['members'];
-		// Or show the actual people viewing the topic?
-		else
-			echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) || $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')');
-
-		// Now show how many guests are here too.
-		echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_topic'], '
-				</p>';
-	}
-
 	echo '
 				<form action="', $scripturl, '?action=quickmod2;topic=', $context['current_topic'], '.', $context['start'], '" method="post" accept-charset="', $context['character_set'], '" name="quickModForm" id="quickModForm" style="margin: 0;" onsubmit="return oQuickModify.bInEditMode ? oQuickModify.modifySave(\'' . $context['session_id'] . '\', \'' . $context['session_var'] . '\') : false">';
 
@@ -212,78 +198,68 @@ function template_main()
 		// Show information about the poster of this message.
 		echo '
 						<div class="poster">
-							<h4>';
-
-		// Show online and offline buttons?
-		if (!empty($modSettings['onlineEnable']) && !$message['member']['is_guest'])
-			echo '
-								', $context['can_send_pm'] ? '<a href="' . $message['member']['online']['href'] . '" title="' . $message['member']['online']['label'] . '">' : '', '<img src="', $message['member']['online']['image_href'], '" alt="', $message['member']['online']['text'], '" />', $context['can_send_pm'] ? '</a>' : '';
+							<ul class="dropmenu">
+								<li>
+									<h4>';
 
 		// Show a link to the member's profile.
 		echo '
-								', $message['member']['link'], '
-							</h4>
-							<ul class="reset smalltext" id="msg_', $message['id'], '_extra_info">';
+										<a href="', $scripturl, '?action=profile;u=', $message['member']['id'], '">
+											<span style="padding: 6px; display: block;">', $message['member']['name'], '</span>';
 
-		// Show the member's custom title, if they have one.
-		if (!empty($message['member']['title']))
+		// Show avatars, images, etc.?
+		if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image']))
 			echo '
-								<li class="title">', $message['member']['title'], '</li>';
 
-		// Show the member's primary group (like 'Administrator') if they have one.
-		if (!empty($message['member']['group']))
+											', $message['member']['avatar']['image'], '';
+
 			echo '
-								<li class="membergroup">', $message['member']['group'], '</li>';
+										</a>
+									</h4>';
+
+		// [WIP] The new member info dropdown starts here. Note that conditionals have not been fully checked yet.
+			echo '
+									<ul class="smalltext" id="msg_', $message['id'], '_extra_info">';
 
 		// Don't show these things for guests.
 		if (!$message['member']['is_guest'])
 		{
+
 			// Show the post group if and only if they have no other group or the option is on, and they are in a post group.
 			if ((empty($settings['hide_post_group']) || $message['member']['group'] == '') && $message['member']['post_group'] != '')
 				echo '
-								<li class="postgroup">', $message['member']['post_group'], '</li>';
-			echo '
-								<li class="icons">', $message['member']['group_icons'], '</li>';
-
-			// Show avatars, images, etc.?
-			if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($message['member']['avatar']['image']))
-				echo '
-								<li class="avatar">
-									<a href="', $scripturl, '?action=profile;u=', $message['member']['id'], '">
-										', $message['member']['avatar']['image'], '
-									</a>
-								</li>';
+										<li class="postgroup">', $message['member']['post_group'], '</li>';
 
 			// Show how many posts they have made.
 			if (!isset($context['disabled_fields']['posts']))
 				echo '
-								<li class="postcount">', $txt['member_postcount'], ': ', $message['member']['posts'], '</li>';
+										<li class="postcount">', $txt['member_postcount'], ': ', $message['member']['posts'], '</li>';
 
 			// Is karma display enabled?  Total or +/-?
 			if ($modSettings['karmaMode'] == '1')
 				echo '
-								<li class="karma">', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '</li>';
+										<li class="karma">', $modSettings['karmaLabel'], ' ', $message['member']['karma']['good'] - $message['member']['karma']['bad'], '</li>';
 			elseif ($modSettings['karmaMode'] == '2')
 				echo '
-								<li class="karma">', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '</li>';
+										<li class="karma">', $modSettings['karmaLabel'], ' +', $message['member']['karma']['good'], '/-', $message['member']['karma']['bad'], '</li>';
 
 			// Is this user allowed to modify this member's karma?
 			if ($message['member']['karma']['allow'])
 				echo '
-								<li class="karma_allow">
-									<a href="', $scripturl, '?action=modifykarma;sa=applaud;uid=', $message['member']['id'], ';topic=', $context['current_topic'], '.' . $context['start'], ';m=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $modSettings['karmaApplaudLabel'], '</a>
-									<a href="', $scripturl, '?action=modifykarma;sa=smite;uid=', $message['member']['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';m=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $modSettings['karmaSmiteLabel'], '</a>
-								</li>';
+										<li class="karma_allow">
+											<a href="', $scripturl, '?action=modifykarma;sa=applaud;uid=', $message['member']['id'], ';topic=', $context['current_topic'], '.' . $context['start'], ';m=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $modSettings['karmaApplaudLabel'], '</a>
+											<a href="', $scripturl, '?action=modifykarma;sa=smite;uid=', $message['member']['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';m=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $modSettings['karmaSmiteLabel'], '</a>
+										</li>';
 
 			// Show the member's gender icon?
 			if (!empty($settings['show_gender']) && $message['member']['gender']['image'] != '' && !isset($context['disabled_fields']['gender']))
 				echo '
-								<li class="gender">', $txt['gender'], ': ', $message['member']['gender']['image'], '</li>';
+										<li class="gender">', $txt['gender'], ': ', $message['member']['gender']['image'], '</li>';
 
 			// Show their personal text?
 			if (!empty($settings['show_blurb']) && $message['member']['blurb'] != '')
 				echo '
-								<li class="blurb">', $message['member']['blurb'], '</li>';
+										<li class="blurb">', $message['member']['blurb'], '</li>';
 
 			// Any custom fields to show as icons?
 			if (!empty($message['member']['custom_fields']))
@@ -294,62 +270,55 @@ function template_main()
 					if ($custom['placement'] != 1 || empty($custom['value']))
 						continue;
 					if (empty($shown))
-					{
-						$shown = true;
-						echo '
-								<li class="im_icons">
-									<ul>';
-					}
+							{
+								$shown = true;
+								echo '
+										<li class="im_icons">
+											<ol>';
+							}
 					echo '
-										<li>', $custom['value'], '</li>';
+												<li>', $custom['value'], '</li>';
 				}
-				if ($shown)
-					echo '
-									</ul>
-								</li>';
+
+			if ($shown)
+				echo '
+											</ol>
+										</li>';
 			}
 
 			// This shows the popular messaging icons.
 			if ($message['member']['has_messenger'] && $message['member']['can_view_profile'])
 				echo '
-								<li class="im_icons">
-									<ul>
-										', !empty($message['member']['icq']['link']) ? '<li>' . $message['member']['icq']['link'] . '</li>' : '', '
-										', !empty($message['member']['msn']['link']) ? '<li>' . $message['member']['msn']['link'] . '</li>' : '', '
-										', !empty($message['member']['aim']['link']) ? '<li>' . $message['member']['aim']['link'] . '</li>' : '', '
-										', !empty($message['member']['yim']['link']) ? '<li>' . $message['member']['yim']['link'] . '</li>' : '', '
-									</ul>
-								</li>';
-
-			// Show the profile, website, email address, and personal message buttons.
+										<li class="im_icons">
+											<hr />
+											<ol>
+												', !empty($message['member']['icq']['link']) ? '<li>' . $message['member']['icq']['link'] . '</li>' : '', '
+												', !empty($message['member']['msn']['link']) ? '<li>' . $message['member']['msn']['link'] . '</li>' : '', '
+												', !empty($message['member']['aim']['link']) ? '<li>' . $message['member']['aim']['link'] . '</li>' : '', '
+												', !empty($message['member']['yim']['link']) ? '<li>' . $message['member']['yim']['link'] . '</li>' : '', '
+											</ol>
+										</li>';
+
+			// Show the website and email address buttons.
 			if ($message['member']['show_profile_buttons'])
 			{
 				echo '
-								<li class="profile">
-									<ul>';
-				// Don't show the profile button if you're not allowed to view the profile.
-				if ($message['member']['can_view_profile'])
-					echo '
-										<li><a href="', $message['member']['href'], '">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/icons/profile_sm.png" alt="' . $txt['view_profile'] . '" title="' . $txt['view_profile'] . '" />' : $txt['view_profile']), '</a></li>';
+										<li class="profile">
+											<ol>';
 
 				// Don't show an icon if they haven't specified a website.
 				if ($message['member']['website']['url'] != '' && !isset($context['disabled_fields']['website']))
 					echo '
-										<li><a href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank" class="new_win">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/www_sm.png" alt="' . $message['member']['website']['title'] . '" />' : $txt['www']), '</a></li>';
+												<li><a href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank" class="new_win">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/www_sm.png" alt="' . $message['member']['website']['title'] . '" />' : $txt['www']), '</a></li>';
 
 				// Don't show the email address if they want it hidden.
 				if (in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum')) && $context['can_send_email'])
 					echo '
-										<li><a href="', $scripturl, '?action=emailuser;sa=email;msg=', $message['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.png" alt="' . $txt['email'] . '" title="' . $txt['email'] . '" />' : $txt['email']), '</a></li>';
+												<li><a href="', $scripturl, '?action=emailuser;sa=email;msg=', $message['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.png" alt="' . $txt['email'] . '" title="' . $txt['email'] . '" />' : $txt['email']), '</a></li>';
 
-				// Since we know this person isn't a guest, you *can* message them.
-				if ($context['can_send_pm'])
 					echo '
-										<li><a href="', $scripturl, '?action=pm;sa=send;u=', $message['member']['id'], '" title="', $message['member']['online']['is_online'] ? $txt['pm_online'] : $txt['pm_offline'], '">', $settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/im_' . ($message['member']['online']['is_online'] ? 'on' : 'off') . '.png" alt="' . ($message['member']['online']['is_online'] ? $txt['pm_online'] : $txt['pm_offline']) . '" />' : ($message['member']['online']['is_online'] ? $txt['pm_online'] : $txt['pm_offline']), '</a></li>';
-
-				echo '
-									</ul>
-								</li>';
+											</ol>
+										</li>';
 			}
 
 			// Any custom fields for standard placement?
@@ -358,86 +327,122 @@ function template_main()
 				foreach ($message['member']['custom_fields'] as $custom)
 					if (empty($custom['placement']) || empty($custom['value']))
 						echo '
-								<li class="custom">', $custom['title'], ': ', $custom['value'], '</li>';
+										<li class="custom">', $custom['title'], ': ', $custom['value'], '</li>';
 			}
 
-			// Are we showing the warning status?
-			if ($message['member']['can_see_warning'])
-				echo '
-								<li class="warning">', $context['can_issue_warning'] ? '<a href="' . $scripturl . '?action=profile;area=issuewarning;u=' . $message['member']['id'] . '">' : '', '<img src="', $settings['images_url'], '/warning_', $message['member']['warning_status'], '.png" alt="', $txt['user_warn_' . $message['member']['warning_status']], '" />', $context['can_issue_warning'] ? '</a>' : '', '<span class="warn_', $message['member']['warning_status'], '">', $txt['warn_' . $message['member']['warning_status']], '</span></li>';
 		}
+
 		// Otherwise, show the guest's email.
 		elseif (!empty($message['member']['email']) && in_array($message['member']['show_email'], array('yes', 'yes_permission_override', 'no_through_forum')) && $context['can_send_email'])
 			echo '
-								<li class="email"><a href="', $scripturl, '?action=emailuser;sa=email;msg=', $message['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.png" alt="' . $txt['email'] . '" title="' . $txt['email'] . '" />' : $txt['email']), '</a></li>';
+										<li class="email"><a href="', $scripturl, '?action=emailuser;sa=email;msg=', $message['id'], '" rel="nofollow">', ($settings['use_image_buttons'] ? '<img src="' . $settings['images_url'] . '/email_sm.png" alt="' . $txt['email'] . '" title="' . $txt['email'] . '" />' : $txt['email']), '</a></li>';
 
-		// Done with the information about the poster... on to the post itself.
-		echo '
-							</ul>
-						</div>
-						<div class="postarea">
-							<div class="flow_hidden">
-								<div class="keyinfo">
-									<div class="messageicon">
-										<img src="', $message['icon_url'] . '" alt=""', $message['can_modify'] ? ' id="msg_icon_' . $message['id'] . '"' : '', ' />
-									</div>
-									<h5 id="subject_', $message['id'], '">
-										<a href="', $message['href'], '" rel="nofollow">', $message['subject'], '</a>
-									</h5>
-									<div class="smalltext">&#171; <strong>', !empty($message['counter']) ? $txt['reply_noun'] . ' #' . $message['counter'] : '', ' ', $txt['on'], ':</strong> ', $message['time'], ' &#187;</div>
-									<div id="msg_', $message['id'], '_quick_mod"></div>
-								</div>';
+		// Stuff for the staff to wallop them with.
+		// Maybe they want to report this post to the moderator(s)?
+		if ($context['can_report_moderator'])
+			echo '
+										<li class="report_link"><hr /><a href="', $scripturl, '?action=reporttm;topic=', $context['current_topic'], '.', $message['counter'], ';msg=', $message['id'], '">', $txt['report_to_mod'], '</a></li>';
 
-		// If this is the first post, (#0) just say when it was posted - otherwise give the reply #.
-		if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg'])
+		// Can we issue a warning because of this post?  Remember, we can't give guests warnings.
+		if ($context['can_issue_warning'] && !$message['is_message_author'] && !$message['member']['is_guest'])
 			echo '
-								<ul class="reset smalltext quickbuttons">';
+										<li class="issue_warning"><a href="', $scripturl, '?action=profile;area=issuewarning;u=', $message['member']['id'], ';msg=', $message['id'], '"><img src="', $settings['images_url'], '/warn.png" alt="', $txt['issue_warning_post'], '" title="', $txt['issue_warning_post'], '" /></a></li>';
 
-		// Maybe we can approve it, maybe we should?
-		if ($message['can_approve'])
+			//echo '
+			//						<img class="centericon" src="', $settings['images_url'], '/ip.png" alt="" />';
+
+		// Show the IP to this user for this post - because you can moderate?
+		if (!empty($context['can_moderate_forum']) && !empty($message['member']['ip']))
 			echo '
-									<li><a href="', $scripturl, '?action=moderate;area=postmod;sa=approve;topic=', $context['current_topic'], '.', $context['start'], ';msg=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '"  class="approve_button">', $txt['approve'], '</a></li>';
+										<li class="poster_ip"><a href="', $scripturl, '?action=', !empty($message['member']['is_guest']) ? 'trackip' : 'profile;area=tracking;sa=ip;u=' . $message['member']['id'], ';searchip=', $message['member']['ip'], '">', $message['member']['ip'], '</a> <a href="', $scripturl, '?action=helpadmin;help=see_admin_ip" onclick="return reqOverlayDiv(this.href);" class="help">(?)</a></li>';
 
-		// Can they reply? Have they turned on quick reply?
-		if ($context['can_quote'] && !empty($options['display_quick_reply']))
+		// Or, should we show it because this is you?
+		elseif ($message['can_see_ip'])
 			echo '
-									<li><a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';last_msg=', $context['topic_last_message'], '" onclick="return oQuickReply.quote(', $message['id'], ');" class="quote_button">', $txt['quote'], '</a></li>';
+										<li class="poster_ip"><a href="', $scripturl, '?action=helpadmin;help=see_member_ip" onclick="return reqOverlayDiv(this.href);" class="help">', $message['member']['ip'], '</a></li>';
 
-		// So... quick reply is off, but they *can* reply?
-		elseif ($context['can_quote'])
+		// Okay, are you at least logged in?  Then we can show something about why IPs are logged...
+		elseif (!$context['user']['is_guest'])
 			echo '
-									<li><a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';last_msg=', $context['topic_last_message'], '" class="quote_button">', $txt['quote'], '</a></li>';
+										<li class="poster_ip"><a href="', $scripturl, '?action=helpadmin;help=see_member_ip" onclick="return reqOverlayDiv(this.href);" class="help">', $txt['logged'], '</a></li>';
 
-		// Can the user modify the contents of this post?
-		if ($message['can_modify'])
+		// Otherwise, you see NOTHING!
+		else
 			echo '
-									<li><a href="', $scripturl, '?action=post;msg=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], '" class="modify_button">', $txt['modify'], '</a></li>';
+										<li class="poster_ip">', $txt['logged'], '</li>';
 
-		// How about... even... remove it entirely?!
-		if ($message['can_remove'])
+		// Done with the information about the poster... on to the post itself.
 			echo '
-									<li><a href="', $scripturl, '?action=deletemsg;topic=', $context['current_topic'], '.', $context['start'], ';msg=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(\'', $txt['remove_message'], '?\');" class="remove_button">', $txt['remove'], '</a></li>';
+									</ul>
+								</li>';
 
-		// What about splitting it off the rest of the topic?
-		if ($context['can_split'] && !empty($context['real_num_replies']))
+		// Show the post group icons, but not for guests.
+		if (!$message['member']['is_guest'])
 			echo '
-									<li><a href="', $scripturl, '?action=splittopics;topic=', $context['current_topic'], '.0;at=', $message['id'], '" class="split_button">', $txt['split'], '</a></li>';
+								<li class="icons">', $message['member']['group_icons'], '</li>';
 
-		// Can we restore topics?
-		if ($context['can_restore_msg'])
+		// Show the member's primary group (like 'Administrator') if they have one.
+		if (!empty($message['member']['group']))
 			echo '
-									<li><a href="', $scripturl, '?action=restoretopic;msgs=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '" class="restore_button">', $txt['restore_message'], '</a></li>';
+								<li class="membergroup">', $message['member']['group'], '</li>';
 
-		// Show a checkbox for quick moderation?
-		if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove'])
+		// Show the member's custom title, if they have one.
+		if (!empty($message['member']['title']))
 			echo '
-									<li class="inline_mod_check" style="display: none;" id="in_topic_mod_check_', $message['id'], '"></li>';
+								<li class="title">', $message['member']['title'], '</li>';
 
-		if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg'])
+		// Show online and offline buttons? PHP could do with a little bit of cleaning up here for brevity, but it works.
+		// The plan is to make these buttons act sensibly, and link to your own inbox in your own posts (with new PM notification).
+		// Still has a little bit of hard-coded text. This may be a place where translators should be able to write inclusive strings,
+		// instead of dealing with $txt['by'] etc in the markup. Must be brief to work, anyway. Cannot ramble on at all.
+		if (($context['can_send_pm']) && ($message['is_message_author']))
+		{
 			echo '
-								</ul>';
+								<li class="poster_online"><a href="', $scripturl,'?action=pm">', $txt['pm_short'], ' ', $context['user']['unread_messages'] > 0 ? '[<strong>'. $context['user']['unread_messages'] . '</strong>]' : '' , '</a></li>';
+		}
 
-		echo '
+		if (($context['can_send_pm']) && (!$message['is_message_author']) && (!$message['member']['is_guest']))
+		{
+			if(!empty($modSettings['onlineEnable']))
+				echo '
+								<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>';
+		}
+
+		if ((!$context['can_send_pm']) && (!empty($modSettings['onlineEnable'])))
+			echo '
+								<li class="poster_online">', ($message['member']['online']['is_online']) ? $txt['online'] : $txt['offline'], '<img src="'. $message['member']['online']['image_href']. '" alt="" /></li>';
+
+		// Are we showing the warning status?
+		// Don't show these things for guests.
+		if ((!$message['member']['is_guest']) &&($message['member']['can_see_warning']))
+			echo '
+								<li class="warning">', $context['can_issue_warning'] ? '<a href="' . $scripturl . '?action=profile;area=issuewarning;u=' . $message['member']['id'] . '">' : '', '<img src="', $settings['images_url'], '/warning_', $message['member']['warning_status'], '.png" alt="', $txt['user_warn_' . $message['member']['warning_status']], '" />', $context['can_issue_warning'] ? '</a>' : '', '<span class="warn_', $message['member']['warning_status'], '">', $txt['warn_' . $message['member']['warning_status']], '</span></li>';
+
+			echo '
+							</ul>';
+			echo '
+						</div>
+						<div class="postarea">
+							<div class="keyinfo">
+								<div class="messageicon" ', ($message['icon_url'] !== $settings['images_url'] . '/post/xx.png') ? '' : 'style="position: absolute; z-index: -1;"', '>
+									<img src="', $message['icon_url'] . '" alt=""', $message['can_modify'] ? ' id="msg_icon_' . $message['id'] . '"' : '', ' />
+								</div>
+								<h5 id="subject_', $message['id'], '">
+									<a href="', $message['href'], '" rel="nofollow" title="', !empty($message['counter']) ? $txt['reply_noun'] . ' #' . $message['counter'] : '', ' - ', $message['subject'], '">', !empty($message['counter']) ? '' : '<strong>OP: </strong>', '', $message['time'], '</a>';
+
+		// Show "<< Last Edit: Time by Person >>" if this post was edited.
+		if ($settings['show_modify'] && !empty($message['modified']['name']))
+			echo '
+									<span class="smalltext modified" id="modified_', $message['id'], '">
+										', $txt['last_edit'], ': ', $message['modified']['time'], ' ', $txt['by'], ' ', $message['modified']['name'], '
+									</span>';
+
+			echo '
+								</h5>
+								<div id="msg_', $message['id'], '_quick_mod"></div>
 							</div>';
 
 		// Ignoring this user? Hide the post.
@@ -461,22 +466,16 @@ function template_main()
 								<div class="inner" id="msg_', $message['id'], '"', '>', $message['body'], '</div>
 							</div>';
 
-		// Can the user modify the contents of this post?  Show the modify inline image.
-		if ($message['can_modify'])
-			echo '
-							<img src="', $settings['images_url'], '/icons/modify_inline.png" alt="', $txt['modify_msg'], '" title="', $txt['modify_msg'], '" class="modifybutton" id="modify_button_', $message['id'], '" style="cursor: pointer; display: none;" onclick="oQuickModify.modifyMsg(\'', $message['id'], '\')" />';
-
 		// Assuming there are attachments...
 		if (!empty($message['attachment']))
 		{
 			echo '
-							<div id="msg_', $message['id'], '_footer" class="attachments smalltext">
-								<div style="overflow: ', isBrowser('is_firefox') ? 'visible' : 'auto', ';">';
+							<div id="msg_', $message['id'], '_footer" class="attachments">';
 
 			$last_approved_state = 1;
 			$attachments_per_line = 4;
 			$i = 0;
-			
+
 			foreach ($message['attachment'] as $attachment)
 			{
 				// Show a special box for unapproved attachments...
@@ -484,33 +483,37 @@ function template_main()
 				{
 					$last_approved_state = 0;
 					echo '
-											<fieldset>
-												<legend>', $txt['attach_awaiting_approve'];
+								<fieldset>
+									<legend>', $txt['attach_awaiting_approve'];
 
 					if ($context['can_approve'])
 						echo '
-												&nbsp;[<a href="', $scripturl, '?action=attachapprove;sa=all;mid=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['approve_all'], '</a>]';
+										&nbsp;[<a href="', $scripturl, '?action=attachapprove;sa=all;mid=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['approve_all'], '</a>]';
 
 					echo '
-												</legend>';
+									</legend>';
 				}
-				
+
 				echo '
-									<div class="floatleft padding">
-										<div class="attachments_top">';
+									<div class="floatleft">';
 
 				if ($attachment['is_image'])
 				{
+						echo '
+										<div class="attachments_top">';
+
 					if ($attachment['thumbnail']['has_thumb'])
 						echo '
-											<a href="', $attachment['href'], ';image" id="link_', $attachment['id'], '" onclick="', $attachment['thumbnail']['javascript'], '"><img src="', $attachment['thumbnail']['href'], '" alt="" id="thumb_', $attachment['id'], '" /></a><br />';
+											<a href="', $attachment['href'], ';image" id="link_', $attachment['id'], '" onclick="', $attachment['thumbnail']['javascript'], '"><img src="', $attachment['thumbnail']['href'], '" alt="" id="thumb_', $attachment['id'], '" /></a>';
 					else
 						echo '
-											<img src="' . $attachment['href'] . ';image" alt="" width="' . $attachment['width'] . '" height="' . $attachment['height'] . '"/><br />';
+											<img src="' . $attachment['href'] . ';image" alt="" width="' . $attachment['width'] . '" height="' . $attachment['height'] . '"/>';
+
+						echo '
+										</div>';
 				}
-				
+
 				echo '
-										</div>
 										<div class="attachments_bot">
 											<a href="' . $attachment['href'] . '"><img src="' . $settings['images_url'] . '/icons/clip.png" class="centericon" alt="*" />&nbsp;' . $attachment['name'] . '</a> ';
 
@@ -520,76 +523,97 @@ function template_main()
 				echo '
 											<br />', $attachment['size'], ($attachment['is_image'] ? ', ' . $attachment['real_width'] . 'x' . $attachment['real_height'] . '<br />' . $txt['attach_viewed'] : '<br />' . $txt['attach_downloaded']) . ' ' . $attachment['downloads'] . ' ' . $txt['attach_times'] . '
 										</div>';
-				
+
 				echo '
 									</div>';
-				
+
 				// Next attachment line ?
 				if (++$i % $attachments_per_line === 0)
 					echo '
-									<br class="clear" />';
+									<hr />';
 			}
 
-			// no more attachments, clear the float if its open
-			if ($i % $attachments_per_line !== 0)
-				echo '
-									<br class="clear" />';
-			
 			// If we had unapproved attachments clean up.
 			if ($last_approved_state == 0)
 				echo '
-											</fieldset>';
+								</fieldset>';
 
 			echo '
-								</div>
 							</div>';
 		}
 
 		echo '
-						</div>
-						<div class="moderatorbar">
-							<div class="smalltext modified" id="modified_', $message['id'], '">';
+						</div>';
 
-		// Show "<< Last Edit: Time by Person >>" if this post was edited.
-		if ($settings['show_modify'] && !empty($message['modified']['name']))
+		// Show the quickbuttons, for various operations on posts.
+		if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg'])
 			echo '
-								&#171; <em>', $txt['last_edit'], ': ', $message['modified']['time'], ' ', $txt['by'], ' ', $message['modified']['name'], '</em> &#187;';
+						<ul class="quickbuttons">';
 
-		echo '
-							</div>
-							<div class="smalltext reportlinks">';
+		// Maybe we can approve it, maybe we should?
+		if ($message['can_approve'] || $message['can_unapprove'])
+			echo '
+							<li><a href="', $scripturl, '?action=moderate;area=postmod;sa=approve;topic=', $context['current_topic'], '.', $context['start'], ';msg=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '"  class="', $message['can_unapprove'] ? 'un' : '', 'approve_button">', $txt[($message['can_unapprove'] ? 'un' : '') . 'approve'], '</a></li>';
 
-		// Maybe they want to report this post to the moderator(s)?
-		if ($context['can_report_moderator'])
+		// Can they reply? Have they turned on quick reply?
+		if ($context['can_quote'] && !empty($options['display_quick_reply']))
 			echo '
-								<a href="', $scripturl, '?action=reporttm;topic=', $context['current_topic'], '.', $message['counter'], ';msg=', $message['id'], '">', $txt['report_to_mod'], '</a> &nbsp;';
+							<li><a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';last_msg=', $context['topic_last_message'], '" onclick="return oQuickReply.quote(', $message['id'], ');" class="quote_button">', $txt['quote'], '</a></li>';
 
-		// Can we issue a warning because of this post?  Remember, we can't give guests warnings.
-		if ($context['can_issue_warning'] && !$message['is_message_author'] && !$message['member']['is_guest'])
+		// So... quick reply is off, but they *can* reply?
+		elseif ($context['can_quote'])
 			echo '
-								<a href="', $scripturl, '?action=profile;area=issuewarning;u=', $message['member']['id'], ';msg=', $message['id'], '"><img src="', $settings['images_url'], '/warn.png" alt="', $txt['issue_warning_post'], '" title="', $txt['issue_warning_post'], '" /></a>';
-		echo '
-								<img class="centericon" src="', $settings['images_url'], '/ip.png" alt="" />';
+							<li><a href="', $scripturl, '?action=post;quote=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], ';last_msg=', $context['topic_last_message'], '" class="quote_button">', $txt['quote'], '</a></li>';
 
-		// Show the IP to this user for this post - because you can moderate?
-		if (!empty($context['can_moderate_forum']) && !empty($message['member']['ip']))
+		// Can the user modify the contents of this post?  Show the modify inline image.
+		if ($message['can_modify'])
 			echo '
-								<a href="', $scripturl, '?action=', !empty($message['member']['is_guest']) ? 'trackip' : 'profile;area=tracking;sa=ip;u=' . $message['member']['id'], ';searchip=', $message['member']['ip'], '">', $message['member']['ip'], '</a> <a href="', $scripturl, '?action=helpadmin;help=see_admin_ip" onclick="return reqWin(this.href);" class="help">(?)</a>';
-		// Or, should we show it because this is you?
-		elseif ($message['can_see_ip'])
+							<li class="quick_edit"><img src="', $settings['images_url'], '/icons/modify_inline.png" alt="', $txt['modify_msg'], '" title="', $txt['modify_msg'], '" class="modifybutton" id="modify_button_', $message['id'], '" style="cursor: pointer; display: none; margin: 0 0 0 0;" onclick="oQuickModify.modifyMsg(\'', $message['id'], '\')" />', $txt['quick_edit'], '</li>';
+
+		// Can the user modify the contents of this post?
+		if ($message['can_modify'])
 			echo '
-								<a href="', $scripturl, '?action=helpadmin;help=see_member_ip" onclick="return reqWin(this.href);" class="help">', $message['member']['ip'], '</a>';
-		// Okay, are you at least logged in?  Then we can show something about why IPs are logged...
-		elseif (!$context['user']['is_guest'])
+							<li class="post_options">', $txt['post_options'], '';
+
 			echo '
-								<a href="', $scripturl, '?action=helpadmin;help=see_member_ip" onclick="return reqWin(this.href);" class="help">', $txt['logged'], '</a>';
-		// Otherwise, you see NOTHING!
-		else
+								<ul>';
+
+				// Can the user modify the contents of this post?
+				if ($message['can_modify'])
+					echo '
+									<li><a href="', $scripturl, '?action=post;msg=', $message['id'], ';topic=', $context['current_topic'], '.', $context['start'], '" class="modify_button">', $txt['modify'], '</a></li>';
+
+				// How about... even... remove it entirely?!
+				if ($message['can_remove'])
+					echo '
+									<li><a href="', $scripturl, '?action=deletemsg;topic=', $context['current_topic'], '.', $context['start'], ';msg=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(\'', $txt['remove_message'], '?\');" class="remove_button">', $txt['remove'], '</a></li>';
+
+				// What about splitting it off the rest of the topic?
+				if ($context['can_split'] && !empty($context['real_num_replies']))
+					echo '
+									<li><a href="', $scripturl, '?action=splittopics;topic=', $context['current_topic'], '.0;at=', $message['id'], '" class="split_button">', $txt['split'], '</a></li>';
+
+				// Can we restore topics?
+				if ($context['can_restore_msg'])
+					echo '
+									<li><a href="', $scripturl, '?action=restoretopic;msgs=', $message['id'], ';', $context['session_var'], '=', $context['session_id'], '" class="restore_button">', $txt['restore_message'], '</a></li>';
+
 			echo '
-								', $txt['logged'];
+								</ul>
+							</li>';
+
+		// Show a checkbox for quick moderation?
+		if (!empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $message['can_remove'])
+			echo '
+							<li class="inline_mod_check" style="display: none;" id="in_topic_mod_check_', $message['id'], '"></li>';
+
+		if ($message['can_approve'] || $context['can_reply'] || $message['can_modify'] || $message['can_remove'] || $context['can_split'] || $context['can_restore_msg'])
+
+			echo '
+						</ul>';
 
 		echo '
-							</div>';
+						<div class="moderatorbar">';
 
 		// Are there any custom profile fields for above the signature?
 		if (!empty($message['member']['custom_fields']))
@@ -635,8 +659,10 @@ function template_main()
 	echo '
 			<div class="pagesection">
 				', template_button_strip($context['normal_buttons'], 'right'), '
-				<div class="pagelinks floatleft">', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . ' &nbsp;&nbsp;<a href="#top"><strong>' . $txt['go_up'] . '</strong></a>' : '', '</div>
-				<div class="nextlinks_bottom">', $context['previous_next'], '</div>
+				', !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '<a href="#top" class="topbottom floatleft">' . $txt['go_up'] . '</a>' : '', '
+				<div class="pagelinks floatleft">
+					', $context['page_index'], '
+				</div>
 			</div>';
 
 	// Show the lower breadcrumbs.
@@ -736,7 +762,6 @@ function template_main()
 		}
 		echo '
 							<div class="padding">
-								<hr class="hrcolor" />
 								<input type="submit" name="post" value="', $txt['post'], '" onclick="return submitThisOnce(this);" accesskey="s" tabindex="', $context['tabindex']++, '" class="button_submit" />
 								<input type="submit" name="preview" value="', $txt['preview'], '" onclick="return submitThisOnce(this);" accesskey="p" tabindex="', $context['tabindex']++, '" class="button_submit" />';
 
@@ -744,8 +769,16 @@ 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 '
-								<br class="clear_right" />
 							</div>
 						</form>
 					</div>
@@ -756,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>
@@ -869,39 +917,12 @@ function template_main()
 					}';
 
 	if (!empty($ignoredMsgs))
-	{
 		echo '
-					var aIgnoreToggles = new Array();';
-
-		foreach ($ignoredMsgs as $msgid)
-		{
-			echo '
-					aIgnoreToggles[', $msgid, '] = new smc_Toggle({
-						bToggleEnabled: true,
-						bCurrentlyCollapsed: true,
-						aSwappableContainers: [
-							\'msg_', $msgid, '_extra_info\',
-							\'msg_', $msgid, '\',
-							\'msg_', $msgid, '_footer\',
-							\'msg_', $msgid, '_quick_mod\',
-							\'modify_button_', $msgid, '\',
-							\'msg_', $msgid, '_signature\'
-
-						],
-						aSwapLinks: [
-							{
-								sId: \'msg_', $msgid, '_ignored_link\',
-								msgExpanded: \'\',
-								msgCollapsed: ', JavaScriptEscape($txt['show_ignore_user_post']), '
-							}
-						]
-					});';
-		}
-	}
+					ignore_toggles([', implode(', ', $ignoredMsgs), '], ', JavaScriptEscape($txt['show_ignore_user_post']), ');';
 
 	echo '
 				// ]]></script>';
-	
+
 }
 
 ?>

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

@@ -28,7 +28,7 @@ function template_fatal_error()
 				', $context['error_title'], '
 			</h3>
 		</div>
-		<div class="windowbg">
+		<div class="windowbg generic_list_wrapper">
 			<div ', $context['error_code'], 'class="padding">', $context['error_message'], '</div>
 		</div>
 	</div>
@@ -44,12 +44,12 @@ function template_error_log()
 	global $context, $settings, $options, $scripturl, $txt, $modSettings;
 
 	echo '
-		<form 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'], '">';
-	
+		<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">
-					<a href="', $scripturl, '?action=helpadmin;help=error_log" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['errlog'], '
+					<a href="', $scripturl, '?action=helpadmin;help=error_log" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['errlog'], '
 				</h3>
 			</div>
 			<div class="pagesection">
@@ -81,7 +81,7 @@ function template_error_log()
 
 	echo '
 				<tr class="titlebg">
-					<td colspan="3" class="righttext" style="padding-right: 1.2ex">
+					<td colspan="3" class="righttext" style="padding: 4px 8px;">
 						<label for="check_all1"><strong>', $txt['check_all'], '</strong></label>&nbsp;
 						<input type="checkbox" id="check_all1" onclick="invertAll(this, this.form, \'delete[]\'); this.form.check_all2.checked = this.checked;" class="input_check" />
 					</td>
@@ -92,58 +92,58 @@ function template_error_log()
 		echo '
 				<tr class="windowbg">
 					<td class="centertext" colspan="2">', $txt['errlog_no_entries'], '</td>
-				</tr>';		
-			
+				</tr>';
+
 	// we have some errors, must be some mods installed :P
 	foreach ($context['errors'] as $error)
 	{
 		echo '
 				<tr class="windowbg', $error['alternate'] ? '2' : '', '">
-					<td class="half_width">
-						<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=id_member;value=', $error['member']['id'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_member'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_member'], '" /></a>
-						<strong>', $error['member']['link'], '</strong><br />
-						<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=ip;value=', $error['member']['ip'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_ip'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_ip'], '" /></a>
-						<strong><a href="', $scripturl, '?action=trackip;searchip=', $error['member']['ip'], '">', $error['member']['ip'], '</a></strong>&nbsp;&nbsp;
-						<br />&nbsp;
-					</td>
-					<td class="half_width">
-						<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? '' : ';desc', $context['has_filter'] ? $context['filter']['href'] : '', '" title="', $txt['reverse_direction'], '"><img src="', $settings['images_url'], '/sort_', $context['sort_direction'], '.png" alt="', $txt['reverse_direction'], '" /></a>
-						', $error['time'], '
-						<br />';
+					<td>
+
+						<div style="float: left; width: 50%; line-height: 1.8em; padding: 0 4px 4px 4px; vertical-align: bottom;">
+							<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=id_member;value=', $error['member']['id'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_member'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_member'], '" /></a>
+							<strong>', $error['member']['link'], '</strong><br />
+							<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? '' : ';desc', $context['has_filter'] ? $context['filter']['href'] : '', '" title="', $txt['reverse_direction'], '"><img src="', $settings['images_url'], '/sort_', $context['sort_direction'], '.png" alt="', $txt['reverse_direction'], '" /></a>
+							', $error['time'], '<br />
+							<a  href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=ip;value=', $error['member']['ip'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_ip'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_ip'], '" /></a>
+							<strong><a href="', $scripturl, '?action=trackip;searchip=', $error['member']['ip'], '">', $error['member']['ip'], '</a></strong>&nbsp;&nbsp;<br />
+						</div>
+
+						<div style="float: left; width: 50%; line-height: 1.8em; padding: 0 4px;">';
 
 		if ($error['member']['session'] != '')
 			echo '
-						<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=session;value=', $error['member']['session'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_session'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_session'], '" /></a>
-						', $error['member']['session'], '
-						<br />';
+							<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=session;value=', $error['member']['session'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_session'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_session'], '" /></a>
+							', $error['member']['session'], '<br />
+							<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=error_type;value=', $error['error_type']['type'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_type'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_type'], '" /></a>
+							', $txt['error_type'], ': ', $error['error_type']['name'], '<br />
+							<a style="display: table-cell; padding: 4px 0; width: 20px; vertical-align: top;" href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=message;value=', $error['message']['href'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_message'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_message'], '" /></a>
+							<span style="display: table-cell;">', $error['message']['html'], '</span>';
 
-		echo '
-						<a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=error_type;value=', $error['error_type']['type'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_type'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_type'], '" /></a>
-						', $txt['error_type'], ': ', $error['error_type']['name'], '
-					</td>';
+			echo '
+						</div>
 
-		echo '
-					<td rowspan="2" class="checkbox_column">
-						<input type="checkbox" name="delete[]" value="', $error['id'], '" class="input_check" />
-					</td>
-				</tr>
-				<tr class="windowbg', $error['alternate'] ? '2' : '', '">
-					<td colspan="2">
-						<div class="clear_left floatleft"><a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=url;value=', $error['url']['href'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_url'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_url'], '" /></a></div>
-						<div class="floatleft marginleft"><a href="', $error['url']['html'], '">', $error['url']['html'], '</a></div>
-						<div class="clear_left floatleft"><a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=message;value=', $error['message']['href'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_message'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_message'], '" /></a></div>
-						<div class="floatleft marginleft">', $error['message']['html'], '</div>';
+						<div style="float: left; width: 100%; padding: 4px 0; line-height: 1.6em; border-top: 1px solid #e3e3e3;">
+							<a style="display: table-cell; padding: 4px; width: 20px; vertical-align: top;" href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=url;value=', $error['url']['href'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_url'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_url'], '" /></a>
+							<a style="display: table-cell;" href="', $error['url']['html'], '">', $error['url']['html'], '</a>
+						</div>';
 
 		if (!empty($error['file']))
 			echo '
-						<div class="clear_left floatleft"><a href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=file;value=', $error['file']['search'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_file'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_file'], '" /></a></div>
-						<div class="floatleft marginleft">
-							', $txt['file'], ': ', $error['file']['link'], '<br />
-							', $txt['line'], ': ', $error['file']['line'], '
+						<div style="float: left; width: 100%; padding: 4px 0; line-height: 1.6em; border-top: 1px solid #e3e3e3;">
+							<a style="display: table-cell; padding: 4px; width: 20px; vertical-align: top;" href="', $scripturl, '?action=admin;area=logs;sa=errorlog', $context['sort_direction'] == 'down' ? ';desc' : '', ';filter=file;value=', $error['file']['search'], '" title="', $txt['apply_filter'], ': ', $txt['filter_only_file'], '"><img src="', $settings['images_url'], '/filter.png" alt="', $txt['apply_filter'], ': ', $txt['filter_only_file'], '" /></a>
+							<div style="display: table-cell;">
+								', $txt['file'], ': ', $error['file']['link'], '<br />
+								', $txt['line'], ': ', $error['file']['line'], '
+							</div>
 						</div>';
 
 		echo '
 					</td>
+					<td class="checkbox_column">
+						<input type="checkbox" name="delete[]" value="', $error['id'], '" class="input_check" />
+					</td>
 				</tr>';
 	}
 
@@ -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" />

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

@@ -19,7 +19,12 @@ function template_control_richedit($editor_id, $smileyContainer = null, $bbcCont
 
 	echo '
 		<div>
-			<textarea class="editor" name="', $editor_id, '" id="', $editor_id, '" rows="', $editor_context['rows'], '" cols="600" onselect="storeCaret(this);" onclick="storeCaret(this);" onkeyup="storeCaret(this);" onchange="storeCaret(this);" tabindex="', $context['tabindex']++, '" style="height: ', $editor_context['height'], '; ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? 'border: 1px solid red;' : '', '">', $editor_context['value'], '</textarea>
+			<div>
+				<div>
+					<textarea class="editor" name="', $editor_id, '" id="', $editor_id, '" rows="" cols="600" onselect="storeCaret(this);" onclick="storeCaret(this);" onkeyup="storeCaret(this);" onchange="storeCaret(this);" tabindex="', $context['tabindex']++, '" style="height: ', $editor_context['height'], '; ', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? 'border: 1px solid red;' : '', '">', $editor_context['value'], '</textarea>
+				</div>
+				<div id="', $editor_id, '_resizer" class="richedit_resize"></div>
+			</div>
 		</div>
 		<input type="hidden" name="', $editor_id, '_mode" id="', $editor_id, '_mode" value="0" />
 		<script type="text/javascript"><!-- // --><![CDATA[
@@ -29,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"';
@@ -94,14 +99,16 @@ 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();
-				}
+				}', isset($context['post_error']['no_message']) || isset($context['post_error']['long_message']) ? '
+				$(".sceditor-container").find("textarea").each(function() {$(this).css({border: "1px solid red"})});
+				$(".sceditor-container").find("iframe").each(function() {$(this).css({border: "1px solid red"})});' : '', '
 			});';
 
 		// Now for backward compatibility let's collect few infos in the good ol' style
@@ -134,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?!

+ 22 - 37
Themes/default/GenericList.template.php

@@ -24,9 +24,13 @@ function template_show_list($list_id = null)
 
 	if (isset($cur_list['form']))
 		echo '
-	<form action="', $cur_list['form']['href'], '" method="post"', empty($cur_list['form']['name']) ? '' : ' name="' . $cur_list['form']['name'] . '" id="' . $cur_list['form']['name'] . '"', ' accept-charset="', $context['character_set'], '">
+	<form class="generic_list_wrapper" action="', $cur_list['form']['href'], '" method="post"', empty($cur_list['form']['name']) ? '' : ' name="' . $cur_list['form']['name'] . '" id="' . $cur_list['form']['name'] . '"', ' accept-charset="', $context['character_set'], '">
 		<div class="generic_list">';
 
+	else
+		echo '
+		<div class="generic_list_wrapper">';
+
 	// Show the title of the table (if any).
 	if (!empty($cur_list['title']))
 		echo '
@@ -35,13 +39,11 @@ function template_show_list($list_id = null)
 					', $cur_list['title'], '
 				</h3>
 			</div>';
+
 	// This is for the old style menu with the arrows "> Test | Test 1"
 	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 '
@@ -51,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 '
@@ -60,20 +65,12 @@ function template_show_list($list_id = null)
 		if (!empty($cur_list['items_per_page']))
 			echo '
 				<div class="floatleft">
-					<div class="pagesection">', $txt['pages'], ': ', $cur_list['page_index'], '</div>
+					<div class="pagesection">', $cur_list['page_index'], '</div>
 				</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>';
 	}
@@ -106,7 +103,7 @@ function template_show_list($list_id = null)
 		echo '
 				</tr>
 			</thead>';
-	} 
+	}
 
 		echo '
 			<tbody>';
@@ -125,9 +122,9 @@ function template_show_list($list_id = null)
 		foreach ($cur_list['rows'] as $id => $row)
 		{
 			echo '
-				<tr class="windowbg', $alternate ? '2' : '', '" id="list_', $list_id, '_', $id, '">';
+				<tr class="windowbg', $alternate ? '2' : '', $row['class'], '"', $row['style'], ' id="list_', $list_id, '_', $id, '">';
 
-			foreach ($row as $row_data)
+			foreach ($row['data'] as $row_data)
 				echo '
 					<td', empty($row_data['class']) ? '' : ' class="' . $row_data['class'] . '"', empty($row_data['style']) ? '' : ' style="' . $row_data['style'] . '"', '>', $row_data['value'], '</td>';
 
@@ -142,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">';
@@ -151,35 +148,19 @@ function template_show_list($list_id = null)
 		if (!empty($cur_list['items_per_page']))
 			echo '
 				<div class="floatleft">
-					<div class="pagesection">', $txt['pages'], ': ', $cur_list['page_index'], '</div>
+					<div class="pagesection">', $cur_list['page_index'], '</div>
 				</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)
@@ -191,6 +172,10 @@ function template_show_list($list_id = null)
 	</form>';
 	}
 
+	else
+		echo '
+		</div>';
+
 	// Tabs at the bottom.  Usually bottom alligned.
 	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'] == 'bottom'))
 		template_create_list_menu($cur_list['list_menu'], 'bottom');

+ 15 - 28
Themes/default/GenericMenu.template.php

@@ -16,6 +16,8 @@ function template_generic_menu_sidebar_above()
 	global $context, $settings, $options, $scripturl, $txt, $modSettings;
 
 	// This is the main table - we need it so we can keep the content to the right of it.
+	// [WIP] Why is there a span id="admin_menu"? #admin_menu is also the div that wraps the drop menu system.
+	// Is this some bonkers leftover span from 2.0 beta days? It has no content and is not listed in the CSS.
 	echo '
 	<div id="main_container">
 		<div id="left_admsection"><span id="admin_menu"></span>';
@@ -32,20 +34,8 @@ function template_generic_menu_sidebar_above()
 		echo '
 			<div class="adm_section">
 				<div class="cat_bar">
-					<h4 class="catbg">';
-
-		if ($firstSection && !empty($menu_context['can_toggle_drop_down']))
-		{
-			echo '
-							<a href="', $menu_context['toggle_url'], '">', $section['title'],'<img src="', $context['menu_image_path'], '/change_menu', $context['right_to_left'] ? '' : '2', '.png" alt="!" /></a>';
-		}
-		else
-		{
-			echo '
-						', $section['title'];
-		}
-
-		echo '
+					<h4 class="catbg">
+						', $section['title'], '
 					</h4>
 				</div>
 				<ul class="smalltext left_admmenu">';
@@ -101,8 +91,7 @@ function template_generic_menu_sidebar_below()
 
 	echo '
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // This contains the html for the side bar of the admin center, which is used for all admin pages.
@@ -114,19 +103,17 @@ function template_generic_menu_dropdown_above()
 	$context['cur_menu_id'] = isset($context['cur_menu_id']) ? $context['cur_menu_id'] + 1 : 1;
 	$menu_context = &$context['menu_data_' . $context['cur_menu_id']];
 
-	if (!empty($menu_context['can_toggle_drop_down']))
-		echo '
-	<a href="', $menu_context['toggle_url'], '"><img id="menu_toggle" src="', $context['menu_image_path'], '/change_menu', $context['right_to_left'] ? '2' : '', '.png" alt="*" /></a>';
+	echo '
+<div id="admin_menu">';
 
 	echo '
-<div id="admin_menu">
 	<ul class="dropmenu" id="dropdown_menu_', $context['cur_menu_id'], '">';
 
 	// Main areas first.
 	foreach ($menu_context['sections'] as $section)
 	{
 		echo '
-			<li><a class="', !empty($section['selected']) ? 'active ' : '', '" href="', $section['url'], $menu_context['extra_parameters'], '">', $section['title'] , '</a>
+			<li ', !empty($section['areas']) ? 'class="subsections"' : '', '><a class="', !empty($section['selected']) ? 'active ' : '', '" href="', $section['url'], $menu_context['extra_parameters'], '">', $section['title'] , '</a>
 				<ul>';
 
 		// For every area of this section show a link to that area (bold if it's currently selected.)
@@ -231,19 +218,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;
@@ -266,7 +253,7 @@ function template_generic_menu_tabs(&$menu_context)
 			echo '<img src="', $settings['images_url'], '/icons/', !empty($selected_tab['icon']) ? $selected_tab['icon'] : $tab_context['icon'], '" alt="" class="icon" />';
 
 		if (!empty($selected_tab['help']) || !empty($tab_context['help']))
-			echo '<a href="', $scripturl, '?action=helpadmin;help=', !empty($selected_tab['help']) ? $selected_tab['help'] : $tab_context['help'], '" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a>';
+			echo '<a href="', $scripturl, '?action=helpadmin;help=', !empty($selected_tab['help']) ? $selected_tab['help'] : $tab_context['help'], '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a>';
 
 		echo $tab_context['title'];
 	}
@@ -284,7 +271,7 @@ function template_generic_menu_tabs(&$menu_context)
 	if (!empty($settings['use_tabs']))
 	{
 		echo '
-	<p class="windowbg description">
+	<p class="description">
 		', !empty($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], '
 	</p>';
 
@@ -346,7 +333,7 @@ function template_generic_menu_tabs(&$menu_context)
 
 		echo '
 	</p>
-	<p class="windowbg description">', isset($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], '</p>';
+	<p class="description">', isset($selected_tab['description']) ? $selected_tab['description'] : $tab_context['description'], '</p>';
 	}
 }
 

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

@@ -50,7 +50,7 @@ function template_login()
 		echo '<p><strong>&mdash;', $txt['or'], '&mdash;</strong></p>
 				<dl>
 					<dt>', $txt['openid'], ':</dt>
-					<dd><input type="text" name="openid_identifier" class="input_text openid_login" size="17" />&nbsp;<a href="', $scripturl, '?action=helpadmin;help=register_openid" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="centericon" /></a></dd>
+					<dd><input type="text" name="openid_identifier" class="input_text openid_login" size="17" />&nbsp;<a href="', $scripturl, '?action=helpadmin;help=register_openid" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="centericon" /></a></dd>
 				</dl>
 				<hr />';
 
@@ -220,7 +220,7 @@ function template_admin_login()
 	echo '
 			<strong>', $txt['password'], ':</strong>
 			<input type="password" name="', $context['sessionCheckType'], '_pass" size="24" class="input_password" />
-			<a href="', $scripturl, '?action=helpadmin;help=securityDisable_why" onclick="return reqWin(this.href);" class="help"><img class="icon" src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><br />
+			<a href="', $scripturl, '?action=helpadmin;help=securityDisable_why" onclick="return reqOverlayDiv(this.href);" class="help"><img class="icon" src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><br />
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-login_token_var'], '" value="', $context['admin-login_token'], '" />
 			<input type="submit" style="margin-top: 1em;" value="', $txt['login'], '" class="button_submit" />';

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

@@ -67,7 +67,7 @@ function template_ban_edit()
 						</legend>
 						<input type="radio" name="full_ban" id="full_ban" value="1" onclick="fUpdateStatus();"', $context['ban']['cannot']['access'] ? ' checked="checked"' : '', ' class="input_radio" /> <label for="full_ban">', $txt['ban_full_ban'], '</label><br />
 						<input type="radio" name="full_ban" id="partial_ban" value="0" onclick="fUpdateStatus();"', !$context['ban']['cannot']['access'] ? ' checked="checked"' : '', ' class="input_radio" /> <label for="partial_ban">', $txt['ban_partial_ban'], '</label><br />
-						<input type="checkbox" name="cannot_post" id="cannot_post" value="1"', $context['ban']['cannot']['post'] ? ' checked="checked"' : '', ' class="ban_restriction input_radio" /> <label for="cannot_post">', $txt['ban_cannot_post'], '</label> (<a href="', $scripturl, '?action=helpadmin;help=ban_cannot_post" onclick="return reqWin(this.href);">?</a>)<br />
+						<input type="checkbox" name="cannot_post" id="cannot_post" value="1"', $context['ban']['cannot']['post'] ? ' checked="checked"' : '', ' class="ban_restriction input_radio" /> <label for="cannot_post">', $txt['ban_cannot_post'], '</label> (<a href="', $scripturl, '?action=helpadmin;help=ban_cannot_post" onclick="return reqOverlayDiv(this.href);">?</a>)<br />
 						<input type="checkbox" name="cannot_register" id="cannot_register" value="1"', $context['ban']['cannot']['register'] ? ' checked="checked"' : '', ' class="ban_restriction input_radio" /> <label for="cannot_register">', $txt['ban_cannot_register'], '</label><br />
 						<input type="checkbox" name="cannot_login" id="cannot_login" value="1"', $context['ban']['cannot']['login'] ? ' checked="checked"' : '', ' class="ban_restriction input_radio" /> <label for="cannot_login">', $txt['ban_cannot_login'], '</label><br />
 					</fieldset>

+ 7 - 18
Themes/default/ManageBoards.template.php

@@ -92,18 +92,14 @@ function template_main()
 		// Button to add a new board.
 		echo '
 					</ul>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['mboards_new_board'], '" class="button_submit" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
-					<br class="clear_right" />
 				</div>
 			</div>
-		</form>
-		<br class="clear" />';
+		</form>';
 	}
 	echo '
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Template for editing/adding a category on the forum.
@@ -158,8 +154,7 @@ function template_modify_category()
 
 	// Table footer.
 	echo '
-					</dl>
-					<hr class="hrcolor" />';
+					</dl>';
 
 	if (isset($context['category']['is_new']))
 		echo '
@@ -180,12 +175,10 @@ function template_modify_category()
 						<input type="hidden" name="empty" value="1" />';
 
 	echo '
-					<br class="clear_right" />
 				</div>
 			</div>
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // A template to confirm if a user wishes to delete a category - and whether they want to save the boards.
@@ -239,8 +232,7 @@ function template_confirm_category_delete()
 				</div>
 			</div>
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Below is the template for adding/editing an board on the forum.
@@ -545,7 +537,6 @@ function template_modify_board()
 		echo '<div class="information">', $txt['mboards_recycle_disabled_delete'], '</div>';
 
 	echo '
-					<hr class="hrcolor" />
 					<input type="hidden" name="rid" value="', $context['redirect_location'], '" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="', $context['admin-be-' . $context['board']['id'] . '_token_var'], '" value="', $context['admin-be-' . $context['board']['id'] . '_token'], '" />';
@@ -567,12 +558,11 @@ function template_modify_board()
 		echo '
 					<span', $context['board']['is_recycle'] ? ' style="visibility:hidden">' : '>', '<input type="submit" name="delete" value="', $txt['mboards_delete_board'], '" onclick="return confirm(\'', $txt['boardConfirm'], '\');"', ' class="button_submit" /></span>';
 	echo '
-				<br class="clear_right" />
 				</div>
 			</div>
 		</form>
 	</div>
-	<br class="clear" />
+
 <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 <script type="text/javascript"><!-- // --><![CDATA[
 	var oModeratorSuggest = new smc_AutoSuggest({
@@ -683,8 +673,7 @@ function template_confirm_board_delete()
 				</div>
 			</div>
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 ?>

+ 15 - 15
Themes/default/ManageMail.template.php

@@ -16,26 +16,26 @@ function template_browse()
 
 	echo '
 	<div id="manage_mail">
-		<div class="cat_bar">
-			<h3 class="catbg">', $txt['mailqueue_stats'], '</h3>
-		</div>
-		<div class="windowbg">
-			<div class="content">
-				<dl class="settings">
-					<dt><strong>', $txt['mailqueue_size'], '</strong></dt>
-					<dd>', $context['mail_queue_size'], '</dd>
-					<dt><strong>', $txt['mailqueue_oldest'], '</strong></dt>
-					<dd>', $context['oldest_mail'], '</dd>
-				</dl>
+		<div id="mailqueue_stats">
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['mailqueue_stats'], '</h3>
 			</div>
-		</div>
-		<br class="clear" />';
+			<div class="windowbg">
+				<div class="content">
+					<dl class="settings">
+						<dt><strong>', $txt['mailqueue_size'], '</strong></dt>
+						<dd>', $context['mail_queue_size'], '</dd>
+						<dt><strong>', $txt['mailqueue_oldest'], '</strong></dt>
+						<dd>', $context['oldest_mail'], '</dd>
+					</dl>
+				</div>
+			</div>
+		</div>';
 
 	template_show_list('mail_queue');
 
 	echo '
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 ?>

+ 129 - 83
Themes/default/ManageMaintenance.template.php

@@ -31,18 +31,16 @@ function template_maintain_database()
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=optimize" method="post" accept-charset="', $context['character_set'], '">
 					<p>', $txt['maintain_optimize_info'], '</p>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['maintain_run_now'], '" 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'], '" />
-					<br class="clear_right" />
 				</form>
 			</div>
 		</div>
 
 		<div class="cat_bar">
 			<h3 class="catbg">
-			<a href="', $scripturl, '?action=helpadmin;help=maintenance_backup" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['maintain_backup'], '
+			<a href="', $scripturl, '?action=helpadmin;help=maintenance_backup" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['maintain_backup'], '
 			</h3>
 		</div>
 
@@ -53,7 +51,6 @@ function template_maintain_database()
 
 	if ($db_type == 'sqlite')
 		echo '
-					<hr class="hrcolor" />
 					<p>
 						<input type="submit" value="', $txt['maintain_backup_save'], '" id="submitDump" class="button_submit" />
 						<br class="clear_right" />
@@ -76,10 +73,8 @@ function template_maintain_database()
 						<label for="data"><input type="checkbox" name="data" id="data" onclick="document.getElementById(\'submitDump\').disabled = !document.getElementById(\'struct\').checked &amp;&amp; !document.getElementById(\'data\').checked;" checked="checked" class="input_check" /> ', $txt['maintain_backup_data'], '</label><br />
 						<label for="compress"><input type="checkbox" name="compress" id="compress" value="gzip"', $context['suggested_method'] == 'zipped_file' ? ' checked="checked"' : '', ' class="input_check" /> ', $txt['maintain_backup_gz'], '</label>
 					</p>
-					<hr class="hrcolor" />
 					<p>
 						<input ', $context['use_maintenance'] == 2 ? 'disabled="disabled" ' : '', 'type="submit" value="', $txt['maintain_backup_save'], '" id="submitDump" onclick="return document.getElementById(\'struct\').checked || document.getElementById(\'data\').checked;" class="button_submit" />
-						<br class="clear_right" />
 					</p>';
 	}
 
@@ -90,6 +85,26 @@ function template_maintain_database()
 			</div>
 		</div>';
 
+	// Show an option to convert the body column of the post table to MEDIUMTEXT or TEXT
+	if (isset($context['convert_to']))
+	{
+		echo '
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt[$context['convert_to'] . '_title'], '</h3>
+		</div>
+		<div class="windowbg">
+			<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>' : '', '
+					<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>
+		</div>';
+	}
+
 	// Show an option to convert to UTF-8 if we're not on UTF-8 yet.
 	if ($context['convert_utf8'])
 	{
@@ -102,7 +117,6 @@ function template_maintain_database()
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertutf8" method="post" accept-charset="', $context['character_set'], '">
 					<p>', $txt['utf8_introduction'], '</p>
 					', !empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext' ? '<p class="errorbox">' . $txt['utf8_cannot_convert_fulltext'] . '</p>' : '', '
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['maintain_run_now'], '" class="button_submit" ', !empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext' ? 'disabled="disabled"' : '', '/><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'], '" />
@@ -122,9 +136,7 @@ function template_maintain_database()
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertentities" method="post" accept-charset="', $context['character_set'], '">
 					<p>', $txt['entity_convert_introduction'], '</p>
-					<hr class="hrcolor" />
 					<input type="submit" 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>
@@ -133,8 +145,7 @@ function template_maintain_database()
 	}
 
 	echo '
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Template for the routine maintenance tasks.
@@ -142,27 +153,28 @@ function template_maintain_routine()
 {
 	global $context, $settings, $options, $txt, $scripturl, $modSettings;
 
+	// Starts off with general maintenance procedures.
+	echo '
+	<div id="manage_maintenance">';
+
 	// If maintenance has finished tell the user.
 	if (!empty($context['maintenance_finished']))
-		echo '
-			<div class="maintenance_finished">
-				', sprintf($txt['maintain_done'], $context['maintenance_finished']), '
-			</div>';
+	echo '
+		<div class="maintenance_finished">
+			', sprintf($txt['maintain_done'], $context['maintenance_finished']), '
+		</div>';
 
-	// Starts off with general maintenance procedures.
 	echo '
-	<div id="manage_maintenance">
 		<div class="cat_bar">
 			<h3 class="catbg">', $txt['maintain_version'], '</h3>
 		</div>
 		<div class="windowbg">
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=routine;activity=version" method="post" accept-charset="', $context['character_set'], '">
-					<p>', $txt['maintain_version_info'], '</p>
-					<hr class="hrcolor" />
-					<input type="submit" value="', $txt['maintain_run_now'], '" class="button_submit" />
-					<br class="clear_right" />
-					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<p>', $txt['maintain_version_info'], '
+						<input type="submit" value="', $txt['maintain_run_now'], '" class="button_submit" />
+						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					</p>
 				</form>
 			</div>
 		</div>
@@ -172,12 +184,11 @@ function template_maintain_routine()
 		<div class="windowbg2">
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=repairboards" method="post" accept-charset="', $context['character_set'], '">
-					<p>', $txt['maintain_errors_info'], '</p>
-					<hr class="hrcolor" />
-					<input type="submit" 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'], '" />
+					<p>', $txt['maintain_errors_info'], '
+						<input type="submit" value="', $txt['maintain_run_now'], '" 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'], '" />
+					</p>
 				</form>
 			</div>
 		</div>
@@ -187,12 +198,11 @@ function template_maintain_routine()
 		<div class="windowbg">
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=routine;activity=recount" method="post" accept-charset="', $context['character_set'], '">
-					<p>', $txt['maintain_recount_info'], '</p>
-					<hr class="hrcolor" />
-					<input type="submit" 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'], '" />
+					<p>', $txt['maintain_recount_info'], '
+						<input type="submit" value="', $txt['maintain_run_now'], '" 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'], '" />
+					</p>
 				</form>
 			</div>
 		</div>
@@ -202,12 +212,11 @@ function template_maintain_routine()
 		<div class="windowbg2">
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=routine;activity=logs" method="post" accept-charset="', $context['character_set'], '">
-					<p>', $txt['maintain_logs_info'], '</p>
-					<hr class="hrcolor" />
-					<input type="submit" 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'], '" />
+					<p>', $txt['maintain_logs_info'], '
+						<input type="submit" value="', $txt['maintain_run_now'], '" 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'], '" />
+					</p>
 				</form>
 			</div>
 		</div>
@@ -217,17 +226,15 @@ function template_maintain_routine()
 		<div class="windowbg">
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=routine;activity=cleancache" method="post" accept-charset="', $context['character_set'], '">
-					<p>', $txt['maintain_cache_info'], '</p>
-					<hr class="hrcolor" />
-					<input type="submit" 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'], '" />
+					<p>', $txt['maintain_cache_info'], '
+						<input type="submit" value="', $txt['maintain_run_now'], '" 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'], '" />
+					</p>
 				</form>
 			</div>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Template for the member maintenance tasks.
@@ -235,13 +242,6 @@ function template_maintain_members()
 {
 	global $context, $settings, $options, $txt, $scripturl;
 
-	// If maintenance has finished tell the user.
-	if (!empty($context['maintenance_finished']))
-		echo '
-			<div class="maintenance_finished">
-				', sprintf($txt['maintain_done'], $context['maintenance_finished']), '
-			</div>';
-
 	echo '
 	<script type="text/javascript"><!-- // --><![CDATA[
 		var warningMessage = \'\';
@@ -252,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++)
 			{
@@ -293,7 +294,16 @@ function template_maintain_members()
 		}
 		setTimeout("checkAttributeValidity();", 500);
 	// ]]></script>
-	<div id="manage_maintenance">
+	<div id="manage_maintenance">';
+
+	// If maintenance has finished tell the user.
+	if (!empty($context['maintenance_finished']))
+	echo '
+		<div class="maintenance_finished">
+			', sprintf($txt['maintain_done'], $context['maintenance_finished']), '
+		</div>';
+
+	echo '
 		<div class="cat_bar">
 			<h3 class="catbg">', $txt['maintain_reattribute_posts'], '</h3>
 		</div>
@@ -327,10 +337,8 @@ 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>
-					<hr class="hrcolor" />
-					<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" />
-					<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>
@@ -338,7 +346,7 @@ function template_maintain_members()
 		</div>
 		<div class="cat_bar">
 			<h3 class="catbg">
-				<a href="', $scripturl, '?action=helpadmin;help=maintenance_members" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['maintain_members'], '
+				<a href="', $scripturl, '?action=helpadmin;help=maintenance_members" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" class="icon" alt="', $txt['help'], '" /></a> ', $txt['maintain_members'], '
 			</h3>
 		</div>
 		<div class="windowbg">
@@ -360,9 +368,7 @@ function template_maintain_members()
 
 	echo '
 					</div>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['maintain_old_remove'], '" onclick="return confirm(\'', $txt['maintain_members_confirm'], '\');" 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>
@@ -375,16 +381,13 @@ function template_maintain_members()
 			<div class="content">
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=members;activity=recountposts" method="post" accept-charset="', $context['character_set'], '" id="membersRecountForm">
 					<p>', $txt['maintain_recountposts_info'], '</p>
-					<hr class="hrcolor" />
 					<input type="submit" 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>
 		</div>
 	</div>
-	<br class="clear" />
 
 	<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 	<script type="text/javascript"><!-- // --><![CDATA[
@@ -426,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\');
@@ -502,9 +505,21 @@ function template_maintain_topics()
 	echo '
 						</div>
 					</div>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['maintain_old_remove'], '" onclick="return confirm(\'', $txt['maintain_old_confirm'], '\');" 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>
+		</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>
@@ -553,15 +568,12 @@ function template_maintain_topics()
 	}
 	echo '
 					</select></p>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['move_topics_now'], '" onclick="if (document.getElementById(\'id_board_from\').options[document.getElementById(\'id_board_from\').selectedIndex].disabled || document.getElementById(\'id_board_from\').options[document.getElementById(\'id_board_to\').selectedIndex].disabled) return false; var confirmText = \'', $txt['move_topics_confirm'] . '\'; return confirm(confirmText.replace(/%board_from%/, document.getElementById(\'id_board_from\').options[document.getElementById(\'id_board_from\').selectedIndex].text.replace(/^=+&gt;&nbsp;/, \'\')).replace(/%board_to%/, document.getElementById(\'id_board_to\').options[document.getElementById(\'id_board_to\').selectedIndex].text.replace(/^=+&gt;&nbsp;/, \'\')));" class="button_submit" />
-					<br class="clear_right" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 				</form>
 			</div>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Simple template for showing results of our optimization...
@@ -594,8 +606,7 @@ function template_optimize()
 				<p><a href="', $scripturl, '?action=admin;area=maintain">', $txt['maintain_return'], '</a></p>
 			</div>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 function template_convert_utf8()
@@ -626,17 +637,14 @@ function template_convert_utf8()
 						<dt><strong>', $txt['utf8_target_charset'], ': </strong></dt>
 						<dd>', $txt['utf8_utf8'], '</dd>
 					</dl>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['utf8_proceed'], '" 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'], '" />
 					<input type="hidden" name="proceed" value="1" />
 				</form>
 			</div>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 function template_convert_entities()
@@ -652,14 +660,52 @@ function template_convert_entities()
 			<div class="content">
 				<p>', $txt['entity_convert_introduction'], '</p>
 				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertentities;start=0;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
-				<hr class="hrcolor" />
 				<input type="submit" value="', $txt['entity_convert_proceed'], '" class="button_submit" />
-				<br class="clear_right" />
 				</form>
 			</div>
 		</div>
-	</div>
-	<br class="clear" />';
+	</div>';
+}
+
+function template_convert_msgbody()
+{
+	global $context, $txt, $settings, $scripturl;
+
+	echo '
+	<div id="manage_maintenance">
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt[$context['convert_to'] . '_title'], '</h3>
+		</div>
+		<div class="windowbg">
+			<div class="content">
+				<p>', $txt['body_checking_introduction'], '</p>';
+	if (!empty($context['exceeding_messages']))
+	{
+		echo '
+				<p class="noticebox">', $txt['exceeding_messages'], '
+				<ul>
+					<li>
+					', implode('</li><li>', $context['exceeding_messages']), '
+					</li>
+				</ul>';
+		if (!empty($context['exceeding_messages_morethan']))
+			echo '
+				<p>', $context['exceeding_messages_morethan'], '</p>';
+	}
+	else
+		echo '
+				<p class="infobox">', $txt['convert_to_text'], '</p>';
+
+	echo '
+				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertmsgbody" method="post" accept-charset="', $context['character_set'], '">
+				<hr class="hrcolor" />
+				<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'], '" />
+				<input type="submit" name="do_conversion" value="', $txt['entity_convert_proceed'], '" class="button_submit" />
+				</form>
+			</div>
+		</div>
+	</div>';
 }
 
 ?>

+ 8 - 15
Themes/default/ManageMembergroups.template.php

@@ -126,9 +126,7 @@ function template_new_group()
 	echo '
 						</dd>
 					</dl>
-					<hr class="hrcolor" />
 					<input type="submit" value="', $txt['membergroups_add_group'], '" class="button_submit" />
-					<br class="clear_right" />
 				</div>
 			</div>';
 	if ($context['undefined_group'])
@@ -148,8 +146,7 @@ function template_new_group()
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-mmg_token_var'], '" value="', $context['admin-mmg_token'], '" />
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 function template_edit_group()
@@ -304,17 +301,14 @@ function template_edit_group()
 	}
 	echo '
 					</dl>
-					<hr class="hrcolor" />
 					<input type="submit" name="save" value="', $txt['membergroups_edit_save'], '" class="button_submit" />', $context['group']['allow_delete'] ? '
 					<input type="submit" name="delete" value="' . $txt['membergroups_delete'] . '" onclick="return confirm(\'' . $txt['membergroups_confirm_delete'] . '\');" class="button_submit" />' : '', '
-					<br class="clear_right" />
 				</div>
 			</div>
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-mmg_token_var'], '" value="', $context['admin-mmg_token'], '" />
 		</form>
 	</div>
-	<br class="clear" />
 		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/suggest.js?alp21"></script>
 		<script type="text/javascript"><!-- // --><![CDATA[
 			var oModeratorSuggest = new smc_AutoSuggest({
@@ -420,8 +414,7 @@ function template_add_edit_group_boards_list($collapse = true)
 	}
 
 	echo '
-							</ul>
-							<br class="clear" />';
+							</ul>';
 
 	if (empty($modSettings['deny_boards_access']))
 		echo '
@@ -462,7 +455,7 @@ function template_group_members()
 
 	echo '
 	<div id="admincenter">
-		<form action="', $scripturl, '?action=', $context['current_action'], (isset($context['admin_area']) ? ';area=' . $context['admin_area'] : '') , ';sa=members;group=', $context['group']['id'], '" method="post" accept-charset="', $context['character_set'], '">
+		<form action="', $scripturl, '?action=', $context['current_action'], (isset($context['admin_area']) ? ';area=' . $context['admin_area'] : '') , ';sa=members;group=', $context['group']['id'], '" method="post" accept-charset="', $context['character_set'], '" id="view_group">
 			<div class="cat_bar">
 				<h3 class="catbg">', $context['page_title'], '</h3>
 			</div>
@@ -592,24 +585,24 @@ 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 '
-			<div class="cat_bar">
-				<h3 class="catbg">', $txt['membergroups_members_add_title'], '</h3>
+			<div class="cat_bar cat_bar_odd">
+				<h3 class="catbg catbg_odd">', $txt['membergroups_members_add_title'], '</h3>
 			</div>
 			<div class="windowbg">
 				<div class="content">

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

@@ -16,7 +16,7 @@ function template_search_members()
 
 	echo '
 	<div id="admincenter">
-		<form action="', $scripturl, '?action=admin;area=viewmembers" method="post" accept-charset="', $context['character_set'], '">
+		<form action="', $scripturl, '?action=admin;area=viewmembers" method="post" accept-charset="', $context['character_set'], '" id="admin_form_wrapper">
 			<div class="cat_bar">
 				<h3 class="catbg">
 					<span class="floatleft">', $txt['search_for'], '</span>
@@ -156,7 +156,6 @@ function template_search_members()
 					</div>
 				</div>
 			</div>
-			<br class="clear" />
 			<div class="title_bar">
 				<h3 class="titlebg">', $txt['member_part_of_these_membergroups'], '</h3>
 			</div>
@@ -234,8 +233,7 @@ function template_search_members()
 			</div><br />
 			<input type="submit" value="', $txt['search'], '" class="button_submit" />
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 function template_admin_browse()
@@ -252,7 +250,7 @@ function template_admin_browse()
 	{
 		echo '
 		<br />
-		<form action="', $scripturl, '?action=admin;area=viewmembers" method="post" accept-charset="', $context['character_set'], '" name="postFormOutstanding" id="postFormOutstanding" onsubmit="return onOutstandingSubmit();">
+		<form id="admin_form_wrapper" action="', $scripturl, '?action=admin;area=viewmembers" method="post" accept-charset="', $context['character_set'], '" name="postFormOutstanding" id="postFormOutstanding" onsubmit="return onOutstandingSubmit();">
 			<div class="cat_bar">
 				<h3 class="catbg">', $txt['admin_browse_outstanding'], '</h3>
 			</div>
@@ -305,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'], '" />
@@ -319,8 +317,7 @@ function template_admin_browse()
 	}
 
 	echo '
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 ?>

+ 7 - 8
Themes/default/ManageNews.template.php

@@ -16,7 +16,7 @@ function template_email_members()
 
 	echo '
 	<div id="admincenter">
-		<form action="', $scripturl, '?action=admin;area=news;sa=mailingcompose" method="post" class="flow_hidden" accept-charset="', $context['character_set'], '">
+		<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>
 			</div>
@@ -47,7 +47,7 @@ function template_email_members()
 			</div>
 			<br />
 
-			<div class="cat_bar">
+			<div id="advanced_panel_header" class="cat_bar">
 				<h3 class="catbg">
 					<img id="advanced_panel_toggle" class="panel_toggle" style="display: none; src="', $settings['images_url'], '/', empty($context['show_advanced_options']) ? 'collapse' : 'expand', '.png" alt="*" />
 					<a href="#" id="advanced_panel_link" >', $txt['advanced'], '</a>
@@ -107,7 +107,7 @@ function template_email_members()
 						<dd>
 							<input type="checkbox" name="email_force" id="email_force" value="1" class="input_check" />
 						</dd>
-					</dl><br class="clear" />
+					</dl>
 				</div>
 			</div>
 			<div class="righttext">
@@ -115,8 +115,7 @@ function template_email_members()
 				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			</div>
 		</form>
-	</div>
-	<br class="clear" />';
+	</div>';
 
 	// This is some javascript for the simple/advanced toggling and member suggest
 	echo '
@@ -210,7 +209,7 @@ function template_email_members_compose()
 		<form name="newsmodify" action="', $scripturl, '?action=admin;area=news;sa=mailingsend" method="post" accept-charset="', $context['character_set'], '">
 			<div class="cat_bar">
 				<h3 class="catbg">
-					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['admin_newsletters'], '
+					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['admin_newsletters'], '
 				</h3>
 			</div>
 			<div class="information">
@@ -246,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>
@@ -408,7 +407,7 @@ function template_email_members_send()
 		<form action="', $scripturl, '?action=admin;area=news;sa=mailingsend" method="post" accept-charset="', $context['character_set'], '" name="autoSubmit" id="autoSubmit">
 			<div class="cat_bar">
 				<h3 class="catbg">
-					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a> ', $txt['admin_newsletters'], '
+					<a href="', $scripturl, '?action=helpadmin;help=email_members" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a> ', $txt['admin_newsletters'], '
 				</h3>
 			</div>
 			<div class="windowbg">

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

@@ -62,7 +62,7 @@ function template_permission_index()
 		echo '
 					<tr class="windowbg', $alternate ? '2' : '', '">
 						<td>
-							', $group['name'], $group['id'] == -1 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_guests" onclick="return reqWin(this.href);">?</a>)' : ($group['id'] == 0 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_regular_members" onclick="return reqWin(this.href);">?</a>)' : ($group['id'] == 1 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_administrator" onclick="return reqWin(this.href);">?</a>)' : ($group['id'] == 3 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_moderator" onclick="return reqWin(this.href);">?</a>)' : '')));
+							', $group['name'], $group['id'] == -1 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_guests" onclick="return reqOverlayDiv(this.href);">?</a>)' : ($group['id'] == 0 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_regular_members" onclick="return reqOverlayDiv(this.href);">?</a>)' : ($group['id'] == 1 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_administrator" onclick="return reqOverlayDiv(this.href);">?</a>)' : ($group['id'] == 3 ? ' (<a href="' . $scripturl . '?action=helpadmin;help=membergroup_moderator" onclick="return reqOverlayDiv(this.href);">?</a>)' : '')));
 
 		if (!empty($group['children']))
 			echo '
@@ -107,7 +107,7 @@ function template_permission_index()
 						<legend>', $txt['permissions_with_selection'], '</legend>
 						<dl class="settings admin_permissions">
 							<dt>
-								', $txt['permissions_apply_pre_defined'], ' <a href="', $scripturl, '?action=helpadmin;help=permissions_quickgroups" onclick="return reqWin(this.href);">(?)</a>:
+								', $txt['permissions_apply_pre_defined'], ' <a href="', $scripturl, '?action=helpadmin;help=permissions_quickgroups" onclick="return reqOverlayDiv(this.href);">(?)</a>:
 							</dt>
 							<dd>
 								<select name="predefined">
@@ -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'], '" />
@@ -664,7 +663,7 @@ function template_modify_group_simple($type)
 				echo '
 					<tr id="perm_div_', $id_group, '_', $permission['id'], '" class="', $alternate ? 'windowbg' : 'windowbg2', '">
 						<td valign="top" width="10" style="padding-right: 1ex;">
-							', $permission['help_index'] ? '<a href="' . $scripturl . '?action=helpadmin;help=' . $permission['help_index'] . '" onclick="return reqWin(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a>' : '', '
+							', $permission['help_index'] ? '<a href="' . $scripturl . '?action=helpadmin;help=' . $permission['help_index'] . '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a>' : '', '
 						</td>
 						<td valign="top" width="100%" align="left" style="padding-bottom: 2px;">', $permission['name'], '</td>';
 
@@ -912,7 +911,7 @@ function template_modify_group_classic($type)
 					echo '
 							<tr class="', $alternate ? 'windowbg' : 'windowbg2', '">
 								<td width="10">
-									', $permission['show_help'] ? '<a href="' . $scripturl . '?action=helpadmin;help=permissionhelp_' . $permission['id'] . '" onclick="return reqWin(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a>' : '', '
+									', $permission['show_help'] ? '<a href="' . $scripturl . '?action=helpadmin;help=permissionhelp_' . $permission['id'] . '" onclick="return reqOverlayDiv(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.png" alt="' . $txt['help'] . '" /></a>' : '', '
 								</td>';
 
 					if ($permission['has_own_any'])

+ 12 - 15
Themes/default/ManageSearch.template.php

@@ -24,7 +24,7 @@ function template_modify_weights()
 				<div class="content">
 					<dl class="settings">
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_frequency" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight1_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_frequency" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight1_val">
 							', $txt['search_weight_frequency'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -32,7 +32,7 @@ function template_modify_weights()
 							<span id="weight1" class="search_weight">', $context['relative_weights']['search_weight_frequency'], '%</span>
 						</dd>
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_age" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight2_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_age" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight2_val">
 							', $txt['search_weight_age'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -40,7 +40,7 @@ function template_modify_weights()
 							<span id="weight2" class="search_weight">', $context['relative_weights']['search_weight_age'], '%</span>
 						</dd>
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_length" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight3_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_length" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight3_val">
 							', $txt['search_weight_length'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -48,7 +48,7 @@ function template_modify_weights()
 							<span id="weight3" class="search_weight">', $context['relative_weights']['search_weight_length'], '%</span>
 						</dd>
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_subject" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight4_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_subject" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight4_val">
 							', $txt['search_weight_subject'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -56,7 +56,7 @@ function template_modify_weights()
 							<span id="weight4" class="search_weight">', $context['relative_weights']['search_weight_subject'], '%</span>
 						</dd>
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_first_message" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight5_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_first_message" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a><label for="weight5_val">
 							', $txt['search_weight_first_message'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -64,7 +64,7 @@ function template_modify_weights()
 							<span id="weight5" class="search_weight">', $context['relative_weights']['search_weight_first_message'], '%</span>
 						</dd>
 						<dt class="large_caption">
-							<a href="', $scripturl, '?action=helpadmin;help=search_weight_frequency" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" align="top" /></a><label for="weight6_val">
+							<a href="', $scripturl, '?action=helpadmin;help=search_weight_frequency" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" align="top" /></a><label for="weight6_val">
 							', $txt['search_weight_sticky'], ':</label>
 						</dt>
 						<dd class="large_caption">
@@ -102,7 +102,7 @@ function template_select_search_method()
 				<h3 class="catbg">', $txt['search_method'], '</h3>
 			</div>
 			<div class="information">
-				<div class="smalltext" style="font-weight: normal;"><a href="', $scripturl, '?action=helpadmin;help=search_why_use_index" onclick="return reqWin(this.href);">', $txt['search_create_index_why'], '</a></div>
+				<div class="smalltext" style="font-weight: normal;"><a href="', $scripturl, '?action=helpadmin;help=search_why_use_index" onclick="return reqOverlayDiv(this.href);">', $txt['search_create_index_why'], '</a></div>
 			</div>
 			<div class="windowbg">
 				<div class="content">
@@ -385,23 +385,20 @@ function template_show_spider_logs()
 	template_show_list('spider_logs');
 
 	echo '
-		<br />
-		<div class="cat_bar">
-			<h3 class="catbg">', $txt['spider_logs_delete'], '</h3>
-		</div>
-		<form action="', $scripturl, '?action=admin;area=sengines;sa=logs" method="post" accept-charset="', $context['character_set'], '">
+		<form id="admin_form_wrapper" action="', $scripturl, '?action=admin;area=sengines;sa=logs" method="post" accept-charset="', $context['character_set'], '">
+			<div class="cat_bar">
+				<h3 class="catbg">', $txt['spider_logs_delete'], '</h3>
+			</div>
 			<div class="windowbg">
 				<div class="content">
 					<p>
 						', $txt['spider_logs_delete_older'], '
 						<input type="text" name="older" id="older" value="7" size="3" class="input_text" />
 						', $txt['spider_logs_delete_day'], '
-					</p>	
-					<hr class="hrcolor" />
+					</p>
 					<input type="submit" name="delete_entries" value="', $txt['spider_logs_delete_submit'], '" onclick="if (document.getElementById(\'older\').value &lt; 1 &amp;&amp; !confirm(\'' . addcslashes($txt['spider_logs_delete_confirm'], "'") . '\')) return false; return true;" class="button_submit" />
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="', $context['admin-sl_token_var'], '" value="', $context['admin-sl_token'], '" />
-					<br class="clear_right" />
 				</div>
 			</div>
 		</form>

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

@@ -17,6 +17,10 @@ function template_main()
 
 	echo '
 	<div class="main_section" id="memberlist">
+		<div class="pagesection">
+			', template_button_strip($context['memberlist_buttons'], 'right'), '
+			<div class="pagelinks floatleft">', $txt['pages'], ': ', $context['page_index'], '</div>
+		</div>
 		<div class="cat_bar">
 			<h4 class="catbg">
 				<span class="floatleft">', $txt['members_list'], '</span>';
@@ -25,10 +29,6 @@ function template_main()
 				<span class="floatright">', $context['letter_links'], '</span>';
 		echo '
 			</h4>
-		</div>
-		<div class="pagesection">
-			', template_button_strip($context['memberlist_buttons'], 'right'), '
-			<div class="pagelinks floatleft">', $txt['pages'], ': ', $context['page_index'], '</div>
 		</div>';
 
 	echo '
@@ -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">
@@ -163,14 +163,14 @@ function template_search()
 	echo '
 	<form action="', $scripturl, '?action=mlist;sa=search" method="post" accept-charset="', $context['character_set'], '">
 		<div id="memberlist">
+			<div class="pagesection">
+				', template_button_strip($context['memberlist_buttons'], 'right'), '
+			</div>
 			<div class="cat_bar">
 				<h3 class="catbg mlist">
 					', !empty($settings['use_buttons']) ? '<img src="' . $settings['images_url'] . '/buttons/search.png" alt="" class="icon" />' : '', $txt['mlist_search'], '
 				</h3>
 			</div>
-			<div class="pagesection">
-				', template_button_strip($context['memberlist_buttons'], 'right'), '
-			</div>
 			<div id="memberlist_search" class="clear">
 				<div class="roundframe">
 					<dl id="mlist_search" class="settings">

+ 37 - 31
Themes/default/MessageIndex.template.php

@@ -62,7 +62,6 @@ function template_main()
 						<a href="', $scripturl, '?action=moderate;area=postmod;sa=', ($board['unapproved_topics'] > 0 ? 'topics' : 'posts'), ';brd=', $board['id'], ';', $context['session_var'], '=', $context['session_id'], '" title="', sprintf($txt['unapproved_posts'], $board['unapproved_topics'], $board['unapproved_posts']), '" class="moderation_link">(!)</a>';
 
 			echo '
-
 						<p>', $board['description'] , '</p>';
 
 			// Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.)
@@ -122,10 +121,6 @@ function template_main()
 	</div>';
 	}
 
-	if (!empty($options['show_board_desc']) && $context['description'] != '')
-		echo '
-	<p class="description_board">', $context['description'], '</p>';
-
 	// They can only mark read if they are logged in and it's enabled!
 	if (!$context['user']['is_logged'] || !$settings['show_mark_read'])
 		unset($context['normal_buttons']['markread']);
@@ -134,10 +129,34 @@ function template_main()
 	{
 		echo '
 	<div class="pagesection">
-		<div class="pagelinks floatleft">', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '&nbsp;&nbsp;<a href="#bot"><strong>' . $txt['go_down'] . '</strong></a>' : '', '</div>
+		', !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '<a href="#bot" class="topbottom floatleft">' . $txt['go_down'] . '</a>' : '', '
+		<div class="pagelinks floatleft">', $context['page_index'], '</div>
 		', template_button_strip($context['normal_buttons'], 'right'), '
 	</div>';
 
+	if (!empty($options['show_board_desc']) && $context['description'] != '')
+		echo '
+	<div class="cat_bar description_header">
+		<h3 class="catbg">', $context['name'], '</h3>
+	</div>
+	<p class="description_board">
+		', $context['description'], '<br/>';
+
+	if (!empty($context['moderators']))
+		echo '
+		', count($context['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $context['link_moderators']), '.&nbsp;&nbsp;';
+
+		if (!empty($settings['display_who_viewing']))
+		{
+			if ($settings['display_who_viewing'] == 1)
+				echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members'];
+			else
+				echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')');
+			echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], '';
+		}
+		echo '
+	</p>';
+
 		// If Quick Moderation is enabled start the form.
 		if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] > 0 && !empty($context['topics']))
 			echo '
@@ -187,20 +206,6 @@ function template_main()
 			</thead>
 			<tbody>';
 
-		if (!empty($settings['display_who_viewing']))
-		{
-			echo '
-				<tr class="windowbg2 whos_viewing">
-					<td colspan="', !empty($context['can_quick_mod']) ? '6' : '5', '" class="smalltext">';
-			if ($settings['display_who_viewing'] == 1)
-				echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members'];
-			else
-				echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . ((empty($context['view_num_hidden']) or $context['can_moderate_forum']) ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')');
-			echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board'], '
-					</td>
-				</tr>';
-		}
-
 		// If this person can approve items and we have some awaiting approval tell them.
 		if (!empty($context['unapproved_posts_message']))
 		{
@@ -237,19 +242,19 @@ function template_main()
 			// [WIP] Markup can be cleaned up later. CSS can go in the CSS files later.
 			echo '
 				<tr>
-					<td class="icon1 ', $color_class, '" style="display: none;">
+					<td class="', $color_class, ' icon1" style="display: none;">
 						<img src="', $settings['images_url'], '/topic/', $topic['class'], '.png" alt="" />
 					</td>
-					<td class="icon2 ', $color_class, '">
+					<td class="', $color_class, ' icon2">
 						<div style="position: relative; width: 40px; margin: auto;">
 							<img src="', $topic['first_post']['icon_url'], '" alt="" />
 							', $topic['is_posted_in'] ? '<img src="'. $settings['images_url']. '/icons/profile_sm.png" alt="" style="position: absolute; z-index: 5; right: 4px; bottom: -3px;" />' : '','
 						</div>
 					</td>
-					<td class="subject ', $alternate_class, '">
+					<td class="', $alternate_class, ' subject">
 						<div ', (!empty($topic['quick_mod']['modify']) ? 'id="topic_' . $topic['first_post']['id'] . '" onmouseout="mouse_on_div = 0;" onmouseover="mouse_on_div = 1;" ondblclick="modify_topic(\'' . $topic['id'] . '\', \'' . $topic['first_post']['id'] . '\');"' : ''), '>';
 
-			// [WIP] MEthinks the orange icons look better if they aren't all over the page.
+			// [WIP] Methinks the orange icons look better if they aren't all over the page.
 			// Is this topic new? (assuming they are logged in!)
 			if ($topic['new'] && $context['user']['is_logged'])
 					echo '
@@ -263,12 +268,12 @@ function template_main()
 							</p>
 						</div>
 					</td>
-					<td class="stats ', $color_class, '">
+					<td class="', $color_class, ' stats">
 						', $topic['replies'], ' ', $txt['replies'], '
 						<br />
 						', $topic['views'], ' ', $txt['views'], '
 					</td>
-					<td class="lastpost ', $alternate_class, '">
+					<td class="', $alternate_class, ' lastpost">
 						<a href="', $topic['last_post']['href'], '"><img src="', $settings['images_url'], '/icons/last_post.png" alt="', $txt['last_post'], '" title="', $txt['last_post'], '" /></a>
 						', $topic['last_post']['time'], '<br />
 						', $txt['by'], ' ', $topic['last_post']['member']['link'], '
@@ -278,7 +283,7 @@ function template_main()
 			if (!empty($context['can_quick_mod']))
 			{
 				echo '
-					<td class="moderation ', $color_class, '" align="center">';
+					<td class="', $color_class, ' moderation" align="center">';
 				if ($options['display_quick_mod'] == 1)
 					echo '
 						<input type="checkbox" name="topics[]" value="', $topic['id'], '" class="input_check" />';
@@ -348,7 +353,8 @@ function template_main()
 		echo '
 	<div class="pagesection">
 		', template_button_strip($context['normal_buttons'], 'right'), '
-		<div class="pagelinks">', $txt['pages'], ': ', $context['page_index'], !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '&nbsp;&nbsp;<a href="#top"><strong>' . $txt['go_up'] . '</strong></a>' : '', '</div>
+		', !empty($modSettings['topbottomEnable']) ? $context['menu_separator'] . '<a href="#top" class="topbottom floatleft">' . $txt['go_up'] . '</a>' : '', '
+		<div class="pagelinks">', $context['page_index'], '</div>
 	</div>';
 	}
 
@@ -362,13 +368,13 @@ function template_main()
 
 	if (!$context['no_topic_listing'])
 		echo '
-			<p class="floatleft smalltext">', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? '
-				<img src="' . $settings['images_url'] . '/topic/my_normal_post.png" alt="" class="centericon" /> ' . $txt['participation_caption'] . '<br />' : '', '
+			<p class="floatleft">', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? '
+				<img src="' . $settings['images_url'] . '/icons/profile_sm.png" alt="" class="centericon" /> ' . $txt['participation_caption'] . '<br />' : '', '
 				<img src="' . $settings['images_url'] . '/topic/normal_post.png" alt="" class="centericon" /> ' . $txt['normal_topic'] . '<br />
 				<img src="' . $settings['images_url'] . '/topic/hot_post.png" alt="" class="centericon" /> ' . sprintf($txt['hot_topics'], $modSettings['hotTopicPosts']) . '<br />
 				<img src="' . $settings['images_url'] . '/topic/veryhot_post.png" alt="" class="centericon" /> ' . sprintf($txt['very_hot_topics'], $modSettings['hotTopicVeryPosts']) . '
 			</p>
-			<p class="smalltext">
+			<p>
 				<img src="' . $settings['images_url'] . '/icons/quick_lock.png" alt="" class="centericon" /> ' . $txt['locked_topic'] . '<br />' . ($modSettings['enableStickyTopics'] == '1' ? '
 				<img src="' . $settings['images_url'] . '/icons/quick_sticky.png" alt="" class="centericon" /> ' . $txt['sticky_topic'] . '<br />' : '') . ($modSettings['pollMode'] == '1' ? '
 				<img src="' . $settings['images_url'] . '/topic/normal_poll.png" alt="" class="centericon" /> ' . $txt['poll'] : '') . '

+ 19 - 23
Themes/default/ModerationCenter.template.php

@@ -55,7 +55,7 @@ function template_latest_news()
 	echo '
 		<div class="cat_bar">
 			<h3 class="catbg">
-				<a href="', $scripturl, '?action=helpadmin;help=live_news" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['mc_latest_news'], '
+				<a href="', $scripturl, '?action=helpadmin;help=live_news" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="icon" /></a> ', $txt['mc_latest_news'], '
 			</h3>
 		</div>
 		<div class="windowbg">
@@ -242,14 +242,14 @@ function template_reported_posts()
 	global $settings, $options, $context, $txt, $scripturl;
 
 	echo '
-	<form action="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
+	<form id="reported_posts" action="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';start=', $context['start'], '" method="post" accept-charset="', $context['character_set'], '">
 		<div class="cat_bar">
 			<h3 class="catbg">
 				', $context['view_closed'] ? $txt['mc_reportedp_closed'] : $txt['mc_reportedp_active'], '
 			</h3>
 		</div>
-		<div class="pagesection floatleft">
-			', $txt['pages'], ': ', $context['page_index'], '
+		<div class="pagesection">
+			<div class="pagelinks">', $context['page_index'], '</div>
 		</div>';
 
 	// Make the buttons.
@@ -261,21 +261,13 @@ function template_reported_posts()
 	foreach ($context['reports'] as $report)
 	{
 		echo '
-		<div class="', $report['alternate'] ? 'windowbg' : 'windowbg2', '">
+		<div class="generic_list_wrapper ', $report['alternate'] ? 'windowbg' : 'windowbg2', '">
 			<div class="content">
-				<div>
-					<div class="floatleft">
-						<strong><a href="', $report['topic_href'], '">', $report['subject'], '</a></strong> ', $txt['mc_reportedp_by'], ' <strong>', $report['author']['link'], '</strong>
-					</div>
-					<div class="floatright">
-						<a href="', $report['report_href'], '">', $details_button, '</a>
-						<a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';ignore=', (int) !$report['ignore'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '" ', !$report['ignore'] ? 'onclick="return confirm(\'' . $txt['mc_reportedp_ignore_confirm'] . '\');"' : '', '>', $report['ignore'] ? $unignore_button : $ignore_button, '</a>
-						<a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';close=', (int) !$report['closed'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '">', $close_button, '</a>
-						', !$context['view_closed'] ? '<input type="checkbox" name="close[]" value="' . $report['id'] . '" class="input_check" />' : '', '
-					</div>
-				</div><br />
+				<h5>
+					<strong><a href="', $report['topic_href'], '">', $report['subject'], '</a></strong> ', $txt['mc_reportedp_by'], ' <strong>', $report['author']['link'], '</strong>
+				</h5>
 				<div class="smalltext">
-					&#171; ', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], ' &#187;<br />';
+					', $txt['mc_reportedp_last_reported'], ': ', $report['last_updated'], '&nbsp;-&nbsp;';
 
 		// Prepare the comments...
 		$comments = array();
@@ -283,10 +275,17 @@ function template_reported_posts()
 			$comments[$comment['member']['id']] = $comment['member']['link'];
 
 		echo '
-					&#171; ', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), ' &#187;
+					', $txt['mc_reportedp_reported_by'], ': ', implode(', ', $comments), '
 				</div>
 				<hr />
 				', $report['body'], '
+				<br />
+				<ul class="quickbuttons">
+					<li><a href="', $report['report_href'], '">', $details_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';ignore=', (int) !$report['ignore'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '" ', !$report['ignore'] ? 'onclick="return confirm(\'' . $txt['mc_reportedp_ignore_confirm'] . '\');"' : '', '>', $report['ignore'] ? $unignore_button : $ignore_button, '</a></li>
+					<li><a href="', $scripturl, '?action=moderate;area=reports', $context['view_closed'] ? ';sa=closed' : '', ';close=', (int) !$report['closed'], ';rid=', $report['id'], ';start=', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '">', $close_button, '</a></li>
+					<li>', !$context['view_closed'] ? '<input type="checkbox" name="close[]" value="' . $report['id'] . '" class="input_check" />' : '', '</li>
+				</ul>
 			</div>
 		</div>';
 	}
@@ -302,16 +301,13 @@ function template_reported_posts()
 
 	echo '
 		<div class="pagesection">
-			<div class="floatleft">
-				', $txt['pages'], ': ', $context['page_index'], '
-			</div>
+			<div class="pagelinks floatleft">', $context['page_index'], '</div>
 			<div class="floatright">
 				', !$context['view_closed'] ? '<input type="submit" name="close_selected" value="' . $txt['mc_reportedp_close_selected'] . '" class="button_submit" />' : '', '
 			</div>
 		</div>
 		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
-	</form>
-	<br class="clear" />';
+	</form>';
 }
 
 // Show a list of all the unapproved posts

+ 7 - 7
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[
@@ -536,7 +536,7 @@ function template_browse()
 		echo '
 		<div class="cat_bar">
 			<h3 class="catbg">
-				<a href="', $scripturl, '?action=helpadmin;help=latest_packages" onclick="return reqWin(this.href);" class="help"><img class="icon" src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a> ', $txt['packages_latest'], '
+				<a href="', $scripturl, '?action=helpadmin;help=latest_packages" onclick="return reqOverlayDiv(this.href);" class="help"><img class="icon" src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" /></a> ', $txt['packages_latest'], '
 			</h3>
 		</div>
 		<div class="windowbg2">
@@ -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" />
@@ -1585,7 +1585,7 @@ function template_file_permissions()
 							<label for="method_individual"><strong>', $txt['package_file_perms_apply'], '</strong></label>
 						</dt>
 						<dd>
-							<em class="smalltext">', $txt['package_file_perms_custom'], ': <input type="text" name="custom_value" value="0755" maxlength="4" size="5" class="input_text" />&nbsp;<a href="', $scripturl, '?action=helpadmin;help=chmod_flags" onclick="return reqWin(this.href);" class="help">(?)</a></em>
+							<em class="smalltext">', $txt['package_file_perms_custom'], ': <input type="text" name="custom_value" value="0755" maxlength="4" size="5" class="input_text" />&nbsp;<a href="', $scripturl, '?action=helpadmin;help=chmod_flags" onclick="return reqOverlayDiv(this.href);" class="help">(?)</a></em>
 						</dd>
 						<dt>
 							<input type="radio" name="method" value="predefined" id="method_predefined" class="input_radio" />

+ 126 - 6
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>';
 
@@ -1234,7 +1291,7 @@ function template_send()
 				sBccLinkContainerId: \'bcc_link_container\',
 				bBccShowByDefault: ', empty($context['recipients']['bcc']) && empty($context['bcc_value']) ? 'false' : 'true', ',
 				sShowBccLinkTemplate: ', JavaScriptEscape('
-					<a href="#" id="bcc_link">' . $txt['make_bcc'] . '</a> <a href="' . $scripturl . '?action=helpadmin;help=pm_bcc" onclick="return reqWin(this.href);">(?)</a>'
+					<a href="#" id="bcc_link">' . $txt['make_bcc'] . '</a> <a href="' . $scripturl . '?action=helpadmin;help=pm_bcc" onclick="return reqOverlayDiv(this.href);">(?)</a>'
 				), '
 			});
 		';
@@ -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>';
+}
 ?>

+ 67 - 11
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'])
@@ -478,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,9 +523,9 @@ function template_main()
 
 	// Finally, the submit buttons.
 	echo '
-					<hr class="hrcolor" />
-					<span class="smalltext" >
-						', isBrowser('is_firefox') ? $txt['shortcuts_firefox'] : $txt['shortcuts'], '
+					<br class="clear_right" />
+					<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']);
@@ -506,7 +537,6 @@ function template_main()
 
 	echo '
 					</span>
-					<br class="clear_right" />
 				</div>
 			</div>
 			<br class="clear" />';
@@ -747,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>';
 
@@ -770,16 +827,15 @@ function template_main()
 			echo '
 				<div class="', $post['alternate'] == 0 ? 'windowbg' : 'windowbg2', ' core_posts">
 				<div class="content" id="msg', $post['id'], '">
-					<div class="floatleft">
-						<h5>', $txt['posted_by'], ': ', $post['poster'], '</h5>
-						<span class="smalltext">&#171;&nbsp;<strong>', $txt['on'], ':</strong> ', $post['time'], '&nbsp;&#187;</span>
-					</div>';
+					<h5 class="floatleft">
+						<span>', $txt['posted_by'], '</span>&nbsp;', $post['poster'], '
+					</h5>&nbsp;-&nbsp;', $post['time'];
 
 			if ($context['can_quote'])
 			{
 				echo '
-					<ul class="reset smalltext quickbuttons" id="msg_', $post['id'], '_quote">
-						<li><a href="#postmodify" onclick="return insertQuoteFast(', $post['id'], ');" class="quote_button"><span>',$txt['bbc_quote'],'</span></a></li>
+					<ul class="quickbuttons" id="msg_', $post['id'], '_quote">
+						<li><a href="#postmodify" onclick="return insertQuoteFast(', $post['id'], ');" class="quote_button">',$txt['bbc_quote'],'</a></li>
 					</ul>';
 			}
 

+ 148 - 103
Themes/default/Profile.template.php

@@ -49,12 +49,12 @@ function template_summary()
 
 	// Display the basic information about the user
 	echo '
+<div class="cat_bar">
+	<h3 class="catbg">
+		<img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />', $txt['summary'], '
+	</h3>
+</div>
 <div id="profileview" class="flow_auto">
-	<div class="cat_bar">
-		<h3 class="catbg">
-			<img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />', $txt['summary'], '
-		</h3>
-	</div>
 	<div id="basicinfo">
 		<div class="windowbg">
 			<div class="content flow_auto">
@@ -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>';
 
@@ -336,7 +342,7 @@ function template_showPosts()
 			</h3>
 		</div>
 		<div class="pagesection">
-			<span>', $txt['pages'], ': ', $context['page_index'], '</span>
+			<div class="pagelinks">', $context['page_index'], '</div>
 		</div>';
 
 	// Button shortcuts
@@ -352,26 +358,24 @@ function template_showPosts()
 		foreach ($context['posts'] as $post)
 		{
 			echo '
-		<div class="topic">
 			<div class="', $post['alternate'] == 0 ? 'windowbg2' : 'windowbg', ' core_posts">
 				<div class="content">
 					<div class="counter">', $post['counter'], '</div>
 					<div class="topic_details">
 						<h5><strong><a href="', $scripturl, '?board=', $post['board']['id'], '.0">', $post['board']['name'], '</a> / <a href="', $scripturl, '?topic=', $post['topic'], '.', $post['start'], '#msg', $post['id'], '">', $post['subject'], '</a></strong></h5>
-						<span class="smalltext">&#171;&nbsp;<strong>', $txt['on'], ':</strong> ', $post['time'], '&nbsp;&#187;</span>
+						<span class="smalltext">', $post['time'], '</span>
 					</div>
 					<div class="list_posts">';
 
 			if (!$post['approved'])
 				echo '
-					<div class="approve_post">
-						<em>', $txt['post_awaiting_approval'], '</em>
-					</div>';
+						<div class="approve_post">
+							<em>', $txt['post_awaiting_approval'], '</em>
+						</div>';
 
 			echo '
 					', $post['body'], '
-					</div>
-				</div>';
+					</div>';
 
 			if ($post['can_reply'] || $post['can_mark_notify'] || $post['can_delete'])
 				echo '
@@ -404,58 +408,12 @@ function template_showPosts()
 				</div>';
 
 			echo '
-				<br class="clear" />
-			</div>
-		</div>';
+				</div>
+			</div>';
 		}
 	}
 	else
-	{
-		echo '
-		<table border="0" width="100%" cellspacing="1" cellpadding="2" class="table_grid" align="center">
-			<thead>
-				<tr class="titlebg">
-					<th class="first_th lefttext" scope="col" width="25%">
-						<a href="', $scripturl, '?action=profile;u=', $context['current_member'], ';area=showposts;sa=attach;sort=filename', ($context['sort_direction'] == 'down' && $context['sort_order'] == 'filename' ? ';asc' : ''), '">
-							', $txt['show_attach_filename'], '
-							', ($context['sort_order'] == 'filename' ? '<img src="' . $settings['images_url'] . '/sort_' . ($context['sort_direction'] == 'down' ? 'down' : 'up') . '.png" alt="" />' : ''), '
-						</a>
-					</th>
-					<th scope="col" width="12%">
-						<a href="', $scripturl, '?action=profile;u=', $context['current_member'], ';area=showposts;sa=attach;sort=downloads', ($context['sort_direction'] == 'down' && $context['sort_order'] == 'downloads' ? ';asc' : ''), '">
-							', $txt['show_attach_downloads'], '
-							', ($context['sort_order'] == 'downloads' ? '<img src="' . $settings['images_url'] . '/sort_' . ($context['sort_direction'] == 'down' ? 'down' : 'up') . '.png" alt="" />' : ''), '
-						</a>
-					</th>
-					<th class="lefttext" scope="col" width="30%">
-						<a href="', $scripturl, '?action=profile;u=', $context['current_member'], ';area=showposts;sa=attach;sort=subject', ($context['sort_direction'] == 'down' && $context['sort_order'] == 'subject' ? ';asc' : ''), '">
-							', $txt['message'], '
-							', ($context['sort_order'] == 'subject' ? '<img src="' . $settings['images_url'] . '/sort_' . ($context['sort_direction'] == 'down' ? 'down' : 'up') . '.png" alt="" />' : ''), '
-						</a>
-					</th>
-					<th class="last_th lefttext" scope="col">
-						<a href="', $scripturl, '?action=profile;u=', $context['current_member'], ';area=showposts;sa=attach;sort=posted', ($context['sort_direction'] == 'down' && $context['sort_order'] == 'posted' ? ';asc' : ''), '">
-						', $txt['show_attach_posted'], '
-						', ($context['sort_order'] == 'posted' ? '<img src="' . $settings['images_url'] . '/sort_' . ($context['sort_direction'] == 'down' ? 'down' : 'up') . '.png" alt="" />' : ''), '
-						</a>
-					</th>
-				</tr>
-			</thead>
-			<tbody>';
-
-		// Looks like we need to do all the attachments instead!
-		$alternate = false;
-		foreach ($context['attachments'] as $attachment)
-		{
-			echo '
-				<tr class="', $attachment['approved'] ? ($alternate ? 'windowbg' : 'windowbg2') : 'approvebg', '">
-					<td><a href="', $scripturl, '?action=dlattach;topic=', $attachment['topic'], '.0;attach=', $attachment['id'], '">',	$attachment['filename'], '</a>', !$attachment['approved'] ? '&nbsp;<em>(' . $txt['awaiting_approval'] . ')</em>' : '', '</td>
-					<td align="center">', $attachment['downloads'], '</td>
-					<td><a href="', $scripturl, '?topic=', $attachment['topic'], '.msg', $attachment['msg'], '#msg', $attachment['msg'], '" rel="nofollow">', $attachment['subject'], '</a></td>
-					<td>', $attachment['posted'], '</td>
-				</tr>';
-			$alternate = !$alternate;
-		}
+		template_show_list('attachments');
 
 	// No posts? Just end the table with a informative message.
 	if ((isset($context['attachments']) && empty($context['attachments'])) || (!isset($context['attachments']) && empty($context['posts'])))
@@ -469,11 +427,84 @@ function template_showPosts()
 		echo '
 			</tbody>
 		</table>';
-	}
+
 	// Show more page numbers.
 	echo '
 		<div class="pagesection" style="margin-bottom: 0;">
-			<span>', $txt['pages'], ': ', $context['page_index'], '</span>
+			<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>';
 }
 
@@ -486,6 +517,7 @@ function template_editBuddies()
 	$buddy_fields = array('icq', 'aim', 'yim', 'msn');
 
 	echo '
+	<div class="generic_list_wrapper" id="edit_buddies">
 		<div class="title_bar">
 			<h3 class="titlebg">
 				<img src="', $settings['images_url'], '/icons/online.png" alt="" class="icon" />', $txt['editBuddies'], '
@@ -546,11 +578,11 @@ function template_editBuddies()
 	}
 
 	echo '
-		</table>';
+		</table>
+	</div>';
 
 	// Add a new buddy?
 	echo '
-	<br />
 	<form action="', $scripturl, '?action=profile;u=', $context['id_member'], ';area=lists;sa=buddies" method="post" accept-charset="', $context['character_set'], '">
 		<div class="tborder add_buddy">
 			<div class="title_bar">
@@ -573,7 +605,6 @@ function template_editBuddies()
 	echo '
 				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 				<input type="submit" value="', $txt['buddy_add_button'], '" class="button_submit" />
-				<br class="clear_right" />
 			</div>
 		</div>
 	</form>
@@ -598,6 +629,7 @@ function template_editIgnoreList()
 	global $context, $settings, $options, $scripturl, $modSettings, $txt;
 
 	echo '
+	<div class="generic_list_wrapper" id="edit_buddies">
 		<div class="title_bar">
 			<h3 class="titlebg">
 				<img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />', $txt['editIgnoreList'], '
@@ -648,11 +680,11 @@ function template_editIgnoreList()
 	}
 
 	echo '
-		</table>';
+		</table>
+	</div>';
 
 	// Add to the ignore list?
 	echo '
-	<br />
 	<form action="', $scripturl, '?action=profile;u=', $context['id_member'], ';area=lists;sa=ignore" method="post" accept-charset="', $context['character_set'], '">
 		<div class="tborder add_buddy">
 			<div class="title_bar">
@@ -675,7 +707,6 @@ function template_editIgnoreList()
 	echo '
 				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 				<input type="submit" value="', $txt['ignore_add_button'], '" class="button_submit" />
-				<br class="clear_right" />
 			</div>
 		</div>
 	</form>
@@ -701,6 +732,7 @@ function template_trackActivity()
 
 	// The first table shows IP information about the user.
 	echo '
+		<div class="generic_list_wrapper">
 			<div class="title_bar">
 				<h3 class="titlebg"><strong>', $txt['view_ips_by'], ' ', $context['member']['name'], '</strong></h3>
 			</div>';
@@ -712,7 +744,7 @@ function template_trackActivity()
 					<dl class="noborder">
 						<dt>', $txt['most_recent_ip'], ':
 							', (empty($context['last_ip2']) ? '' : '<br />
-							<span class="smalltext">(<a href="' . $scripturl . '?action=helpadmin;help=whytwoip" onclick="return reqWin(this.href);">' . $txt['why_two_ip_address'] . '</a>)</span>'), '
+							<span class="smalltext">(<a href="' . $scripturl . '?action=helpadmin;help=whytwoip" onclick="return reqOverlayDiv(this.href);">' . $txt['why_two_ip_address'] . '</a>)</span>'), '
 						</dt>
 						<dd>
 							<a href="', $scripturl, '?action=profile;area=tracking;sa=ip;searchip=', $context['last_ip'], ';u=', $context['member']['id'], '">', $context['last_ip'], '</a>';
@@ -745,7 +777,7 @@ function template_trackActivity()
 					</dl>
 				</div>
 			</div>
-			<br class="clear" />';
+		</div>';
 
 	// Show the track user list.
 	template_show_list('track_user_list');
@@ -774,11 +806,10 @@ function template_trackIP()
 					</dd>
 				</dl>
 				<input type="submit" value="', $txt['trackIP'], '" class="button_submit" />
-				<br class="clear_right" />
 			</form>
 		</div>
 	</div>
-	<br class="clear" />';
+	<div class="generic_list_wrapper">';
 
 	// The table inbetween the first and second table shows links to the whois server for every region.
 	if ($context['single_ip'])
@@ -794,8 +825,7 @@ function template_trackIP()
 					<a href="', $server['url'], '" target="_blank" class="new_win"', isset($context['auto_whois_server']) && $context['auto_whois_server']['name'] == $server['name'] ? ' style="font-weight: bold;"' : '', '>', $server['name'], '</a><br />';
 			echo '
 				</div>
-			</div>
-		<br class="clear" />';
+			</div>';
 	}
 
 	// The second table lists all the members who have been logged as using this IP address.
@@ -828,10 +858,12 @@ function template_trackIP()
 
 		echo '
 			</tbody>
-		</table>
-		<br />';
+		</table>';
 	}
 
+	echo '
+	</div>';
+
 	template_show_list('track_message_list');
 
 	echo '<br />';
@@ -853,7 +885,7 @@ function template_showPermissions()
 	if ($context['member']['has_all_permissions'])
 	{
 		echo '
-		<p class="windowbg description">', $txt['showPermissions_all'], '</p>';
+		<p class="description">', $txt['showPermissions_all'], '</p>';
 	}
 	else
 	{
@@ -1001,14 +1033,14 @@ function template_statPanel()
 
 	// First, show a few text statistics such as post/topic count.
 	echo '
+	<div class="cat_bar">
+		<h3 class="catbg">
+			<img src="', $settings['images_url'], '/stats_info.png" alt="" class="icon" />
+			', $txt['statPanel_generalStats'], ' - ', $context['member']['name'], '
+		</h3>
+	</div>
 	<div id="profileview">
 		<div id="generalstats">
-			<div class="cat_bar">
-				<h3 class="catbg">
-					<img src="', $settings['images_url'], '/stats_info.png" alt="" class="icon" />
-					', $txt['statPanel_generalStats'], ' - ', $context['member']['name'], '
-				</h3>
-			</div>
 			<div class="windowbg2">
 				<div class="content">
 					<dl>
@@ -1155,8 +1187,7 @@ function template_statPanel()
 		</div>';
 
 	echo '
-	</div>
-	<br class="clear" />';
+	</div>';
 }
 
 // Template for editing profile options.
@@ -1186,7 +1217,7 @@ function template_edit_options()
 	// Have we some description?
 	if ($context['page_desc'])
 		echo '
-			<p class="windowbg description">', $context['page_desc'], '</p>';
+			<p class="description">', $context['page_desc'], '</p>';
 
 	echo '
 			<div class="windowbg2">
@@ -1361,10 +1392,8 @@ function template_edit_options()
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="hidden" name="u" value="', $context['id_member'], '" />
 					<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
-					<br class="clear_right" />
 				</div>
 			</div>
-			<br />
 		</form>';
 
 	// Some javascript!
@@ -1629,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>
@@ -1665,7 +1713,7 @@ function template_notification()
 					<img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />', $txt['profile'], '
 				</h3>
 			</div>
-			<p class="windowbg description">', $txt['notification_info'], '</p>
+			<p class="description">', $txt['notification_info'], '</p>
 			<div class="windowbg2">
 				<div class="content">
 					<dl class="settings">';
@@ -1728,11 +1776,11 @@ function template_notification()
 					<hr class="hrcolor" />
 					<div>
 						<input id="notify_submit" type="submit" value="', $txt['notify_save'], '" class="button_submit" />
-						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />', !empty($context['token_check']) ? '
+						<input type="hidden" name="' . $context[$context['token_check'] . '_token_var'] . '" value="' . $context[$context['token_check'] . '_token'] . '" />' : '', '
 						<input type="hidden" name="u" value="', $context['id_member'], '" />
 						<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
 					</div>
-					<br class="clear_right" />
 				</div>
 			</div>
 		</form>
@@ -1976,8 +2024,7 @@ function template_ignoreboards()
 	}
 
 	echo '
-				</ul>
-				<br class="clear" />';
+				</ul>';
 
 	// Show the standard "Save Settings" profile button.
 	template_profile_save();
@@ -2333,7 +2380,6 @@ function template_issueWarning()
 					<input type="submit" name="preview" id="preview_button" value="', $txt['preview'], '" class="button_submit" />
 					<input type="submit" name="save" value="', $context['user']['is_owner'] ? $txt['change_profile'] : $txt['profile_warning_issue'], '" class="button_submit" />
 				</div>
-				<br class="clear" />
 			</div>
 		</div>
 	</form>';
@@ -2548,11 +2594,11 @@ function template_profile_group_manage()
 	echo '
 							<dt>
 								<strong>', $txt['primary_membergroup'], ': </strong><br />
-								<span class="smalltext">[<a href="', $scripturl, '?action=helpadmin;help=moderator_why_missing" onclick="return reqWin(this.href);">', $txt['moderator_why_missing'], '</a>]</span>
+								<span class="smalltext">[<a href="', $scripturl, '?action=helpadmin;help=moderator_why_missing" onclick="return reqOverlayDiv(this.href);">', $txt['moderator_why_missing'], '</a>]</span>
 							</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']))
@@ -2805,7 +2851,7 @@ function template_profile_timeformat_modify()
 	echo '
 							<dt>
 								<strong><label for="easyformat">', $txt['time_format'], ':</label></strong><br />
-								<a href="', $scripturl, '?action=helpadmin;help=time_format" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="floatleft" /></a>
+								<a href="', $scripturl, '?action=helpadmin;help=time_format" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="floatleft" /></a>
 								<span class="smalltext">&nbsp;<label for="time_format">', $txt['date_format'], '</label></span>
 							</dt>
 							<dd>
@@ -2882,12 +2928,12 @@ function template_authentication_method()
 					<img src="', $settings['images_url'], '/icons/profile_sm.png" alt="" class="icon" />', $txt['authentication'], '
 				</h3>
 			</div>
-			<p class="windowbg description">', $txt['change_authentication'], '</p>
+			<p class="description">', $txt['change_authentication'], '</p>
 			<div class="windowbg2">
 				<div class="content">
 					<dl>
 						<dt>
-							<input type="radio" onclick="updateAuthMethod();" name="authenticate" value="openid" id="auth_openid"', $context['auth_method'] == 'openid' ? ' checked="checked"' : '', ' class="input_radio" /><label for="auth_openid"><strong>', $txt['authenticate_openid'], '</strong></label>&nbsp;<em><a href="', $scripturl, '?action=helpadmin;help=register_openid" onclick="return reqWin(this.href);" class="help">(?)</a></em><br />
+							<input type="radio" onclick="updateAuthMethod();" name="authenticate" value="openid" id="auth_openid"', $context['auth_method'] == 'openid' ? ' checked="checked"' : '', ' class="input_radio" /><label for="auth_openid"><strong>', $txt['authenticate_openid'], '</strong></label>&nbsp;<em><a href="', $scripturl, '?action=helpadmin;help=register_openid" onclick="return reqOverlayDiv(this.href);" class="help">(?)</a></em><br />
 							<input type="radio" onclick="updateAuthMethod();" name="authenticate" value="passwd" id="auth_pass"', $context['auth_method'] == 'password' ? ' checked="checked"' : '', ' class="input_radio" /><label for="auth_pass"><strong>', $txt['authenticate_password'], '</strong></label>
 						</dt>
 						<dd>
@@ -2943,7 +2989,6 @@ function template_authentication_method()
 					<input type="hidden" name="u" value="', $context['id_member'], '" />
 					<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
 					<input type="submit" value="', $txt['change_profile'], '" class="button_submit" />
-					<br class="clear_right" />
 				</div>
 			</div>
 		</form>';

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