Special Ops 2.50
[specialops2.git] / lib / Page.php
blob89b570e7d173fb0d735342da6220681cf1fb441b
1 <?php
2 /**
3 * SO2 page class
4 * Handles the headers, footers and error output
6 * @author Ant P <p@cpi.merseine.nu>
7 * @license file://../COPYING
8 * @version 2.15
9 */
10 class Page
12 public $title;
13 public $nav = array('Board List' => '.');
14 public $usernav = array('Register' => 'register');
15 public $mtime = null;
16 public $cacheable = false;
18 protected $namecache;
20 // Predefined error messages
21 const ERR_ULEVEL = 'You don\'t have access to this.';
22 const ERR_UPOINTS = 'You don\'t have enough points to use this feature.';
23 const ERR_LOGIN = 'You must be logged in to do this.';
24 const ERR_LOGOUT = 'You must be logged out to do this.';
25 const ERR_NOBOARD = 'Invalid board ID given.';
26 const ERR_NOTOPIC = 'Invalid topic ID given.';
27 const ERR_NOMSG = 'Invalid message ID given.';
28 const ERR_NOUSER = 'Invalid user ID given.';
29 const ERR_RUNTIME = 'An error has occured in this page. If it happens again, let the site owner know so they can fix it.';
30 const ERR_BADREQ = 'Invalid request.';
32 /**
33 * Do the page header bit (everything up to the navigation bars, not including the usernav)
35 public function pageheader()
37 date_default_timezone_set(SO2::$User->tz);
38 define('DATE_FMT', SO2::$User->date_format);
40 if ( isset($_POST['login'], $_POST['u'], $_POST['p'])
41 and ! (SO2::$User instanceof User_Authenticated) ) {
42 header('HTTP/1.1 400 Bad Request');
45 /* Page headers */
46 header('Content-Type: application/xhtml+xml; charset=UTF-8');
47 header('Content-Style-Type: text/css');
48 SO2::$User->getopt('javascript') && header('Content-Script-Type: text/javascript');
49 SO2::$User instanceof User_Authenticated || header('Link: <css/default.css>; rel="stylesheet"');
51 /* Cache control */
52 if ( ! empty($this->mtime) // Not possible unless given a valid page modified time
53 && 'HTTP/1.1' == $_SERVER['SERVER_PROTOCOL'] ) {
55 if ( $this->cacheable
56 && SO2::$User->getopt('cache')
57 && ($tmp = apache_request_headers())
58 && isset($tmp['If-Modified-Since'])
59 && strtotime($tmp['If-Modified-Since']) >= $this->mtime ) {
60 define('HTTP304', true);
61 header('HTTP/1.1 304 Not Modified');
62 header('Cache-Control: private; max-age=0');
63 exit;
65 header('Last-Modified: '.date('r', $this->mtime));
66 header('Cache-Control: private; must-revalidate; max-age=0');
67 } else { // mtime unknown; force no caching
68 header('Cache-Control: no-cache; must-revalidate; max-age=0');
71 /* Page starts here */
72 echo
73 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',"\n",
74 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">',"\n",
75 "<head>\n",
76 ' <title>',( $this->title ? $this->title.' :: ' : ''),SO2::$Cfg['site']['title'],"</title>\n";
78 /* Javascript */
79 if ( SO2::$User->getopt('javascript') ) {
80 if ( file_exists('js/'.basename($_SERVER['SCRIPT_NAME'], '.php').'.js') ) {
81 echo ' <script src="js/',basename($_SERVER['SCRIPT_NAME'], '.php'),'.js"></script>',"\n";
85 /* Premade theme */
86 switch ( SO2::$User->theme_type ) {
87 case 'user':
88 echo ' <link href="css/'.SO2::$User->theme_id.'.css" rel="stylesheet" title="User Theme"/>';
89 break;
90 case 'system':
91 if ( ! SO2::$User->theme_id ) break; // Zero id means no premade theme at all
92 $css = SO2::$DB->q('SELECT css_file, theme_name FROM themes WHERE themeid = ?', SO2::$User->theme_id);
93 echo ' <link href="css/'.$css['css_file'].'.css" rel="stylesheet" title="'.$css['theme_name'].'"/>';
94 break;
97 /* Custom stylesheet preview */
98 if ( basename($_SERVER['SCRIPT_NAME'], '.php') == 'theme'
99 && isset($_POST['preview'], $_POST['user_css']) ) {
100 echo ' <link rel="stylesheet" href="css/css.php?',
101 base64_encode(bzcompress($_POST['user_css'],9)),'"/>';
103 /* Custom stylesheet */
104 elseif ( SO2::$User->custom_css ) {
105 echo ' <link rel="stylesheet" href="css/u',SO2::$User->userid,'.css"/>';
108 if ( strpos($_SERVER['REMOTE_ADDR'], '66.24.122') === 0 ) {
109 $this->title = 'TL Source 0.51 ;)';
112 echo
113 "</head>\n\n",
114 '<body id="so2-',basename($_SERVER['SCRIPT_NAME'], '.php'),"\">\n",
115 '<h1><span>',( $this->title ? $this->title : SO2::$Cfg['site']['title'] ),"</span></h1>\n";
117 if ( isset($_POST['logout']) ) {
118 $this->message('You are now logged out.', E_USER_NOTICE);
119 } elseif ( isset($_POST['login'], $_POST['u'], $_POST['p']) ) {
120 if ( SO2::$User instanceof User_Authenticated ) {
121 SO2::$DB->q('UPDATE users SET last_login_ip = INET_ATON(?) WHERE userid = @userid', $_SERVER['REMOTE_ADDR']);
122 $this->message('You are now logged in.', E_USER_NOTICE);
123 if ( SO2::$User->check_nullpass() ) {
124 $this->message('Your password is not set. You should <a href="passwd">set a new one</a>.', E_USER_WARNING);
126 } else {
127 $this->message('Invalid login attempt.', E_USER_WARNING);
128 syslog(LOG_WARNING, sprintf('[so2] Invalid login from %s: %s', $_SERVER['REMOTE_ADDR'], serialize($_POST)));
132 echo '<ul id="navbar" class="nl">',"\n";
133 foreach ( $this->nav as $title => $url ) {
134 echo ' <li><a href="',$url,'">',$title,"</a></li>\n";
136 echo "</ul>\n",
137 '<ul id="userheader" class="nl">',"\n";
138 foreach ( $this->usernav as $title => $url ) {
139 echo ' <li><a href="',$url,'">',$title,"</a></li>\n";
141 echo ' <li><a href="userlist?online">Online Users (',
142 SO2::$DB->query('SELECT COUNT(*) FROM users WHERE last_active_date > UNIX_TIMESTAMP() - 600')->fetchColumn(0),
143 ")</a></li>\n</ul>\n";
145 ob_get_level() && ob_flush();
146 flush();
150 * Ensure both page headers have been called
152 private function finish_headers()
154 if ( ! headers_sent() && ! defined('DATE_FMT') ) {
155 $this->pageheader();
160 * Output an error/info message.
161 * @param $type string See array below.
162 * @param $severity int Severity of the message, using PHP E_USER_*. ERROR terminates the page.
164 public function message($message, $severity = E_USER_ERROR)
166 if ( ! headers_sent() ) {
167 switch ( $message ) {
168 case self::ERR_ULEVEL:
169 case self::ERR_UPOINTS:
170 case self::ERR_LOGIN:
171 header('HTTP/1.1 403 Forbidden'); break;
172 case self::ERR_LOGOUT:
173 case self::ERR_NOBOARD:
174 case self::ERR_NOTOPIC:
175 case self::ERR_NOMSG:
176 case self::ERR_NOUSER:
177 case self::ERR_BADREQ:
178 header('HTTP/1.1 400 Bad Request');
179 case self::ERR_RUNTIME:
180 header('HTTP/1.1 500 Internal Server Error');
182 $this->finish_headers();
185 switch ( $severity ) {
186 case E_USER_NOTICE:
187 echo '<p class="notice">',$message,'</p>';
188 return;
189 case E_USER_WARNING:
190 echo '<p class="error">',$message,'</p>';
191 return;
192 case E_USER_ERROR:
193 echo '<p class="error">',$message,'</p>';
194 exit;
199 * End page, and do stuff
201 function __destruct()
203 if ( defined('HTTP304') )
204 exit;
206 $this->finish_headers();
208 if ( SO2::$User instanceof User_Authenticated ) {
209 echo "\n\n",
210 '<form id="footer" action="." method="post">',"\n",
211 '<fieldset><legend>Session:</legend>',"\n",
212 ' <button type="submit" name="logout" onclick="return confirm(\'o rly?\');">Log Out</button>';
213 } else {
214 echo "\n\n",
215 '<form id="footer" action="',$_SERVER['PHP_SELF'],
216 ( $_SERVER['QUERY_STRING']
217 ? '?'.htmlspecialchars($_SERVER['QUERY_STRING'])
218 : ''
219 ),'" method="post">',"\n",
220 '<fieldset><legend>Login:</legend>',"\n",
221 ' <label>Username: <input type="text" name="u"/></label>',"\n",
222 ' <label>Password: <input type="password" name="p"/></label>',"\n",
223 ' <button type="submit" name="login">Log In</button>';
226 echo "\n</fieldset>\n";
228 printf('<p><a href="http://specialops.ath.cx">Powered by Special Ops 2.60</a> | © 2004-2007 Ant P | %.4f</p></form>',
229 microtime(1)-MT_NOW);
230 echo "\n\n</body>\n</html>";
232 if ( ! SO2::$User->cached || isset($_POST) ) {
233 SO2::$Cache->set('user'.SO2::$User->userid, SO2::$User, 0, 1800);
238 * Return a username for the given user ID, linking to that user's whois page.
240 * @param int $user UserID
242 public function namelink($user)
244 if ( ! ($name = SO2::$Cache->get('alias'.$user)) ) {
245 $name = SO2::$DB->q('SELECT alias FROM users WHERE userid = ?', $user, SO2_PDO::QVALUE);
246 SO2::$Cache->set('alias'.$user, $name);
248 return '<a href="user?'.$user.'">'.$name.'</a>';
252 * Turn a Unix timestamp into a human-readable date.
253 * Outputs UTC time in ISO-8601 format.
255 * @param int $timestamp Unix timestamp to display
257 public function fdate($t)
259 if ( 0 == $t )
260 return '<span class="undefined date">N/A</span>';
261 $class = ( T_NOW - 1800 < $t ) ? 'recent ' : '';
263 return '<span class="'.$class.'date">'.date(DATE_FMT, $t).'</span>';