diff options
author | Luke Shumaker <LukeShu@sbcglobal.net> | 2011-08-01 01:22:36 -0400 |
---|---|---|
committer | Luke Shumaker <LukeShu@sbcglobal.net> | 2011-08-01 01:22:36 -0400 |
commit | 09dfe32eb6b538225686fd6ed0220240010bc574 (patch) | |
tree | 29c1afc5e79519ba8689a3d5d170c312d3cf5033 /src/views |
initial commit.
Partway through a rewrite. I have some old files I didn't want to entirely delete.
Diffstat (limited to 'src/views')
25 files changed, 1447 insertions, 0 deletions
diff --git a/src/views/Template.class.php b/src/views/Template.class.php new file mode 100644 index 0000000..62b8ba6 --- /dev/null +++ b/src/views/Template.class.php @@ -0,0 +1,287 @@ +<?php + +class Template { + private $indent = 0; + private $ret = false; + private $base = '/'; + private $mm = null; + + public function status($status) { + header($_SERVER["SERVER_PROTOCOL"]." $status"); + header("Status: $status"); + } + + public function __construct($base_url, $mm=null) { + $this->base = $base_url; + $this->mm = $mm; + } + + public function setRet($ret) { + $this->ret = $ret; + } + + private function tabs() { + $str = ''; + for ($i=0;$i<$this->indent;$i++) { $str .= "\t"; } + return $str; + } + + private function attr($attr='') { + $tags=''; + if (is_array($attr)) { + foreach($attr as $key=>$value) { + $tags .= " $key=\"$value\""; + } + } + return $tags; + } + + public function tag($tag, $attr='', $content=false) { + $tags = $this->attr($attr); + $str = $this->tabs()."<$tag$tags"; + if ($content===false) { + $str.= " />"; + } else { + $str.= ">$content</$tag>"; + } + $str.= "\n"; + if ($this->ret) return $str; + echo $str; + } + + public function openTag($tag, $attr='') { + $tags = $this->attr($attr); + $str = $this->tabs()."<$tag$tags>\n"; + $this->indent++; + if ($this->ret) return $str; + echo $str; + } + + public function closeTag($tag) { + $this->indent--; + $str = $this->tabs()."</$tag>\n"; + if ($this->ret) return $str; + echo $str; + } + + public function text($text) { + $str = $this->tabs().$text."\n"; + if ($this->ret) return $str; + echo $str; + } + + public function paragraph($text, $attr='', $return=false) { + $tabs = $this->tabs(); + $tags = $this->attr($attr); + $str = $tabs."<p$tags>"; + $str.= wordwrap($text, 78-($this->indent*8), "\n$tabs "); + $str.= "</p>\n"; + if ($this->ret||$return) return $str; + echo $str; + } + + public function link($target, $text, $return=false) { + $ret = $this->ret; + $this->ret = $return; + $str = $this->tag('a', array('href'=>$target), $text); + $this->ret = $ret; + if ($this->ret||$return) return $str; + echo $str; + } + public function url($page) { + return $this->base.rawurlencode($page); + } + + public function row($cells) { + $str = $this->openTag('tr'); + foreach ($cells as $cell) + $str.= $this->tag('td', array(), $cell); + $str.= $this->closeTag('tr'); + if ($this->ret) return $str; + echo $str; + } + private function css($file, $media) { + $str.= $this->tag('link', array('rel'=>"stylesheet", + 'type'=>"text/css", + 'href'=>$this->url($file), + 'media'=>$media)); + if ($this->ret) return $str; + echo $str; + } + public function header($title) { + $mm = $this->mm; + if ($mm==null) { + $username = false; + } else { + $username = $mm->getUsername($mm->isLoggedIn()); + } + + $ret = $this->ret; + $this->ret = true; + + $logged_in = ($username!==false); + + $str = '<?xml version="1.0" encoding="utf-8"?>'."\n"; + $str.= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"'."\n"; + $str.= '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'."\n"; + + $xmlns = "http://www.w3.org/1999/xhtml"; + $str.= $this->openTag('html', array('xmlns'=>$xmlns, + 'lang'=>"en-us", + 'dir'=>"ltr")); + $this->indent = 0; // don't indent for the <html> tag + + $str.= $this->openTag('head'); + $str.= $this->tag('title', array(), htmlspecialchars($title)); + $str.= $this->css('style.css', 'all'); + $str.= $this->css('screen.css', 'screen'); + $str.= $this->css('logo-style.css', 'screen'); + $str.= $this->closeTag('head'); + + $body_class = 'logged'.($logged_in?'in':'out'); + $str.= $this->openTag('body', array('class'=>$body_class)); + + $str.= $this->openTag('div', array('class'=>'infobar')); + if ($logged_in) { + $user = htmlentities($username); + + $str.= $this->link($this->url(''), "Home"); + $str.= $this->link($this->url("users/$user"),"@$user"); + $str.= $this->logout_button('Logout'); + } else { + $url=$_SERVER['REQUEST_URI']; + $str.= $this->openTag('form', + array('action'=>$this->url('auth'), + 'method'=>'post')); + $str.= $this->tag('input', array('type'=>'hidden', + 'name'=>'action', + 'value'=>'login')); + $str.= $this->tag('input', array('type'=>'hidden', + 'name'=>'url', + 'value'=>$url)); + $str.= $this->tag('label', + array('for'=>'username'),'Username:'); + $str.= $this->tag('input', array('type'=>'text', + 'name'=>'username', + 'id'=>'username')); + $str.= $this->tag('label', + array('for'=>'password'),'Password:'); + $str.= $this->tag('input', array('type'=>'password', + 'name'=>'password', + 'id'=>'password')); + $str.= $this->tag('input', array('type'=>'submit', + 'value'=>'Login')); + $str.= $this->closeTag('form'); + } + $str.= $this->closeTag('div'); + + $str.= $this->openTag('div',array('class'=>'main')); + $str.= $this->openTag('div',array('class'=>'main_sub')); + + $this->ret = $ret; + if ($this->ret) return $str; + echo $str; + } + + public function footer() { + $str = $this->closeTag('div'); + $str.= $this->closeTag('div'); + $str.= $this->closeTag('body'); + $str.= $this->closeTag('html'); + + if ($this->ret) return $str; + echo $str; + } + + public function openFieldset($name, $lock=false) { + $class = ($lock?' class="readonly"':''); + $str = $this->text("<fieldset$class><legend>$name</legend><ul>"); + $this->indent++; + if ($this->ret) return $str; + echo $str; + } + + public function closeFieldset() { + $this->indent--; + $str = $this->text("</ul></fieldset>"); + if ($this->ret) return $str; + echo $str; + } + + public function input($id, $label, $hint, $html) { + $str = $this->openTag('li'); + $str.= $this->tag('label', array('for'=>$id), $label); + $str.= $this->text($html); + if (strlen($hint)>0) { + $str.=$this->paragraph($hint, + Array('class'=>'form_data')); + } + $str.= $this->closeTag('li'); + if ($this->ret) return $str; + echo $str; + } + + private function inputStr($type, $id, $default, $lock) { + $value = htmlentities($default); + $tag = ($lock?"readonly='readonly' ":''); + return "<input type='$type' name='$id' id='$id' value=\"$value\" $tag/>"; + } + + public function inputText($id, $label, $hint='', $default='', $lock=FALSE) { + return $this->input($id, $label, $hint, + $this->inputStr('text', $id, $default, $lock)); + } + + public function inputPassword($id, $label, $hint='', $default='', $lock=FALSE) { + return $this->input($id, $label, $hint, + $this->inputStr('password', $id, $default, $lock)); + } + + public function inputNewPassword($id, $label, $default='', $lock=FALSE) { + return $this->input($id, $label, + "Type the same password twice, to make sure you don't mistype.", + $this->inputStr('password', $id, $default, $lock). + "\n".$this->tabs()."\t". + $this->inputStr('password', $id.'_verify', $default,$lock)); + } + public function inputBool($name, $value, $label, $default=FALSE, $lock=FALSE) { + $attrib = array('type'=>'checkbox', + 'id'=>$name.'_'.$value, + 'name'=>$name.'[]', + 'value'=>$value); + if ($default) $attrib['checked']='checked'; + if ($lock ) $attrib['readonly']='readonly'; + + $str = $this->openTag('li'); + $str.= $this->tag('input', $attrib); + $str.= $this->tag('label', array('for'=>$id), $label); + $str.= $this->closeTag('li'); + + if ($this->ret) return $str; + echo $str; + + } + + public function inputP($text, $error=false) { + $str = $this->openTag('li'); + $str.=$this->paragraph($text, + array('class'=>($error?' error':''))); + $str.= $this->closeTag('li'); + if ($this->ret) return $str; + echo $str; + } + + public function logout_button($text) { + $str = $this->openTag('form',array('action'=>$this->url('auth'), + 'method'=>"post", + 'style'=>'display:inline')); + $str.= $this->tag('input', array('type'=>'hidden', + 'name'=>'action', + 'value'=>'logout')); + $str.= $this->tag('input', array('type'=>'submit', + 'value'=>$text)); + $str.= $this->closeTag('form'); + if ($this->ret) return $str; + echo $str; + } +} diff --git a/src/views/pages/404.php b/src/views/pages/404.php new file mode 100644 index 0000000..f15d39e --- /dev/null +++ b/src/views/pages/404.php @@ -0,0 +1,11 @@ +<?php global $mm; +/** + * This is the global 404 page for MessageManager, top-level views + * should generally provide a more specific one for their sub-directories + */ +$mm->status('404 Not Found'); +$t = $mm->template(); + +$mm->header('Page Not Found'); +$t->paragraph("Awe man, the page you requested wasn't found."); +$mm->footer(); diff --git a/src/views/pages/auth.php b/src/views/pages/auth.php new file mode 100644 index 0000000..2132d67 --- /dev/null +++ b/src/views/pages/auth.php @@ -0,0 +1,65 @@ +<?php global $mm; +/** + * This is the view for the main login page. + */ + +// TODO: We should probably check to make sure PAGE is just 'auth' or +// 'auth/', and not something like 'auth/foobar', for which we should +// throw a 404. + +@$action = $_POST['action']; +switch ($action) { +case 'login': login(); break; +case 'logout': logout(); break; +case '': maybe_login(); break; +default: badrequest(); break; +} + +function maybe_login() { + global $mm; + $uid = $mm->isLoggedIn(); + if ($uid===false) { + login(); + } else { + $mm->header('Authentication'); + $t = $mm->template(); + + $username = $mm->getUsername($uid); + + $t->openTag('div',array('class'=>'login')); + $t->text("Logged in as ".htmlentities($username).'.'); + $t->logout_button('Logout'); + $t->closeTag('div'); + + $mm->footer(); + } +} + +function login() { + include(VIEWPATH.'/pages/auth/login.php'); +} + +function logout() { + global $mm; + $t = $mm->template(); + + $mm->logout(); + + $mm->header('Authentication'); + $t->paragraph('Logged out'); + $mm->footer(); +} + +function badrequest() { + global $mm; + $mm->status('400 Bad Request'); + $t = $mm->template(); + + $mm->header('Authentication'); + $t->paragraph('The recieved POST request was malformed/invalid. '. + 'If you got here from a link, this is a bug; '. + 'Let the admin know.'. + 'If you got here from outside, then the API is being '. + 'missused.'); + $mm->footer(); +} diff --git a/src/views/pages/auth/badrequest.html.php b/src/views/pages/auth/badrequest.html.php new file mode 100644 index 0000000..c1fe726 --- /dev/null +++ b/src/views/pages/auth/badrequest.html.php @@ -0,0 +1,11 @@ +<?php global $VARS; +$t = $VARS['template']; + +$t->status('400 Bad Request'); +$t->header('Authentication'); +$t->paragraph('The recieved POST request was malformed/invalid. '. + 'If you got here from a link, this is a bug; '. + 'Let the admin know.'. + 'If you got here from outside, then the API is being '. + 'used incorrectly.'); +$t->footer(); diff --git a/src/views/pages/auth/index.html.php b/src/views/pages/auth/index.html.php new file mode 100644 index 0000000..ac80140 --- /dev/null +++ b/src/views/pages/auth/index.html.php @@ -0,0 +1,12 @@ +<?php global $VARS; +$t = $VARS['template']; +$username = $VARS['username']; + +$t->header('Authentication'); + +$t->openTag('div',array('class'=>'login')); +$t->text("Logged in as ".htmlentities($username).'.'); +$t->logout_button('Logout'); +$t->closeTag('div'); + +$t->footer();
\ No newline at end of file diff --git a/src/views/pages/auth/login.html.php b/src/views/pages/auth/login.html.php new file mode 100644 index 0000000..a246a9e --- /dev/null +++ b/src/views/pages/auth/login.html.php @@ -0,0 +1,49 @@ +<?php global $VARS; +$t = $VARS['template']; +$username = $VARS['username']; +$password = $VARS['password']; + +$t->header('Authentication'); + +$t->openTag('form',array('action'=>$t->url('auth'), 'method'=>"post")); +$t->openFieldset('Login'); +switch ($VARS['login_code']) { +case -1: break; +case 0: + $t->inputP('Successfully logged in as '. + htmlentities($username).'.'); + if (isset($VARS['url'])) { + $url = htmlentities($VARS['url']); + $t->inputP($t->link($url, + 'Return to the page you were on.', + true)); + } + $t->closeFieldset(); + $t->closeTag('form'); + return; + break; +case 1: + $t->inputP("Password does not match username.", + array('class'=>'error')); + break; +case 2: + $t->inputP("Username <q>$username</q> does not exist."); + $username = ''; + break; +} +$t->inputText( 'username', 'Username:', '', $username); +$t->inputPassword('password', 'Password:', '', $password); +$t->openTag('li'); +$t->tag('input', array('type'=>'submit', 'value'=>'Login')); +$t->closeTag('li'); +$t->closeFieldset(); +$t->tag('input', array('type'=>'hidden', + 'name'=>'action', + 'value'=>'login')); +if (isset($VARS['url'])) { + $url = htmlentities($VARS['url']); + $t->tag('input', array('type'=>'hidden', + 'name'=>'url', + 'value'=>$url)); +} +$t->closeTag('form'); diff --git a/src/views/pages/auth/login.php b/src/views/pages/auth/login.php new file mode 100644 index 0000000..8a175eb --- /dev/null +++ b/src/views/pages/auth/login.php @@ -0,0 +1,63 @@ +<?php global $mm; +/** + * This isn't a separate URL, but this is what the 'auth' view loads + * when the user is attempting to log in. + * Logically, I don't think it should be in a separate file, but I think the + * general flow of things is easier to follow and edit and maintain. + */ +$username = ''; +$password = ''; + +$t = $mm->template(); + +$login = -1; +if ( isset($_POST['username']) && isset($_POST['password'])) { + $username = $_POST['username']; + $password = $_POST['password']; + $login = $mm->login($username, $password); +} + +$mm->header('Authentication'); + +$t->openTag('form',array('action'=>$mm->baseUrl().'auth','method'=>"post")); +$t->openFieldset('Login'); +switch ($login) { +case -1: break; +case 0: + $t->inputP('Successfully logged in as '. + htmlentities($username).'.'); + if (isset($_POST['url'])) { + $url = htmlentities($_POST['url']); + $t->inputP($t->link($url, + 'Return to the page you were on.', + true)); + } + $t->closeFieldset(); + $t->closeTag('form'); + return; + break; +case 1: + $t->inputP("Password does not match username.", + array('class'=>'error')); + break; +case 2: + $t->inputP("Username <q>$username</q> does not exist."); + $username = ''; + break; +} +$t->inputText( 'username', 'Username:', '', $username); +$t->inputPassword('password', 'Password:', '', $password); +$t->openTag('li'); +$t->tag('input', array('type'=>'submit', 'value'=>'Login')); +$t->closeTag('li'); +$t->closeFieldset(); +$t->tag('input', array('type'=>'hidden', + 'name'=>'action', + 'value'=>'login')); +if (isset($_POST['url'])) { + $url = htmlentities($_POST['url']); + $t->tag('input', array('type'=>'hidden', + 'name'=>'url', + 'value'=>$url)); +} +$t->closeTag('form'); diff --git a/src/views/pages/auth/logout.html.php b/src/views/pages/auth/logout.html.php new file mode 100644 index 0000000..2d00998 --- /dev/null +++ b/src/views/pages/auth/logout.html.php @@ -0,0 +1,6 @@ +<?php global $VARS; +$t = $VARS['template']; + +$t->header('Authentication'); +$t->paragraph('Logged out'); +$t->footer(); diff --git a/src/views/pages/groups.php b/src/views/pages/groups.php new file mode 100644 index 0000000..03f625f --- /dev/null +++ b/src/views/pages/groups.php @@ -0,0 +1,41 @@ +<?php global $mm; + +global $illegal_names; +$illegal_names = array('', 'new'); +global $groupname, $uid;// We will use these to pass the groupname to sub-views. + +$page_parts = explode('/', PAGE); +if (isset($page_parts[1])) { + $username = $page_parts[1]; + if ($username == '') { + unset($username); + } +} + +if (isset($username)) { // URI: "users/*" + // We'll be handing this off to another view. + if ($username === 'new') { + include(VIEWPATH.'/pages/users/new.php'); + } + + $uid = $mm->getUID($username); + if ($mm->getStatus($uid)===3) $uid = false; // ignore groups. + + if ($uid===false) { + include(VIEWPATH.'/pages/users/404.php'); + } else { + include(VIEWPATH.'/pages/users/individual.php'); + } +} else { // URI: "users" + $method = $_SERVER['REQUEST_METHOD']; + switch ($method) { + case 'PUT': + case 'POST': + // We're POSTing a new user + include(VIEWPATH.'/pages/users/create.php'); + case 'HEAD': // fall-through to GET + case 'GET': + // We're GETing an existing user + include(VIEWPATH.'/pages/users/index.php'); + } +} diff --git a/src/views/pages/http404.html.php b/src/views/pages/http404.html.php new file mode 100644 index 0000000..ffdeb07 --- /dev/null +++ b/src/views/pages/http404.html.php @@ -0,0 +1,15 @@ +<?php global $VARS; +$t = $VARS['template']; + +$routed = implode('/', $VARS['routed']); +$remainder = implode('/', $VARS['remainder']); +$full = $routed.'/'.$remainder; + +$t->status('404 Not Found'); +$t->header('Page Not Found'); +$t->paragraph("Awe man, the page you requested wasn't found."); +$t->paragraph('This folder was found: '. + '<tt>'.$t->link($t->url($routed), $routed.'/', true).'</tt>'); +$t->paragraph("But this file in it wasn't: ". + '<tt>'.$full.'</tt>'); +$t->footer(); diff --git a/src/views/pages/index.php b/src/views/pages/index.php new file mode 100644 index 0000000..ad68559 --- /dev/null +++ b/src/views/pages/index.php @@ -0,0 +1,7 @@ +<?php global $mm; +$t = $mm->template(); + +$mm->header("Main Page"); +$t->paragraph("This is the main index page."); +$t->link($mm->baseUrl().'users', 'List of all users'); +$mm->footer(); diff --git a/src/views/pages/messages.php b/src/views/pages/messages.php new file mode 100644 index 0000000..da57596 --- /dev/null +++ b/src/views/pages/messages.php @@ -0,0 +1,222 @@ +<?php
+// the first ~20 lines are so that this can be called from the command line,
+// with mail piped in. This allows us to hook it into a local mail handler.
+
+global $BASE, $m;
+
+$cmdline = isset($argv[0]); // called from the command line
+@$method = $_SERVER['REQUEST_METHOD']; // What HTTP method was used
+
+if (!isset($BASE)) {
+ $pages = dirname(__FILE__);
+ $src = dirname($pages);
+ $BASE = dirname($src);
+ set_include_path(get_include_path()
+ .PATH_SEPARATOR. "$BASE/src/lib"
+ .PATH_SEPARATOR. "$BASE/src/ext"
+ );
+}
+
+if (!$cmdline) {
+ require_once('MessageManager.class.php');
+ $m = new MessageManager($BASE.'/conf.php');
+}
+
+$uid = $m->isLoggedIn();
+$auth = ($uid!==false) && ($m->getStatus($uid)>0);
+if (!$cmdline && !$auth) {
+ $m->status('401 Unauthorized');
+ $m->header('Unauthorized');
+ $t = $m->template();
+ $t->tag('h1',array(),"401: Unauthorized");
+ $t->paragraph('You need to be logged in to view messages. :(');
+ $m->footer();
+ exit();
+}
+
+@$method = $_SERVER['REQUEST_METHOD'];
+if ( ($method=='PUT') || ($method=='POST') || $cmdline ) {
+ // We're going to be uploading a new message.
+
+ // so uniqid isn't 'secure', it doesn't need to be, it's to prevent
+ // random collisions.
+ $tmpfile = "$BASE/tmp/".uniqid(getmypid().'.');
+ $infile = ($cmdline?'php://stdin':'php://input');
+ $out = fopen($tmpfile, "w");
+ $in = fopen($infile, "r");
+ while ($data = fread($in, 1024))
+ fwrite($out, $data);
+ fclose($out);
+ fclose($in);
+ //apache_request_headers()
+ require_once('MimeMailParser.class.php');
+ $parser = new MimeMailParser();
+ $parser->setPath($tmpfile);
+ $id = preg_replace('/<(.*)>/', '$1',
+ $parser->getHeader('message-id'));
+ $id = str_replace('/', '', $id); // for security reasons
+ $msg_file = "$BASE/msg/$id";
+ rename($tmpfile, $msg_file);
+
+ if (!$cmdline) {
+ $m->status('201 Created');
+ header("Location: ".$m->baseUrl().'messages/'.$id);
+ }
+ exit();
+}
+
+global $PAGE, $BASE;
+$page_parts = explode('/',$PAGE);
+@$msg = $page_parts[1];
+if ($msg == '') {
+ $m->header('Message Index');
+ $t = $m->template();
+ $t->tag('h1',array(),"Message Index");
+
+ require_once('MimeMailParser.class.php');
+ $parser = new MimeMailParser();
+ $messages = array();
+ $dh = opendir("$BASE/msg");
+ while (($file = readdir($dh)) !== false) {
+ $path = "$BASE/msg/$file";
+ if (is_file($path)) {
+ $parser->setPath($path);
+
+ $date_string = $parser->getHeader('date');
+ $date = strtotime($date_string);
+ if (!isset($messages[$date])) $messages[$date] = array();
+ $messages[$date][] =
+ array('id'=>$file,
+ 'subject'=>$parser->getHeader('subject'));
+ }
+ }
+ closedir($dh);
+
+ $t->openTag('table');
+ foreach ($messages as $date => $message_array) {
+ foreach ($message_array as $message) {
+ $url = $m->baseUrl().'messages/'.$message['id'];
+ $subject = htmlentities($message['subject']);
+ $date_str = date('Y-m-d H:i:s',$date);
+ $t->row(array(
+ $t->link($url, $subject, true),
+ $t->link($url, $date_str, true)
+ ));
+ }
+ }
+ $t->closeTag('table');
+
+ $m->footer();
+ exit();
+}
+
+@$msg_file = "$BASE/msg/$msg";
+if (!is_file($msg_file)) {
+ $m->status('404 Not Found');
+ $m->header('Message not found | MessageManager');
+ $t = $m->template();
+ $t->tag('h1',array(),'404: Not Found');
+ $t->paragraph('The message <q>'.htmlentities($msg).'</q> was not '.
+ 'found in our database.');
+ $m->footer();
+ exit();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// In the interest of code reusability, most of the following code is //
+// independent of message manager. This section is stubs to bind into //
+// MessageManager. //
+$msg_file = $msg_file;
+$msg_id = $msg;
+@$part = $page_parts[2];
+@$subpart = $page_parts[3];
+function url($id, $part='',$subpart='') {
+ global $m;
+ return $m->baseUrl().'messages/'.$id.'/'.($part?"$part/$subpart":'');
+}
+// With the exception of one line (tagged with XXX), the following code is //
+// not specific to MessageManager. //
+// At some point I may contemplate making this use the template engine, but //
+// I like the idea of it being self-standing. //
+////////////////////////////////////////////////////////////////////////////////
+
+require_once('MimeMailParser.class.php');
+$parser = new MimeMailParser();
+$parser->setPath($msg_file);
+
+function messageLink($id) {
+ if (is_array($id)) { $id = $id[1]; }
+ return '<<a href="'.url($id).'">'.$id.'</a>>';
+}
+function parseMessageIDs($string) {
+ $base = $_SERVER['REQUEST_URL'];
+ $safe = htmlentities($string);
+ $html = preg_replace_callback(
+ '/<([^>]*)>/',
+ 'messageLink',
+ $safe);
+ return $html;
+}
+
+function row($c1, $c2) {
+ echo '<tr><td>'.$c1.'</td><td>'.$c2."</td></tr>\n";
+}
+switch ($part) {
+case '': // Show a frame for all the other parts
+ $m->header('View Message | MessageManager');
+ $t = $m->template();
+ echo "<table>\n";
+ row('To:' , htmlentities($parser->getHeader('to' )));
+ row('From:' , htmlentities($parser->getHeader('from' )));
+ row('Subject:' , htmlentities($parser->getHeader('subject' )));
+ row('In-Reply-to:', parseMessageIDs($parser->getHeader('in-reply-to')));
+ row('References:' , parseMessageIDs($parser->getHeader('references' )));
+ echo "</table>\n";
+ echo "<div class='message-body'>\n";
+ if ($parser->getMessageBodyPart('html')!==false) {
+ echo "<h2>HTML</h2>\n";
+ echo '<iframe src="'.url($msg_id,'body','html').'" ></iframe>'."\n";
+ }
+ if ($parser->getMessageBodyPart('text')!==false) {
+ echo "<h2>Plain Text</h2>\n";
+ echo '<iframe src="'.url($msg_id,'body','text').'" ></iframe>'."\n";
+ }
+ echo "</div>\n";
+ echo "<h2>Attachments</h2>\n";
+ echo "<table>\n";
+ $attachments = $parser->getAttachments();
+ foreach ($attachments as $id => $attachment) {
+ echo "<tr>";
+ echo '<td>'.htmlentities($attachment->getContentType())."</td>";
+ echo '<td><a href="'.url($msg_id,'attachment',$id).'">';
+ echo htmlentities($attachment->getFilename());
+ echo "</a></td>";
+ echo "</tr>\n";
+ }
+ echo "</table>\n";
+ $m->footer();// XXX: this is specific to MessageManager
+ break;
+case 'body':
+ $type = $subpart;
+ switch ($type) {
+ case 'text': header('Content-type: text/plain'); break;
+ case 'html': header('Content-type: text/html' ); break;
+ default:
+ }
+ echo $parser->getMessageBody($type);
+ break;
+case 'attachment':
+ $attachment_id = $subpart;
+ $attachments = $parser->getAttachments();
+ $attachment = $attachments[$attachment_id];
+
+ $type = $attachment->getContentType();
+ $filename = $attachment->getFilename();
+
+ header('Content-Type: '.$type);
+ header('Content-Disposition: attachment; filename='.$filename );
+ while($bytes = $attachment->read()) {
+ echo $bytes;
+ }
+ break;
+}
diff --git a/src/views/pages/plugins.php b/src/views/pages/plugins.php new file mode 100644 index 0000000..a526871 --- /dev/null +++ b/src/views/pages/plugins.php @@ -0,0 +1,61 @@ +<?php + +global $m; +require_once('MessageManager.class.php'); +$m = new MessageManager($BASE.'/conf.php'); + +$uid = $m->isLoggedIn(); +$auth = ($uid!==false) && ($m->getStatus($uid)>=2); + +if (!$auth) { + $m->status('401 Unauthorized'); + $m->header('Unauthorized'); + $t = $m->template(); + $t->tag('h1',array(),"401: Unauthorized"); + $t->paragraph('You need to be logged in as an admin (at least user '. + 'level 2) to edit global plugin settings. :('); + $m->footer(); + exit(); +} + +$m->header('Administrator Plugin Management'); + +$t = $m->template(); + +$t->openTag('form',array('method'=>'post','action'=>$m->baseUrl().plugins)); + +global $BASE; +set_include_path(get_include_path().PATH_SEPARATOR."$BASE/src/plugins"); + +$plugin_list = $m->getSysConf('plugins'); +$plugins = explode(',', $plugin_list); +foreach ($plugins as $plugin) { + $t->openFieldSet($plugin); + + require_once("$plugin.class.php"); + $description = call_user_func("$plugin::description"); + $params = call_user_func("$plugin::configList"); + + $t->inputP($description); + + foreach ($params as $param => $type) { + $name = $plugin.'_'.$param; + if (isset($_POST[$name])) { + $m->setPluginConf($plugin, $param, $_POST[$name]); + } + $value = $m->getPluginConf($plugin, $param); + $hint = "Type: $type"; + switch ($type) { + case 'text': + case 'int': + $t->inputText( $name, $param, $hint, $value); break; + case 'password': + $t->inputPassword($name, $param, $hint, $value); break; + } + } + $t->closeFieldSet(); +} + +$t->tag('input', array('type'=>'submit', 'value'=>'Save')); +$t->closeTag('form'); +$m->footer();
\ No newline at end of file diff --git a/src/views/pages/users.php b/src/views/pages/users.php new file mode 100644 index 0000000..9c12ee7 --- /dev/null +++ b/src/views/pages/users.php @@ -0,0 +1,44 @@ +<?php global $mm; + +global $illegal_names; +$illegal_names = array('', 'new'); +global $username, $uid;// We will use these to pass the username to sub-views. + +$page_parts = explode('/', PAGE); +if (isset($page_parts[1])) { + $username = $page_parts[1]; + if ($username == '') { + unset($username); + } +} + +if (isset($username)) { // URI: "users/*" + // We'll be handing this off to another view. + if ($username === 'new') { + include(VIEWPATH.'/pages/users/new.php'); + exit(); + } + + $uid = $mm->getUID($username); + if ($mm->getStatus($uid)===3) $uid = false; // ignore groups. + + if ($uid===false) { + include(VIEWPATH.'/pages/users/404.php'); + } else { + include(VIEWPATH.'/pages/users/individual.php'); + } +} else { // URI: "users" + $method = $_SERVER['REQUEST_METHOD']; + switch ($method) { + case 'PUT': + case 'POST': + // We're POSTing a new user + include(VIEWPATH.'/pages/users/create.php'); + break; + case 'HEAD': // fall-through to GET + case 'GET': + // We're GETing an existing user + include(VIEWPATH.'/pages/users/index.php'); + break; + } +} diff --git a/src/views/pages/users/401.html.php b/src/views/pages/users/401.html.php new file mode 100644 index 0000000..0a5a1ce --- /dev/null +++ b/src/views/pages/users/401.html.php @@ -0,0 +1,15 @@ +<?php global $VARS; +$t = $VARS['template']; + +$t->status('401 Unauthorized'); +$t->header('Unauthorized'); +$t->tag('h1', array(), "401: Unauthorized"); +if ($VARS['uid']===false) { + // Not logged in + $t->paragraph('You need to be logged in to view user-data.'); +} else { + // Logged in, so the account must not activated + $t->paragraph('Your account needs to be activated by an administrator '. + 'to view user-data.'); +} +$t->footer(); diff --git a/src/views/pages/users/404.html.php b/src/views/pages/users/404.html.php new file mode 100644 index 0000000..00f9dca --- /dev/null +++ b/src/views/pages/users/404.html.php @@ -0,0 +1,10 @@ +<?php global $VARS; +$t = $VARS['template']; +$username = $VARS['username']; + +$t->status('404 Not Found'); +$t->header('User Not Found'); +$t->tag('h1',array(),"404: Not Found"); +$t->paragraph('No user with the name <q>'. + htmlentities($username).'</q> exists.'); +$t->footer(); diff --git a/src/views/pages/users/500.html.php b/src/views/pages/users/500.html.php new file mode 100644 index 0000000..27038a4 --- /dev/null +++ b/src/views/pages/users/500.html.php @@ -0,0 +1,13 @@ +<?php global $VARS, $mm; +$t = $VARS['template']; + +$t->status('500 Internal Server Error'); +$t->header('Unknown error'); +$t->paragraph("An unknown error was encountered when creating ". + "the user. The username appears to be free, and ". + "the passwords match, so I'm assuming that the ". + "error is on our end. Sorry."); +$t->paragraph("Here's a dump of the SQL error stack, it may ". + "help us find the issue:"); +$t->tag('pre', array(), htmlentities($mm->mysql_error())); +$t->footer(); diff --git a/src/views/pages/users/created.html.php b/src/views/pages/users/created.html.php new file mode 100644 index 0000000..72aa26e --- /dev/null +++ b/src/views/pages/users/created.html.php @@ -0,0 +1,16 @@ +<?php global $VARS; +$t = $VARS['template']; +$username = $VARS['username']; + +$t->status('201 Created'); +header('Location: '.$t->url("users/$username")); +$t->header('User created'); +$t->paragraph("You can go ahead and fill out more of your ". + "user information, (click the @username link at ". + "the top) but will need to wait for an ". + "administrator to approve your account before ". + "you can really use the site. Actually, ". + "filling your info out might help approval, so ". + "that the administrator can more easily see who ". + "you are."); +$t->footer(); diff --git a/src/views/pages/users/include.php b/src/views/pages/users/include.php new file mode 100644 index 0000000..6e8c90b --- /dev/null +++ b/src/views/pages/users/include.php @@ -0,0 +1,60 @@ +<?php global $mm; + +require_once('User.class.php'); + +/** + * This will take care of possibly updating and displaying a value in the + * 'users' table. + */ +function inputText($user, $name, $label, $hint='') { + if ($user->canEdit()) { + if (isset($_POST["user_$name"])) { + $user->setConf($name, $_POST["user_$name"]); + } + } + + $current_setting = $user->getConf($name); + + global $mm; + $t = $mm->template(); + $t->inputText("user_$name", $label, $hint, $current_setting, + !$user->canEdit()); +} + +function inputArray($user, $name, $arr) { + global $mm; + $t = $mm->template(); + + if (isset($_POST[$name]) && is_array($_POST[$name])) { + $user->setConfArray($name, $_POST[$name]); + } + $defaults = $user->getConfArray($name); + + foreach ($arr as $value => $label) { + $t->inputBool($name, $value, $label, + in_array($value, $defaults), !$user->canEdit()); + } +} + +function inputNewPassword($user, $name, $label) { + @$password1 = $_POST[$name ]; + @$password2 = $_POST[$name.'_verify']; + + // Check the verify box, not main box, so that we don't get tripped by + // browsers annoyingly autocompleting the password. + $is_set = ($password2 != ''); + + global $mm; + $t = $mm->template(); + + if ($is_set) { + $matches = ( $password1 == $password2 ); + if ($matches) { + $user->setPassword($password1); + $t->inputP('Password successfully updated.'); + } else { + $t->inputP("Passwords don't match.", true); + } + } + $t->inputNewPassword($name, $label); +} diff --git a/src/views/pages/users/index.csv.php b/src/views/pages/users/index.csv.php new file mode 100644 index 0000000..527e508 --- /dev/null +++ b/src/views/pages/users/index.csv.php @@ -0,0 +1,27 @@ +<?php global $VARS; +$attribs = $VARS['template']; +$users = $VARS['users']; + +function escape($value) { + if (is_bool($value)) { + return ($value?'true':'false'); + } else { + $chars = "'" . '"' . '\\' . ','; + return addcslashes($str, $chars); + } +} + +$arr = array(); +foreach ($attribs as $attrib) { + $arr[] = escape($attrib['name']); +} +echo implode(',', $arr)."\n"; + +foreach ($users as $user) { + $arr = array(); + foreach ($attribs as $attrib) { + $props = $user[$attrib['key']]; + $arr[] = escape($props['value']); + } + echo implode(',', $arr)."\n"; +} diff --git a/src/views/pages/users/index.html.php b/src/views/pages/users/index.html.php new file mode 100644 index 0000000..5f1ab02 --- /dev/null +++ b/src/views/pages/users/index.html.php @@ -0,0 +1,65 @@ +<?php global $VARS; +$t = $VARS['template']; +$attribs = $VARS['template']; +$users = $VARS['users']; + +$t->header('Users'); + +$t->openTag('form', array('action'=>$t->url('users/index'), + 'method'=>'post')); + +$t->openTag('table'); + +$t->openTag('tr'); +foreach ($attribs as $attrib) { + $t->tag('th', array(), $attrib['name']); +} +$t->tag('th'); +$t->closeTag('tr'); + +foreach ($users as $user) { + $t->openTag('tr'); + + foreach ($attribs as $attrib) { + $props = $user[$attrib['key']]; + + $value = $props['value']; + $editable = $props['editable']; + $post_key = $props['post_key']; + $bool = is_bool($value); + + $arr = array('name'=>$post_key); + if (!$editable) { + $arr['readonly'] = 'readonly'; + if ($bool) $arr['disabled'] = $disabled; + } + if ($bool) { + if ($value==true) { + $arr['checked'] = 'checked'; + } + $arr['value'] = 'true'; + $arr['type'] = 'checkbox'; + } else { + $arr['value'] = $value; + $arr['type'] = 'text'; + } + + $t->openTag('td'); + $t->tag('input', $arr); + $t->closeTag('td'); + } + + $t->openTag('td'); + $t->link($t->url('users/'.$user['auth_name']['value']), 'More'); + $t->closeTag('td'); + + $t->closeTag('tr'); +} + +$t->closeTag('table'); + +$t->tag('input', array('type'=>'submit', + 'value'=>'Save/Update')); +$t->closeTag('form'); + +$t->footer(); diff --git a/src/views/pages/users/index.php b/src/views/pages/users/index.php new file mode 100644 index 0000000..d801faf --- /dev/null +++ b/src/views/pages/users/index.php @@ -0,0 +1,116 @@ +<?php global $mm; + +$logged_in_user = $mm->getAuthObj($mm->isLoggedIn()); +if (!$logged_in_user->isUser()) { + include(VIEWPATH.'/pages/users/401.php'); + exit(); +} + +function attrib($key, $name, $check=false) { + return array('key'=>$key, 'name'=>$name, 'checkbox'=>$check); +} + +function getSetConf($user, $key) { + global $mm; + $logged_in_user = $mm->getAuthObj($mm->isLoggedIn()); + $uid = $user->getUID(); + $post_key = $key."[$uid]"; + @$value = $_POST[$post_key]; + $editable = $user->canEdit(); + $edit = isset($_POST[$post_key]); + + switch ($key) { + case 'auth_name': + if ($editable && $edit) $user->setName($value); + $value = $user->getName(); + break; + case 'auth_user': + $editable = $editable && $logged_in_user->isAdmin(); + if ($editable && $edit) $user->setUser($value=='true'); + $value = $user->isUser(); + break; + case 'auth_admin': + $editable = $editable && $logged_in_user->isAdmin(); + if ($editable && $edit) $user->setAdmin($value=='true'); + $value = $user->isAdmin(); + break; + default: + if ($editable && $edit) $user->setConf($key, $value); + $value = $user->getConf($key); + break; + } + + return array( + 'value'=>$value, + 'post_key'=>$post_key, + 'editable'=>$editable); +} + +$attribs = array(attrib('auth_user', 'Active', true), + attrib('lastname','Last'), + attrib('firstname','First'), + attrib('hsclass','Class of'), + attrib('phone','Phone number'), + attrib('email','Email'), + attrib('auth_name', 'Username'), + ); + +//////////////////////////////////////////////////////////////////////////////// + +$t = $mm->template(); +$mm->header('Users'); + +$t->openTag('form', array('action'=>$mm->baseUrl().'users', + 'method'=>'post')); + +$t->openTag('table'); + +$t->openTag('tr'); +foreach ($attribs as $attrib) { + $t->tag('th', array(), $attrib['name']); +} +$t->tag('th'); +$t->closeTag('tr'); + +$uids = $mm->listUsers(); +foreach ($uids as $uid) { + $user = $mm->getAuthObj($uid); + $t->openTag('tr'); + + foreach ($attribs as $attrib) { + $props = getSetConf($user, $attrib['key']); + + $arr = array('name'=>$props['post_key']); + if (!$props['editable']) { + $arr['readonly'] = 'readonly'; + if ($attrib['checkbox']) $arr['disabled'] = $disabled; + } + if ($attrib['checkbox']) { + if ($props['value']) + $arr['checked'] = 'checked'; + $arr['value'] = 'true'; + $arr['type'] = 'checkbox'; + } else { + $arr['value'] = $props['value']; + $arr['type'] = 'text'; + } + + $t->openTag('td'); + $t->tag('input', $arr); + $t->closeTag('td'); + } + + $t->openTag('td'); + $t->link($mm->baseUrl().'users/'.$user->getName(), 'More'); + $t->closeTag('td'); + + $t->closeTag('tr'); +} + +$t->closeTag('table'); + +$t->tag('input', array('type'=>'submit', + 'value'=>'Save/Update')); +$t->closeTag('form'); + +$mm->footer();
\ No newline at end of file diff --git a/src/views/pages/users/individual.html.php b/src/views/pages/users/individual.html.php new file mode 100644 index 0000000..4d6e4fc --- /dev/null +++ b/src/views/pages/users/individual.html.php @@ -0,0 +1,105 @@ +<?php global $VARS, $CONTACT_METHODS; +$t = $VARS['template']; +$user = $VARS['user']; + +function inputText($user, $key, $label, $hint='') { + global $VARS; $t = $VARS['template']; + $current_setting = $user->getConf($key); + $t->inputText("user_$key", $label, $hint, $current_setting, + !$user->canEdit()); +} + +function inputArray($user, $key, $arr) { + global $VARS; $t = $VARS['template']; + $defaults = $user->getConfArray($key); + + foreach ($arr as $value => $label) { + $t->inputBool($name, $value, $label, + in_array($value, $defaults), !$user->canEdit()); + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +$t->header("Users: $username"); + +$t->tag('h1', array(), ($user->canEdit()?'Edit':'View')." User (UID: $uid)"); + +if ($user->canEdit()) { + $t->openTag('form', array('method'=>'post', + 'action'=>$t->url("users/$username"))); +} else { + $t->openTag('form'); +} + +$t->openFieldset("Login / Authentication"); +// Username //////////////////////////////////////////////////////////////////// +if (isset($VARS['changed name']) && !$VARS['changed_name']) { + $t->inputP("Error setting username to ". + "<q>$new_name</q>. This is probably because". + " a user with that name already exists.", + true); +} +$t->inputText('auth_name','Username', + "This is the name you use to log in, but it is also a ". + "short name that is used in various places, think of it ". + "as a sort of <q>Twitter name</q>.", + $user->getName(), !$user->canEdit()); +// Password //////////////////////////////////////////////////////////////////// +if (@$VARS['pw_updated']===true) { + $t->inputP('Password successfully updated.'); +} +if (@$VARS['pw mixmatch']===true) { + $t->inputP("Passwords don't match.", true); +} +if ($user->canEdit()) inputNewPassword($user, 'auth_password','Reset Password'); +//////////////////////////////////////////////////////////////////////////////// +$t->closeFieldset(); + +$t->openFieldset("Information"); +inputText($user, 'firstname','First Name',''); +inputText($user, 'lastname','Last Name',''); +inputText($user, 'hsclass','Highschool Class of', + 'Please put the full year (ex: 2012)'); +$t->closeFieldset(); + + +$t->openFieldset("Contact"); +// TODO: I should make this a setting for admins to set. +$hints = array('email'=> + "Right now you can only have one email address, ". + "but I'm working on making it so you can have ". + "multiple.", + 'phone'=> + "A home phone number isn't much use here because it is ". + "used to text-message you (if you enable it), and ". + "contact you at competition." + ); +$use_arr = array(); +foreach ($CONTACT_METHODS as $method) { + inputText($user, + $method->addr_slug, + ucwords($method->addr_word), + $hints[$method->addr_slug]); + $use_arr[$method->verb_slug] = ucwords($method->verb_word); +} + +$t->inputP("When I recieve a message, notify me using the following methods:"); +inputArray($user, 'use', $use_arr); +$t->closeFieldSet(); + + +$t->openFieldSet('Groups'); +$group_arr = array(); +foreach ($VARS['groups'] as $group_name) { + $group_arr[$group_name] = ucwords($group_name); +} +inputArray($user, 'groups', $group_arr); +$t->closeFieldset(); + +if ($user->canEdit()) { + $t->tag('input', array('type'=>'submit', 'value'=>'Save')); +} +$t->closeTag('form'); +$t->footer(); diff --git a/src/views/pages/users/individual.php b/src/views/pages/users/individual.php new file mode 100644 index 0000000..2483e6b --- /dev/null +++ b/src/views/pages/users/individual.php @@ -0,0 +1,89 @@ +<?php global $mm, $uid; +// Honestly, the functions in this include should be in this file, but that +// would make this file too messy. +require_once(VIEWPATH.'/pages/users/include.php'); + +$user = $mm->getAuthObj($uid); + +if (!$user->canRead()) { + include(VIEWPATH.'/pages/users/401.php'); + exit(); +} + +// Read/Change the username +$username = $user->getName(); +if (isset($_POST['auth_name'])) { + $new_name = $_POST['auth_name']; + if ($new_name != $username) { + global $illegal_names; + if (!in_array($new_name, $illegal_names)) { + $changed_name = $user->setName($new_name); + $username = $user->getName(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +$t = $mm->template(); +$mm->header("Users: $username"); + +$t->tag('h1', array(), ($user->canEdit()?'Edit':'View')." User (UID: $uid)"); + +if ($user->canEdit()) { + $t->openTag('form', array('method'=>'post', + 'action'=>$mm->baseUrl()."users/$username")); +} else { + $t->openTag('form'); +} + +$t->openFieldset("Login / Authentication"); +if (isset($changed_name) && !$changed_name) { + $t->inputP("Error setting username to ". + "<q>$new_name</q>. This is probably because". + " a user with that name already exists.", + true); +} + +$t->inputText('auth_name','Username', + "This is the name you use to log in, but it is also a ". + "short name that is used in various places, think of it ". + "as a sort of <q>Twitter name</q>.", + $username,!$user->canEdit()); +if ($user->canEdit()) inputNewPassword($user, 'auth_password','Reset Password'); +$t->closeFieldset(); + +$t->openFieldset("Information"); +inputText($user, 'firstname','First Name',''); +inputText($user, 'lastname','Last Name',''); +inputText($user, 'hsclass','Highschool Class of','Please put the full year (ex: 2012)'); +$t->closeFieldset(); + +$t->openFieldset("Contact"); +inputText($user, 'email', 'Email', + "Right now you can only have one email address, ". + "but I'm working on making it so you can have ". + "multiple."); +inputText($user, 'phone', 'Cell Number', + "A home phone number isn't much use here because it is ". + "used to text-message you (if you enable it), and ". + "contact you at competition."); +$t->inputP("When I recieve a message, notify me using the following methods:"); +inputArray($user, 'use', array('email'=>'Email', + 'sms'=>'Text Message')); +$t->closeFieldSet(); + +$t->openFieldSet('Groups'); +$groups = $mm->listGroupNames(); +$group_arr = array(); +foreach ($groups as $group_name) { + $group_arr[$group_name] = ucwords($group_name); +} +inputArray($user, 'groups', $group_arr); +$t->closeFieldset(); + +if ($user->canEdit()) { + $t->tag('input', array('type'=>'submit', 'value'=>'Save')); +} +$t->closeTag('form'); +$mm->footer(); diff --git a/src/views/pages/users/new.html.php b/src/views/pages/users/new.html.php new file mode 100644 index 0000000..f2dacb5 --- /dev/null +++ b/src/views/pages/users/new.html.php @@ -0,0 +1,37 @@ +<?php global $VARS; +$t = $VARS['template']; + +$t->header('Create new user'); + +$t->openTag('form', array('method'=>'post', + 'action'=>$t->url('users'))); + +$t->openFieldset("New User: basic login"); +if (in_array('illegal name', $VARS['errors'])) { + $t->inputP("That is a forbidden username.", true); +} +if (in_array('user exists', $VARS['errors'])) { + $t->inputP("A user with that name already exists."); +} +$t->inputText('auth_name','Username', + "This is the name you use to log in, but it is also a ". + "short name that is used in various places, think of it ". + "as a sort of <q>Twitter name</q>.",'',$VARS['username']); + +@$password = $VARS['password1']; +if ($in_array('pw mixmatch', $VARS['errors'])) { + $t->inputP("The passwords didn't match.", true); + $password = ''; +} +if (in_array('no pw', $VARS['errors'])) { + $t->inputP("You must set a password.", true); + $password = ''; +} +$t->inputNewPassword('auth_password','Password', $password); +$t->closeFieldset(); + +$t->tag('input', array('type'=>'submit', 'value'=>'Submit')); + +$t->closeTag('form'); + +$t->footer(); |