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
namespace api\blog;
use api\utils\imgUtils;
use DOMDocument;
use PDO;
@ -125,7 +127,7 @@ class blogData
public function getCategories(): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT categories FROM blog;");
$stmt = $conn->prepare("SELECT DISTINCT categories FROM blog;");
$stmt->execute();
// set the resulting array to associative
@ -415,7 +417,7 @@ class blogData
}
else
{
rename($from . $file,$to . $file);
rename($from . $file, $to . $file);
}
}
}
@ -430,17 +432,15 @@ class blogData
/**
* 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
*/
public function getPostsByCategory(mixed $category)
public function getPostsByCategory(string $category): array
{
$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->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
{
private blogData $blogData;
/**
* 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
@ -134,14 +135,14 @@ class blogRoutes implements routesInterface
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
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
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")
{
@ -218,7 +219,7 @@ class blogRoutes implements routesInterface
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
$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)
{
$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")));
return $response->withStatus(400);

View File

@ -1,5 +1,7 @@
<?php
namespace api\project;
use api\utils\imgUtils;
use PDO;
use Psr\Http\Message\UploadedFileInterface;
@ -162,7 +164,7 @@ class projectData
* @param UploadedFileInterface $img - Image preview of the project
* @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();

View File

@ -1,4 +1,5 @@
<?php
namespace api\project;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "projectData.php";
@ -37,7 +38,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write($json);
if(array_key_exists("errorMessage", $result))
if (array_key_exists("errorMessage", $result))
{
$response->withStatus(404);
}

View File

@ -31,7 +31,7 @@ class timelineData
}
return array("errorMessage" => "Error, edu data not found");
}
/**
* Get all work data
* @return array - Array of all work data or error message
@ -178,7 +178,7 @@ class timelineData
return "error";
}
/**
* Create new education data
* @param string $dateFrom - Start date
@ -202,7 +202,7 @@ class timelineData
}
return false;
}
/**
* Create new work data
* @param string $dateFrom - Start date

View File

@ -1,4 +1,5 @@
<?php
namespace api\timeline;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "timelineData.php";
@ -39,7 +40,7 @@ class timelineRoutes implements routesInterface
return $response;
}
if($args["timeline"] == "work")
if ($args["timeline"] == "work")
{
$response->getBody()->write(json_encode($this->timelineData->getWorkData()));
return $response;

View File

@ -1,5 +1,7 @@
<?php
namespace api\user;
use Firebase\JWT\JWT;
use PDO;
@ -54,7 +56,7 @@ class userData
"exp" => $future
];
return JWT::encode($payload,$secretKey,"HS256");
return JWT::encode($payload, $secretKey, "HS256");
}
/**
@ -108,7 +110,7 @@ class userData
</body>
</html>
";
mail($email, "Reset Password Verification Code", $message, $headers1);
return $token;
}
@ -116,7 +118,7 @@ class userData
/**
* Change password for an email with new password
* @param $email string Email
* @param $password string Password
* @param $password string Password
* @return bool - true if the password was changed, false if not
*/
public function changePassword(string $email, string $password): bool

View File

@ -1,6 +1,7 @@
<?php
namespace api\user;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "userData.php";
use api\utils\routesInterface;

View File

@ -45,7 +45,7 @@ class middleware
$app->addBodyParsingMiddleware();
$app->addRoutingMiddleware();
}
/**
* SameSite Cookie Configuration
* @param App $app - Slim App
@ -68,7 +68,7 @@ class middleware
return $response->withHeader("Content-Type", "application/json");
});
}
/**
* JWT Authentication
* @param App $app - Slim App
@ -131,5 +131,5 @@ class middleware
$app->addErrorMiddleware(true, true, true);
}
}

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
namespace api\blog;
use api\utils\imgUtils;
use DOMDocument;
use PDO;
@ -91,7 +93,8 @@ class blogData
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
if ($result)
{
return $result;
}
@ -113,7 +116,8 @@ class blogData
// set the resulting array to associative
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result) {
if ($result)
{
return $result;
}
@ -123,7 +127,7 @@ class blogData
public function getCategories(): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT categories FROM blog;");
$stmt = $conn->prepare("SELECT DISTINCT categories FROM blog;");
$stmt->execute();
// set the resulting array to associative
@ -428,17 +432,15 @@ class blogData
/**
* 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
*/
public function getPostsByCategory(mixed $category)
public function getPostsByCategory(string $category): array
{
$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->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
{
private blogData $blogData;
/**
* 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
@ -29,9 +30,11 @@ class blogRoutes implements routesInterface
*/
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();
if (array_key_exists("errorMessage", $post)) {
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
@ -40,10 +43,13 @@ class blogRoutes implements routesInterface
return $response;
});
$app->get("/blog/categories/{category}", function (Request $request, Response $response, $args) {
if ($args["category"] != null) {
$app->get("/blog/categories/{category}", function (Request $request, Response $response, $args)
{
if ($args["category"] != null)
{
$post = $this->blogData->getPostsByCategory($args["category"]);
if (array_key_exists("errorMessage", $post)) {
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
@ -72,11 +78,15 @@ class blogRoutes implements routesInterface
return $response;
});
$app->get("/blog/post/{type}", function (Request $request, Response $response, $args) {
if ($args["type"] != null) {
if ($args["type"] == "latest") {
$app->get("/blog/post/{type}", function (Request $request, Response $response, $args)
{
if ($args["type"] != null)
{
if ($args["type"] == "latest")
{
$post = $this->blogData->getLatestBlogPost();
if (array_key_exists("errorMessage", $post)) {
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
@ -85,9 +95,11 @@ class blogRoutes implements routesInterface
return $response;
}
if ($args["type"] == "featured") {
if ($args["type"] == "featured")
{
$post = $this->blogData->getFeaturedBlogPost();
if (array_key_exists("errorMessage", $post)) {
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
@ -123,7 +135,8 @@ class blogRoutes implements routesInterface
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
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
return $response->withStatus(400);
@ -206,7 +219,8 @@ class blogRoutes implements routesInterface
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
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
return $response->withStatus(400);

View File

@ -39,7 +39,7 @@ new userRoutes($app);
$app->post("/contact", function (Request $request, Response $response)
{
$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")));
return $response->withStatus(400);

View File

@ -1,5 +1,7 @@
<?php
namespace api\project;
use api\utils\imgUtils;
use PDO;
use Psr\Http\Message\UploadedFileInterface;
@ -162,7 +164,7 @@ class projectData
* @param UploadedFileInterface $img - Image preview of the project
* @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();

View File

@ -1,4 +1,5 @@
<?php
namespace api\project;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "projectData.php";
@ -37,7 +38,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write($json);
if(array_key_exists("errorMessage", $result))
if (array_key_exists("errorMessage", $result))
{
$response->withStatus(404);
}

View File

@ -31,7 +31,7 @@ class timelineData
}
return array("errorMessage" => "Error, edu data not found");
}
/**
* Get all work data
* @return array - Array of all work data or error message
@ -178,7 +178,7 @@ class timelineData
return "error";
}
/**
* Create new education data
* @param string $dateFrom - Start date
@ -202,7 +202,7 @@ class timelineData
}
return false;
}
/**
* Create new work data
* @param string $dateFrom - Start date

View File

@ -1,4 +1,5 @@
<?php
namespace api\timeline;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "timelineData.php";
@ -39,7 +40,7 @@ class timelineRoutes implements routesInterface
return $response;
}
if($args["timeline"] == "work")
if ($args["timeline"] == "work")
{
$response->getBody()->write(json_encode($this->timelineData->getWorkData()));
return $response;

View File

@ -1,5 +1,7 @@
<?php
namespace api\user;
use Firebase\JWT\JWT;
use PDO;
@ -54,7 +56,7 @@ class userData
"exp" => $future
];
return JWT::encode($payload,$secretKey,"HS256");
return JWT::encode($payload, $secretKey, "HS256");
}
/**
@ -108,7 +110,7 @@ class userData
</body>
</html>
";
mail($email, "Reset Password Verification Code", $message, $headers1);
return $token;
}
@ -116,7 +118,7 @@ class userData
/**
* Change password for an email with new password
* @param $email string Email
* @param $password string Password
* @param $password string Password
* @return bool - true if the password was changed, false if not
*/
public function changePassword(string $email, string $password): bool

View File

@ -1,6 +1,7 @@
<?php
namespace api\user;
require_once __DIR__ . "/../utils/routesInterface.php";
require_once __DIR__ . "/../utils/routesInterface.php";
require_once "userData.php";
use api\utils\routesInterface;

View File

@ -45,7 +45,7 @@ class middleware
$app->addBodyParsingMiddleware();
$app->addRoutingMiddleware();
}
/**
* SameSite Cookie Configuration
* @param App $app - Slim App
@ -68,7 +68,7 @@ class middleware
return $response->withHeader("Content-Type", "application/json");
});
}
/**
* JWT Authentication
* @param App $app - Slim App
@ -131,5 +131,5 @@ class middleware
$app->addErrorMiddleware(true, true, true);
}
}

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;
}
section.largePost:first-child {
section.largePost:not(:last-child) {
border-bottom: 5px solid var(--mutedGrey);
}

View File

@ -7,5 +7,6 @@
@import "../../css/footer.css";
@import "blogPosts.css";
@import "home.css";
@import "category.css";
@import "prism.css";

View File

@ -3,21 +3,25 @@ const scrollLimit = 150;
document.addEventListener('DOMContentLoaded', () =>
{
goToURL(window.location.pathname);
goToURL(window.location.pathname);
});
window.addEventListener('popstate', _ =>
{
goToURL(window.history.state);
goToURL(window.history.state);
});
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) {
document.querySelector("nav").classList.add("scrolled");
} else {
document.querySelector("nav").classList.remove("scrolled");
}
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)
{
document.querySelector('nav').classList.add('scrolled');
}
else
{
document.querySelector('nav').classList.remove('scrolled');
}
};
/**
@ -26,39 +30,41 @@ window.onscroll = () => {
*/
function goToURL(url)
{
// Get the current URL and split it into an array
let urlArray = url.split('/');
if (url === "/blog/" || url === "/blog")
{
loadHomeContent();
// window.history.pushState(null, null, url);
return;
}
// Check if the URL is a post page
if (urlArray[2] === 'post')
{
// Create a new URL with the dynamic part
// window.history.pushState(null, null, url);
loadIndividualPost(urlArray[urlArray.length - 1]).catch(err => console.log(err));
return;
}
if (urlArray[2] === 'category') {
// Create a new URL with the dynamic part
// window.history.pushState(null, null, url);
if (urlArray[3]) {
loadPostsByCategory(urlArray[urlArray.length - 1]);
return;
}
loadAllCategories();
return;
}
show404();
// Get the current URL and split it into an array
let urlArray = url.split('/');
if (url === '/blog/' || url === '/blog')
{
loadHomeContent();
// window.history.pushState(null, null, url);
return;
}
// Check if the URL is a post page
if (urlArray[2] === 'post')
{
// Create a new URL with the dynamic part
// window.history.pushState(null, null, url);
loadIndividualPost(urlArray[urlArray.length - 1]).catch(err => console.log(err));
return;
}
if (urlArray[2] === 'category')
{
// Create a new URL with the dynamic part
// window.history.pushState(null, null, url);
if (urlArray[3])
{
loadPostsByCategory(urlArray[urlArray.length - 1]);
return;
}
loadAllCategories();
return;
}
show404();
}
/**
@ -66,80 +72,92 @@ function goToURL(url)
* @param post the post object
* @returns {HTMLDivElement} the outer content of the post
*/
function createLargePost(post) {
let outerContent = document.createElement("div");
outerContent.classList.add("outerContent");
let img = document.createElement("img");
img.className = "banner";
img.src = post.headerImg;
img.alt = post.title;
outerContent.appendChild(img);
let content = document.createElement("div");
content.classList.add("content");
let postContent = document.createElement("div");
postContent.classList.add("postContent");
let categories = "";
post.categories.split(", ").forEach(category => {
categories += `<a href="/blog/category/${category}" class="link">${category}</a>`
if (post.categories.split(", ").length > 1) {
categories += ", ";
}
});
window.categories = categories;
postContent.innerHTML = `
function createLargePost(post)
{
let outerContent = document.createElement('div');
outerContent.classList.add('outerContent');
let img = document.createElement('img');
img.className = 'banner';
img.src = post.headerImg;
img.alt = post.title;
outerContent.appendChild(img);
let content = document.createElement('div');
content.classList.add('content');
let postContent = document.createElement('div');
postContent.classList.add('postContent');
let categories = '';
post.categories.split(', ').forEach(category =>
{
categories += `<a href="/blog/category/${category}" class="link">${category}</a>`;
if (post.categories.split(', ').length > 1)
{
categories += ', ';
}
});
if (categories.endsWith(', '))
{
categories = categories.substring(0, categories.length - 2);
}
postContent.innerHTML = `
<h2>${post.title}</h2>
<h3>Last updated: ${post.dateModified} | ${categories}</h3>
<p>${post.abstract}</p>
<a href="/blog/post/${post.title}#disqus_thread" class="btn btnPrimary">See Post</a>
`;
content.appendChild(postContent);
outerContent.appendChild(content);
return outerContent;
content.appendChild(postContent);
outerContent.appendChild(content);
return outerContent;
}
/**
* Loads the home content
*/
function loadHomeContent() {
fetch("/api/blog/post").then(res => res.json().then(json => {
for (let i = 0; i < json.length; i++) {
if (json[i].featured === 1) {
let featuredPost = document.createElement("section");
featuredPost.classList.add("largePost");
featuredPost.id = "featuredPost";
let h1 = document.createElement("h1");
h1.innerHTML = "featured post";
featuredPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
featuredPost.appendChild(outerContent);
document.querySelector("#main").prepend(featuredPost);
}
if (i === 0) {
let latestPost = document.createElement("section");
latestPost.classList.add("largePost");
latestPost.id = "latestPost";
let h1 = document.createElement("h1");
h1.innerHTML = "latest post";
latestPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
latestPost.appendChild(outerContent);
document.querySelector("#main").prepend(latestPost);
}
}
}))
function loadHomeContent()
{
fetch('/api/blog/post').then(res => res.json().then(json =>
{
for (let i = 0; i < json.length; i++)
{
if (json[i].featured === 1)
{
let featuredPost = document.createElement('section');
featuredPost.classList.add('largePost');
featuredPost.id = 'featuredPost';
let h1 = document.createElement('h1');
h1.innerHTML = 'featured post';
featuredPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
featuredPost.appendChild(outerContent);
document.querySelector('#main').prepend(featuredPost);
}
if (i === 0)
{
let latestPost = document.createElement('section');
latestPost.classList.add('largePost');
latestPost.id = 'latestPost';
let h1 = document.createElement('h1');
h1.innerHTML = 'latest post';
latestPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
latestPost.appendChild(outerContent);
document.querySelector('#main').prepend(latestPost);
}
}
}));
}
/**
* Gets the latest and featured posts
* @returns {Promise<any[]>} the latest and featured posts
*/
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());
return [latestPost, featuredPost];
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());
return [latestPost, featuredPost];
}
/**
@ -147,80 +165,95 @@ async function getLatestAndFeaturedPosts() {
* @param text the csv text
* @returns {string[]} the array
*/
function csvToArray(text) {
let p = '';
let arr = [''];
let i = 0;
let s = true;
let l = null;
for (l of text) {
if ('"' === l) {
if (s && l === p) {
arr[i] += l;
}
s = !s;
} else if (',' === l && s) {
l = arr[++i] = '';
} else if ('\n' === l && s) {
if ('\r' === p) row[i] = row[i].slice(0, -1);
arr = arr[++r] = [l = ''];
i = 0;
} else {
arr[i] += l;
}
p = l;
}
return arr;
function csvToArray(text)
{
let p = '';
let arr = [''];
let i = 0;
let s = true;
let l = null;
for (l of text)
{
if ('"' === l)
{
if (s && l === p)
{
arr[i] += l;
}
s = !s;
}
else if (',' === l && s)
{
l = arr[++i] = '';
}
else if ('\n' === l && s)
{
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
* @returns {Promise<*[]>} the categories
*/
async function getCategories() {
let categories = await fetch("/api/blog/categories").then(res => res.json());
let modifiedCategories = [];
categories.forEach(category => modifiedCategories.push(csvToArray(category.categories)));
return modifiedCategories;
async function getCategories()
{
let categories = await fetch('/api/blog/categories').then(res => res.json());
let modifiedCategories = [];
categories.forEach(category => modifiedCategories = modifiedCategories.concat(csvToArray(category.categories.replace(/\s*,\s*/g, ','))));
return [...new Set(modifiedCategories)];
}
/**
* Creates the categories
* @param {*[]} categoriesList the categories
*/
function createCategories(categoriesList) {
let categories = "";
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="link">${category}</a>`));
return categories;
function createCategories(categoriesList)
{
let categories = '';
categoriesList.forEach(category => categories += `<a href="/blog/category/${category}" class="link">${category}</a>`);
return categories;
}
/**
* Creates the button categories
* @param {string[][]} categoriesList - the categories
*/
function createButtonCategories(categoriesList) {
let categories = "";
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="btn btnOutline">${category}</a>`));
return categories;
function createButtonCategories(categoriesList)
{
let categories = '';
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="btn btnOutline">${category}</a>`));
return categories;
}
/**
* Creates the side content
* @returns {HTMLElement} the aside element
*/
async function createSideContent() {
let posts = await getLatestAndFeaturedPosts();
let latestPost = posts[0];
let featuredPost = posts[1];
let categoriesList = await getCategories();
let categories = createCategories(categoriesList);
let sideContent = document.createElement("aside");
sideContent.classList.add("sideContent");
sideContent.innerHTML = `
async function createSideContent()
{
let posts = await getLatestAndFeaturedPosts();
let latestPost = posts[0];
let featuredPost = posts[1];
let categoriesList = await getCategories();
let categories = createCategories(categoriesList);
let sideContent = document.createElement('aside');
sideContent.classList.add('sideContent');
sideContent.innerHTML = `
<div class="authorInfo">
<div class="picture">
<img src="/imgs/profile.jpg"
@ -276,97 +309,141 @@ async function createSideContent() {
${categories}
</div>
`;
return sideContent;
return sideContent;
}
/**
* Trys to load the individual post if not runs the 404 function
* @param title
*/
async function loadIndividualPost(title) {
document.title = "Rohit Pai - " + decodeURI(title);
await fetch(`/api/blog/post/${title}`).then(async res => {
if (!res.ok) {
show404();
return;
}
await res.json().then(async json => {
// create the post
let post = document.createElement("section");
post.classList.add("post");
post.id = "individualPost";
let mainContent = document.createElement("div");
mainContent.classList.add("mainContent");
let article = document.createElement("article");
article.innerHTML = `
async function loadIndividualPost(title)
{
document.title = 'Rohit Pai - ' + decodeURI(title);
await fetch(`/api/blog/post/${title}`).then(async res =>
{
if (!res.ok)
{
show404();
return;
}
await res.json().then(async json =>
{
// create the post
let post = document.createElement('section');
post.classList.add('post');
post.id = 'individualPost';
let mainContent = document.createElement('div');
mainContent.classList.add('mainContent');
let article = document.createElement('article');
article.innerHTML = `
<h1>${json.title}</h1>
<div class="byLine">
<h3>Last updated: ${json.dateModified}</h3>
<h3>${createButtonCategories([csvToArray(json.categories)])}</h3>
<h3>${createButtonCategories([csvToArray(json.categories.replace(/\s*,\s*/g, ','))])}</h3>
</div>
<div class="cover" style="background-image: url('${json.headerImg}')"></div>
${json.body}
`;
let comments = document.createElement("section");
comments.classList.add("comments");
comments.innerHTML = `<h2>Comments</h2>
let comments = document.createElement('section');
comments.classList.add('comments');
comments.innerHTML = `<h2>Comments</h2>
<div id="disqus_thread"></div>
`;
mainContent.appendChild(article);
mainContent.appendChild(comments);
let sideContent = await createSideContent();
post.appendChild(mainContent);
post.appendChild(sideContent);
document.querySelector("#main").appendChild(post);
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
};
(function () { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://rohitpaiportfolio.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
d.body.appendChild(s);
})();
});
});
mainContent.appendChild(article);
mainContent.appendChild(comments);
let sideContent = await createSideContent();
post.appendChild(mainContent);
post.appendChild(sideContent);
document.querySelector('#main').appendChild(post);
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
};
(function ()
{ // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://rohitpaiportfolio.disqus.com/embed.js';
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
* @param category the category
*/
function loadPostsByCategory(category) {
document.title = "Rohit Pai - " + decodeURI(category);
fetch(`/api/blog/post/category/${category}`).then(res => res.json().then(json => {
let posts = document.createElement("section");
posts.classList.add("posts");
posts.id = "postsByCategory";
let h1 = document.createElement("h1");
h1.innerHTML = category;
posts.appendChild(h1);
for (let i = 0; i < json.length; i++) {
let outerContent = createLargePost(json[i]);
posts.appendChild(outerContent);
}
document.querySelector("#main").appendChild(posts);
}));
function loadPostsByCategory(category)
{
document.title = 'Rohit Pai - ' + decodeURI(category);
fetch(`/api/blog/categories/${category}`).then(res => res.json().then(json =>
{
let main = document.querySelector('#main');
let posts = document.createElement('section');
posts.classList.add('catPosts');
posts.id = 'postsByCategory';
let h1 = document.createElement('h1');
h1.innerHTML = decodeURI(category);
main.appendChild(h1);
for (let i = 0; i < json.length; i++)
{
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
*/
function show404() {
document.querySelector("#main").innerHTML = `
function show404()
{
document.querySelector('#main').innerHTML = `
<div class="error">
<div class="fof">
<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("body", editors["CKEditorAddPost"].getData());
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]);
fetch("/api/blog/post", {
@ -316,7 +316,7 @@ document.querySelector("#editPostForm").addEventListener("submit", e =>
data["abstract"] = document.querySelector("#editPostAbstract").value;
data["body"] = editors["CKEditorEditPost"].getData();
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();
imgData.append("headerImg", document.querySelector("#editHeaderImg").files[0]);