Merge pull request 'edit-delete-post' (#36) from edit-delete-post into master

Reviewed-on: #36
This commit is contained in:
Rohit Pai 2023-07-12 03:39:51 +01:00
commit 24dfe885bd
27 changed files with 1550 additions and 316 deletions

View File

@ -21,7 +21,8 @@ class blogData
public function getBlogPosts(): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT ID, title, dateCreated, dateModified, body, categories FROM blog ORDER BY dateCreated DESC;");
$stmt = $conn->prepare("SELECT ID, title, dateCreated, dateModified, body, categories, featured
FROM blog ORDER BY dateCreated;");
$stmt->execute();
// set the resulting array to associative
@ -99,6 +100,164 @@ class blogData
return array("errorMessage" => "Error, blog post could not found");
}
/**
* Delete a blog post with the given ID
* @param int $ID - ID of the blog post to delete
* @return string - Success or error message
*/
public function deletePost(int $ID): string
{
$conn = dbConn();
$stmtCheckPost = $conn->prepare("SELECT * FROM blog WHERE ID = :ID");
$stmtCheckPost->bindParam(":ID", $ID);
$stmtCheckPost->execute();
$result = $stmtCheckPost->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "post not found";
}
if ($result["featured"] === 1)
{
return "cannot delete";
}
$stmt = $conn->prepare("DELETE FROM blog WHERE ID = :ID");
$stmt->bindParam(":ID", $ID);
if ($stmt->execute())
{
$imagUtils = new imgUtils();
$imagUtils->deleteDirectory("../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/");
return "success";
}
return "error";
}
/**
* Update the blog post with the given ID
* @param int $ID - ID of the blog post to update
* @param string $title - Title of the blog post
* @param bool $featured - Whether the blog post is featured or not
* @param string $body - Body of the blog post
* @param string $dateModified - Date the blog post was modified
* @param string $categories - Categories of the blog post
* @return bool|string - Success or error message
*/
public function updatePost(int $ID, string $title, bool $featured, string $body, string $dateModified, string $categories): bool|string
{
$conn = dbConn();
$stmtCheckPost = $conn->prepare("SELECT * FROM blog WHERE ID = :ID");
$stmtCheckPost->bindParam(":ID", $ID);
$stmtCheckPost->execute();
$result = $stmtCheckPost->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "post not found";
}
if (!$featured && $result["featured"] === 1)
{
return "unset feature";
}
if ($featured)
{
$stmtUnsetFeatured = $conn->prepare("UPDATE blog SET featured = 0 WHERE featured = 1;");
$stmtUnsetFeatured->execute();
}
$to = "../blog/imgs/" . $title . "_" . $result["folderID"] . "/";
if ($result["title"] !== $title)
{
$from = "../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/";
mkdir($to, 0777, true);
rename($result["headerImg"], $to . basename($result["headerImg"]));
$body = $this->changeHTMLSrc($body, $to, $from);
rmdir($from);
}
$from = "../blog/imgs/tmp/";
$newBody = $this->changeHTMLSrc($body, $to, $from);
$stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, body = :body, dateModified = :dateModified, categories = :categories WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":title", $title);
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":dateModified", $dateModified);
$stmt->bindParam(":categories", $categories);
return $stmt->execute();
}
/**
* Creates a new post di rectory, uploads the header image and moves the images from the
* temp folder to the new folder, then updates the post html to point to the new images, finally
* it creates the post in the database
* @param string $title - Title of the blog post
* @param string $body - Body of the blog post
* @param string $dateCreated - Date the blog post was created
* @param bool $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message
*/
public function createPost(string $title, string $body, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string
{
$conn = dbConn();
$folderID = uniqid();
$targetFile = array("imgLocation" => "../blog/imgs/placeholder.png");
$targetDir = "../blog/imgs/" . $title . "_" . $folderID . "/";
mkdir($targetDir, 0777, true);
if ($headerImg !== null)
{
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $headerImg);
}
if (!is_array($targetFile))
{
return $targetFile;
}
$newBody = $this->changeHTMLSrc($body, $targetDir, "../blog/imgs/tmp/");
if ($featured)
{
$stmtMainProject = $conn->prepare("UPDATE blog SET featured = 0 WHERE featured = 1;");
$stmtMainProject->execute();
}
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, body, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :body, :categories, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $isFeatured);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID);
if ($stmt->execute())
{
return intval($conn->lastInsertId());
}
return "Error, couldn't create post";
}
/**
* Upload the images in the post to temp folder and return image location
* @param UploadedFileInterface $img - Image to upload
@ -131,37 +290,60 @@ class blogData
}
/**
* Creates a new post directory, uploads the header image and moves the images from the
* temp folder to the new folder, then updates the post html to point to the new images, finally
* it creates the post in the database
* @param string $title - Title of the blog post
* @param string $body - Body of the blog post
* @param string $dateCreated - Date the blog post was created
* @param string $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message
* Upload the header image of the post and update the database
* @param int $ID - ID of the post
* @param UploadedFileInterface $img - Image to upload
* @return string|array - String with error message or array with the location of the uploaded file
*/
public function createPost(string $title, string $body, string $dateCreated, string $featured, string $categories, UploadedFileInterface $headerImg): int|string
public function uploadHeaderImage(int $ID, UploadedFileInterface $img): string|array
{
$conn = dbConn();
$targetFile = "";
$folderID = uniqid();
if ($headerImg !== null)
$stmt = $conn->prepare("SELECT * FROM blog WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
$targetDir = "../blog/imgs/" . $title . "_" . $folderID . "/";
mkdir($targetDir, 0777, true);
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $headerImg);
return "Couldn't find the post";
}
$targetFile = array("imgLocation" => ".../blog/imgs/placeholder.png");
$targetDir = "../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/";
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $img);
if (!is_array($targetFile))
{
return $targetFile;
}
if (file_exists($targetFile["imgLocation"]))
{
unlink($result["headerImg"]);
$stmt = $conn->prepare("UPDATE blog SET headerImg = :headerImg WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->execute();
if ($stmt->rowCount() > 0)
{
return $targetFile;
}
return "Couldn't update the post";
}
return "Couldn't upload the image";
}
/**
* Change the HTML src of the images in the post to point to the new location
* @param string $body - Body of the post
* @param string $to - New location of the images
* @param string $from - Old location of the images
* @return string - Body of the post with the new image locations
*/
public function changeHTMLSrc(string $body, string $to, string $from): string
{
$htmlDoc = new DOMDocument();
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
@ -172,24 +354,25 @@ class blogData
foreach ($imgs as $img)
{
$src = $img->getAttribute("src");
$src = urldecode($src);
$srcList[] = $src;
$fileName = basename($src);
$img->setAttribute("src", $targetDir . $fileName);
$img->setAttribute("src", $to . $fileName);
}
$files = scandir("../blog/imgs/tmp/");
$files = scandir($from);
foreach ($files as $file)
{
if ($file != "." && $file != "..")
{
if (!in_array("../blog/imgs/tmp/" . $file, $srcList))
if (!in_array($from . $file, $srcList))
{
unlink("../blog/imgs/tmp/" . $file);
unlink($from . $file);
}
else
{
rename("../blog/imgs/tmp/" . $file, $targetDir . $file);
rename($from . $file, $to . $file);
}
}
}
@ -199,23 +382,6 @@ class blogData
{
$newBody .= $htmlDoc->saveHTML($node);
}
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, body, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :body, :categories, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID);
if ($stmt->execute())
{
return intval($conn->lastInsertId());
}
return "Error, couldn't create post";
return $newBody;
}
}

View File

@ -29,12 +29,122 @@ class blogRoutes implements routesInterface
*/
public function createRoutes(App $app): void
{
$app->post("/blog/post", function (Request $request, Response $response, array $args)
$app->get("/blog/post", function (Request $request, Response $response)
{
$posts = $this->blogData->getBlogPosts();
$json = json_encode($posts);
$response->getBody()->write($json);
if (array_key_exists("errorMessage", $posts))
{
$response->withStatus(404);
}
return $response;
});
$app->get("/blog/post/{id}", function (Request $request, Response $response, $args)
{
if ($args["id"] != null)
{
$post = $this->blogData->getBlogPost($args["id"]);
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
$response->getBody()->write(json_encode($post));
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->patch("/blog/post/{id}", function (Request $request, Response $response, $args)
{
$data = $request->getParsedBody();
if ($args["id"] != null)
{
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateModified"]) || empty($data["categories"]))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
return $response->withStatus(400);
}
$message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["body"], $data["dateModified"], $data["categories"]);
if ($message === "post not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, post not found")));
return $response->withStatus(404);
}
if ($message === "unset featured")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, cannot unset featured post, try updating another post to be featured first")));
return $response->withStatus(409);
}
if (!is_bool($message) || $message === false)
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => $message)));
return $response->withStatus(500);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->delete("/blog/post/{id}", function (Request $request, Response $response, $args)
{
if ($args["id"] != null)
{
$message = $this->blogData->deletePost($args["id"]);
if ($message === "post not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, post not found")));
return $response->withStatus(404);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, something went wrong")));
return $response->withStatus(500);
}
if ($message === "cannot delete")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, cannot delete featured post")));
return $response->withStatus(409);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->post("/blog/post", function (Request $request, Response $response)
{
$data = $request->getParsedBody();
$files = $request->getUploadedFiles();
$headerImg = $files["headerImg"];
if (empty($data["title"]) || empty($data["body"]) || empty($data["dateCreated"]) || empty($data["featured"]) || empty($data["categories"]))
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateCreated"]) || empty($data["categories"]))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Error, empty data sent")));
@ -46,7 +156,8 @@ class blogRoutes implements routesInterface
$headerImg = null;
}
$insertedID = $this->blogData->createPost($data["title"], $data["body"], $data["dateCreated"], $data["featured"], $data["categories"], $headerImg);
$featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["body"], $data["dateCreated"], $featured, $data["categories"], $headerImg);
if (!is_int($insertedID))
{
// uh oh something went wrong
@ -74,9 +185,36 @@ class blogRoutes implements routesInterface
return $response->withStatus(500);
}
$response->getBody()->write(json_encode($message));
return $response->withStatus(201);
});
$app->post("/blog/headerImage/{id}", function (Request $request, Response $response, $args)
{
$files = $request->getUploadedFiles();
if ($args["id"] != null)
{
if (empty($files))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => array("message" => "Error, empty data sent"))));
return $response->withStatus(400);
}
$message = $this->blogData->uploadHeaderImage($args["id"], $files["headerImg"]);
if (!is_array($message))
{
$response->getBody()->write(json_encode(array("error" => array("message" => $message))));
return $response->withStatus(500);
}
$response->getBody()->write(json_encode($message));
return $response->withStatus(201);
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
}
}

View File

@ -39,30 +39,32 @@ class projectData
* Update project data in the database with the given ID
* @param string $ID - ID of the project in the database to update
* @param string $title - Title of the project
* @param string $isMainProject - Is the project a main project or not
* @param bool $isMainProject - Is the project a main project or not
* @param string $information - Information about the project
* @param string $projectLink - Link to the project
* @param string $gitLink - Link to the git repository
* @return bool|string - True if project was updated, false if not and there was an error, or an error string
*/
public function updateProjectData(string $ID, string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): bool|string
public function updateProjectData(string $ID, string $title, bool $isMainProject, string $information, string $projectLink, string $gitLink): bool|string
{
$conn = dbConn();
if ($isMainProject === "false")
{
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
if ($result["isMainProject"] === "1")
{
return "unset main project";
}
if (!$result)
{
return "project not found";
}
if ($isMainProject === "true")
if (!$isMainProject && $result["isMainProject"] === "1")
{
return "unset main project";
}
if ($isMainProject)
{
$stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0 WHERE isMainProject = 1;");
$stmtMainProject->execute();
@ -70,7 +72,7 @@ class projectData
$stmt = $conn->prepare("UPDATE projects SET title = :title, isMainProject = :isMainProject, information = :information, projectLink = :projectLink, gitLink = :gitLink WHERE ID = :ID");
$stmt->bindParam(":title", $title);
$isMainProj = ($isMainProject === "true") ? 1 : 0;
$isMainProj = $isMainProject ? 1 : 0;
$stmt->bindParam(":isMainProject", $isMainProj);
$stmt->bindParam(":information", $information);
$stmt->bindParam(":projectLink", $projectLink);
@ -89,12 +91,16 @@ class projectData
$conn = dbConn();
// check if the project is a main project if it is return false
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "project not found";
}
if ($result["isMainProject"] === "1")
{
return "cannot delete";
@ -158,6 +164,20 @@ class projectData
*/
public function uploadImage(int $ID, UploadedFileInterface $img): string | array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT ID FROM projects WHERE ID = :ID");
$stmt->bindParam(":ID", $ID);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "Project with ID $ID not found";
}
$targetDir = "../imgs/projects/";
$imgUtils = new imgUtils();
$targetFile = $imgUtils->uploadFile($targetDir, $img);
@ -171,7 +191,6 @@ class projectData
{
$this->deleteImage($ID);
// update the database with the new image location
$conn = dbConn();
$stmt = $conn->prepare("UPDATE projects SET imgLocation = :imgLocation WHERE ID = :ID");
$stmt->bindParam(":imgLocation", $targetFile["imgLocation"]);
$stmt->bindParam(":ID", $ID);

View File

@ -39,7 +39,7 @@ class projectRoutes implements routesInterface
if(array_key_exists("errorMessage", $result))
{
$response = $response->withStatus(404);
$response->withStatus(404);
}
//use content type json to indicate json data on frontend.
@ -49,7 +49,7 @@ class projectRoutes implements routesInterface
$app->patch("/projectData/{id}", function (Request $request, Response $response, array $args)
{
$data = $request->getParsedBody();
if ($args["id"] != "undefined")
if ($args["id"] != null)
{
if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
{
@ -58,7 +58,15 @@ class projectRoutes implements routesInterface
return $response->withStatus(400);
}
$update = $this->projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]);
$isMainProject = $data["isMainProject"] === "true";
$update = $this->projectData->updateProjectData($args["id"], $data["title"], $isMainProject, $data["information"], $data["projectLink"], $data["gitLink"]);
if ($update === "project not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Project with ID " . $args["id"] . " not found")));
return $response->withStatus(404);
}
if ($update === "unset main project")
{
@ -73,6 +81,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
return $response->withStatus(500);
}
return $response;
}
@ -85,11 +94,12 @@ class projectRoutes implements routesInterface
if ($args["id"] != null)
{
$message = $this->projectData->deleteProjectData($args["id"]);
if ($message === "error")
if ($message === "project not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong or the project with ID ".$args["id"]."does not exist")));
return $response->withStatus(500);
$response->getBody()->write(json_encode(array("error" => "Project with ID " . $args["id"] . " not found")));
return $response->withStatus(404);
}
if ($message === "cannot delete")
@ -99,6 +109,13 @@ class projectRoutes implements routesInterface
return $response->withStatus(409);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
return $response->withStatus(500);
}
return $response;
}

View File

@ -58,19 +58,33 @@ class timelineData
* @param string $dateTo - End date
* @param string $grade - Grade
* @param string $course - Course
* @param string $id - ID of the education data
* @return bool - True if successful, false if not
* @param string $ID - ID of the education data
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $id): bool
public function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM edu WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("UPDATE edu SET startPeriod = :dateFrom, endPeriod = :dateTo, grade = :grade, course = :course WHERE ID = :id;");
$stmt->bindParam(":dateFrom", $dateFrom);
$stmt->bindParam(":dateTo", $dateTo);
$stmt->bindParam(":grade", $grade);
$stmt->bindParam(":course", $course);
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
@ -80,11 +94,21 @@ class timelineData
* @param string $companyName - Company name
* @param string $area - Area
* @param string $title - Title
* @param string $id - ID of the work data
* @return bool - True if successful, false if not
* @param string $ID - ID of the work data
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $id): bool
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM work WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$conn = dbConn();
$stmt = $conn->prepare("UPDATE work SET startPeriod = :dateFrom, endPeriod = :dateTo, companyName = :companyName, area = :area, title = :title WHERE ID = :id;");
$stmt->bindParam(":dateFrom", $dateFrom);
@ -92,34 +116,67 @@ class timelineData
$stmt->bindParam(":companyName", $companyName);
$stmt->bindParam(":area", $area);
$stmt->bindParam(":title", $title);
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
* Delete education data by ID
* @param int $id
* @return bool - True if successful, false if not
* @param int $ID
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function deleteEduData(int $id): bool
public function deleteEduData(int $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM edu WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("DELETE FROM edu WHERE ID = :id;");
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
* Delete work data by ID
* @param int $id
* @return bool - True if successful, false if not
* @param int $ID
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
function deleteWorkData(int $id): bool
function deleteWorkData(int $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM work WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("DELETE FROM work WHERE ID = :id;");
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**

View File

@ -53,7 +53,7 @@ class timelineRoutes implements routesInterface
$app->patch("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
{
$data = $request->getParsedBody();
if ($args["timeline"] == "edu" && $args["id"] != "undefined")
if ($args["timeline"] == "edu" && $args["id"] != null)
{
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
{
@ -61,8 +61,16 @@ class timelineRoutes implements routesInterface
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
return $response->withStatus(400);
}
$message = $this->timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]);
if (!$this->timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]))
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Edu data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -82,7 +90,16 @@ class timelineRoutes implements routesInterface
return $response->withStatus(400);
}
if (!$this->timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]))
$message = $this->timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Work data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -101,7 +118,16 @@ class timelineRoutes implements routesInterface
{
if ($args["timeline"] == "edu" && $args["id"] != null)
{
if (!$this->timelineData->deleteEduData($args["id"]))
$message = $this->timelineData->deleteEduData($args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Edu data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -113,7 +139,16 @@ class timelineRoutes implements routesInterface
if ($args["timeline"] == "work" && $args["id"] != null)
{
if (!$this->timelineData->deleteWorkData($args["id"]))
$message = $this->timelineData->deleteWorkData($args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Work data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));

View File

@ -46,7 +46,7 @@ class userData
public function createToken(string $username): string
{
$now = time();
$future = strtotime('+6 hour', $now);
$future = strtotime('+2 day', $now);
$secretKey = getSecretKey();
$payload = [
"jti" => $username,

View File

@ -36,15 +36,19 @@ class userRoutes implements routesInterface
if (empty($data["username"]) || empty($data["password"]))
{
// uh oh userData sent empty data
// uh oh user sent empty data
return $response->withStatus(400);
}
if ($this->user->checkUser($data["username"], $data["password"]))
{
// yay, userData is logged in
// yay, user is logged in
$_SESSION["token"] = $this->user->createToken($data["username"]);
$_SESSION["username"] = $data["username"];
$inactive = 60 * 60 * 48; // 2 days
$_SESSION["timeout"] = time() + $inactive;
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));
return $response;
}
@ -62,15 +66,24 @@ class userRoutes implements routesInterface
{
if (empty($_SESSION["token"]) && empty($_SESSION["username"]))
{
// uh oh userData not logged in
// uh oh user not logged in
return $response->withStatus(401);
}
$inactive = 60 * 60 * 48; // 2 days
$sessionLife = time() - $_SESSION["timeout"];
if ($sessionLife > $inactive)
{
// uh oh user session expired
session_destroy();
return $response->withStatus(401);
}
if (empty($_SESSION["token"]))
{
// userData is logged in but no token was created
// user is logged in but no token was created
$_SESSION["token"] = $this->user->createToken($_SESSION["username"]);
return $response;
return $response->withStatus(201);
}
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));

View File

@ -3,6 +3,8 @@
namespace api\utils;
use Psr\Http\Message\UploadedFileInterface;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class imgUtils
{
@ -40,4 +42,29 @@ class imgUtils
return array("imgLocation" => $targetFile);
}
/**
* Deletes a directory and all its contents
* @param string $path - Path to the directory to delete
*/
public function deleteDirectory(string $path): void
{
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path,
RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $file)
{
if ($file->isDir())
{
rmdir($file->getPathname());
}
else
{
unlink($file->getPathname());
}
}
rmdir($path);
}
}

2
dist/css/main.css vendored

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

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,8 @@ class blogData
public function getBlogPosts(): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT ID, title, dateCreated, dateModified, body, categories FROM blog ORDER BY dateCreated DESC;");
$stmt = $conn->prepare("SELECT ID, title, dateCreated, dateModified, body, categories, featured
FROM blog ORDER BY dateCreated;");
$stmt->execute();
// set the resulting array to associative
@ -99,6 +100,164 @@ class blogData
return array("errorMessage" => "Error, blog post could not found");
}
/**
* Delete a blog post with the given ID
* @param int $ID - ID of the blog post to delete
* @return string - Success or error message
*/
public function deletePost(int $ID): string
{
$conn = dbConn();
$stmtCheckPost = $conn->prepare("SELECT * FROM blog WHERE ID = :ID");
$stmtCheckPost->bindParam(":ID", $ID);
$stmtCheckPost->execute();
$result = $stmtCheckPost->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "post not found";
}
if ($result["featured"] === 1)
{
return "cannot delete";
}
$stmt = $conn->prepare("DELETE FROM blog WHERE ID = :ID");
$stmt->bindParam(":ID", $ID);
if ($stmt->execute())
{
$imagUtils = new imgUtils();
$imagUtils->deleteDirectory("../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/");
return "success";
}
return "error";
}
/**
* Update the blog post with the given ID
* @param int $ID - ID of the blog post to update
* @param string $title - Title of the blog post
* @param bool $featured - Whether the blog post is featured or not
* @param string $body - Body of the blog post
* @param string $dateModified - Date the blog post was modified
* @param string $categories - Categories of the blog post
* @return bool|string - Success or error message
*/
public function updatePost(int $ID, string $title, bool $featured, string $body, string $dateModified, string $categories): bool|string
{
$conn = dbConn();
$stmtCheckPost = $conn->prepare("SELECT * FROM blog WHERE ID = :ID");
$stmtCheckPost->bindParam(":ID", $ID);
$stmtCheckPost->execute();
$result = $stmtCheckPost->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "post not found";
}
if (!$featured && $result["featured"] === 1)
{
return "unset feature";
}
if ($featured)
{
$stmtUnsetFeatured = $conn->prepare("UPDATE blog SET featured = 0 WHERE featured = 1;");
$stmtUnsetFeatured->execute();
}
$to = "../blog/imgs/" . $title . "_" . $result["folderID"] . "/";
if ($result["title"] !== $title)
{
$from = "../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/";
mkdir($to, 0777, true);
rename($result["headerImg"], $to . basename($result["headerImg"]));
$body = $this->changeHTMLSrc($body, $to, $from);
rmdir($from);
}
$from = "../blog/imgs/tmp/";
$newBody = $this->changeHTMLSrc($body, $to, $from);
$stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, body = :body, dateModified = :dateModified, categories = :categories WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":title", $title);
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":dateModified", $dateModified);
$stmt->bindParam(":categories", $categories);
return $stmt->execute();
}
/**
* Creates a new post di rectory, uploads the header image and moves the images from the
* temp folder to the new folder, then updates the post html to point to the new images, finally
* it creates the post in the database
* @param string $title - Title of the blog post
* @param string $body - Body of the blog post
* @param string $dateCreated - Date the blog post was created
* @param bool $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message
*/
public function createPost(string $title, string $body, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string
{
$conn = dbConn();
$folderID = uniqid();
$targetFile = array("imgLocation" => "../blog/imgs/placeholder.png");
$targetDir = "../blog/imgs/" . $title . "_" . $folderID . "/";
mkdir($targetDir, 0777, true);
if ($headerImg !== null)
{
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $headerImg);
}
if (!is_array($targetFile))
{
return $targetFile;
}
$newBody = $this->changeHTMLSrc($body, $targetDir, "../blog/imgs/tmp/");
if ($featured)
{
$stmtMainProject = $conn->prepare("UPDATE blog SET featured = 0 WHERE featured = 1;");
$stmtMainProject->execute();
}
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, body, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :body, :categories, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $isFeatured);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID);
if ($stmt->execute())
{
return intval($conn->lastInsertId());
}
return "Error, couldn't create post";
}
/**
* Upload the images in the post to temp folder and return image location
* @param UploadedFileInterface $img - Image to upload
@ -131,37 +290,60 @@ class blogData
}
/**
* Creates a new post directory, uploads the header image and moves the images from the
* temp folder to the new folder, then updates the post html to point to the new images, finally
* it creates the post in the database
* @param string $title - Title of the blog post
* @param string $body - Body of the blog post
* @param string $dateCreated - Date the blog post was created
* @param string $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message
* Upload the header image of the post and update the database
* @param int $ID - ID of the post
* @param UploadedFileInterface $img - Image to upload
* @return string|array - String with error message or array with the location of the uploaded file
*/
public function createPost(string $title, string $body, string $dateCreated, string $featured, string $categories, UploadedFileInterface $headerImg): int|string
public function uploadHeaderImage(int $ID, UploadedFileInterface $img): string|array
{
$conn = dbConn();
$targetFile = "";
$folderID = uniqid();
if ($headerImg !== null)
$stmt = $conn->prepare("SELECT * FROM blog WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
$targetDir = "../blog/imgs/" . $title . "_" . $folderID . "/";
mkdir($targetDir, 0777, true);
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $headerImg);
return "Couldn't find the post";
}
$targetFile = array("imgLocation" => ".../blog/imgs/placeholder.png");
$targetDir = "../blog/imgs/" . $result["title"] . "_" . $result["folderID"] . "/";
$imagUtils = new imgUtils();
$targetFile = $imagUtils->uploadFile($targetDir, $img);
if (!is_array($targetFile))
{
return $targetFile;
}
if (file_exists($targetFile["imgLocation"]))
{
unlink($result["headerImg"]);
$stmt = $conn->prepare("UPDATE blog SET headerImg = :headerImg WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->execute();
if ($stmt->rowCount() > 0)
{
return $targetFile;
}
return "Couldn't update the post";
}
return "Couldn't upload the image";
}
/**
* Change the HTML src of the images in the post to point to the new location
* @param string $body - Body of the post
* @param string $to - New location of the images
* @param string $from - Old location of the images
* @return string - Body of the post with the new image locations
*/
public function changeHTMLSrc(string $body, string $to, string $from): string
{
$htmlDoc = new DOMDocument();
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
@ -172,24 +354,25 @@ class blogData
foreach ($imgs as $img)
{
$src = $img->getAttribute("src");
$src = urldecode($src);
$srcList[] = $src;
$fileName = basename($src);
$img->setAttribute("src", $targetDir . $fileName);
$img->setAttribute("src", $to . $fileName);
}
$files = scandir("../blog/imgs/tmp/");
$files = scandir($from);
foreach ($files as $file)
{
if ($file != "." && $file != "..")
{
if (!in_array("../blog/imgs/tmp/" . $file, $srcList))
if (!in_array($from . $file, $srcList))
{
unlink("../blog/imgs/tmp/" . $file);
unlink($from . $file);
}
else
{
rename("../blog/imgs/tmp/" . $file, $targetDir . $file);
rename($from . $file, $to . $file);
}
}
}
@ -199,23 +382,6 @@ class blogData
{
$newBody .= $htmlDoc->saveHTML($node);
}
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, body, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :body, :categories, :folderID);");
$stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated);
$stmt->bindParam(":featured", $featured);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID);
if ($stmt->execute())
{
return intval($conn->lastInsertId());
}
return "Error, couldn't create post";
return $newBody;
}
}

View File

@ -29,12 +29,122 @@ class blogRoutes implements routesInterface
*/
public function createRoutes(App $app): void
{
$app->post("/blog/post", function (Request $request, Response $response, array $args)
$app->get("/blog/post", function (Request $request, Response $response)
{
$posts = $this->blogData->getBlogPosts();
$json = json_encode($posts);
$response->getBody()->write($json);
if (array_key_exists("errorMessage", $posts))
{
$response->withStatus(404);
}
return $response;
});
$app->get("/blog/post/{id}", function (Request $request, Response $response, $args)
{
if ($args["id"] != null)
{
$post = $this->blogData->getBlogPost($args["id"]);
if (array_key_exists("errorMessage", $post))
{
$response->getBody()->write(json_encode($post));
return $response->withStatus(404);
}
$response->getBody()->write(json_encode($post));
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->patch("/blog/post/{id}", function (Request $request, Response $response, $args)
{
$data = $request->getParsedBody();
if ($args["id"] != null)
{
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateModified"]) || empty($data["categories"]))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
return $response->withStatus(400);
}
$message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["body"], $data["dateModified"], $data["categories"]);
if ($message === "post not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, post not found")));
return $response->withStatus(404);
}
if ($message === "unset featured")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, cannot unset featured post, try updating another post to be featured first")));
return $response->withStatus(409);
}
if (!is_bool($message) || $message === false)
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => $message)));
return $response->withStatus(500);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->delete("/blog/post/{id}", function (Request $request, Response $response, $args)
{
if ($args["id"] != null)
{
$message = $this->blogData->deletePost($args["id"]);
if ($message === "post not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, post not found")));
return $response->withStatus(404);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, something went wrong")));
return $response->withStatus(500);
}
if ($message === "cannot delete")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Error, cannot delete featured post")));
return $response->withStatus(409);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
$app->post("/blog/post", function (Request $request, Response $response)
{
$data = $request->getParsedBody();
$files = $request->getUploadedFiles();
$headerImg = $files["headerImg"];
if (empty($data["title"]) || empty($data["body"]) || empty($data["dateCreated"]) || empty($data["featured"]) || empty($data["categories"]))
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateCreated"]) || empty($data["categories"]))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Error, empty data sent")));
@ -46,7 +156,8 @@ class blogRoutes implements routesInterface
$headerImg = null;
}
$insertedID = $this->blogData->createPost($data["title"], $data["body"], $data["dateCreated"], $data["featured"], $data["categories"], $headerImg);
$featured = $data["featured"] === "true";
$insertedID = $this->blogData->createPost($data["title"], $data["body"], $data["dateCreated"], $featured, $data["categories"], $headerImg);
if (!is_int($insertedID))
{
// uh oh something went wrong
@ -74,9 +185,36 @@ class blogRoutes implements routesInterface
return $response->withStatus(500);
}
$response->getBody()->write(json_encode($message));
return $response->withStatus(201);
});
$app->post("/blog/headerImage/{id}", function (Request $request, Response $response, $args)
{
$files = $request->getUploadedFiles();
if ($args["id"] != null)
{
if (empty($files))
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => array("message" => "Error, empty data sent"))));
return $response->withStatus(400);
}
$message = $this->blogData->uploadHeaderImage($args["id"], $files["headerImg"]);
if (!is_array($message))
{
$response->getBody()->write(json_encode(array("error" => array("message" => $message))));
return $response->withStatus(500);
}
$response->getBody()->write(json_encode($message));
return $response->withStatus(201);
}
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
return $response->withStatus(400);
});
}
}

View File

@ -39,30 +39,32 @@ class projectData
* Update project data in the database with the given ID
* @param string $ID - ID of the project in the database to update
* @param string $title - Title of the project
* @param string $isMainProject - Is the project a main project or not
* @param bool $isMainProject - Is the project a main project or not
* @param string $information - Information about the project
* @param string $projectLink - Link to the project
* @param string $gitLink - Link to the git repository
* @return bool|string - True if project was updated, false if not and there was an error, or an error string
*/
public function updateProjectData(string $ID, string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): bool|string
public function updateProjectData(string $ID, string $title, bool $isMainProject, string $information, string $projectLink, string $gitLink): bool|string
{
$conn = dbConn();
if ($isMainProject === "false")
{
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
if ($result["isMainProject"] === "1")
{
return "unset main project";
}
if (!$result)
{
return "project not found";
}
if ($isMainProject === "true")
if (!$isMainProject && $result["isMainProject"] === "1")
{
return "unset main project";
}
if ($isMainProject)
{
$stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0 WHERE isMainProject = 1;");
$stmtMainProject->execute();
@ -70,7 +72,7 @@ class projectData
$stmt = $conn->prepare("UPDATE projects SET title = :title, isMainProject = :isMainProject, information = :information, projectLink = :projectLink, gitLink = :gitLink WHERE ID = :ID");
$stmt->bindParam(":title", $title);
$isMainProj = ($isMainProject === "true") ? 1 : 0;
$isMainProj = $isMainProject ? 1 : 0;
$stmt->bindParam(":isMainProject", $isMainProj);
$stmt->bindParam(":information", $information);
$stmt->bindParam(":projectLink", $projectLink);
@ -89,12 +91,16 @@ class projectData
$conn = dbConn();
// check if the project is a main project if it is return false
$stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
$stmtMainProject->bindParam(":ID", $ID);
$stmtMainProject->execute();
$result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "project not found";
}
if ($result["isMainProject"] === "1")
{
return "cannot delete";
@ -158,6 +164,20 @@ class projectData
*/
public function uploadImage(int $ID, UploadedFileInterface $img): string | array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT ID FROM projects WHERE ID = :ID");
$stmt->bindParam(":ID", $ID);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "Project with ID $ID not found";
}
$targetDir = "../imgs/projects/";
$imgUtils = new imgUtils();
$targetFile = $imgUtils->uploadFile($targetDir, $img);
@ -171,7 +191,6 @@ class projectData
{
$this->deleteImage($ID);
// update the database with the new image location
$conn = dbConn();
$stmt = $conn->prepare("UPDATE projects SET imgLocation = :imgLocation WHERE ID = :ID");
$stmt->bindParam(":imgLocation", $targetFile["imgLocation"]);
$stmt->bindParam(":ID", $ID);

View File

@ -39,7 +39,7 @@ class projectRoutes implements routesInterface
if(array_key_exists("errorMessage", $result))
{
$response = $response->withStatus(404);
$response->withStatus(404);
}
//use content type json to indicate json data on frontend.
@ -49,7 +49,7 @@ class projectRoutes implements routesInterface
$app->patch("/projectData/{id}", function (Request $request, Response $response, array $args)
{
$data = $request->getParsedBody();
if ($args["id"] != "undefined")
if ($args["id"] != null)
{
if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
{
@ -58,7 +58,15 @@ class projectRoutes implements routesInterface
return $response->withStatus(400);
}
$update = $this->projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]);
$isMainProject = $data["isMainProject"] === "true";
$update = $this->projectData->updateProjectData($args["id"], $data["title"], $isMainProject, $data["information"], $data["projectLink"], $data["gitLink"]);
if ($update === "project not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Project with ID " . $args["id"] . " not found")));
return $response->withStatus(404);
}
if ($update === "unset main project")
{
@ -73,6 +81,7 @@ class projectRoutes implements routesInterface
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
return $response->withStatus(500);
}
return $response;
}
@ -85,11 +94,12 @@ class projectRoutes implements routesInterface
if ($args["id"] != null)
{
$message = $this->projectData->deleteProjectData($args["id"]);
if ($message === "error")
if ($message === "project not found")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong or the project with ID ".$args["id"]."does not exist")));
return $response->withStatus(500);
$response->getBody()->write(json_encode(array("error" => "Project with ID " . $args["id"] . " not found")));
return $response->withStatus(404);
}
if ($message === "cannot delete")
@ -99,6 +109,13 @@ class projectRoutes implements routesInterface
return $response->withStatus(409);
}
if ($message === "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
return $response->withStatus(500);
}
return $response;
}

View File

@ -58,19 +58,33 @@ class timelineData
* @param string $dateTo - End date
* @param string $grade - Grade
* @param string $course - Course
* @param string $id - ID of the education data
* @return bool - True if successful, false if not
* @param string $ID - ID of the education data
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $id): bool
public function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM edu WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("UPDATE edu SET startPeriod = :dateFrom, endPeriod = :dateTo, grade = :grade, course = :course WHERE ID = :id;");
$stmt->bindParam(":dateFrom", $dateFrom);
$stmt->bindParam(":dateTo", $dateTo);
$stmt->bindParam(":grade", $grade);
$stmt->bindParam(":course", $course);
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
@ -80,11 +94,21 @@ class timelineData
* @param string $companyName - Company name
* @param string $area - Area
* @param string $title - Title
* @param string $id - ID of the work data
* @return bool - True if successful, false if not
* @param string $ID - ID of the work data
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $id): bool
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM work WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$conn = dbConn();
$stmt = $conn->prepare("UPDATE work SET startPeriod = :dateFrom, endPeriod = :dateTo, companyName = :companyName, area = :area, title = :title WHERE ID = :id;");
$stmt->bindParam(":dateFrom", $dateFrom);
@ -92,34 +116,67 @@ class timelineData
$stmt->bindParam(":companyName", $companyName);
$stmt->bindParam(":area", $area);
$stmt->bindParam(":title", $title);
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
* Delete education data by ID
* @param int $id
* @return bool - True if successful, false if not
* @param int $ID
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
public function deleteEduData(int $id): bool
public function deleteEduData(int $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM edu WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("DELETE FROM edu WHERE ID = :id;");
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**
* Delete work data by ID
* @param int $id
* @return bool - True if successful, false if not
* @param int $ID
* @return string - "not found" if the ID is not found, "ok" if successful, "error" if not
*/
function deleteWorkData(int $id): bool
function deleteWorkData(int $ID): string
{
$conn = dbConn();
$chkStmt = $conn->prepare("SELECT ID FROM work WHERE ID = :id;");
$chkStmt->bindParam(":id", $ID);
$chkStmt->execute();
$result = $chkStmt->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "not found";
}
$stmt = $conn->prepare("DELETE FROM work WHERE ID = :id;");
$stmt->bindParam(":id", $id);
return $stmt->execute();
$stmt->bindParam(":id", $ID);
if ($stmt->execute())
{
return "ok";
}
return "error";
}
/**

View File

@ -53,7 +53,7 @@ class timelineRoutes implements routesInterface
$app->patch("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
{
$data = $request->getParsedBody();
if ($args["timeline"] == "edu" && $args["id"] != "undefined")
if ($args["timeline"] == "edu" && $args["id"] != null)
{
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
{
@ -61,8 +61,16 @@ class timelineRoutes implements routesInterface
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
return $response->withStatus(400);
}
$message = $this->timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]);
if (!$this->timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]))
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Edu data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -82,7 +90,16 @@ class timelineRoutes implements routesInterface
return $response->withStatus(400);
}
if (!$this->timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]))
$message = $this->timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Work data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -101,7 +118,16 @@ class timelineRoutes implements routesInterface
{
if ($args["timeline"] == "edu" && $args["id"] != null)
{
if (!$this->timelineData->deleteEduData($args["id"]))
$message = $this->timelineData->deleteEduData($args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Edu data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
@ -113,7 +139,16 @@ class timelineRoutes implements routesInterface
if ($args["timeline"] == "work" && $args["id"] != null)
{
if (!$this->timelineData->deleteWorkData($args["id"]))
$message = $this->timelineData->deleteWorkData($args["id"]);
if ($message == "not found")
{
// uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Work data with ID " . $args["id"] . " was not found")));
return $response->withStatus(404);
}
if ($message == "error")
{
// uh oh something went wrong
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));

View File

@ -46,7 +46,7 @@ class userData
public function createToken(string $username): string
{
$now = time();
$future = strtotime('+6 hour', $now);
$future = strtotime('+2 day', $now);
$secretKey = getSecretKey();
$payload = [
"jti" => $username,

View File

@ -36,15 +36,19 @@ class userRoutes implements routesInterface
if (empty($data["username"]) || empty($data["password"]))
{
// uh oh userData sent empty data
// uh oh user sent empty data
return $response->withStatus(400);
}
if ($this->user->checkUser($data["username"], $data["password"]))
{
// yay, userData is logged in
// yay, user is logged in
$_SESSION["token"] = $this->user->createToken($data["username"]);
$_SESSION["username"] = $data["username"];
$inactive = 60 * 60 * 48; // 2 days
$_SESSION["timeout"] = time() + $inactive;
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));
return $response;
}
@ -62,15 +66,24 @@ class userRoutes implements routesInterface
{
if (empty($_SESSION["token"]) && empty($_SESSION["username"]))
{
// uh oh userData not logged in
// uh oh user not logged in
return $response->withStatus(401);
}
$inactive = 60 * 60 * 48; // 2 days
$sessionLife = time() - $_SESSION["timeout"];
if ($sessionLife > $inactive)
{
// uh oh user session expired
session_destroy();
return $response->withStatus(401);
}
if (empty($_SESSION["token"]))
{
// userData is logged in but no token was created
// user is logged in but no token was created
$_SESSION["token"] = $this->user->createToken($_SESSION["username"]);
return $response;
return $response->withStatus(201);
}
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));

View File

@ -3,6 +3,8 @@
namespace api\utils;
use Psr\Http\Message\UploadedFileInterface;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class imgUtils
{
@ -40,4 +42,29 @@ class imgUtils
return array("imgLocation" => $targetFile);
}
/**
* Deletes a directory and all its contents
* @param string $path - Path to the directory to delete
*/
public function deleteDirectory(string $path): void
{
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path,
RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $file)
{
if ($file->isDir())
{
rmdir($file->getPathname());
}
else
{
unlink($file->getPathname());
}
}
rmdir($path);
}
}

View File

@ -4,7 +4,7 @@ document.addEventListener('DOMContentLoaded', () =>
goToURL(window.location.pathname);
});
window.addEventListener('popstate', e =>
window.addEventListener('popstate', _ =>
{
goToURL(window.history.state);
});

View File

@ -10,13 +10,14 @@
--primaryDefault: hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1);
--primaryHover: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1);
--timelineItemBrdr: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1);
--errorDefault: hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1);
--errorDefault: hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) + 10%), 1);
--errorHover: hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1);
--grey: hsla(0, 0%, 39%, 1);
--notAvailableDefault: hsla(0, 0%, 39%, 1);
--notAvailableHover: hsla(0, 0%,32%, 1);
--notAvailableHover: hsla(0, 0%, 32%, 1);
--mutedGrey: hsla(0, 0%, 78%, 1);
--mutedBlack: hsla(0, 0%, 0%, 0.25);
--mutedGreen: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) + 20%), 0.5);
--navBack: hsla(0, 0%, 30%, 1);
/* Font Sizes */

View File

@ -42,10 +42,11 @@ div.editorContainer > *, div.projectsGrid > * {
main.editor section {
display: none;
flex-direction: column;
}
section#addPost {
display: block;
section#editPost {
display: flex;
}
div.modifyBtnContainer {
@ -232,7 +233,7 @@ section#projects form.projItem:not(.editing) div.formControl.infoContainer texta
color: #000000;
}
section#addPost form {
section#addPost form, section#editPost form {
margin: auto 4rem;
}
@ -252,3 +253,29 @@ form .formControl .ck.ck-editor__main .ck-content {
form .formControl .ck-editor__editable {
min-height: 400px;
}
section#editPost {
justify-content: center;
}
section#editPost h2 {
align-self: flex-start;
}
section#editPost table {
border-collapse: collapse;
border-style: hidden;
align-self: center;
margin-bottom: 5em;
}
section#editPost table td, th {
border: 1px solid var(--mutedGrey);
text-align: left;
padding: 8px;
min-width: 10rem;
}
section#editPost form {
margin-bottom: 2em;
}

View File

@ -15,11 +15,11 @@
<li><a href="#" id="goToCV"><span>&lt;</span>CV<span>&gt;</span></a></li>
<li><a href="#" id="goToProjects"><span>&lt;</span>Projects<span>&gt;</span></a></li>
<li><a href="#" id="blog" class="active"><span>&lt;</span>Blog<span>&gt;</span> <i
class="fa fa-caret-right"></i></a></li>
class="fa fa-caret-right"></i>
</a></li>
<li class="dropdown">
<ul>
<li><a href="#" id="goToAddPost"><span>&lt;</span>Add Blog Post<span>&gt;</span></a>
</li>
<li><a href="#" id="goToAddPost"><span>&lt;</span>Add Blog Post<span>&gt;</span></a></li>
<li><a href="#" id="goToEditPost" class="active"><span>&lt;</span>Edit Blog
Post<span>&gt;</span></a></li>
</ul>
@ -189,8 +189,9 @@
</div>
<div class="formControl" style="align-items: stretch;">
<label for="CKEditor">Post</label>
<div id="CKEditor">
<label for="CKEditorAddPost">Post</label>
<div id="CKEditorAddPost">
</div>
</div>
@ -207,6 +208,64 @@
</form>
</section>
<section id="editPost">
<h2>edit post</h2>
<table>
<thead>
<tr>
<th>Title</th>
<th>Date Created</th>
<th>Date Modified</th>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<form action="" id="editPostForm" method="POST">
<div class="formControl">
<label for="editPostTitle">Title </label>
<input type="text" name="editPostTitle" id="editPostTitle" required>
</div>
<div class="formControl">
<label class="checkContainer" for="editIsFeatured">Is It The Featured Post
<input type="checkbox" id="editIsFeatured" name="editIsFeatured">
<span class="checkmark"></span>
</label>
</div>
<div class="formControl">
<label for="editHeaderImg">Header Image</label>
<input type="file" name="editHeaderImg" id="editHeaderImg">
</div>
<div class="formControl">
<label for="editPostCategories">Categories</label>
<input type="text" name="editPostCategories" id="editPostCategories"
pattern='(?:,|\n|^)("(?:(?:"")*[^"]*)*"|[^",\n]*|(?:\n|$))' title="CSV format"
oninvalid="this.setCustomValidity('This field takes a CSV like format')"
oninput="this.setCustomValidity('')" required>
</div>
<div class="formControl" style="align-items: stretch;">
<label for="CKEditorEditPost">Post</label>
<div id="CKEditorEditPost">
</div>
</div>
<div class="error hidden" id="editPostError">
<button class="close" type="button">&times;</button>
<div></div>
</div>
<div class="success hidden" id="editPostSuccess">
<button class="close" type="button">&times;</button>
<div></div>
</div>
<input type="submit" value="Add new blog post" class="btn btnPrimary boxShadowIn boxShadowOut">
</form>
</section>
</main>

View File

@ -1,6 +1,7 @@
let dateOptions = {month: 'short', year: 'numeric'};
let textareaLoaded = false;
let editor = null;
let editors = {};
let posts = null;
document.addEventListener('DOMContentLoaded', () =>
{
// check if the userData is logged in, if not redirect to log in
@ -15,76 +16,61 @@ document.addEventListener('DOMContentLoaded', () =>
document.querySelector("#dateFromE").max = new Date().toISOString().split("T")[0];
document.querySelector("#dateFromW").max = new Date().toISOString().split("T")[0];
fetch("/api/timelineData/edu").then(res =>
fetch("/api/timelineData/edu").then(res => res.json().then(json =>
{
res.json().then(json =>
if (res.ok)
{
if (res.ok)
for (let i = 0; i < json.length; i++)
{
for (let i = 0; i < json.length; i++)
{
addEduData(json[i].ID, json[i].startPeriod, json[i].endPeriod, json[i].grade, json[i].course);
}
return;
addEduData(json[i].ID, json[i].startPeriod, json[i].endPeriod, json[i].grade, json[i].course);
}
document.querySelector("#edu").innerHTML = "No education data found";
})
});
return;
}
document.querySelector("#edu").innerHTML = "No education data found";
}));
fetch("/api/timelineData/work").then(res =>
fetch("/api/timelineData/work").then(res => res.json().then(json =>
{
res.json().then(json =>
if (res.ok)
{
if (res.ok)
for (let i = 0; i < json.length; i++)
{
for (let i = 0; i < json.length; i++)
{
let endPeriod = json[i].endPeriod === "0000-00-00" ? "Present" : json[i].endPeriod;
addWorkData(json[i].ID, json[i].startPeriod, endPeriod, json[i].companyName, json[i].area, json[i].title);
}
return;
let endPeriod = json[i].endPeriod === "0000-00-00" ? "Present" : json[i].endPeriod;
addWorkData(json[i].ID, json[i].startPeriod, endPeriod, json[i].companyName, json[i].area, json[i].title);
}
document.querySelector("#edu").innerHTML = "No education data found";
})
});
return;
}
document.querySelector("#edu").innerHTML = "No education data found";
}));
fetch("/api/projectData").then(res =>
fetch("/api/projectData").then(res => res.json().then(json =>
{
res.json().then(json =>
if (res.ok)
{
if (res.ok)
json.forEach(item =>
{
json.forEach(item =>
{
addProject(item["ID"], (item["isMainProject"] === 1 ? "true" : "false"), (item["imgLocation"] === "") ? "../imgs/placeholder.png" : item["imgLocation"], item["title"], item["information"], item["projectLink"], item["gitLink"]);
})
return;
}
document.querySelector("#projList").innerHTML = "No project data found";
})
})
addProject(item["ID"], (item["isMainProject"] === 1 ? "true" : "false"), (item["imgLocation"] === "") ? "../imgs/placeholder.png" : item["imgLocation"], item["title"], item["information"], item["projectLink"], item["gitLink"]);
})
return;
}
document.querySelector("#projList").innerHTML = "No project data found";
}))
fetch("/api/blog/post").then(res => res.json().then(json =>
{
if (res.ok)
{
posts = json;
json.forEach(item =>
{
addPostInfo(item["ID"], item["title"], item["dateCreated"], item["dateModified"]);
})
}
}));
// CKEditor stuff
ClassicEditor.create(document.querySelector("#CKEditor"), {
placeholder: "Write something amazing...",
simpleUpload: {
uploadUrl: '/api/blog/uploadPostImage',
headers: {
Authorization: "Bearer " + localStorage.getItem("token")
}
}
}).then(CKEditor =>
{
editor = CKEditor;
}).catch(error =>
{
console.error('Oops, something went wrong!');
console.error('Please, report the following error on https://github.com/ckeditor/ckeditor5/issues with the build id and the error stack trace:');
console.warn('Build id: 1eo8ioyje2om-vgar4aghypdm');
console.error(error);
});
})
createEditors("CKEditorAddPost", "CKEditorEditPost");
});
document.querySelector("body").addEventListener("click", () =>
{
@ -265,11 +251,17 @@ document.querySelector("#addProj").addEventListener("submit", e =>
document.querySelector("#addPostForm").addEventListener("submit", e =>
{
e.preventDefault();
if (editors["CKEditorAddPost"].getData() === "")
{
showErrorMessage("Post body cannot be empty", "addPost");
return;
}
let data = new FormData();
data.append("title", document.querySelector("#postTitle").value);
data.append("body", editor.getData());
data.append("featured", document.querySelector("#isFeatured").checked ? "1" : "0");
data.append("body", editors["CKEditorAddPost"].getData());
data.append("dateCreated", new Date().toISOString().slice(0, 19).replace('T', ' '));
data.append("featured", document.querySelector("#isFeatured").checked ? "true" : "false");
data.append("categories", document.querySelector("#postCategories").value);
data.append("headerImg", document.querySelector("#headerImg").files[0]);
@ -279,12 +271,13 @@ document.querySelector("#addPostForm").addEventListener("submit", e =>
headers: {
"Authorization": "Bearer " + localStorage.getItem("token")
}
}).then(res =>
}).then(res => res.json().then(json =>
{
if (res.ok)
{
document.querySelector("#addPostForm").reset();
editor.setData("");
editors["CKEditorAddPost"].setData("");
addPostInfo(json.ID, data.get("title"), data.get("dateCreated"), data.get("dateModified"));
showSuccessMessage("Post added successfully", "addPost");
return;
}
@ -296,7 +289,92 @@ document.querySelector("#addPostForm").addEventListener("submit", e =>
}
res.json().then(json => showErrorMessage(json.error, "addPost"));
});
}));
});
document.querySelector("#editPostForm").addEventListener("submit", e =>
{
e.preventDefault();
let id = document.querySelector("#editPostForm input[type='submit']").id;
if (id === "")
{
showErrorMessage("Currently not editing any post", "editPost");
return;
}
if (editors["CKEditorEditPost"].getData() === "")
{
showErrorMessage("Post body cannot be empty", "editPost");
return;
}
let data = {};
data["title"] = document.querySelector("#editPostTitle").value;
data["featured"] = document.querySelector("#editIsFeatured").checked ? "1" : "0";
data["body"] = editors["CKEditorEditPost"].getData();
data["dateModified"] = new Date().toISOString().slice(0, 19).replace('T', ' ');
data["categories"] = document.querySelector("#editPostCategories").value;
let imgData = new FormData();
imgData.append("headerImg", document.querySelector("#editHeaderImg").files[0]);
fetch("/api/blog/post/" + id, {
method: "PATCH",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + localStorage.getItem("token")
}
}).then(res =>
{
if (res.ok)
{
if (imgData.get("headerImg") === "undefined")
{
document.querySelector("#editPostForm").reset();
document.querySelector("#editPostForm input[type='submit']").id = "";
editors["CKEditorEditPost"].setData("");
showSuccessMessage("Post edited successfully", "editPost");
return;
}
return fetch("/api/blog/headerImage/" + id, {
method: "POST",
body: imgData,
headers: {
"Authorization": "Bearer " + localStorage.getItem("token")
}
});
}
if (res.status === 401)
{
window.location.href = "./";
return;
}
res.json().then(json => showErrorMessage(json.error, "editPost"));
}).then(res => res.json().then(json =>
{
if (res.ok)
{
document.querySelector("#editPostForm").reset();
document.querySelector("#editPostForm input[type='submit']").id = "";
editors["CKEditorEditPost"].setData("");
showSuccessMessage("Post edited successfully", "editPost");
return;
}
if (res.status === 401)
{
window.location.href = "./";
return;
}
showErrorMessage(json.error.message, "editPost");
}));
});
@ -330,6 +408,14 @@ document.querySelector("#goToAddPost").addEventListener("click", () =>
document.querySelector("#blog").classList.add("active");
});
document.querySelector("#goToEditPost").addEventListener("click", () =>
{
textareaLoaded = false;
addActiveClass("goToEditPost");
goToPage("editPost");
document.querySelector("#blog").classList.add("active");
});
document.querySelector("#logout").addEventListener("click", () =>
{
fetch("/api/user/logout").then(res =>
@ -356,9 +442,16 @@ document.querySelector("#addPostError .close").addEventListener("click", () =>
document.querySelector("#addPostSuccess .close").addEventListener("click", () =>
document.querySelector("#addPostSuccess").classList.toggle("hidden"));
document.querySelector("#editPostError .close").addEventListener("click", () =>
document.querySelector("#editPostError").classList.toggle("hidden"));
document.querySelector("#editPostSuccess .close").addEventListener("click", () =>
document.querySelector("#editPostSuccess").classList.toggle("hidden"));
/**
* Goes to the page with the given id of the section
* @param {string} id The id of the section to go to
* @param {string} id - The id of the section to go to
*/
function goToPage(id)
{
@ -367,7 +460,7 @@ function goToPage(id)
element.style.display = "none";
if (element.id === id)
{
element.style.display = "block";
element.style.display = "flex";
}
});
@ -375,7 +468,7 @@ function goToPage(id)
/**
* Removes the active class from all nav items and adds it to the one with the given id
* @param {string} id The id to add the active class to
* @param {string} id - The id to add the active class to
*/
function addActiveClass(id)
{
@ -389,10 +482,70 @@ function addActiveClass(id)
});
}
/**
* Toggles the editing mode of the project item with the given id
* @param id {number} - the id of the project item from the database
*/
function editProjectItem(id)
{
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
document.querySelector(`#title${id}proj`).toggleAttribute("disabled");
document.querySelector(`#info${id}proj`).toggleAttribute("disabled");
}
/**
* Creates new CKEditor instances
* @param ids {string[]} - the ids of the divs to create editors for
*/
function createEditors(...ids)
{
ids.forEach(id =>
{
ClassicEditor.create(document.querySelector(`#${id}`), {
placeholder: "Write something amazing...",
simpleUpload: {
uploadUrl: '/api/blog/uploadPostImage',
headers: {
Authorization: "Bearer " + localStorage.getItem("token")
}
}
}).then(CKEditor =>
{
editors[id] = CKEditor;
}).catch(error =>
{
console.error('Oops, something went wrong!');
console.error('Please, report the following error on https://github.com/ckeditor/ckeditor5/issues with the build id and the error stack trace:');
console.warn('Build id: 1eo8ioyje2om-vgar4aghypdm');
console.error(error);
});
})
}
/**
* Edits the post with the given id
* @param {number} id - the id of the post from the database
*/
function editPostItem(id)
{
posts.forEach(post =>
{
if (post.ID === id)
{
document.querySelector("#editPostTitle").value = post.title;
document.querySelector("#editIsFeatured").checked = (post.featured === 1);
document.querySelector("#editPostCategories").value = post.categories;
editors["CKEditorEditPost"].setData(post.body);
document.querySelector("#editPostForm input[type='submit']").id = id;
}
})
}
/**
* Shows respective error message for form
* @param {string} message The error message to show
* @param {string} form The form to show the error message for
* @param {string} message - The error message to show
* @param {string} form - The form to show the error message for
*/
function showErrorMessage(message, form)
{
@ -413,7 +566,7 @@ function showSuccessMessage(message, form)
/**
* Switches the timeline item between edit and view mode
* @param id the id of the timeline item from the database
* @param id - the id of the timeline item from the database
*/
function editCVItem(id)
{
@ -534,8 +687,8 @@ function addWorkData(ID, startPeriod, endPeriod, companyName, area, jobTitle, pr
/**
* Updates the edu timeline item with the given id
* and data from the form
* @param {number} id the id of the edu timeline item from the database
* @param {SubmitEvent} e the event that triggered the function
* @param {number} id - the id of the edu timeline item from the database
* @param {SubmitEvent} e - the event that triggered the function
*/
function updateEduItem(id, e)
{
@ -582,8 +735,8 @@ function updateEduItem(id, e)
/**
* Updates the work timeline item with the given id
* and data from the form
* @param {number} id the id of the work timeline item from the database
* @param {SubmitEvent} e the event that triggered the function
* @param {number} id - the id of the work timeline item from the database
* @param {SubmitEvent} e - the event that triggered the function
*/
function updateWorkItem(id, e)
{
@ -630,7 +783,7 @@ function updateWorkItem(id, e)
/**
* Deletes the timeline item with the given id
* @param {number} id the id of the timeline item
* @param {number} id - the id of the timeline item
*/
function deleteEduItem(id)
{
@ -658,7 +811,7 @@ function deleteEduItem(id)
/**
* Updates the timeline item with the given id
* @param {number} id the id of the timeline item from the database
* @param {number} id - the id of the timeline item from the database
*/
function deleteWorkItem(id)
{
@ -688,8 +841,8 @@ function deleteWorkItem(id)
/**
* Updates the project item with the given id
* and data from the form
* @param {number} id the id from the database
* @param {SubmitEvent} e the event of the form that was submitted
* @param {string} id - the base id of the element
* @param {SubmitEvent} e - the event of the form that was submitted
*/
function updateProjectItem(id, e)
{
@ -785,20 +938,9 @@ function updateProjectItem(id, e)
}
/**
* Toggles the editing mode of the project item with the given id
* @param id {number} the id of the project item from the database
*/
function editProjectItem(id)
{
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
document.querySelector(`#title${id}`).removeAttribute("disabled");
document.querySelector(`#info${id}`).removeAttribute("disabled");
}
/**
* Deletes the project item with the given id
* @param id {number} the id of the project item from the database
* @param id {number} - the id of the project item from the database
*/
function deleteProjectItem(id)
{
@ -827,24 +969,25 @@ function deleteProjectItem(id)
/**
* Adds a new project to the page
* @param {number} id the id of the project from the database
* @param {string} isMainProject is it the main project
* @param {string} imgLocation the relative path of the image
* @param {string} title the title of the project
* @param {string} information the information about the project
* @param {string} projectLink the link to the project
* @param {string} gitLink the link to the git repository
* @param {number} ID - the id of the project from the database
* @param {string} isMainProject - is it the main project
* @param {string} imgLocation - the relative path of the image
* @param {string} title - the title of the project
* @param {string} information - the information about the project
* @param {string} projectLink - the link to the project
* @param {string} gitLink - the link to the git repository
*/
function addProject(id , isMainProject, imgLocation, title, information, projectLink, gitLink)
function addProject(ID, isMainProject, imgLocation, title, information, projectLink, gitLink)
{
let projectItem = document.createElement("form");
projectItem.id = "projectItem" + id;
let id = ID + "proj";
projectItem.id = "projectItem" + ID;
projectItem.classList.add("projItem");
projectItem.onsubmit = e => updateProjectItem(id, e);
projectItem.innerHTML = `
<div class="modifyBtnContainer">
<button class="edit" type="button" id="edit${id}" onclick="editProjectItem(${id})"><i class="fa-solid fa-pen-to-square"></i></button>
<button class="delete" type="button" id="delete${id}" onclick="deleteProjectItem(${id})"><i class="fa-solid fa-trash"></i></button>
<button class="edit" type="button" id="edit${id}" onclick="editProjectItem(${ID})"><i class="fa-solid fa-pen-to-square"></i></button>
<button class="delete" type="button" id="delete${id}" onclick="deleteProjectItem(${ID})"><i class="fa-solid fa-trash"></i></button>
</div>
<img class="displayedImage" id="projectImage${id}" src="${imgLocation}" alt="image preivew of the project">
<div class="formControl imageContainer">
@ -889,3 +1032,63 @@ function addProject(id , isMainProject, imgLocation, title, information, project
document.querySelector("#projList").appendChild(projectItem);
}
/**
* Deletes the post item with the given id
* @param {number} id - the id of the post item from the database
*/
function deletePostItem(id)
{
fetch("/api/blog/post/" + id, {
method: "DELETE",
headers: {
"Authorization": "Bearer " + localStorage.getItem("token")
}
}).then(res =>
{
if (res.ok)
{
document.querySelector(`#postInfo${id}`).remove();
return;
}
if (res.status === 401)
{
window.location.href = "./";
return;
}
res.json().then(json => alert(json.error));
});
}
/**
* Adds a new post info to the edit post table
* @param {number} ID - the id of the post
* @param {string} title - the title of the post
* @param {string} dateCreated - the date the post was created
* @param {string} dateModified - the date the post was modified
*/
function addPostInfo(ID, title, dateCreated, dateModified)
{
let postInfo = document.createElement("tr");
let id = ID + "post";
postInfo.id = "postInfo" + ID;
postInfo.innerHTML = `
<td>
${title}
</td>
<td>
${new Date(dateCreated).toLocaleDateString()}
</td>
<td>
${new Date(dateModified).toLocaleDateString()}
</td>
<td>
<button class="edit" type="button" id="edit${id}" onclick="editPostItem(${ID})"><i class="fa-solid fa-pen-to-square"></i></button>
<button class="delete" type="button" id="delete${id}" onclick="deletePostItem(${ID})"><i class="fa-solid fa-trash"></i></button>
</td>
`;
document.querySelector("#editPost table tbody").appendChild(postInfo);
}