summaryrefslogtreecommitdiff
path: root/src/views/pages
diff options
context:
space:
mode:
authorLuke Shumaker <LukeShu@sbcglobal.net>2011-08-01 01:22:36 -0400
committerLuke Shumaker <LukeShu@sbcglobal.net>2011-08-01 01:22:36 -0400
commit09dfe32eb6b538225686fd6ed0220240010bc574 (patch)
tree29c1afc5e79519ba8689a3d5d170c312d3cf5033 /src/views/pages
initial commit.
Partway through a rewrite. I have some old files I didn't want to entirely delete.
Diffstat (limited to 'src/views/pages')
-rw-r--r--src/views/pages/404.php11
-rw-r--r--src/views/pages/auth.php65
-rw-r--r--src/views/pages/auth/badrequest.html.php11
-rw-r--r--src/views/pages/auth/index.html.php12
-rw-r--r--src/views/pages/auth/login.html.php49
-rw-r--r--src/views/pages/auth/login.php63
-rw-r--r--src/views/pages/auth/logout.html.php6
-rw-r--r--src/views/pages/groups.php41
-rw-r--r--src/views/pages/http404.html.php15
-rw-r--r--src/views/pages/index.php7
-rw-r--r--src/views/pages/messages.php222
-rw-r--r--src/views/pages/plugins.php61
-rw-r--r--src/views/pages/users.php44
-rw-r--r--src/views/pages/users/401.html.php15
-rw-r--r--src/views/pages/users/404.html.php10
-rw-r--r--src/views/pages/users/500.html.php13
-rw-r--r--src/views/pages/users/created.html.php16
-rw-r--r--src/views/pages/users/include.php60
-rw-r--r--src/views/pages/users/index.csv.php27
-rw-r--r--src/views/pages/users/index.html.php65
-rw-r--r--src/views/pages/users/index.php116
-rw-r--r--src/views/pages/users/individual.html.php105
-rw-r--r--src/views/pages/users/individual.php89
-rw-r--r--src/views/pages/users/new.html.php37
24 files changed, 1160 insertions, 0 deletions
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 '&lt;<a href="'.url($id).'">'.$id.'</a>&gt;';
+}
+function parseMessageIDs($string) {
+ $base = $_SERVER['REQUEST_URL'];
+ $safe = htmlentities($string);
+ $html = preg_replace_callback(
+ '/&lt;([^>]*)&gt;/',
+ '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();