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

Reviewed-on: #44
This commit is contained in:
Rohit Pai 2023-10-24 15:54:20 +01:00
commit a697ea2ac8
28 changed files with 410 additions and 280 deletions

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);
} }
} }

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")));

2
dist/api/index.php vendored
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);

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();

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);
} }

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;

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");
} }
/** /**

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;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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);
} }
} }

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);

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);

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();

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);
} }

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;

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");
} }
/** /**

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
src/blog/css/category.css Normal file
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%
}

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);
} }

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";

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>

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]);