categories-page #44

Merged
rodude123 merged 3 commits from categories-page into master 2023-10-24 15:54:21 +01:00
28 changed files with 372 additions and 279 deletions
Showing only changes of commit a0567a25f5 - Show all commits

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

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

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");
} }
/** /**
@ -108,7 +110,7 @@ class userData
</body> </body>
</html> </html>
"; ";
mail($email, "Reset Password Verification Code", $message, $headers1); mail($email, "Reset Password Verification Code", $message, $headers1);
return $token; return $token;
} }
@ -116,7 +118,7 @@ class userData
/** /**
* Change password for an email with new password * Change password for an email with new password
* @param $email string Email * @param $email string Email
* @param $password string Password * @param $password string Password
* @return bool - true if the password was changed, false if not * @return bool - true if the password was changed, false if not
*/ */
public function changePassword(string $email, string $password): bool public function changePassword(string $email, string $password): bool

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;

View File

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

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

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");
} }
/** /**
@ -108,7 +110,7 @@ class userData
</body> </body>
</html> </html>
"; ";
mail($email, "Reset Password Verification Code", $message, $headers1); mail($email, "Reset Password Verification Code", $message, $headers1);
return $token; return $token;
} }
@ -116,7 +118,7 @@ class userData
/** /**
* Change password for an email with new password * Change password for an email with new password
* @param $email string Email * @param $email string Email
* @param $password string Password * @param $password string Password
* @return bool - true if the password was changed, false if not * @return bool - true if the password was changed, false if not
*/ */
public function changePassword(string $email, string $password): bool public function changePassword(string $email, string $password): bool

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;

View File

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

View File

@ -0,0 +1,8 @@
main > h1 {
padding-left: 3em;
}
section.catPosts .largePost {
margin-bottom: 3em;
}

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,39 +30,41 @@ 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(); }
return;
} loadAllCategories();
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; }
});
postContent.innerHTML = `
if (categories.endsWith(', '))
{
categories = categories.substring(0, categories.length - 2);
}
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]);
if (i === 0) { featuredPost.appendChild(outerContent);
let latestPost = document.createElement("section"); document.querySelector('#main').prepend(featuredPost);
latestPost.classList.add("largePost"); }
latestPost.id = "latestPost";
let h1 = document.createElement("h1"); if (i === 0)
h1.innerHTML = "latest post"; {
latestPost.appendChild(h1); let latestPost = document.createElement('section');
let outerContent = createLargePost(json[i]); latestPost.classList.add('largePost');
latestPost.appendChild(outerContent); latestPost.id = 'latestPost';
document.querySelector("#main").prepend(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 * 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 latestPost = posts[0]; let posts = await getLatestAndFeaturedPosts();
let featuredPost = posts[1]; let latestPost = posts[0];
let categoriesList = await getCategories(); let featuredPost = posts[1];
let categories = createCategories(categoriesList); let categoriesList = await getCategories();
let categories = createCategories(categoriesList);
let sideContent = document.createElement("aside");
sideContent.classList.add("sideContent"); let sideContent = document.createElement('aside');
sideContent.innerHTML = ` sideContent.classList.add('sideContent');
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,68 +309,75 @@ 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();
await res.json().then(async json => { return;
// create the post }
let post = document.createElement("section");
post.classList.add("post"); await res.json().then(async json =>
post.id = "individualPost"; {
let mainContent = document.createElement("div"); // create the post
mainContent.classList.add("mainContent"); let post = document.createElement('section');
let article = document.createElement("article"); post.classList.add('post');
article.innerHTML = ` post.id = 'individualPost';
let mainContent = document.createElement('div');
mainContent.classList.add('mainContent');
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
var d = document, s = d.createElement('script'); (function ()
s.src = 'https://rohitpaiportfolio.disqus.com/embed.js'; { // DON'T EDIT BELOW THIS LINE
s.setAttribute('data-timestamp', +new Date()); var d = document, s = d.createElement('script');
d.body.appendChild(s); s.src = 'https://rohitpaiportfolio.disqus.com/embed.js';
})(); s.setAttribute('data-timestamp', +new Date());
}); d.body.appendChild(s);
}); })();
});
});
} }
function loadAllCategories() { function loadAllCategories()
{
} }
@ -345,28 +385,41 @@ function loadAllCategories() {
* 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]);