1<?php
2/**
3 * MyBB 1.8
4 * Copyright 2014 MyBB Group, All Rights Reserved
5 *
6 * Website: http://www.mybb.com
7 * License: http://www.mybb.com/about/license
8 *
9 */
10
11/*
12 * MyBB Admin CP Page Generation Class
13 */
14class DefaultPage
15{
16
17	/**
18	 * @var string The current style in use.
19	 */
20	public $style;
21
22	/**
23	 * @var array The primary menu items.
24	 */
25	public $menu = array();
26
27	/**
28	 * @var string The side bar menu items.
29	 */
30	public $submenu = '';
31
32	/**
33	 * @var string The module we're currently in.
34	 */
35	public $active_module;
36
37	/**
38	 * @var string The action we're currently performing.
39	 */
40	public $active_action;
41
42	/**
43	 * @var string Content for the side bar of the page if we have one.
44	 */
45	public $sidebar;
46
47	/**
48	 * @var array The breadcrumb trail leading up to this page.
49	 */
50	public $_breadcrumb_trail = array();
51
52	/**
53	 * @var string Any additional information to add between the <head> tags.
54	 */
55	public $extra_header = "";
56
57	/**
58	 * @var string Any additional messages to add after the flash messages are shown.
59	 */
60	public $extra_messages = array();
61
62	/**
63	 * @var string Show a post verify error
64	 */
65	public $show_post_verify_error = '';
66
67	/**
68	 * Output the page header.
69	 *
70	 * @param string $title The title of the page.
71	 */
72	function output_header($title="")
73	{
74		global $mybb, $admin_session, $lang, $plugins;
75
76		$args = array(
77			'this' => &$this,
78			'title' => &$title,
79		);
80
81		$plugins->run_hooks("admin_page_output_header", $args);
82
83		if(!$title)
84		{
85			$title = $lang->mybb_admin_panel;
86		}
87
88		$rtl = "";
89		if($lang->settings['rtl'] == 1)
90		{
91			$rtl = " dir=\"rtl\"";
92		}
93
94		echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
95		echo "<html xmlns=\"http://www.w3.org/1999/xhtml\"{$rtl}>\n";
96		echo "<head profile=\"http://gmpg.org/xfn/1\">\n";
97		echo "	<title>".$title."</title>\n";
98		echo "	<meta name=\"author\" content=\"MyBB Group\" />\n";
99		echo "	<meta name=\"copyright\" content=\"Copyright ".COPY_YEAR." MyBB Group.\" />\n";
100		echo "	<link rel=\"stylesheet\" href=\"styles/".$this->style."/main.css?ver=1813\" type=\"text/css\" />\n";
101		echo "	<link rel=\"stylesheet\" href=\"styles/".$this->style."/modal.css?ver=1813\" type=\"text/css\" />\n";
102
103		// Load stylesheet for this module if it has one
104		if(file_exists(MYBB_ADMIN_DIR."styles/{$this->style}/{$this->active_module}.css"))
105		{
106			echo "	<link rel=\"stylesheet\" href=\"styles/{$this->style}/{$this->active_module}.css\" type=\"text/css\" />\n";
107		}
108
109		echo "	<script type=\"text/javascript\" src=\"../jscripts/jquery.js?ver=1823\"></script>\n";
110		echo "	<script type=\"text/javascript\" src=\"../jscripts/jquery.plugins.min.js?ver=1821\"></script>\n";
111		echo "	<script type=\"text/javascript\" src=\"../jscripts/general.js?ver=1821\"></script>\n";
112		echo "	<script type=\"text/javascript\" src=\"./jscripts/admincp.js?ver=1821\"></script>\n";
113		echo "	<script type=\"text/javascript\" src=\"./jscripts/tabs.js\"></script>\n";
114
115		echo "	<link rel=\"stylesheet\" href=\"jscripts/jqueryui/css/redmond/jquery-ui.min.css\" />\n";
116		echo "	<link rel=\"stylesheet\" href=\"jscripts/jqueryui/css/redmond/jquery-ui.structure.min.css\" />\n";
117		echo "	<link rel=\"stylesheet\" href=\"jscripts/jqueryui/css/redmond/jquery-ui.theme.min.css\" />\n";
118		echo "	<script src=\"jscripts/jqueryui/js/jquery-ui.min.js?ver=1813\"></script>\n";
119
120		// Stop JS elements showing while page is loading (JS supported browsers only)
121		echo "  <style type=\"text/css\">.popup_button { display: none; } </style>\n";
122		echo "  <script type=\"text/javascript\">\n".
123				"//<![CDATA[\n".
124				"	document.write('<style type=\"text/css\">.popup_button { display: inline; } .popup_menu { display: none; }<\/style>');\n".
125                "//]]>\n".
126                "</script>\n";
127
128		echo "	<script type=\"text/javascript\">
129//<![CDATA[
130var loading_text = '{$lang->loading_text}';
131var cookieDomain = '{$mybb->settings['cookiedomain']}';
132var cookiePath = '{$mybb->settings['cookiepath']}';
133var cookiePrefix = '{$mybb->settings['cookieprefix']}';
134var cookieSecureFlag = '{$mybb->settings['cookiesecureflag']}';
135var imagepath = '../images';
136
137lang.unknown_error = \"{$lang->unknown_error}\";
138lang.saved = \"{$lang->saved}\";
139//]]>
140</script>\n";
141		echo $this->extra_header;
142		echo "</head>\n";
143		echo "<body>\n";
144		echo "<div id=\"container\">\n";
145		echo "	<div id=\"logo\"><h1><span class=\"invisible\">{$lang->mybb_admin_cp}</span></h1></div>\n";
146		$username = htmlspecialchars_uni($mybb->user['username']);
147		echo "	<div id=\"welcome\"><span class=\"logged_in_as\">{$lang->logged_in_as} <a href=\"index.php?module=user-users&amp;action=edit&amp;uid={$mybb->user['uid']}\" class=\"username\">{$username}</a></span> | <a href=\"{$mybb->settings['bburl']}\" target=\"_blank\" class=\"forum\">{$lang->view_board}</a> | <a href=\"index.php?action=logout&amp;my_post_key={$mybb->post_code}\" class=\"logout\">{$lang->logout}</a></div>\n";
148		echo $this->_build_menu();
149		echo "	<div id=\"page\">\n";
150		echo "		<div id=\"left_menu\">\n";
151		echo $this->submenu;
152		echo $this->sidebar;
153		echo "		</div>\n";
154		echo "		<div id=\"content\">\n";
155		echo "			<div class=\"breadcrumb\">\n";
156		echo $this->_generate_breadcrumb();
157		echo "			</div>\n";
158		echo "           <div id=\"inner\">\n";
159		if(isset($admin_session['data']['flash_message']) && $admin_session['data']['flash_message'])
160		{
161			$message = $admin_session['data']['flash_message']['message'];
162			$type = $admin_session['data']['flash_message']['type'];
163			echo "<div id=\"flash_message\" class=\"{$type}\">\n";
164			echo "{$message}\n";
165			echo "</div>\n";
166			update_admin_session('flash_message', '');
167		}
168
169		if(!empty($this->extra_messages) && is_array($this->extra_messages))
170		{
171			foreach($this->extra_messages as $message)
172			{
173				switch($message['type'])
174				{
175					case 'success':
176					case 'error':
177						echo "<div id=\"flash_message\" class=\"{$message['type']}\">\n";
178						echo "{$message['message']}\n";
179						echo "</div>\n";
180						break;
181					default:
182						$this->output_error($message['message']);
183						break;
184				}
185			}
186		}
187
188		if($this->show_post_verify_error == true)
189		{
190			$this->output_error($lang->invalid_post_verify_key);
191		}
192	}
193
194	/**
195	 * Output the page footer.
196	 *
197	 * @param bool $quit
198	 */
199	function output_footer($quit=true)
200	{
201		global $mybb, $maintimer, $db, $lang, $plugins;
202
203		$args = array(
204			'this' => &$this,
205			'quit' => &$quit,
206		);
207
208		$plugins->run_hooks("admin_page_output_footer", $args);
209
210		$memory_usage = get_friendly_size(get_memory_usage());
211
212		$totaltime = format_time_duration($maintimer->stop());
213		$querycount = $db->query_count;
214
215		if(my_strpos(getenv("REQUEST_URI"), "?"))
216		{
217			$debuglink = htmlspecialchars_uni(getenv("REQUEST_URI")) . "&amp;debug=1#footer";
218		}
219		else
220		{
221			$debuglink = htmlspecialchars_uni(getenv("REQUEST_URI")) . "?debug=1#footer";
222		}
223
224		echo "			</div>\n";
225		echo "		</div>\n";
226		echo "	<br style=\"clear: both;\" />";
227		echo "	<br style=\"clear: both;\" />";
228		echo "	</div>\n";
229		echo "<div id=\"footer\"><p class=\"generation\">".$lang->sprintf($lang->generated_in, $totaltime, $debuglink, $querycount, $memory_usage)."</p><p class=\"powered\">Powered By <a href=\"https://mybb.com/\" target=\"_blank\" rel=\"noopener\">MyBB</a>, &copy; 2002-".COPY_YEAR." <a href=\"https://mybb.com/\" target=\"_blank\" rel=\"noopener\">MyBB Group</a>.</p></div>\n";
230		if($mybb->debug_mode)
231		{
232			echo $db->explain;
233		}
234		echo "</div>\n";
235		echo "</body>\n";
236		echo "</html>\n";
237
238		if($quit != false)
239		{
240			exit;
241		}
242	}
243
244	/**
245	 * Add an item to the page breadcrumb trail.
246	 *
247	 * @param string $name The name of the item to add.
248	 * @param string $url The URL to the item we're adding (if there is one)
249	 */
250	function add_breadcrumb_item($name, $url="")
251	{
252		$this->_breadcrumb_trail[] = array("name" => $name, "url" => $url);
253	}
254
255	/**
256	 * Generate a breadcrumb trail.
257	 *
258	 * @return bool|string
259	 */
260	function _generate_breadcrumb()
261	{
262		if(!is_array($this->_breadcrumb_trail))
263		{
264			return false;
265		}
266		$trail = "";
267		foreach($this->_breadcrumb_trail as $key => $crumb)
268		{
269			if(isset($this->_breadcrumb_trail[$key+1]))
270			{
271				$trail .= "<a href=\"".$crumb['url']."\">".$crumb['name']."</a>";
272				if(isset($this->_breadcrumb_trail[$key+2]))
273				{
274					$trail .= " &raquo; ";
275				}
276			}
277			else
278			{
279				$trail .= "<span class=\"active\">".$crumb['name']."</span>";
280			}
281		}
282		return $trail;
283	}
284
285	/**
286	 * Output a success message.
287	 *
288	 * @param string $message The message to output.
289	 */
290	function output_success($message)
291	{
292		echo "<div class=\"success\">{$message}</div>\n";
293	}
294
295	/**
296	 * Output an alert/warning message.
297	 *
298	 * @param string $message The message to output.
299	 * @param string $id The ID of the alert/warning (optional)
300	 */
301	function output_alert($message, $id="")
302	{
303		if($id)
304		{
305			$id = " id=\"{$id}\"";
306		}
307		echo "<div class=\"alert\"{$id}>{$message}</div>\n";
308	}
309
310	/**
311	 * Output an inline message.
312	 *
313	 * @param string $message The message to output.
314	 */
315	function output_inline_message($message)
316	{
317		echo "<div class=\"inline_message\">{$message}</div>\n";
318	}
319
320	/**
321	 * Output a single error message.
322	 *
323	 * @param string $error The message to output.
324	 */
325	function output_error($error)
326	{
327		echo "<div class=\"error\">\n";
328		echo "{$error}\n";
329		echo "</div>\n";
330	}
331
332	/**
333	 * Output one or more inline error messages.
334	 *
335	 * @param array $errors Array of error messages to output.
336	 */
337	function output_inline_error($errors)
338	{
339		global $lang;
340
341		if(!is_array($errors))
342		{
343			$errors = array($errors);
344		}
345		echo "<div class=\"error\">\n";
346		echo "<p><em>{$lang->encountered_errors}</em></p>\n";
347		echo "<ul>\n";
348		foreach($errors as $error)
349		{
350			echo "<li>{$error}</li>\n";
351		}
352		echo "</ul>\n";
353		echo "</div>\n";
354	}
355
356	/**
357	 * Generate the login page.
358	 *
359	 * @param string $message The any message to output on the page if there is one.
360	 * @param string $class The class name of the message (defaults to success)
361	 */
362	function show_login($message="", $class="success")
363	{
364		global $plugins, $lang, $cp_style, $mybb;
365
366		$args = array(
367			'this' => &$this,
368			'message' => &$message,
369			'class' => &$class
370		);
371
372		$plugins->run_hooks('admin_page_show_login_start', $args);
373
374		$copy_year = COPY_YEAR;
375
376		$login_container_width = "";
377		$login_label_width = "";
378
379		// If the language string for "Username" is too cramped then use this to define how much larger you want the gap to be (in px)
380		if(isset($lang->login_field_width))
381		{
382			$login_label_width = " style=\"width: ".((int)$lang->login_field_width+100)."px;\"";
383			$login_container_width = " style=\"width: ".(410+((int)$lang->login_field_width))."px;\"";
384		}
385
386		$login_page = <<<EOF
387<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
388<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
389<head profile="http://gmpg.org/xfn/1">
390<title>{$lang->mybb_admin_login}</title>
391<meta name="author" content="MyBB Group" />
392<meta name="copyright" content="Copyright {$copy_year} MyBB Group." />
393<link rel="stylesheet" href="./styles/{$cp_style}/login.css" type="text/css" />
394<script type="text/javascript" src="../jscripts/jquery.js?ver=1823"></script>
395<script type="text/javascript" src="../jscripts/general.js?ver=1821"></script>
396<script type="text/javascript" src="./jscripts/admincp.js?ver=1821"></script>
397<script type="text/javascript">
398//<![CDATA[
399	loading_text = '{$lang->loading_text}';
400//]]>
401</script>
402</head>
403<body>
404<div id="container"{$login_container_width}>
405	<div id="header">
406		<div id="logo">
407			<h1><a href="../" title="{$lang->return_to_forum}"><span class="invisible">{$lang->mybb_acp}</span></a></h1>
408
409		</div>
410	</div>
411	<div id="content">
412		<h2>{$lang->please_login}</h2>
413EOF;
414		if($message)
415		{
416			$login_page .= "<p id=\"message\" class=\"{$class}\"><span class=\"text\">{$message}</span></p>";
417		}
418		// Make query string nice and pretty so that user can go to his/her preferred destination
419		$query_string = '';
420		if($_SERVER['QUERY_STRING'])
421		{
422			$query_string = '?'.preg_replace('#adminsid=(.{32})#i', '', $_SERVER['QUERY_STRING']);
423			$query_string = preg_replace('#my_post_key=(.{32})#i', '', $query_string);
424			$query_string = str_replace('action=logout', '', $query_string);
425			$query_string = preg_replace('#&+#', '&', $query_string);
426			$query_string = str_replace('?&', '?', $query_string);
427			$query_string = htmlspecialchars_uni($query_string);
428		}
429		switch($mybb->settings['username_method'])
430		{
431			case 0:
432				$lang_username = $lang->username;
433				break;
434			case 1:
435				$lang_username = $lang->username1;
436				break;
437			case 2:
438				$lang_username = $lang->username2;
439				break;
440			default:
441				$lang_username = $lang->username;
442				break;
443		}
444
445		// Secret PIN
446		global $config;
447		if(isset($config['secret_pin']) && $config['secret_pin'] != '')
448		{
449			$secret_pin = "<div class=\"label\"{$login_label_width}><label for=\"pin\">{$lang->secret_pin}</label></div>
450            <div class=\"field\"><input type=\"password\" name=\"pin\" id=\"pin\" class=\"text_input\" /></div>";
451		}
452		else
453		{
454			$secret_pin = '';
455		}
456
457		$login_lang_string = $lang->enter_username_and_password;
458
459		switch($mybb->settings['username_method'])
460		{
461			case 0: // Username only
462				$login_lang_string = $lang->sprintf($login_lang_string, $lang->login_username);
463				break;
464			case 1: // Email only
465				$login_lang_string = $lang->sprintf($login_lang_string, $lang->login_email);
466				break;
467			case 2: // Username and email
468			default:
469				$login_lang_string = $lang->sprintf($login_lang_string, $lang->login_username_and_password);
470				break;
471		}
472
473       	$this_file = htmlspecialchars_uni($_SERVER['SCRIPT_NAME']);
474
475		$login_page .= <<<EOF
476		<p>{$login_lang_string}</p>
477		<form method="post" action="{$this_file}{$query_string}">
478		<div class="form_container">
479
480			<div class="label"{$login_label_width}><label for="username">{$lang_username}</label></div>
481
482			<div class="field"><input type="text" name="username" id="username" class="text_input initial_focus" /></div>
483
484			<div class="label"{$login_label_width}><label for="password">{$lang->password}</label></div>
485			<div class="field"><input type="password" name="password" id="password" class="text_input" /></div>
486            {$secret_pin}
487		</div>
488		<p class="submit">
489			<span class="forgot_password">
490				<a href="../member.php?action=lostpw">{$lang->lost_password}</a>
491			</span>
492
493			<input type="submit" value="{$lang->login}" />
494			<input type="hidden" name="do" value="login" />
495		</p>
496		</form>
497	</div>
498</div>
499</body>
500</html>
501EOF;
502
503		$args = array(
504			'this' => &$this,
505			'login_page' => &$login_page
506		);
507
508		$plugins->run_hooks('admin_page_show_login_end', $args);
509
510		echo $login_page;
511		exit;
512	}
513
514	function show_2fa()
515	{
516		global $lang, $cp_style, $mybb;
517
518		$copy_year = COPY_YEAR;
519
520		$mybb2fa_page = <<<EOF
521<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
522<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
523<head profile="http://gmpg.org/xfn/1">
524<title>{$lang->my2fa}</title>
525<meta name="author" content="MyBB Group" />
526<meta name="copyright" content="Copyright {$copy_year} MyBB Group." />
527<link rel="stylesheet" href="./styles/{$cp_style}/login.css" type="text/css" />
528<script type="text/javascript" src="../jscripts/jquery.js?ver=1823"></script>
529<script type="text/javascript" src="../jscripts/general.js?ver=1821"></script>
530<script type="text/javascript" src="./jscripts/admincp.js?ver=1821"></script>
531<script type="text/javascript">
532//<![CDATA[
533	loading_text = '{$lang->loading_text}';
534//]]>
535</script>
536</head>
537<body>
538<div id="container">
539	<div id="header">
540		<div id="logo">
541			<h1><a href="../" title="{$lang->return_to_forum}"><span class="invisible">{$lang->mybb_acp}</span></a></h1>
542		</div>
543	</div>
544	<div id="content">
545		<h2>{$lang->my2fa}</h2>
546EOF;
547		// Make query string nice and pretty so that user can go to his/her preferred destination
548		$query_string = '';
549		if($_SERVER['QUERY_STRING'])
550		{
551			$query_string = '?'.preg_replace('#adminsid=(.{32})#i', '', $_SERVER['QUERY_STRING']);
552			$query_string = preg_replace('#my_post_key=(.{32})#i', '', $query_string);
553			$query_string = str_replace('action=logout', '', $query_string);
554			$query_string = preg_replace('#&+#', '&', $query_string);
555			$query_string = str_replace('?&', '?', $query_string);
556			$query_string = htmlspecialchars_uni($query_string);
557		}
558		$mybb2fa_page .= <<<EOF
559		<p>{$lang->my2fa_code}</p>
560		<form method="post" action="index.php{$query_string}">
561		<div class="form_container">
562			<div class="label"><label for="code">{$lang->my2fa_label}</label></div>
563			<div class="field"><input type="text" name="code" id="code" class="text_input initial_focus" /></div>
564		</div>
565		<p class="submit">
566			<input type="submit" value="{$lang->login}" />
567			<input type="hidden" name="do" value="do_2fa" />
568		</p>
569		</form>
570	</div>
571</div>
572</body>
573</html>
574EOF;
575		echo $mybb2fa_page;
576		exit;
577	}
578
579	/**
580	 * Generate the lockout page
581	 *
582	 */
583	function show_lockedout()
584	{
585		global $lang, $mybb, $cp_style;
586
587		$copy_year = COPY_YEAR;
588		$allowed_attempts = (int)$mybb->settings['maxloginattempts'];
589		$lockedout_message = $lang->sprintf($lang->error_mybb_admin_lockedout_message, $allowed_attempts);
590
591		print <<<EOF
592<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
593<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
594<head profile="http://gmpg.org/xfn/1">
595<title>{$lang->mybb_admin_cp} - {$lang->error_mybb_admin_lockedout}</title>
596<meta name="author" content="MyBB Group" />
597<meta name="copyright" content="Copyright {$copy_year} MyBB Group." />
598<link rel="stylesheet" href="./styles/{$cp_style}/login.css" type="text/css" />
599</head>
600<body>
601<div id="container">
602	<div id="header">
603		<div id="logo">
604			<h1><a href="../" title="{$lang->return_to_forum}"><span class="invisible">{$lang->mybb_acp}</span></a></h1>
605
606		</div>
607	</div>
608	<div id="content">
609		<h2>{$lang->error_mybb_admin_lockedout}</h2>
610		<div class="alert">{$lockedout_message}</div>
611	</div>
612</div>
613</body>
614</html>
615EOF;
616	exit;
617	}
618
619	/**
620	 * Generate the lockout unlock page
621	 *
622	 * @param string $message The any message to output on the page if there is one.
623	 * @param string $class The class name of the message (defaults to success)
624	 */
625	function show_lockout_unlock($message="", $class="success")
626	{
627		global $lang, $mybb, $cp_style;
628
629		$copy_year = COPY_YEAR;
630
631		$login_label_width = "";
632
633		// If the language string for "Username" is too cramped then use this to define how much larger you want the gap to be (in px)
634		if(isset($lang->login_field_width))
635		{
636			$login_label_width = " style=\"width: ".((int)$lang->login_field_width+100)."px;\"";
637		}
638
639		switch($mybb->settings['username_method'])
640		{
641			case 0:
642				$lang_username = $lang->username;
643				break;
644			case 1:
645				$lang_username = $lang->username1;
646				break;
647			case 2:
648				$lang_username = $lang->username2;
649				break;
650			default:
651				$lang_username = $lang->username;
652				break;
653		}
654
655		if($message)
656		{
657			$message = "<p id=\"message\" class=\"{$class}\"><span class=\"text\">{$message}</span></p>";
658		}
659
660		print <<<EOF
661<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
662<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
663<head profile="http://gmpg.org/xfn/1">
664<title>{$lang->mybb_admin_cp} - {$lang->lockout_unlock}</title>
665<meta name="author" content="MyBB Group" />
666<meta name="copyright" content="Copyright {$copy_year} MyBB Group." />
667<link rel="stylesheet" href="./styles/{$cp_style}/login.css" type="text/css" />
668</head>
669<body>
670<div id="container">
671	<div id="header">
672		<div id="logo">
673			<h1><a href="../" title="{$lang->return_to_forum}"><span class="invisible">{$lang->mybb_acp}</span></a></h1>
674
675		</div>
676	</div>
677	<div id="content">
678		<h2>{$lang->lockout_unlock}</h2>
679		{$message}
680		<p>{$lang->enter_username_and_token}</p>
681		<form method="post" action="index.php">
682		<div class="form_container">
683
684			<div class="label"{$login_label_width}><label for="username">{$lang_username}</label></div>
685
686			<div class="field"><input type="text" name="username" id="username" class="text_input initial_focus" /></div>
687
688			<div class="label"{$login_label_width}><label for="token">{$lang->unlock_token}</label></div>
689			<div class="field"><input type="text" name="token" id="token" class="text_input" /></div>
690		</div>
691		<p class="submit">
692			<span class="forgot_password">
693				<a href="../member.php?action=lostpw">{$lang->lost_password}</a>
694			</span>
695
696			<input type="submit" value="{$lang->unlock_account}" />
697			<input type="hidden" name="action" value="unlock" />
698		</p>
699		</form>
700	</div>
701</div>
702</body>
703</html>
704EOF;
705	exit;
706	}
707
708	/**
709	 * Add an item to the primary navigation menu.
710	 *
711	 * @param string $title The title of the menu item.
712	 * @param string $id The ID of the menu item. This should correspond with the module the menu will run.
713	 * @param string $link The link to follow when the menu item is clicked.
714	 * @param int $order The display order of the menu item. Lower display order means closer to start of the menu.
715	 * @param array $submenu Array of sub menu items if there are any.
716	 */
717	function add_menu_item($title, $id, $link, $order=10, $submenu=array())
718	{
719		$this->_menu[$order][] = array(
720			"title" => $title,
721			"id" => $id,
722			"link" => $link,
723			"submenu" => $submenu
724		);
725	}
726
727	/**
728	 * Build the actual navigation menu.
729	 *
730	 * @return bool|string
731	 */
732	function _build_menu()
733	{
734		if(!is_array($this->_menu))
735		{
736			return false;
737		}
738		$build_menu = "<div id=\"menu\">\n<ul>\n";
739		ksort($this->_menu);
740		foreach($this->_menu as $items)
741		{
742			foreach($items as $menu_item)
743			{
744				$menu_item['link'] = htmlspecialchars_uni($menu_item['link']);
745				if($menu_item['id'] == $this->active_module)
746				{
747					$sub_menu = $menu_item['submenu'];
748					$sub_menu_title = $menu_item['title'];
749					$build_menu .= "<li><a href=\"{$menu_item['link']}\" class=\"active\">{$menu_item['title']}</a></li>\n";
750
751				}
752				else
753				{
754					$build_menu .= "<li><a href=\"{$menu_item['link']}\">{$menu_item['title']}</a></li>\n";
755				}
756			}
757		}
758		$build_menu .= "</ul>\n</div>";
759
760		if(!empty($sub_menu))
761		{
762			$this->_build_submenu($sub_menu_title, $sub_menu);
763		}
764		return $build_menu;
765	}
766
767	/**
768	 * Build a navigation sub menu if we have one.
769	 *
770	 * @param string $title A title for the sub menu.
771	 * @param array $items Array of items for the sub menu.
772	 */
773	function _build_submenu($title, $items)
774	{
775		if(is_array($items))
776		{
777			$sidebar = new sideBarItem($title);
778			$sidebar->add_menu_items($items, $this->active_action);
779			$this->submenu .= $sidebar->get_markup();
780		}
781	}
782
783	/**
784	 * Output a Javascript based tab control on to the page.
785	 *
786	 * @param array $tabs Array of tabs in name => title format. Name should correspond to the name of a DIV containing the tab content.
787	 * @param boolean $observe_onload Whether or not to run the event onload or instantly
788	 * @param string $id The ID to use for the tabs for if you run multiple instances of the tabbing control in one html page
789	 */
790	function output_tab_control($tabs=array(), $observe_onload=true, $id="tabs")
791	{
792		global $plugins;
793		$tabs = $plugins->run_hooks("admin_page_output_tab_control_start", $tabs);
794		echo "<ul class=\"tabs\" id=\"{$id}\">\n";
795		$tab_count = count($tabs);
796		$done = 1;
797		foreach($tabs as $anchor => $title)
798		{
799			$class = "";
800			if($tab_count == $done)
801			{
802				$class .= " last";
803			}
804			if($done == 1)
805			{
806				$class .= " first";
807			}
808			++$done;
809			echo "<li class=\"{$class}\"><a href=\"#tab_{$anchor}\">{$title}</a></li>\n";
810		}
811		echo "</ul>\n";
812		$plugins->run_hooks("admin_page_output_tab_control_end", $tabs);
813	}
814
815	/**
816	 * Output a series of primary navigation tabs for swithcing between items within a particular module/action.
817	 *
818	 * @param array $tabs Nested array of tabs containing possible keys of align, link_target, link_rel, link, title.
819	 * @param string $active The name of the active tab. Corresponds with the key of each tab item.
820	 */
821	function output_nav_tabs($tabs=array(), $active='')
822	{
823		global $plugins;
824		$tabs = $plugins->run_hooks("admin_page_output_nav_tabs_start", $tabs);
825		echo "<div class=\"nav_tabs\">";
826		echo "\t<ul>\n";
827		foreach($tabs as $id => $tab)
828		{
829			$class = '';
830			if($id == $active)
831			{
832				$class = ' active';
833			}
834			if(isset($tab['align']) == "right")
835			{
836				$class .= " right";
837			}
838			$target = '';
839			if(isset($tab['link_target']))
840			{
841				$target = " target=\"{$tab['link_target']}\"";
842			}
843			$rel = '';
844			if(isset($tab['link_rel']))
845			{
846				$rel = " rel=\"{$tab['link_rel']}\"";
847			}
848			if(!isset($tab['link']))
849			{
850				$tab['link'] = '';
851			}
852			echo "\t\t<li class=\"{$class}\"><a href=\"{$tab['link']}\"{$target}{$rel}>{$tab['title']}</a></li>\n";
853			$target = '';
854		}
855		echo "\t</ul>\n";
856		if(!empty($tabs[$active]['description']))
857		{
858			echo "\t<div class=\"tab_description\">{$tabs[$active]['description']}</div>\n";
859		}
860		echo "</div>";
861		$arguments = array('tabs' => $tabs, 'active' => $active);
862		$plugins->run_hooks("admin_page_output_nav_tabs_end", $arguments);
863	}
864
865	/**
866	 * Output a page asking if a user wishes to continue performing a specific action.
867	 *
868	 * @param string $url The URL to be forwarded to.
869	 * @param string $message The confirmation message to output.
870	 * @param string $title The title to use in the output header
871	 */
872	function output_confirm_action($url, $message="", $title="")
873	{
874		global $lang, $plugins;
875
876		$args = array(
877			'this' => &$this,
878			'url' => &$url,
879			'message' => &$message,
880			'title' => &$title,
881		);
882
883		$plugins->run_hooks('admin_page_output_confirm_action', $args);
884
885		if(!$message)
886		{
887			$message = $lang->confirm_action;
888		}
889		$this->output_header($title);
890		$form = new Form($url, 'post');
891
892		echo "<div class=\"confirm_action\">\n";
893		echo "<p>{$message}</p>\n";
894		echo "<br />\n";
895		echo "<p class=\"buttons\">\n";
896		echo $form->generate_submit_button($lang->yes, array('class' => 'button_yes'));
897		echo $form->generate_submit_button($lang->no, array("name" => "no", 'class' => 'button_no'));
898		echo "</p>\n";
899		echo "</div>\n";
900
901		$form->end();
902		$this->output_footer();
903	}
904
905	/**
906	 * Build a clickable MyCode editor for the Admin CP.
907	 *
908	 * @param string $bind The ID of the textarea to bind the editor to.
909	 * @param string $editor_language The language string for the editor.
910	 * @param bool $smilies Whether or not smilies should be included
911	 * @return string The build MyCode editor Javascript.
912	 */
913	function build_codebuttons_editor($bind, $editor_language, $smilies)
914	{
915		global $lang, $mybb, $smiliecache, $smiliecount, $cache;
916
917		// Smilies
918		$emoticon = "";
919		$emoticons_enabled = "false";
920		if($smilies)
921		{
922			if($mybb->settings['smilieinserter'] && $mybb->settings['smilieinsertercols'] && $mybb->settings['smilieinsertertot'])
923			{
924				$emoticon = ",emoticon";
925			}
926			$emoticons_enabled = "true";
927
928			if(!$smiliecount)
929			{
930				$smilie_cache = $cache->read("smilies");
931				if(!is_array($smilie_cache))
932				{
933					$smilie_cache = array();
934				}
935				$smiliecount = count($smilie_cache);
936			}
937
938			if(!$smiliecache)
939			{
940				if(!is_array($smilie_cache))
941				{
942					$smilie_cache = $cache->read("smilies");
943				}
944				foreach($smilie_cache as $smilie)
945				{
946					$smilie['image'] = str_replace("{theme}", "images", $smilie['image']);
947					$smiliecache[$smilie['sid']] = $smilie;
948				}
949			}
950
951			unset($smilie);
952
953			if(is_array($smiliecache))
954			{
955				reset($smiliecache);
956
957				$dropdownsmilies = $moresmilies = $hiddensmilies = "";
958				$i = 0;
959
960				foreach($smiliecache as $smilie)
961				{
962					$finds = explode("\n", $smilie['find']);
963					$finds_count = count($finds);
964
965					// Only show the first text to replace in the box
966					$find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($finds[0]));
967					$image = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($smilie['image']));
968					if(substr($image, 0, 4) != "http")
969					{
970						$image = $mybb->settings['bburl']."/".$image;
971					}
972
973					if(!$mybb->settings['smilieinserter'] || !$mybb->settings['smilieinsertercols'] || !$mybb->settings['smilieinsertertot'] || !$smilie['showclickable'])
974					{
975						$hiddensmilies .= '"'.$find.'": "'.$image.'",';
976					}
977					elseif($i < $mybb->settings['smilieinsertertot'])
978					{
979						$dropdownsmilies .= '"'.$find.'": "'.$image.'",';
980						++$i;
981					}
982					else
983					{
984						$moresmilies .= '"'.$find.'": "'.$image.'",';
985					}
986
987					for($j = 1; $j < $finds_count; ++$j)
988					{
989						$find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($finds[$j]));
990						$hiddensmilies .= '"'.$find.'": "'.$image.'",';
991					}
992				}
993			}
994		}
995
996		$basic1 = $basic2 = $align = $font = $size = $color = $removeformat = $email = $link = $list = $code = $sourcemode = "";
997
998		if($mybb->settings['allowbasicmycode'] == 1)
999		{
1000			$basic1 = "bold,italic,underline,strike|";
1001			$basic2 = "horizontalrule,";
1002		}
1003
1004		if($mybb->settings['allowalignmycode'] == 1)
1005		{
1006			$align = "left,center,right,justify|";
1007		}
1008
1009		if($mybb->settings['allowfontmycode'] == 1)
1010		{
1011			$font = "font,";
1012		}
1013
1014		if($mybb->settings['allowsizemycode'] == 1)
1015		{
1016			$size = "size,";
1017		}
1018
1019		if($mybb->settings['allowcolormycode'] == 1)
1020		{
1021			$color = "color,";
1022		}
1023
1024		if($mybb->settings['allowfontmycode'] == 1 || $mybb->settings['allowsizemycode'] == 1 || $mybb->settings['allowcolormycode'] == 1)
1025		{
1026			$removeformat = "removeformat|";
1027		}
1028
1029		if($mybb->settings['allowemailmycode'] == 1)
1030		{
1031			$email = "email,";
1032		}
1033
1034		if($mybb->settings['allowlinkmycode'] == 1)
1035		{
1036			$link = "link,unlink";
1037		}
1038
1039		if($mybb->settings['allowlistmycode'] == 1)
1040		{
1041			$list = "bulletlist,orderedlist|";
1042		}
1043
1044		if($mybb->settings['allowcodemycode'] == 1)
1045		{
1046			$code = "code,php,";
1047		}
1048
1049		if($mybb->user['sourceeditor'] == 1)
1050		{
1051			$sourcemode = "MyBBEditor.sourceMode(true);";
1052		}
1053
1054		return <<<EOF
1055
1056<script type="text/javascript">
1057var partialmode = {$mybb->settings['partialmode']},
1058opt_editor = {
1059	plugins: "undo",
1060	format: "bbcode",
1061	bbcodeTrim: true,
1062	style: "../jscripts/sceditor/styles/jquery.sceditor.mybb.css",
1063	rtl: {$lang->settings['rtl']},
1064	locale: "mybblang",
1065	enablePasteFiltering: true,
1066	autoUpdate: true,
1067	emoticonsEnabled: {$emoticons_enabled},
1068	emoticons: {
1069		// Emoticons to be included in the dropdown
1070		dropdown: {
1071			{$dropdownsmilies}
1072		},
1073		// Emoticons to be included in the more section
1074		more: {
1075			{$moresmilies}
1076		},
1077		// Emoticons that are not shown in the dropdown but will still be converted. Can be used for things like aliases
1078		hidden: {
1079			{$hiddensmilies}
1080		}
1081	},
1082	emoticonsCompat: true,
1083	toolbar: "{$basic1}{$align}{$font}{$size}{$color}{$removeformat}{$basic2}image,{$email}{$link}|video{$emoticon}|{$list}{$code}quote|maximize,source",
1084};
1085{$editor_language}
1086$(function() {
1087	$("#{$bind}").sceditor(opt_editor);
1088
1089	MyBBEditor = $("#{$bind}").sceditor("instance");
1090	{$sourcemode}
1091});
1092</script>
1093EOF;
1094	}
1095}
1096
1097/**
1098 * A class for generating side bar blocks.
1099 */
1100class DefaultSidebarItem
1101{
1102	/**
1103	 * @var string The title of the side bar block.
1104	 */
1105	private $_title;
1106
1107	/**
1108	 * @var string The contents of the side bar block.
1109	 */
1110	private $_contents;
1111
1112	/**
1113	 * Constructor. Set the title of the side bar block.
1114	 *
1115	 * @param string $title The title of the side bar block.
1116	 */
1117	function __construct($title="")
1118	{
1119		$this->_title = $title;
1120	}
1121
1122	/**
1123	 * Add menus item to the side bar block.
1124	 *
1125	 * @param array $items Array of menu items to add. Each menu item should be a nested array of id, link and title.
1126	 * @param string $active The ID of the active menu item if there is one.
1127	 */
1128	function add_menu_items($items, $active)
1129	{
1130		global $run_module;
1131
1132		$this->_contents = "<ul class=\"menu\">";
1133		foreach($items as $item)
1134		{
1135			if(!check_admin_permissions(array("module" => $run_module, "action" => $item['id']), false))
1136			{
1137				continue;
1138			}
1139
1140			$class = "";
1141			if($item['id'] == $active)
1142			{
1143				$class = "active";
1144			}
1145			$item['link'] = htmlspecialchars_uni($item['link']);
1146			$this->_contents .= "<li class=\"{$class}\"><a href=\"{$item['link']}\">{$item['title']}</a></li>\n";
1147		}
1148		$this->_contents .= "</ul>";
1149	}
1150
1151	/**
1152	 * Sets custom html to the contents variable
1153	 *
1154	 * @param string $html The custom html to set
1155	 */
1156	function set_contents($html)
1157	{
1158		$this->_contents = $html;
1159	}
1160
1161	/**
1162	 * Fetch the HTML markup for the side bar box.
1163	 *
1164	 * @return string
1165	 */
1166	function get_markup()
1167	{
1168		$markup = "<div class=\"left_menu_box\">\n";
1169		$markup .= "<div class=\"title\">{$this->_title}</div>\n";
1170		if($this->_contents)
1171		{
1172			$markup .= $this->_contents;
1173		}
1174		$markup .= "</div>\n";
1175		return $markup;
1176	}
1177}
1178
1179/**
1180 * Generate a Javascript based popup menu.
1181 */
1182class DefaultPopupMenu
1183{
1184	/**
1185	 * @var string The title of the popup menu to be shown on the button.
1186	 */
1187	private $_title;
1188
1189	/**
1190	 * @var string The ID of this popup menu. Must be unique.
1191	 */
1192	private $_id;
1193
1194	/**
1195	 * @var string Built HTML for the items in the popup menu.
1196	 */
1197	private $_items;
1198
1199	/**
1200	 * Initialise a new popup menu.
1201	 *
1202	 * @var string $id The ID of the popup menu.
1203	 * @var string $title The title of the popup menu.
1204	 */
1205	function __construct($id, $title='')
1206	{
1207		$this->_id = $id;
1208		$this->_title = $title;
1209	}
1210
1211	/**
1212	 * Add an item to the popup menu.
1213	 *
1214	 * @param string $text The title of this item.
1215	 * @param string $link The page this item should link to.
1216	 * @param string $onclick The onclick event handler if we have one.
1217	 */
1218	function add_item($text, $link, $onclick='')
1219	{
1220		if($onclick)
1221		{
1222			$onclick = " onclick=\"{$onclick}\"";
1223		}
1224		$this->_items .= "<div class=\"popup_item_container\"><a href=\"{$link}\"{$onclick} class=\"popup_item\">{$text}</a></div>\n";
1225	}
1226
1227	/**
1228	 * Fetch the contents of the popup menu.
1229	 *
1230	 * @return string The popup menu.
1231	 */
1232	function fetch()
1233	{
1234		$popup = "<div class=\"popup_menu\" id=\"{$this->_id}_popup\">\n{$this->_items}</div>\n";
1235		if($this->_title)
1236		{
1237			$popup .= "<a href=\"javascript:;\" id=\"{$this->_id}\" class=\"popup_button\">{$this->_title}</a>\n";
1238		}
1239		$popup .= "<script type=\"text/javascript\">\n";
1240		$popup .= "$(\"#{$this->_id}\").popupMenu();\n";
1241		$popup .= "</script>\n";
1242		return $popup;
1243	}
1244
1245	/**
1246	 * Outputs a popup menu to the browser.
1247	 */
1248	function output()
1249	{
1250		echo $this->fetch();
1251	}
1252}
1253