Merge pull request 'categories-page' (#44) from categories-page into master
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 16s

Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
2023-10-24 15:54:20 +01:00
28 changed files with 410 additions and 280 deletions
+8 -8
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\blog; namespace api\blog;
use api\utils\imgUtils; use api\utils\imgUtils;
use DOMDocument; use DOMDocument;
use PDO; use PDO;
@@ -125,7 +127,7 @@ class blogData
public function getCategories(): array public function getCategories(): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT categories FROM blog;"); $stmt = $conn->prepare("SELECT DISTINCT categories FROM blog;");
$stmt->execute(); $stmt->execute();
// set the resulting array to associative // set the resulting array to associative
@@ -415,7 +417,7 @@ class blogData
} }
else else
{ {
rename($from . $file,$to . $file); rename($from . $file, $to . $file);
} }
} }
} }
@@ -430,17 +432,15 @@ class blogData
/** /**
* Get all posts with the given category * Get all posts with the given category
* @param mixed $category - Category of the post * @param string $category - Category of the post
* @return array - Array of all posts with the given category or error message * @return array - Array of all posts with the given category or error message
*/ */
public function getPostsByCategory(mixed $category) public function getPostsByCategory(string $category): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE categories = :category;"); $stmt = $conn->prepare("SELECT * FROM blog WHERE LOCATE(:category, categories) > 0;");
$stmt->bindParam(":category", $category); $stmt->bindParam(":category", $category);
$stmt->execute(); $stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt->fetchAll(PDO::FETCH_ASSOC);
} }
} }
+4 -3
View File
@@ -12,6 +12,7 @@ use Slim\App;
class blogRoutes implements routesInterface class blogRoutes implements routesInterface
{ {
private blogData $blogData; private blogData $blogData;
/** /**
* constructor used to instantiate a base blog routes, to be used in the index.php file. * constructor used to instantiate a base blog routes, to be used in the index.php file.
* @param App $app - the slim app used to create the routes * @param App $app - the slim app used to create the routes
@@ -134,14 +135,14 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
} }
if (!preg_match('/(?:^|,)(?=[^"]|(")?)"?((?(1)(?:[^"]|"")*|[^,"]*))"?(?=,|$)/mx', $data["categories"])) if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
{ {
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format"))); $response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
return $response->withStatus(400); return $response->withStatus(400);
} }
$message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["abstract"], $data["body"], $data["dateModified"], $data["categories"]); $message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["abstract"], $data["body"], $data["dateModified"], $data["categories"]);
if ($message === "post not found") if ($message === "post not found")
{ {
@@ -218,7 +219,7 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
} }
if (!preg_match('/(?:^|,)(?=[^"]|(")?)"?((?(1)(?:[^"]|"")*|[^,"]*))"?(?=,|$)/mx', $data["categories"])) if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
{ {
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format"))); $response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
+1 -1
View File
@@ -39,7 +39,7 @@ new userRoutes($app);
$app->post("/contact", function (Request $request, Response $response) $app->post("/contact", function (Request $request, Response $response)
{ {
$data = $request->getParsedBody(); $data = $request->getParsedBody();
if(empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"])) if (empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"]))
{ {
$response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields"))); $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields")));
return $response->withStatus(400); return $response->withStatus(400);
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\project; namespace api\project;
use api\utils\imgUtils; use api\utils\imgUtils;
use PDO; use PDO;
use Psr\Http\Message\UploadedFileInterface; use Psr\Http\Message\UploadedFileInterface;
@@ -162,7 +164,7 @@ class projectData
* @param UploadedFileInterface $img - Image preview of the project * @param UploadedFileInterface $img - Image preview of the project
* @return string|array - String with error message or array with the new image location * @return string|array - String with error message or array with the new image location
*/ */
public function uploadImage(int $ID, UploadedFileInterface $img): string | array public function uploadImage(int $ID, UploadedFileInterface $img): string|array
{ {
$conn = dbConn(); $conn = dbConn();
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
namespace api\project; namespace api\project;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "projectData.php"; require_once "projectData.php";
@@ -37,7 +38,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write($json); $response->getBody()->write($json);
if(array_key_exists("errorMessage", $result)) if (array_key_exists("errorMessage", $result))
{ {
$response->withStatus(404); $response->withStatus(404);
} }
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
namespace api\timeline; namespace api\timeline;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "timelineData.php"; require_once "timelineData.php";
@@ -39,7 +40,7 @@ class timelineRoutes implements routesInterface
return $response; return $response;
} }
if($args["timeline"] == "work") if ($args["timeline"] == "work")
{ {
$response->getBody()->write(json_encode($this->timelineData->getWorkData())); $response->getBody()->write(json_encode($this->timelineData->getWorkData()));
return $response; return $response;
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\user; namespace api\user;
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use PDO; use PDO;
@@ -54,7 +56,7 @@ class userData
"exp" => $future "exp" => $future
]; ];
return JWT::encode($payload,$secretKey,"HS256"); return JWT::encode($payload, $secretKey, "HS256");
} }
/** /**
+2 -1
View File
@@ -1,6 +1,7 @@
<?php <?php
namespace api\user; namespace api\user;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "userData.php"; require_once "userData.php";
use api\utils\routesInterface; use api\utils\routesInterface;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+11 -9
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\blog; namespace api\blog;
use api\utils\imgUtils; use api\utils\imgUtils;
use DOMDocument; use DOMDocument;
use PDO; use PDO;
@@ -91,7 +93,8 @@ class blogData
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) { if ($result)
{
return $result; return $result;
} }
@@ -113,7 +116,8 @@ class blogData
// set the resulting array to associative // set the resulting array to associative
$result = $stmt->fetchAll(PDO::FETCH_ASSOC); $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result) { if ($result)
{
return $result; return $result;
} }
@@ -123,7 +127,7 @@ class blogData
public function getCategories(): array public function getCategories(): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT categories FROM blog;"); $stmt = $conn->prepare("SELECT DISTINCT categories FROM blog;");
$stmt->execute(); $stmt->execute();
// set the resulting array to associative // set the resulting array to associative
@@ -428,17 +432,15 @@ class blogData
/** /**
* Get all posts with the given category * Get all posts with the given category
* @param mixed $category - Category of the post * @param string $category - Category of the post
* @return array - Array of all posts with the given category or error message * @return array - Array of all posts with the given category or error message
*/ */
public function getPostsByCategory(mixed $category) public function getPostsByCategory(string $category): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE categories = :category;"); $stmt = $conn->prepare("SELECT * FROM blog WHERE LOCATE(:category, categories) > 0;");
$stmt->bindParam(":category", $category); $stmt->bindParam(":category", $category);
$stmt->execute(); $stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt->fetchAll(PDO::FETCH_ASSOC);
} }
} }
+27 -13
View File
@@ -12,6 +12,7 @@ use Slim\App;
class blogRoutes implements routesInterface class blogRoutes implements routesInterface
{ {
private blogData $blogData; private blogData $blogData;
/** /**
* constructor used to instantiate a base blog routes, to be used in the index.php file. * constructor used to instantiate a base blog routes, to be used in the index.php file.
* @param App $app - the slim app used to create the routes * @param App $app - the slim app used to create the routes
@@ -29,9 +30,11 @@ class blogRoutes implements routesInterface
*/ */
public function createRoutes(App $app): void public function createRoutes(App $app): void
{ {
$app->get("/blog/categories", function (Request $request, Response $response) { $app->get("/blog/categories", function (Request $request, Response $response)
{
$post = $this->blogData->getCategories(); $post = $this->blogData->getCategories();
if (array_key_exists("errorMessage", $post)) { if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post)); $response->getBody()->write(json_encode($post));
return $response->withStatus(404); return $response->withStatus(404);
} }
@@ -40,10 +43,13 @@ class blogRoutes implements routesInterface
return $response; return $response;
}); });
$app->get("/blog/categories/{category}", function (Request $request, Response $response, $args) { $app->get("/blog/categories/{category}", function (Request $request, Response $response, $args)
if ($args["category"] != null) { {
if ($args["category"] != null)
{
$post = $this->blogData->getPostsByCategory($args["category"]); $post = $this->blogData->getPostsByCategory($args["category"]);
if (array_key_exists("errorMessage", $post)) { if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post)); $response->getBody()->write(json_encode($post));
return $response->withStatus(404); return $response->withStatus(404);
} }
@@ -72,11 +78,15 @@ class blogRoutes implements routesInterface
return $response; return $response;
}); });
$app->get("/blog/post/{type}", function (Request $request, Response $response, $args) { $app->get("/blog/post/{type}", function (Request $request, Response $response, $args)
if ($args["type"] != null) { {
if ($args["type"] == "latest") { if ($args["type"] != null)
{
if ($args["type"] == "latest")
{
$post = $this->blogData->getLatestBlogPost(); $post = $this->blogData->getLatestBlogPost();
if (array_key_exists("errorMessage", $post)) { if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post)); $response->getBody()->write(json_encode($post));
return $response->withStatus(404); return $response->withStatus(404);
} }
@@ -85,9 +95,11 @@ class blogRoutes implements routesInterface
return $response; return $response;
} }
if ($args["type"] == "featured") { if ($args["type"] == "featured")
{
$post = $this->blogData->getFeaturedBlogPost(); $post = $this->blogData->getFeaturedBlogPost();
if (array_key_exists("errorMessage", $post)) { if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post)); $response->getBody()->write(json_encode($post));
return $response->withStatus(404); return $response->withStatus(404);
} }
@@ -123,7 +135,8 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
} }
if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"])) { if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
{
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format"))); $response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
return $response->withStatus(400); return $response->withStatus(400);
@@ -206,7 +219,8 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
} }
if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"])) { if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
{
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format"))); $response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
return $response->withStatus(400); return $response->withStatus(400);
+1 -1
View File
@@ -39,7 +39,7 @@ new userRoutes($app);
$app->post("/contact", function (Request $request, Response $response) $app->post("/contact", function (Request $request, Response $response)
{ {
$data = $request->getParsedBody(); $data = $request->getParsedBody();
if(empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"])) if (empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"]))
{ {
$response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields"))); $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields")));
return $response->withStatus(400); return $response->withStatus(400);
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\project; namespace api\project;
use api\utils\imgUtils; use api\utils\imgUtils;
use PDO; use PDO;
use Psr\Http\Message\UploadedFileInterface; use Psr\Http\Message\UploadedFileInterface;
@@ -162,7 +164,7 @@ class projectData
* @param UploadedFileInterface $img - Image preview of the project * @param UploadedFileInterface $img - Image preview of the project
* @return string|array - String with error message or array with the new image location * @return string|array - String with error message or array with the new image location
*/ */
public function uploadImage(int $ID, UploadedFileInterface $img): string | array public function uploadImage(int $ID, UploadedFileInterface $img): string|array
{ {
$conn = dbConn(); $conn = dbConn();
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
namespace api\project; namespace api\project;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "projectData.php"; require_once "projectData.php";
@@ -37,7 +38,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write($json); $response->getBody()->write($json);
if(array_key_exists("errorMessage", $result)) if (array_key_exists("errorMessage", $result))
{ {
$response->withStatus(404); $response->withStatus(404);
} }
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
namespace api\timeline; namespace api\timeline;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "timelineData.php"; require_once "timelineData.php";
@@ -39,7 +40,7 @@ class timelineRoutes implements routesInterface
return $response; return $response;
} }
if($args["timeline"] == "work") if ($args["timeline"] == "work")
{ {
$response->getBody()->write(json_encode($this->timelineData->getWorkData())); $response->getBody()->write(json_encode($this->timelineData->getWorkData()));
return $response; return $response;
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
namespace api\user; namespace api\user;
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use PDO; use PDO;
@@ -54,7 +56,7 @@ class userData
"exp" => $future "exp" => $future
]; ];
return JWT::encode($payload,$secretKey,"HS256"); return JWT::encode($payload, $secretKey, "HS256");
} }
/** /**
+2 -1
View File
@@ -1,6 +1,7 @@
<?php <?php
namespace api\user; namespace api\user;
require_once __DIR__ . "/../utils/routesInterface.php"; require_once __DIR__ . "/../utils/routesInterface.php";
require_once "userData.php"; require_once "userData.php";
use api\utils\routesInterface; use api\utils\routesInterface;
+21
View File
@@ -0,0 +1,21 @@
main > h1 {
padding-left: 3em;
}
section.catPosts .largePost {
margin-bottom: 3em;
}
section.categories {
display: flex
flex-direction row;
justify-content: center;
align-items;
flex-wrap: wrap;
}
section.categories .btnContainer {
flex-basis: 33.3333333%
}
+1 -1
View File
@@ -35,7 +35,7 @@ section.largePost {
padding: 0 5em 1em; padding: 0 5em 1em;
} }
section.largePost:first-child { section.largePost:not(:last-child) {
border-bottom: 5px solid var(--mutedGrey); border-bottom: 5px solid var(--mutedGrey);
} }
+1
View File
@@ -7,5 +7,6 @@
@import "../../css/footer.css"; @import "../../css/footer.css";
@import "blogPosts.css"; @import "blogPosts.css";
@import "home.css"; @import "home.css";
@import "category.css";
@import "prism.css"; @import "prism.css";
+274 -197
View File
@@ -3,21 +3,25 @@ const scrollLimit = 150;
document.addEventListener('DOMContentLoaded', () => document.addEventListener('DOMContentLoaded', () =>
{ {
goToURL(window.location.pathname); goToURL(window.location.pathname);
}); });
window.addEventListener('popstate', _ => window.addEventListener('popstate', _ =>
{ {
goToURL(window.history.state); goToURL(window.history.state);
}); });
window.onscroll = () => { window.onscroll = () =>
// check if scrolled past limit if so add scrolled class to change background of nav {
if (document.body.scrollTop >= scrollLimit || document.documentElement.scrollTop >= scrollLimit) { // check if scrolled past limit if so add scrolled class to change background of nav
document.querySelector("nav").classList.add("scrolled"); if (document.body.scrollTop >= scrollLimit || document.documentElement.scrollTop >= scrollLimit)
} else { {
document.querySelector("nav").classList.remove("scrolled"); document.querySelector('nav').classList.add('scrolled');
} }
else
{
document.querySelector('nav').classList.remove('scrolled');
}
}; };
/** /**
@@ -26,38 +30,40 @@ window.onscroll = () => {
*/ */
function goToURL(url) function goToURL(url)
{ {
// Get the current URL and split it into an array // Get the current URL and split it into an array
let urlArray = url.split('/'); let urlArray = url.split('/');
if (url === "/blog/" || url === "/blog") if (url === '/blog/' || url === '/blog')
{ {
loadHomeContent(); loadHomeContent();
// window.history.pushState(null, null, url); // window.history.pushState(null, null, url);
return; return;
} }
// Check if the URL is a post page // Check if the URL is a post page
if (urlArray[2] === 'post') if (urlArray[2] === 'post')
{ {
// Create a new URL with the dynamic part // Create a new URL with the dynamic part
// window.history.pushState(null, null, url); // window.history.pushState(null, null, url);
loadIndividualPost(urlArray[urlArray.length - 1]).catch(err => console.log(err)); loadIndividualPost(urlArray[urlArray.length - 1]).catch(err => console.log(err));
return; return;
} }
if (urlArray[2] === 'category') { if (urlArray[2] === 'category')
// Create a new URL with the dynamic part {
// window.history.pushState(null, null, url); // Create a new URL with the dynamic part
if (urlArray[3]) { // window.history.pushState(null, null, url);
loadPostsByCategory(urlArray[urlArray.length - 1]); if (urlArray[3])
return; {
} loadPostsByCategory(urlArray[urlArray.length - 1]);
return;
}
loadAllCategories(); loadAllCategories();
return; return;
} }
show404(); show404();
} }
@@ -66,80 +72,92 @@ function goToURL(url)
* @param post the post object * @param post the post object
* @returns {HTMLDivElement} the outer content of the post * @returns {HTMLDivElement} the outer content of the post
*/ */
function createLargePost(post) { function createLargePost(post)
let outerContent = document.createElement("div"); {
outerContent.classList.add("outerContent"); let outerContent = document.createElement('div');
let img = document.createElement("img"); outerContent.classList.add('outerContent');
img.className = "banner"; let img = document.createElement('img');
img.src = post.headerImg; img.className = 'banner';
img.alt = post.title; img.src = post.headerImg;
outerContent.appendChild(img); img.alt = post.title;
let content = document.createElement("div"); outerContent.appendChild(img);
content.classList.add("content"); let content = document.createElement('div');
let postContent = document.createElement("div"); content.classList.add('content');
postContent.classList.add("postContent"); let postContent = document.createElement('div');
let categories = ""; postContent.classList.add('postContent');
post.categories.split(", ").forEach(category => { let categories = '';
categories += `<a href="/blog/category/${category}" class="link">${category}</a>` post.categories.split(', ').forEach(category =>
if (post.categories.split(", ").length > 1) { {
categories += ", "; categories += `<a href="/blog/category/${category}" class="link">${category}</a>`;
} if (post.categories.split(', ').length > 1)
}); {
categories += ', ';
}
});
window.categories = categories; if (categories.endsWith(', '))
{
categories = categories.substring(0, categories.length - 2);
}
postContent.innerHTML = ` postContent.innerHTML = `
<h2>${post.title}</h2> <h2>${post.title}</h2>
<h3>Last updated: ${post.dateModified} | ${categories}</h3> <h3>Last updated: ${post.dateModified} | ${categories}</h3>
<p>${post.abstract}</p> <p>${post.abstract}</p>
<a href="/blog/post/${post.title}#disqus_thread" class="btn btnPrimary">See Post</a> <a href="/blog/post/${post.title}#disqus_thread" class="btn btnPrimary">See Post</a>
`; `;
content.appendChild(postContent); content.appendChild(postContent);
outerContent.appendChild(content); outerContent.appendChild(content);
return outerContent; return outerContent;
} }
/** /**
* Loads the home content * Loads the home content
*/ */
function loadHomeContent() { function loadHomeContent()
fetch("/api/blog/post").then(res => res.json().then(json => { {
for (let i = 0; i < json.length; i++) { fetch('/api/blog/post').then(res => res.json().then(json =>
if (json[i].featured === 1) { {
let featuredPost = document.createElement("section"); for (let i = 0; i < json.length; i++)
featuredPost.classList.add("largePost"); {
featuredPost.id = "featuredPost"; if (json[i].featured === 1)
let h1 = document.createElement("h1"); {
h1.innerHTML = "featured post"; let featuredPost = document.createElement('section');
featuredPost.appendChild(h1); featuredPost.classList.add('largePost');
let outerContent = createLargePost(json[i]); featuredPost.id = 'featuredPost';
featuredPost.appendChild(outerContent); let h1 = document.createElement('h1');
document.querySelector("#main").prepend(featuredPost); h1.innerHTML = 'featured post';
} featuredPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
featuredPost.appendChild(outerContent);
document.querySelector('#main').prepend(featuredPost);
}
if (i === 0) { if (i === 0)
let latestPost = document.createElement("section"); {
latestPost.classList.add("largePost"); let latestPost = document.createElement('section');
latestPost.id = "latestPost"; latestPost.classList.add('largePost');
let h1 = document.createElement("h1"); latestPost.id = 'latestPost';
h1.innerHTML = "latest post"; let h1 = document.createElement('h1');
latestPost.appendChild(h1); h1.innerHTML = 'latest post';
let outerContent = createLargePost(json[i]); latestPost.appendChild(h1);
latestPost.appendChild(outerContent); let outerContent = createLargePost(json[i]);
document.querySelector("#main").prepend(latestPost); latestPost.appendChild(outerContent);
} document.querySelector('#main').prepend(latestPost);
} }
})) }
}));
} }
/** /**
* Gets the latest and featured posts * Gets the latest and featured posts
* @returns {Promise<any[]>} the latest and featured posts * @returns {Promise<any[]>} the latest and featured posts
*/ */
async function getLatestAndFeaturedPosts() { async function getLatestAndFeaturedPosts()
let latestPost = await fetch("/api/blog/post/latest").then(res => res.json()); {
let featuredPost = await fetch("/api/blog/post/featured").then(res => res.json()); let latestPost = await fetch('/api/blog/post/latest').then(res => res.json());
return [latestPost, featuredPost]; let featuredPost = await fetch('/api/blog/post/featured').then(res => res.json());
return [latestPost, featuredPost];
} }
/** /**
@@ -147,80 +165,95 @@ async function getLatestAndFeaturedPosts() {
* @param text the csv text * @param text the csv text
* @returns {string[]} the array * @returns {string[]} the array
*/ */
function csvToArray(text) { function csvToArray(text)
let p = ''; {
let arr = ['']; let p = '';
let i = 0; let arr = [''];
let s = true; let i = 0;
let l = null; let s = true;
for (l of text) { let l = null;
if ('"' === l) { for (l of text)
if (s && l === p) { {
arr[i] += l; if ('"' === l)
} {
s = !s; if (s && l === p)
} else if (',' === l && s) { {
l = arr[++i] = ''; arr[i] += l;
} else if ('\n' === l && s) { }
if ('\r' === p) row[i] = row[i].slice(0, -1); s = !s;
arr = arr[++r] = [l = '']; }
i = 0; else if (',' === l && s)
} else { {
arr[i] += l; l = arr[++i] = '';
} }
p = l; else if ('\n' === l && s)
} {
return arr; if ('\r' === p)
{
row[i] = row[i].slice(0, -1);
}
arr = arr[++r] = [l = ''];
i = 0;
}
else
{
arr[i] += l;
}
p = l;
}
return arr;
} }
/** /**
* Gets the categories * Gets the categories
* @returns {Promise<*[]>} the categories * @returns {Promise<*[]>} the categories
*/ */
async function getCategories() { async function getCategories()
let categories = await fetch("/api/blog/categories").then(res => res.json()); {
let modifiedCategories = []; let categories = await fetch('/api/blog/categories').then(res => res.json());
categories.forEach(category => modifiedCategories.push(csvToArray(category.categories))); let modifiedCategories = [];
return modifiedCategories; categories.forEach(category => modifiedCategories = modifiedCategories.concat(csvToArray(category.categories.replace(/\s*,\s*/g, ','))));
return [...new Set(modifiedCategories)];
} }
/** /**
* Creates the categories * Creates the categories
* @param {*[]} categoriesList the categories * @param {*[]} categoriesList the categories
*/ */
function createCategories(categoriesList) { function createCategories(categoriesList)
let categories = ""; {
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="link">${category}</a>`)); let categories = '';
return categories; categoriesList.forEach(category => categories += `<a href="/blog/category/${category}" class="link">${category}</a>`);
return categories;
} }
/** /**
* Creates the button categories * Creates the button categories
* @param {string[][]} categoriesList - the categories * @param {string[][]} categoriesList - the categories
*/ */
function createButtonCategories(categoriesList) { function createButtonCategories(categoriesList)
let categories = ""; {
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="btn btnOutline">${category}</a>`)); let categories = '';
return categories; categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="btn btnOutline">${category}</a>`));
return categories;
} }
/** /**
* Creates the side content * Creates the side content
* @returns {HTMLElement} the aside element * @returns {HTMLElement} the aside element
*/ */
async function createSideContent() { async function createSideContent()
{
let posts = await getLatestAndFeaturedPosts(); let posts = await getLatestAndFeaturedPosts();
let latestPost = posts[0]; let latestPost = posts[0];
let featuredPost = posts[1]; let featuredPost = posts[1];
let categoriesList = await getCategories(); let categoriesList = await getCategories();
let categories = createCategories(categoriesList); let categories = createCategories(categoriesList);
let sideContent = document.createElement("aside"); let sideContent = document.createElement('aside');
sideContent.classList.add("sideContent"); sideContent.classList.add('sideContent');
sideContent.innerHTML = ` sideContent.innerHTML = `
<div class="authorInfo"> <div class="authorInfo">
<div class="picture"> <div class="picture">
<img src="/imgs/profile.jpg" <img src="/imgs/profile.jpg"
@@ -276,97 +309,141 @@ async function createSideContent() {
${categories} ${categories}
</div> </div>
`; `;
return sideContent; return sideContent;
} }
/** /**
* Trys to load the individual post if not runs the 404 function * Trys to load the individual post if not runs the 404 function
* @param title * @param title
*/ */
async function loadIndividualPost(title) { async function loadIndividualPost(title)
document.title = "Rohit Pai - " + decodeURI(title); {
await fetch(`/api/blog/post/${title}`).then(async res => { document.title = 'Rohit Pai - ' + decodeURI(title);
if (!res.ok) { await fetch(`/api/blog/post/${title}`).then(async res =>
show404(); {
return; if (!res.ok)
} {
show404();
return;
}
await res.json().then(async json => { await res.json().then(async json =>
// create the post {
let post = document.createElement("section"); // create the post
post.classList.add("post"); let post = document.createElement('section');
post.id = "individualPost"; post.classList.add('post');
let mainContent = document.createElement("div"); post.id = 'individualPost';
mainContent.classList.add("mainContent"); let mainContent = document.createElement('div');
let article = document.createElement("article"); mainContent.classList.add('mainContent');
article.innerHTML = ` let article = document.createElement('article');
article.innerHTML = `
<h1>${json.title}</h1> <h1>${json.title}</h1>
<div class="byLine"> <div class="byLine">
<h3>Last updated: ${json.dateModified}</h3> <h3>Last updated: ${json.dateModified}</h3>
<h3>${createButtonCategories([csvToArray(json.categories)])}</h3> <h3>${createButtonCategories([csvToArray(json.categories.replace(/\s*,\s*/g, ','))])}</h3>
</div> </div>
<div class="cover" style="background-image: url('${json.headerImg}')"></div> <div class="cover" style="background-image: url('${json.headerImg}')"></div>
${json.body} ${json.body}
`; `;
let comments = document.createElement("section"); let comments = document.createElement('section');
comments.classList.add("comments"); comments.classList.add('comments');
comments.innerHTML = `<h2>Comments</h2> comments.innerHTML = `<h2>Comments</h2>
<div id="disqus_thread"></div> <div id="disqus_thread"></div>
`; `;
mainContent.appendChild(article); mainContent.appendChild(article);
mainContent.appendChild(comments); mainContent.appendChild(comments);
let sideContent = await createSideContent(); let sideContent = await createSideContent();
post.appendChild(mainContent); post.appendChild(mainContent);
post.appendChild(sideContent); post.appendChild(sideContent);
document.querySelector("#main").appendChild(post); document.querySelector('#main').appendChild(post);
let disqus_config = _ => { let disqus_config = _ =>
this.page.url = window.location.href; // Replace PAGE_URL with your page's canonical URL variable {
this.page.identifier = window.location.href.substring(window.location.href.lastIndexOf("/") + 1); // Replace PAGE_IDENTIFIER with your page's unique identifier variable this.page.url = window.location.href; // Replace PAGE_URL with your page's canonical URL variable
}; this.page.identifier = window.location.href.substring(window.location.href.lastIndexOf('/') + 1); // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function () { // DON'T EDIT BELOW THIS LINE (function ()
var d = document, s = d.createElement('script'); { // DON'T EDIT BELOW THIS LINE
s.src = 'https://rohitpaiportfolio.disqus.com/embed.js'; var d = document, s = d.createElement('script');
s.setAttribute('data-timestamp', +new Date()); s.src = 'https://rohitpaiportfolio.disqus.com/embed.js';
d.body.appendChild(s); s.setAttribute('data-timestamp', +new Date());
})(); d.body.appendChild(s);
}); })();
}); });
});
} }
function loadAllCategories() { /**
* Loads all the categories
*/
async function loadAllCategories()
{
document.title = 'Rohit Pai - Categories';
let categoriesList = await getCategories();
let main = document.querySelector('#main');
let categories = document.createElement('section');
categories.classList.add('categories');
categories.id = 'allCategories';
let h1 = document.createElement('h1');
h1.innerHTML = 'Categories';
main.appendChild(h1);
for (let category of categoriesList)
{
let btnContainer = document.createElement('div');
btnContainer.classList.add('btnContainer');
let btn = document.createElement('a');
btn.classList.add('btn');
btn.classList.add('btnPrimary');
btn.innerHTML = category;
btn.href = `/blog/category/${category}`;
btnContainer.appendChild(btn);
categories.appendChild(btnContainer);
}
main.appendChild(categories);
} }
/** /**
* Loads the posts by category * Loads the posts by category
* @param category the category * @param category the category
*/ */
function loadPostsByCategory(category) { function loadPostsByCategory(category)
document.title = "Rohit Pai - " + decodeURI(category); {
fetch(`/api/blog/post/category/${category}`).then(res => res.json().then(json => { document.title = 'Rohit Pai - ' + decodeURI(category);
let posts = document.createElement("section"); fetch(`/api/blog/categories/${category}`).then(res => res.json().then(json =>
posts.classList.add("posts"); {
posts.id = "postsByCategory"; let main = document.querySelector('#main');
let h1 = document.createElement("h1"); let posts = document.createElement('section');
h1.innerHTML = category; posts.classList.add('catPosts');
posts.appendChild(h1); posts.id = 'postsByCategory';
for (let i = 0; i < json.length; i++) { let h1 = document.createElement('h1');
let outerContent = createLargePost(json[i]); h1.innerHTML = decodeURI(category);
posts.appendChild(outerContent); main.appendChild(h1);
} for (let i = 0; i < json.length; i++)
document.querySelector("#main").appendChild(posts); {
})); let largePost = document.createElement('section');
largePost.classList.add('largePost');
if (i < json.length - 1)
{
largePost.classList.add('categoryPost');
}
largePost.id = `lp-${i + 1}`;
let outerContent = createLargePost(json[i]);
largePost.appendChild(outerContent);
posts.appendChild(largePost);
}
main.appendChild(posts);
}));
} }
/** /**
* Shows the 404 page * Shows the 404 page
*/ */
function show404() { function show404()
document.querySelector("#main").innerHTML = ` {
document.querySelector('#main').innerHTML = `
<div class="error"> <div class="error">
<div class="fof"> <div class="fof">
<h1>Blog post, Category or page not found</h1> <h1>Blog post, Category or page not found</h1>
+2 -2
View File
@@ -263,7 +263,7 @@ document.querySelector("#addPostForm").addEventListener("submit", e =>
data.append("abstract", document.querySelector("#postAbstract").value); data.append("abstract", document.querySelector("#postAbstract").value);
data.append("body", editors["CKEditorAddPost"].getData()); data.append("body", editors["CKEditorAddPost"].getData());
data.append("dateCreated", new Date().toISOString().slice(0, 19).replace('T', ' ')); data.append("dateCreated", new Date().toISOString().slice(0, 19).replace('T', ' '));
data.append("categories", document.querySelector("#postCategories").value); data.append('categories', document.querySelector('#postCategories').value.toLowerCase());
data.append("headerImg", document.querySelector("#headerImg").files[0]); data.append("headerImg", document.querySelector("#headerImg").files[0]);
fetch("/api/blog/post", { fetch("/api/blog/post", {
@@ -316,7 +316,7 @@ document.querySelector("#editPostForm").addEventListener("submit", e =>
data["abstract"] = document.querySelector("#editPostAbstract").value; data["abstract"] = document.querySelector("#editPostAbstract").value;
data["body"] = editors["CKEditorEditPost"].getData(); data["body"] = editors["CKEditorEditPost"].getData();
data["dateModified"] = new Date().toISOString().slice(0, 19).replace('T', ' '); data["dateModified"] = new Date().toISOString().slice(0, 19).replace('T', ' ');
data["categories"] = document.querySelector("#editPostCategories").value; data['categories'] = document.querySelector('#editPostCategories').value.toLowerCase();
let imgData = new FormData(); let imgData = new FormData();
imgData.append("headerImg", document.querySelector("#editHeaderImg").files[0]); imgData.append("headerImg", document.querySelector("#editHeaderImg").files[0]);