diff --git a/about.php b/about.php
index 558cbbf..e3b2426 100644
--- a/about.php
+++ b/about.php
@@ -12,17 +12,8 @@ $depth = "";
$title = "About";
include "res/lib/load.php";
+// ------------------------------------
-echo $GLOBALS['twig']->render('head.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth,
- 'title' =>$title]);
-
-echo $GLOBALS['twig']->render('index.twig.html',
- ['animal'=> "cat"]);
-
-echo $GLOBALS['twig']->render('foot.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth]);
+display_page("about.twig.html", $depth, $title);
?>
diff --git a/admin/index.php b/admin/index.php
index c32d03f..dcdc550 100644
--- a/admin/index.php
+++ b/admin/index.php
@@ -12,18 +12,8 @@ $title = "Control Panel";
$depth = "../";
include "../res/lib/load.php";
-echo $GLOBALS['twig']->render('head.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth,
- 'title' =>$title]);
-
-
-echo $GLOBALS['twig']->render('admin_index.twig.html', []);
-
-echo $GLOBALS['twig']->render('foot.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth]);
-
+// --------------------------------------
+display_page("admin_index.twig.html", $depth, $title);
?>
diff --git a/admin/private/user_create.php b/admin/private/user_create.php
index 4a0f5f2..e45326e 100644
--- a/admin/private/user_create.php
+++ b/admin/private/user_create.php
@@ -11,7 +11,11 @@
$depth = "../../";
include "../../res/lib/load.php";
-$id = $_POST['id'];
+$auth_user = $_POST['auth_user'];
+$auth_pass = $_POST['auth_pass'];
+$auth_user_id = user_name_to_id($auth_user);
+
+$id = intval($_POST['id']);
$name = $_POST['name'];
$full_name = $_POST['full_name'];
$bio = $_POST['bio'];
@@ -19,26 +23,31 @@ $email = $_POST['email'];
$url = $_POST['url'];
$password = $_POST['password'];
$login = $_POST['login'];
+$password = password_hash($password, PASSWORD_BCRYPT, array('cost' => 11));
+
+// -------------------------------------
+
+auth_enforce($auth_user_id, $auth_pass,
+ array("wizard", "archmage"), "make accounts");
$invalid = input_enforce(array($id, $name, $full_name, $bio, $email, $url,
$password, $login),
- array("ID", "username", "full name", "biography", "email",
- "url", "password", "login"),
- array("int", "string", "string", "string", "email",
- "url", "password",
- array("contributor", "spectator", "wizard",
- "admin")));
+ array("ID", "Username", "Full name", "Biography", "E-mail",
+ "URL", "Password", "Login class"),
+ array("free_user_id", "free_user_name", "string", "string",
+ "email", "url", "ne_string",
+ array("spectator", "wizard", "archmage",
+ "contributor")));
-if (!is_bool($invalid)) {
- input_error("Some input is invalid: " . $invalid);
+if (!empty($invalid)) {
+ input_error("Some input is invalid: " . comma_sep($invalid));
}
-$result = user_create($id, $name, $password, $login,
- $full_name, $email, $url, $bio);
-if ($result) {
- header('Location: http://localhost/blagoblag/user.php?id=' . $id);
-} else {
- general_error("Something went wrong.");
-}
+// -------------------------------------
+
+user_create($id, $name, $password, $login,
+ $full_name, $email, $url, $bio);
+
+root_redirect("user.php?name=" . $name);
?>
diff --git a/admin/private/user_destroy.php b/admin/private/user_destroy.php
new file mode 100644
index 0000000..fc957b4
--- /dev/null
+++ b/admin/private/user_destroy.php
@@ -0,0 +1,37 @@
+
diff --git a/index.php b/index.php
index 84959d0..e3294cf 100644
--- a/index.php
+++ b/index.php
@@ -12,22 +12,8 @@ $depth = "";
$title = "";
include "res/lib/load.php";
-// global variable declaration
-$users = user_ids();
-$user = array_map(user_data, $users);
-$posts = post_ids();
-$post = array_map(post_data, $posts);
+// -------------------------------------
-echo $GLOBALS['twig']->render('head.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth,
- 'title' =>$title]);
-
-echo $GLOBALS['twig']->render('index.twig.html', []);
-
-
-echo $GLOBALS['twig']->render('foot.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth]);
+display_page("index.twig.html", $depth, $title);
?>
diff --git a/post.php b/post.php
index 9d33964..00f51e5 100644
--- a/post.php
+++ b/post.php
@@ -13,27 +13,17 @@ $title = "";
include "res/lib/load.php";
// -------------------------------------
-// post environment
-$post_id = $_GET['id'];
-$text = post_text($post_id);
-$author = post_author($post_id);
-$date = post_data($post_id);
+
+$id = $_GET['id'];
+$text = post_text($id);
+$author = post_author($id);
+$date = post_data($id);
+
+$local_exports = array('id' => $id, 'text' => $text, 'author' => $author,
+ 'data' => $data);
+
// -------------------------------------
-echo $GLOBALS['twig']->render('head.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth,
- 'title' =>$title]);
-
-echo $GLOBALS['twig']->render('post.twig.html',
- ['id'=> $post_id,
- 'text' => $text,
- 'author' => $author,
- 'date' => $date]);
-
-
-echo $GLOBALS['twig']->render('foot.twig.html',
- ['theme' => $GLOBALS['theme'],
- 'depth' => $depth]);
+display_page("post.twig.html", $depth, $title, $local_exports);
?>
diff --git a/res/lib/array.php b/res/lib/array.php
index 4c1b091..ef0a123 100644
--- a/res/lib/array.php
+++ b/res/lib/array.php
@@ -11,20 +11,35 @@
// ARRAY -- > STRING
// Turn a 1D array into a comma-seperated string
-function comma_sep($array) {
+function comma_sep($array, $seperator=", ") {
+ global $string;
+ $string = $seperator;
global $stack;
$stack = "";
$comma_print = function($item) {
$GLOBALS['stack'] = $GLOBALS['stack']
- . ", " . $item;
+ . $GLOBALS['string'] . $item;
};
array_map($comma_print, $array);
- $stack = preg_replace('/^, /', '', $stack);
+ $stack = preg_replace('/^' . $string . '/', '', $stack);
return $stack;
}
+
+// STRING STRING [ARRAY] --> ARRAY
+// Return exports for Twig-- with the required global & local exports,
+// along with any optional local ones.
+function make_exports($depth, $title, $local = array()) {
+ $exports = $GLOBALS['twig_exports'];
+
+ $exports['depth'] = $depth;
+ $exports['title'] = $title;
+
+ return array_merge($exports, $local);
+}
+
?>
diff --git a/res/lib/blagoblag.php b/res/lib/blagoblag.php
new file mode 100644
index 0000000..bf7cdc8
--- /dev/null
+++ b/res/lib/blagoblag.php
@@ -0,0 +1,23 @@
+ BOOLEAN
+// Render and display a page, based on it's template-path, title, relative
+// depth, and an optional array of more Twig variable exports.
+function display_page($template, $depth, $title, $local_exports=array()) {
+ echo $GLOBALS['twig']->render("head.twig.html",
+ make_exports($depth, $title, $local_exports));
+ echo $GLOBALS['twig']->render($template,
+ make_exports($depth, $title, $local_exports));
+ echo $GLOBALS['twig']->render("foot.twig.html",
+ make_exports($depth, $title, $local_exports));
+ return true;
+}
diff --git a/res/lib/db.php b/res/lib/db.php
index 5b580b7..80713ac 100644
--- a/res/lib/db.php
+++ b/res/lib/db.php
@@ -30,8 +30,8 @@ if (!db_table_existant("lusers")) {
db_create_table("lusers",
array("id int primary key","username varchar(20)",
"biography longtext","email varchar(50)","website varchar(50)",
- "password_hash varchar(80)","full_name varchar(50)",
- "login varchar(20)")); }
+ "hash char(60)","full_name varchar(50)",
+ "class varchar(20)")); }
if (!db_table_existant("posts")) {
db_create_table("posts",
@@ -51,10 +51,12 @@ if (!db_table_existant("comments")) {
// Execute a DB command
function db_cmd($query) {
$stmt = $GLOBALS['pdo']->query($query);
+ $stack = array();
+
if (is_bool($stmt)) {
return $stmt;
} else {
- return $stmt->fetch();
+ return $stmt->fetchAll();
}
}
@@ -63,7 +65,17 @@ function db_cmd($query) {
// STRING STRING --> ARRAY
// Return all values of a specific column
function db_get_columns($table, $column) {
- return db_cmd("select " . $column . " from " . $table);
+ $result = db_cmd("select " . $column . " from " . $table);
+
+ $result_nest = function($array) {
+ return $array[0];
+ };
+
+ if (is_array($result)) {
+ return array_map($result_nest, $result);
+ } else {
+ return $result;
+ }
}
// STRING STRING VARYING --> ARRAY
@@ -77,7 +89,7 @@ function db_get_rows($table, $identifier, $value) {
// Return the value of a specific column in a given row, identified by an
// 'identifier' column set to the given value
function db_get_cell($table, $identifier, $value, $cell) {
- return db_get_rows($table, $identifier, $value)[$cell];
+ return db_get_rows($table, $identifier, $value)[0][$cell];
}
// --------------------------------------
diff --git a/res/lib/error.php b/res/lib/error.php
index a039aff..6229075 100644
--- a/res/lib/error.php
+++ b/res/lib/error.php
@@ -25,4 +25,12 @@ function input_error($string) {
exit;
}
+// STRING --> NIL
+// Print out a general error, with $string as it's error-message.
+function perm_error($string) {
+ echo $GLOBALS['twig']->render('error.twig.html',
+ ['error_message'=>$string]);
+ exit;
+}
+
?>
diff --git a/res/lib/load.php b/res/lib/load.php
index dd73af6..897836a 100644
--- a/res/lib/load.php
+++ b/res/lib/load.php
@@ -15,6 +15,7 @@ function root($path) {
return $GLOBALS['depth'] . $path;
}
+
// -------------------------------------
include(root("config.php"));
@@ -23,12 +24,42 @@ require_once root("vendor/autoload.php");
include(root("res/lib/string.php"));
include(root("res/lib/array.php"));
include(root("res/lib/user.php"));
-include(root("res/lib/post.php"));
-include(root("res/lib/comment.php"));
+include(root("res/lib/error.php"));
+include(root("res/lib/sterilize.php"));
include(root("res/lib/db.php"));
+include(root("res/lib/url.php"));
+include(root("res/lib/blagoblag.php"));
$loader= new Twig_Loader_Filesystem(root("res/themes/default/html"));
$twig = new Twig_Environment($loader, ['cache' =>
root('cache/')]);
+
+
+// -------------------------------------
+// global variable declaration
+global $users; $users = user_ids();
+global $user; $user = array();
+
+$push_user_data = function($user_id) {
+ $user_name = user_name($user_id);
+ $GLOBALS['user'][$user_id] = user_data($user_id);
+ $GLOBALS['user'][$user_name] = user_data($user_id);
+ };
+
+array_map($push_user_data, $users);
+
+// global $posts; $posts = post_ids();
+// global $post; $post = array();
+// $post = array_map(post_data, $posts);
+
+// -----------------
+
+global $twig_exports;
+$twig_exports = array('theme' => $GLOBALS['theme'],
+ 'users' => $GLOBALS['users'],
+ 'user' => $GLOBALS['user']);
+ //'posts' => $GLOBALS['posts'],
+ //'post' => $GLOBALS['post']);
+
?>
diff --git a/res/lib/sterilize.php b/res/lib/sterilize.php
index c22603e..1208544 100644
--- a/res/lib/sterilize.php
+++ b/res/lib/sterilize.php
@@ -9,8 +9,136 @@
GNU Affero General Public License for more details. */
-function input_enforce($values, $names, $types) {
- $stack = "";
+// NUMBER STRING ARRAY [STRING] --> NIL
+// Make sure a user is both authenticated *and* permitted to do a given task,
+// aka in the right login-class.
+function auth_enforce($id, $password, $permitted, $message="do that") {
+ if (!user_valid_password($id, $password)) {
+ input_error("Sorry, your user-name or password is wrong.");
+ }
- while ($i < length($valuesw)) {
- if (function
+ $class = user_class($id);
+ if (!in_array($class, $permitted)) {
+ perm_error("Mate, only a " . comma_sep($permitted, " or ")
+ . " can " . $message . "-- you hecking "
+ . $class . "!");
+ }
+}
+
+// ARRAY ARRAY ARRAY --> ARRAY
+// Validate a list of values against a corresponding list of types;
+// return a list of names (corresponding to the values) that don't
+// match their types. If an empty array is returned, then, all inputs are
+// valid.
+// Two non-existant "pseudo-types" are accepted-- "url" and "e-mail".
+// A "type" can also be an array of acceptable values.
+// Example:
+// input_enforce(["apple", 4, "ap@ap.co.uk", "never"],
+// ["Fruit", "Age", "E-mail", "Preference"],
+// ["string", "int", "email",
+// ["never", "always", "now"]]);
+//
+// ... it's an ugly, long function, I know...
+function input_enforce($values, $names, $types) {
+ $stack = array();
+ $i = 0;
+
+ while ($i < count($values)) {
+ $value = $values[$i];
+ $type = $types[$i];
+ $name = $names[$i];
+ $res = true;
+
+ switch ($type) {
+ case (is_array($type)):
+ if (!in_array($value, $type)) { $res = false; }
+ break;
+
+ default:
+ $type_check = "is_" . $type;
+ if (!call_user_func($type_check, $value)) {
+ $res = false;
+ }
+ break;
+ }
+
+ if (!$res) {
+ $stack[] = $name;
+ }
+
+ $i++;
+ }
+
+
+ return $stack;
+}
+
+
+// -------------------------------------
+// pseudo-types
+
+// STRING --> BOOLEAN
+// Return whether or not a given string is a valid e-mail.
+function is_email($string) {
+ return filter_var($string, FILTER_VALIDATE_EMAIL);
+}
+
+// STRING --> BOOLEAN
+// Return whether or not a given string is a valid url.
+function is_url($string) {
+ return filter_var($string, FILTER_VALIDATE_URL);
+}
+
+// VARYING --> BOOLEAN
+// Return whether or not a given value is a non-empty string
+function is_ne_string($value) {
+ if (is_string($value) && !empty($value)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// -------------------------------------
+
+// STRING --> BOOLEAN
+// Return whether or not a string is already a username
+function is_user_name($username) {
+ if (user_name_to_id($username)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// NUMBER --> BOOLEAN
+// Return whether or not a number is already a user ID
+function is_user_id($id) {
+ if (in_array($id, $GLOBALS['users'])) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// -------------------------------------
+
+// STRING --> BOOLEAN
+// Return whether or not a given string is a valid (unused) username
+function is_free_user_name($username) {
+ if (!is_user_name($username) && is_ne_string($username)) {
+ return true; } else { return false; }
+}
+
+// STRING --> BOOLEAN
+// Return whether or not a given number is a valid (unused) user ID
+function is_free_user_id($id) {
+ if (!is_user_id($id) && is_int($id)) {
+ return true; } else { return false; }
+}
+
+
+// -------------------------------------
+
+
+?>
diff --git a/res/lib/url.php b/res/lib/url.php
new file mode 100644
index 0000000..e5dd707
--- /dev/null
+++ b/res/lib/url.php
@@ -0,0 +1,40 @@
+ STRING
+// Turn a path (relative to root) into absolute URL
+function root_url($string) {
+ return protocol() . "://" . $_SERVER['HTTP_HOST'] . "/"
+ . $GLOBALS['root']
+ . $string;
+}
+
+
+// NIL --> STRING
+// Return the current protocol (HTTP, HTTPS, etc)
+function protocol() {
+ return explode('/', $_SERVER['SERVER_PROTOCOL'])[0];
+}
+
+
+// STRING --> NIL
+// Redirect to a regular URL (I.E., "https://duckduckgo.com")
+function redirect($url) {
+ header('Location: ' . $url);
+ return;
+}
+
+// STRING --> NIL
+// Redirect to a URL relative to root (I.E., "res/img/")
+function root_redirect($path) {
+ header('Location: ' . root_url($path));
+ return;
+}
diff --git a/res/lib/user.php b/res/lib/user.php
index c8bcba4..fe65fce 100644
--- a/res/lib/user.php
+++ b/res/lib/user.php
@@ -28,16 +28,22 @@ function user_set($id, $variable, $new_value) {
// NUMBER STRING STRING [STRING STRING STRING STRING STRING] --> BOOLEAN
// Create a user of the given specification.
-function user_create($id, $name, $password, $login="Spectator",
+function user_create($id, $name, $password, $class="Spectator",
$full_name=NULL, $email=NULL, $url=NULL, $bio=NULL) {
return db_insert_row("lusers",
- array("id", "username", "password_hash", "login",
+ array("id", "username", "hash", "class",
"full_name", "email", "website", "biography"),
- array($id, $name, $password, $login,
+ array($id, $name, $password, $class,
$full_name, $email, $url, $bio));
}
+// NUMBER --> BOOLEAN
+// Delete a user by their ID.
+function user_delete($id) {
+ return db_cmd("delete from lusers where id = " . $id);
+}
+
// -------------------------------------
@@ -51,7 +57,7 @@ function user_id_to_name($id) {
// STRING --> NUMBER
// Get a user's ID from username.
function user_name_to_id($username) {
- return db_get_cell("lusers", "username", $username, "id");
+ return db_get_cell("lusers", "username", string_wrap($username), "id");
}
@@ -61,13 +67,13 @@ function user_name_to_id($username) {
// NUMBER --> STRING
// Return a username from a user-ID
function user_name($id) {
- return user_id_to_name($id, "username");
+ return user_id_to_name($id);
}
// NUMBER --> STRING
// Return a user's login-class from ID
-function user_login($id) {
- return user_get($id, "login");
+function user_class($id) {
+ return user_get($id, "class");
}
// NUMBER --> STRING
@@ -96,10 +102,46 @@ function user_biography($id) {
// -------------------------------------
+
+// NUMBER --> ARRAY
+// Fetch an array of a user's IDs
+function user_ids() {
+ return db_get_columns("lusers", "id");
+}
+
+
+// -------------------------------------
+
+
// NUMBER --> ARRAY
// Fetch an array of a user's posts (by ID)
-function user_posts($user_id) {
- return db_get_cell("posts", "user", $user_id, "id");
+function user_posts($id) {
+ return db_get_cell("posts", "user", $id, "id");
+}
+
+
+// -------------------------------------
+
+
+// NUMBER --> ARRAY
+// Return an array filled with all of a user's relevant data.
+function user_data($id) {
+ return array('full_name' => user_full_name($id),
+ 'name' => user_name($id),
+ 'bio' => user_biography($id),
+ 'email' => user_email($id),
+ 'website' => user_website($id),
+ 'posts' => user_posts($id));
+}
+
+
+// -------------------------------------
+
+
+// NUMBER STRING --> BOOLEAN
+// Return whether or not a given password is valid.
+function user_valid_password($id, $password) {
+ return password_verify($password, user_get($id, "hash"));
}
?>
diff --git a/res/themes/default/html/admin_index.twig.html b/res/themes/default/html/admin_index.twig.html
index 69f1f91..33e57b4 100644
--- a/res/themes/default/html/admin_index.twig.html
+++ b/res/themes/default/html/admin_index.twig.html
@@ -1,5 +1,17 @@
{{ bio }}