diff --git a/.gitignore b/.gitignore
index 16610a6..c43ecd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
/docs/common/config.php
/assets/upload/**
/docs/www/blog/feed.atom
+/docs/www/blog/article/**
diff --git a/docs/www/blog/blogpost.php b/docs/www/blog/blogpost.php
new file mode 100644
index 0000000..7d48ced
--- /dev/null
+++ b/docs/www/blog/blogpost.php
@@ -0,0 +1,80 @@
+
";
+ $body .= sprintf("
+ %s |
+ Published on: %s |
+ ", $this->title,
+ date("Y-m-d", strtotime($this->date_posted)));
+
+ // Display tags
+ for($i = 0; $i < count($this->tags); $i++) {
+ $tag = $this->tags[$i];
+ $body .= sprintf("
+
+ %s
+ ", $tag["color"], $tag["name"]);
+ }
+
+ // Display publish date and end metadata div
+ $body .= sprintf(" |
+ Last edited on: %s |
",
+ date("Y-m-d", strtotime($this->date_edited)));
+
+ // Display hrule, article content and end the article
+ $body .= sprintf("
%s", $this->content);
+ return $body;
+ }
+
+ /**
+ * Display the comments for this post and their children.
+ */
+ public function display_comments(){
+ $body = "";
+ for($i = 0; $i < count($this->comments); $i++){
+ $body .= $this->comments[$i]->display_comment();
+ }
+ return $body."";
+ }
+
+ /**
+ * Constructor for the blogpost.
+ * $blogpost_id GUID of the blogpost in the database.
+ * $address Readable address of the blogpost.
+ * $title Title of the blogpost.
+ * $content Content of the blogpost article.
+ * $date_posted Timestamp at publishing of article.
+ * $date_edited Timestamp at whioch the article was last edited.
+ * $tags Array of the tags this article has.
+ * $comments Array of Blogpostcomment objects,
+ * the comments of this article.
+ */
+ public function __construct($blogpost_id, $address, $title,
+ $content, $date_posted, $date_edited, $tags, $comments){
+ $this->blogpost_id = $blogpost_id;
+ $this->address = $address;
+ $this->title = $title;
+ $this->content = $content;
+ $this->date_posted = $date_posted;
+ $this->date_edited = $date_edited;
+ $this->tags = $tags;
+ $this->comments = $comments;
+ }
+}
+?>
diff --git a/docs/www/blog/blogpostcomment.php b/docs/www/blog/blogpostcomment.php
new file mode 100644
index 0000000..b28303e
--- /dev/null
+++ b/docs/www/blog/blogpostcomment.php
@@ -0,0 +1,150 @@
+
+
+ ";
+ }
+
+ /**
+ * Load the child comments to this comment, and recursively their children.
+ */
+ public function load_children($conn) {
+ // Prepare new statement for selecting all the child comments.
+ $stmt = $conn->prepare("SELECT comment_id, poster_id, timestamp,
+ content FROM blogpost_comments WHERE blogpost_id = :blogpost_id AND
+ parent_id = :comment_id ORDER BY timestamp ASC;");
+
+ // Bind and execute the comment select
+ $stmt->bindParam(":blogpost_id", $this->blogpost_id);
+ $stmt->bindParam(":comment_id", $this->comment_id);
+ $stmt->execute();
+
+ // Fetch the comments
+ $results_arr = $stmt->fetchall(PDO::FETCH_ASSOC);
+ $comments_arr = [];
+
+ // Prepare comment author selection statement
+ $stmt = $conn->prepare("SELECT username FROM users WHERE
+ user_id = :user_id;");
+
+ // Recursively fetch all the child comments
+ for($i = 0; $i < count($results_arr); $i++) {
+ $com = $results_arr[$i];
+
+ // If comment has a registered author, fetch their name
+ if($com["poster_id"]) {
+ $stmt->bindParam(":user_id", $com["poster_id"]);
+
+ $stmt->execute();
+
+ $result = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ // If user was erased from database, set name to [Deleted]
+ if(!$result) {
+ $username = "[Deleted]";
+ }
+ else {
+ $username = $result["username"];
+ }
+ }
+ else {
+ $username = "[Guest]";
+ }
+
+ $commentObj = new BlogpostComment($com["comment_id"],
+ $com["poster_id"], $username, $this->blogpost_id,
+ $this->blogpost_addr, $com["timestamp"], $com["content"],
+ $this->comment_id);
+ $comments_arr[] = $commentObj;
+ $commentObj->load_children($conn);
+ }
+
+ $this->children = $comments_arr;
+ }
+
+ /**
+ * Constructor for the BlogpostComment object.
+ * $comment_id GUID of the comment.
+ * $poster_id GUID of the comment author.
+ * $poster_name Name of the comment author.
+ * $blogpost_id GUID of the blogpost this comment is under.
+ * $blogpost_addr Human-readable address of the blogpost this
+ comment is under.
+ * $timestamp Timestamp at comment creation.
+ * $content Content of the comment.
+ * $parent_id GUID of the comment this is a reply to (or NULL).
+ */
+ public function __construct($comment_id, $poster_id, $poster_name,
+ $blogpost_id, $blogpost_address, $timestamp, $content, $parent_id) {
+ $this->comment_id = $comment_id;
+ $this->blogpost_id = $blogpost_id;
+ $this->blogpost_addr = $blogpost_address;
+ $this->poster_id = $poster_id;
+ $this->poster_name = $poster_name;
+ $this->timestamp = $timestamp;
+ $this->content = $content;
+ $this->parent_id = $parent_id;
+ }
+}
+?>
diff --git a/docs/www/blog/generatearticle.php b/docs/www/blog/generatearticle.php
new file mode 100644
index 0000000..294aaaf
--- /dev/null
+++ b/docs/www/blog/generatearticle.php
@@ -0,0 +1,294 @@
+prepare("SELECT UUID()");
+ $stmt->execute();
+ $result = $stmt->fetch(PDO::FETCH_ASSOC);
+ $uuid = $result["UUID()"];
+
+ // Prepare the statemtnt
+ $stmt = $conn->prepare("INSERT INTO blogpost_comments
+ ( comment_id, parent_id, blogpost_id, poster_id, content) VALUES
+ (:comment_id, :parent_id, :blogpost_id, :poster_id, :content);");
+
+ // Bind all the parameters
+ $stmt->bindValue(":comment_id", $uuid, PDO::PARAM_STR);
+ $stmt->bindValue(":parent_id", $parentId == "NULL"
+ ? NULL : $parentId, PDO::PARAM_STR);
+ $stmt->bindValue(":blogpost_id", $blogId, PDO::PARAM_STR);
+ $stmt->bindValue(":poster_id", $posterId == "NULL"
+ ? NULL : $posterId, PDO::PARAM_STR);
+ $stmt->bindValue(":content", $content, PDO::PARAM_STR);
+
+ // Execute the statement
+ $stmt->execute();
+
+ return $uuid;
+}
+
+/**
+ * Load comments under a given blog.
+ * Returns array of BlogpostComment objects.
+ */
+function load_comments($conn, $blogId, $blogAddress) {
+ // Prepare new statement for selecting all the child comments.
+ $stmt = $conn->prepare("SELECT comment_id, poster_id, timestamp,
+ content FROM blogpost_comments WHERE blogpost_id = :blogpost_id
+ AND parent_id IS NULL ORDER BY timestamp ASC;");
+
+ // Bind and execute the comment select
+ $stmt->bindParam(":blogpost_id", $blogId);
+ $stmt->execute();
+
+ // Fetch the comments
+ $results_arr = $stmt->fetchall(PDO::FETCH_ASSOC);
+ $comments_arr = [];
+
+ // Prepare comment author selection statement
+ $stmt = $conn->prepare("SELECT username FROM users WHERE
+ user_id = :user_id;");
+
+ // Recursively fetch all the child comments
+ for($i = 0; $i < count($results_arr); $i++) {
+ $com = $results_arr[$i];
+
+ // If comment has a registered author, fetch their name
+ if($com["poster_id"]) {
+ $stmt->bindParam(":user_id", $com["poster_id"]);
+
+ $stmt->execute();
+
+ $result = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ // If user was erased from database, set name to [Deleted]
+ if(!$result) {
+ $username = "[Deleted]";
+ }
+ else {
+ $username = $result["username"];
+ }
+ }
+ else {
+ $username = "[Guest]";
+ }
+
+ $commentObj = new BlogpostComment($com["comment_id"], $com["poster_id"],
+ $username, $blogId, $blogAddress, $com["timestamp"],
+ $com["content"], NULL);
+ $commentObj->load_children($conn);
+ $comments_arr[] = $commentObj;
+ }
+
+ return $comments_arr;
+}
+
+/**
+ * Load info about the blog with a given guid and return corresponding
+ * Blogpost object. NULL if blog couldn't be loaded.
+ */
+function load_blog($conn, $blogId){
+ // Prepare and bind statement for gathering blogpost info
+ $stmt = $conn->prepare("SELECT readable_address, title, content,
+ date_posted, date_edited FROM blogposts WHERE
+ blogpost_id = :blogpost_id;");
+ $stmt->bindParam(":blogpost_id", $blogId);
+
+ // Execute the statement
+ $stmt->execute();
+
+ // Fetch the blogpost
+ $result = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ // If no post with given guid was found,
+ // there is no information to gather, return.
+ if(!$result){
+ return null;
+ }
+
+ // Prepare new statement for selecting the tags for a given blogpost
+ $stmt = $conn->prepare("SELECT name, color FROM
+ blogpost_tags INNER JOIN blogpost_has_tag ON
+ blogpost_tags.tag_id = blogpost_has_tag.tag_id WHERE
+ blogpost_id = :blogpost_id;");
+
+ // Bind and execute the tag select
+ $stmt->bindParam(":blogpost_id", $blogId);
+ $stmt->execute();
+
+ // Fetch the tags
+ $tags_arr = $stmt->fetchall(PDO::FETCH_ASSOC);
+
+ // Set the variables
+ $blogTitle = $result["title"];
+ $blogAddress = $result["readable_address"];
+ $blogContent = $result["content"];
+ $datePosted = $result["date_posted"];
+ $dateEdited = $result["date_edited"];
+ $tags = $tags_arr;
+ $comments = load_comments($conn, $blogId, $blogAddress);
+
+ return new Blogpost($blogId, $blogAddress, $blogTitle, $blogContent,
+ $datePosted, $dateEdited, $tags, $comments);
+}
+
+// Check DB connection
+if($conn == null){
+ header($_SERVER["SERVER_PROTOCOL"]." 503 Service Unavailable", true, 503);
+ include_once($_SERVER["DOCUMENT_ROOT"]."/errors/503.php");
+ die();
+}
+
+// If a human-readable address was provided, extract appropriate id.
+if(isset($_POST["address"])) {
+ $blogAddr = sanitize_input($_POST["address"]);
+
+ // Prepare and bind statement for gathering blogpost address
+ $stmt = $conn->prepare("SELECT blogpost_id
+ FROM blogposts WHERE readable_address = :readable_address;");
+ $stmt->bindParam(":readable_address", $blogAddr);
+
+ // Execute the statement
+ $stmt->execute();
+
+ // Fetch the blogpost
+ $result = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ // If post with given address was found, set the $blogId var.
+ if($result){
+ $blogId = sanitize_input($result["blogpost_id"]);
+ }
+}
+
+// Attempt to load the blogpost
+$blogPost = load_blog($conn, $blogId);
+
+// If blogpost could not be retieved, display warning and die.
+if(!$blogPost) {
+ header($_SERVER["SERVER_PROTOCOL"]." 404 Not Foud", true, 404);
+ //include_once($_SERVER["DOCUMENT_ROOT"]."/errors/404.php");
+ //include_once($COMMONS."/footer.php");
+ die();
+}
+
+// Try to open the file to which to render the blogpost.
+if (!($fp = fopen("article/".$blogPost->address.".php", 'w'))) {
+ header($_SERVER["SERVER_PROTOCOL"]." 500 Could not open file for writing",
+ true, 505);
+ echo "fail";
+ die();
+}
+
+fprintf($fp,
+"
+
+
+ Comments:
+
+
+
+",
+ $COMMONS."/header.php",
+ $blogPost->title,
+ addslashes($blogPost->display_article()),
+ "SEND_COMMAND_ACTION",
+ $blogPost->blogId,
+ $blogPost->address,
+ addslashes($blogPost->display_comments()),
+ $COMMONS."/footer.php");
+
+/*
+// Display the header with title being the blog name
+display_header($blogPost->title);
+
+// Display the blog
+$blogPost->display_article();
+printf("
");
+
+// Display post comment form.
+if(is_null($blogPost->address))
+{
+ printf("
+
+ Comments:
+
+
+ ",
+ htmlspecialchars($_SERVER["PHP_SELF"]), $blogId,
+ isset($_SESSION["current_user"]) ?
+ $_SESSION["current_user"]->user_name : "Guest");
+}
+else
+{
+ printf("
+
+ Comments:
+
+
+ ",
+ htmlspecialchars($_SERVER["PHP_SELF"]), $blogId, $blogPost->address,
+ isset($_SESSION["current_user"]) ?
+ $_SESSION["current_user"]->user_name : "Guest");
+}
+// Display the blog comments
+$blogPost->display_comments();
+*/
+?>
+