added backend search functionality

Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
Rohit Pai 2023-10-31 19:36:51 +00:00
parent 929060ce70
commit b4ab7900db
18 changed files with 1299 additions and 88 deletions

View File

@ -23,7 +23,7 @@ class blogData
public function getBlogPosts(): array public function getBlogPosts(): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog ORDER BY dateCreated DESC;"); $stmt = $conn->prepare("SELECT * FROM blog ORDER BY featured DESC, dateCreated DESC;");
$stmt->execute(); $stmt->execute();
// set the resulting array to associative // set the resulting array to associative
@ -102,28 +102,9 @@ class blogData
} }
/** /**
* Get the blog posts with the given category * Get all unique categories
* @param string $category - Category of the blog post * @return string[] - Array of all categories or error message
* @return array - Array of the blog posts with the given category or error message
*/ */
public function getBlogPostsWithCategory(string $category): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE categories LIKE :category;");
$stmt->bindParam(":category", $category);
$stmt->execute();
// set the resulting array to associative
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result)
{
return $result;
}
return array("errorMessage" => "Error, blog post could not found");
}
public function getCategories(): array public function getCategories(): array
{ {
$conn = dbConn(); $conn = dbConn();
@ -185,11 +166,12 @@ class blogData
* @param bool $featured - Whether the blog post is featured or not * @param bool $featured - Whether the blog post is featured or not
* @param string $abstract - Abstract of the blog post i.e. a short description * @param string $abstract - Abstract of the blog post i.e. a short description
* @param string $body - Body of the blog post * @param string $body - Body of the blog post
* @param string $bodyText - Body of the blog post as plain text
* @param string $dateModified - Date the blog post was modified * @param string $dateModified - Date the blog post was modified
* @param string $categories - Categories of the blog post * @param string $categories - Categories of the blog post
* @return bool|string - Success or error message * @return bool|string - Success or error message
*/ */
public function updatePost(int $ID, string $title, bool $featured, string $abstract, string $body, string $dateModified, string $categories): bool|string public function updatePost(int $ID, string $title, bool $featured, string $abstract, string $body, string $bodyText, string $dateModified, string $categories): bool|string
{ {
$conn = dbConn(); $conn = dbConn();
@ -227,12 +209,13 @@ class blogData
$from = "../blog/imgs/tmp/"; $from = "../blog/imgs/tmp/";
$newBody = $this->changeHTMLSrc($body, $to, $from); $newBody = $this->changeHTMLSrc($body, $to, $from);
$stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, abstract = :abstract, body = :body, dateModified = :dateModified, categories = :categories WHERE ID = :ID;"); $stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, abstract = :abstract, body = :body, bodyText = :bodyText, dateModified = :dateModified, categories = :categories WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID); $stmt->bindParam(":ID", $ID);
$stmt->bindParam(":title", $title); $stmt->bindParam(":title", $title);
$stmt->bindParam(":featured", $featured); $stmt->bindParam(":featured", $featured);
$stmt->bindParam(":abstract", $abstract); $stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody); $stmt->bindParam(":body", $newBody);
$stmt->bindParam(":bodyText", $bodyText);
$stmt->bindParam(":dateModified", $dateModified); $stmt->bindParam(":dateModified", $dateModified);
$stmt->bindParam(":categories", $categories); $stmt->bindParam(":categories", $categories);
@ -246,13 +229,14 @@ class blogData
* @param string $title - Title of the blog post * @param string $title - Title of the blog post
* @param string $abstract - Abstract of the blog post i.e. a short description * @param string $abstract - Abstract of the blog post i.e. a short description
* @param string $body - Body of the blog post * @param string $body - Body of the blog post
* @param string $bodyText - Body of the blog post as plain text
* @param string $dateCreated - Date the blog post was created * @param string $dateCreated - Date the blog post was created
* @param bool $featured - Whether the blog post is featured or not * @param bool $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post * @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post * @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message * @return int|string - ID of the blog post or error message
*/ */
public function createPost(string $title, string $abstract, string $body, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string public function createPost(string $title, string $abstract, string $body, string $bodyText, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string
{ {
$conn = dbConn(); $conn = dbConn();
$folderID = uniqid(); $folderID = uniqid();
@ -282,8 +266,8 @@ class blogData
$stmtMainProject->execute(); $stmtMainProject->execute();
} }
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, categories, folderID) $stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, bodyText, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :categories, :folderID);"); VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :bodyText, :categories, :folderID);");
$stmt->bindParam(":title", $title); $stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated); $stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated); $stmt->bindParam(":dateModified", $dateCreated);
@ -292,6 +276,7 @@ class blogData
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]); $stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":abstract", $abstract); $stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody); $stmt->bindParam(":body", $newBody);
$stmt->bindParam(":bodyText", $bodyText);
$stmt->bindParam(":categories", $categories); $stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID); $stmt->bindParam(":folderID", $folderID);
@ -443,4 +428,38 @@ class blogData
$stmt->execute(); $stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt->fetchAll(PDO::FETCH_ASSOC);
} }
/**
* Search for a blog post with the given search term
* @param string $searchTerm - Search term
* @return array - Array of all posts with the given search term or error message
*/
public function searchBlog(string $searchTerm): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE MATCH(title, bodyText) AGAINST(:searchTerm IN NATURAL LANGUAGE MODE);");
$stmt->bindParam(":searchTerm", $searchTerm);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result)
{
$bodyText = array_column($result, "bodyText");
// go through each body plain text and get the sentence before, the sentence with the search term and the sentence after and append it to a new array
foreach ($bodyText as $key => $text)
{
$text = strtolower($text);
$searchTerm = strtolower($searchTerm);
$pos = strpos($text, $searchTerm);
$start = strrpos(substr($text, 0, $pos), ".") + 1;
$end = strpos($text, ".", $pos);
$result[$key]["bodyText"] = substr($text, $start, $end - $start);
}
return $result;
}
return array("errorMessage" => "Error, could not find posts");
}
} }

View File

@ -123,12 +123,34 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
}); });
$app->get("/blog/search/{searchTerm}", function (Request $request, $response, $args)
{
if ($args["searchTerm"] != null)
{
$posts = $this->blogData->searchBlog($args["searchTerm"]);
$json = json_encode($posts);
$response->getBody()->write($json);
if (array_key_exists("errorMessage", $posts))
{
$response->withStatus(404);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide a search term")));
return $response->withStatus(400);
});
$app->patch("/blog/post/{id}", function (Request $request, Response $response, $args) $app->patch("/blog/post/{id}", function (Request $request, Response $response, $args)
{ {
$data = $request->getParsedBody(); $data = $request->getParsedBody();
if ($args["id"] != null) if ($args["id"] != null)
{ {
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateModified"]) || empty($data["categories"])) if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["bodyText"]) || empty($data["dateModified"]) || empty($data["categories"]))
{ {
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent"))); $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
@ -142,7 +164,7 @@ class blogRoutes implements routesInterface
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["bodyText"], $data["dateModified"], $data["categories"]);
if ($message === "post not found") if ($message === "post not found")
{ {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

2
dist/index.html vendored

File diff suppressed because one or more lines are too long

1006
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
"author": "Rohit Pai", "author": "Rohit Pai",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@ckeditor/ckeditor5-clipboard": "^40.0.0",
"browser-sync": "^2.27.5", "browser-sync": "^2.27.5",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-clean-css": "^4.3.0", "gulp-clean-css": "^4.3.0",
@ -25,5 +26,11 @@
"require": "^0.4.4", "require": "^0.4.4",
"source-map-generator": "^0.8.0", "source-map-generator": "^0.8.0",
"vinyl-ftp": "^0.6.1" "vinyl-ftp": "^0.6.1"
},
"devDependencies": {
"terser-webpack-plugin": "^5.3.9",
"vinyl-named-with-path": "^1.0.0",
"webpack-cli": "^5.1.4",
"webpack-stream": "^7.0.0"
} }
} }

View File

@ -23,7 +23,7 @@ class blogData
public function getBlogPosts(): array public function getBlogPosts(): array
{ {
$conn = dbConn(); $conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog ORDER BY dateCreated DESC;"); $stmt = $conn->prepare("SELECT * FROM blog ORDER BY featured DESC, dateCreated DESC;");
$stmt->execute(); $stmt->execute();
// set the resulting array to associative // set the resulting array to associative
@ -102,28 +102,9 @@ class blogData
} }
/** /**
* Get the blog posts with the given category * Get all unique categories
* @param string $category - Category of the blog post * @return string[] - Array of all categories or error message
* @return array - Array of the blog posts with the given category or error message
*/ */
public function getBlogPostsWithCategory(string $category): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE categories LIKE :category;");
$stmt->bindParam(":category", $category);
$stmt->execute();
// set the resulting array to associative
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result)
{
return $result;
}
return array("errorMessage" => "Error, blog post could not found");
}
public function getCategories(): array public function getCategories(): array
{ {
$conn = dbConn(); $conn = dbConn();
@ -185,11 +166,12 @@ class blogData
* @param bool $featured - Whether the blog post is featured or not * @param bool $featured - Whether the blog post is featured or not
* @param string $abstract - Abstract of the blog post i.e. a short description * @param string $abstract - Abstract of the blog post i.e. a short description
* @param string $body - Body of the blog post * @param string $body - Body of the blog post
* @param string $bodyText - Body of the blog post as plain text
* @param string $dateModified - Date the blog post was modified * @param string $dateModified - Date the blog post was modified
* @param string $categories - Categories of the blog post * @param string $categories - Categories of the blog post
* @return bool|string - Success or error message * @return bool|string - Success or error message
*/ */
public function updatePost(int $ID, string $title, bool $featured, string $abstract, string $body, string $dateModified, string $categories): bool|string public function updatePost(int $ID, string $title, bool $featured, string $abstract, string $body, string $bodyText, string $dateModified, string $categories): bool|string
{ {
$conn = dbConn(); $conn = dbConn();
@ -227,12 +209,13 @@ class blogData
$from = "../blog/imgs/tmp/"; $from = "../blog/imgs/tmp/";
$newBody = $this->changeHTMLSrc($body, $to, $from); $newBody = $this->changeHTMLSrc($body, $to, $from);
$stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, abstract = :abstract, body = :body, dateModified = :dateModified, categories = :categories WHERE ID = :ID;"); $stmt = $conn->prepare("UPDATE blog SET title = :title, featured = :featured, abstract = :abstract, body = :body, bodyText = :bodyText, dateModified = :dateModified, categories = :categories WHERE ID = :ID;");
$stmt->bindParam(":ID", $ID); $stmt->bindParam(":ID", $ID);
$stmt->bindParam(":title", $title); $stmt->bindParam(":title", $title);
$stmt->bindParam(":featured", $featured); $stmt->bindParam(":featured", $featured);
$stmt->bindParam(":abstract", $abstract); $stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody); $stmt->bindParam(":body", $newBody);
$stmt->bindParam(":bodyText", $bodyText);
$stmt->bindParam(":dateModified", $dateModified); $stmt->bindParam(":dateModified", $dateModified);
$stmt->bindParam(":categories", $categories); $stmt->bindParam(":categories", $categories);
@ -246,13 +229,14 @@ class blogData
* @param string $title - Title of the blog post * @param string $title - Title of the blog post
* @param string $abstract - Abstract of the blog post i.e. a short description * @param string $abstract - Abstract of the blog post i.e. a short description
* @param string $body - Body of the blog post * @param string $body - Body of the blog post
* @param string $bodyText - Body of the blog post as plain text
* @param string $dateCreated - Date the blog post was created * @param string $dateCreated - Date the blog post was created
* @param bool $featured - Whether the blog post is featured or not * @param bool $featured - Whether the blog post is featured or not
* @param string $categories - Categories of the blog post * @param string $categories - Categories of the blog post
* @param UploadedFileInterface $headerImg - Header image of the blog post * @param UploadedFileInterface $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message * @return int|string - ID of the blog post or error message
*/ */
public function createPost(string $title, string $abstract, string $body, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string public function createPost(string $title, string $abstract, string $body, string $bodyText, string $dateCreated, bool $featured, string $categories, UploadedFileInterface $headerImg): int|string
{ {
$conn = dbConn(); $conn = dbConn();
$folderID = uniqid(); $folderID = uniqid();
@ -282,8 +266,8 @@ class blogData
$stmtMainProject->execute(); $stmtMainProject->execute();
} }
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, categories, folderID) $stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, bodyText, categories, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :categories, :folderID);"); VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :bodyText, :categories, :folderID);");
$stmt->bindParam(":title", $title); $stmt->bindParam(":title", $title);
$stmt->bindParam(":dateCreated", $dateCreated); $stmt->bindParam(":dateCreated", $dateCreated);
$stmt->bindParam(":dateModified", $dateCreated); $stmt->bindParam(":dateModified", $dateCreated);
@ -292,6 +276,7 @@ class blogData
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]); $stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":abstract", $abstract); $stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody); $stmt->bindParam(":body", $newBody);
$stmt->bindParam(":bodyText", $bodyText);
$stmt->bindParam(":categories", $categories); $stmt->bindParam(":categories", $categories);
$stmt->bindParam(":folderID", $folderID); $stmt->bindParam(":folderID", $folderID);
@ -443,4 +428,38 @@ class blogData
$stmt->execute(); $stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt->fetchAll(PDO::FETCH_ASSOC);
} }
/**
* Search for a blog post with the given search term
* @param string $searchTerm - Search term
* @return array - Array of all posts with the given search term or error message
*/
public function searchBlog(string $searchTerm): array
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM blog WHERE MATCH(title, bodyText) AGAINST(:searchTerm IN NATURAL LANGUAGE MODE);");
$stmt->bindParam(":searchTerm", $searchTerm);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result)
{
$bodyText = array_column($result, "bodyText");
// go through each body plain text and get the sentence before, the sentence with the search term and the sentence after and append it to a new array
foreach ($bodyText as $key => $text)
{
$text = strtolower($text);
$searchTerm = strtolower($searchTerm);
$pos = strpos($text, $searchTerm);
$start = strrpos(substr($text, 0, $pos), ".") + 1;
$end = strpos($text, ".", $pos);
$result[$key]["bodyText"] = substr($text, $start, $end - $start);
}
return $result;
}
return array("errorMessage" => "Error, could not find posts");
}
} }

View File

@ -123,12 +123,34 @@ class blogRoutes implements routesInterface
return $response->withStatus(400); return $response->withStatus(400);
}); });
$app->get("/blog/search/{searchTerm}", function (Request $request, $response, $args)
{
if ($args["searchTerm"] != null)
{
$posts = $this->blogData->searchBlog($args["searchTerm"]);
$json = json_encode($posts);
$response->getBody()->write($json);
if (array_key_exists("errorMessage", $posts))
{
$response->withStatus(404);
}
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Please provide a search term")));
return $response->withStatus(400);
});
$app->patch("/blog/post/{id}", function (Request $request, Response $response, $args) $app->patch("/blog/post/{id}", function (Request $request, Response $response, $args)
{ {
$data = $request->getParsedBody(); $data = $request->getParsedBody();
if ($args["id"] != null) if ($args["id"] != null)
{ {
if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["dateModified"]) || empty($data["categories"])) if (empty($data["title"]) || strlen($data["featured"]) == 0 || empty($data["body"]) || empty($data["bodyText"]) || empty($data["dateModified"]) || empty($data["categories"]))
{ {
// uh oh sent some empty data // uh oh sent some empty data
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent"))); $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
@ -142,7 +164,7 @@ class blogRoutes implements routesInterface
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["bodyText"], $data["dateModified"], $data["categories"]);
if ($message === "post not found") if ($message === "post not found")
{ {

View File

@ -26,8 +26,47 @@ h3 {
line-height: 2.1875rem; line-height: 2.1875rem;
} }
div.menu {
width: 100%;
border-bottom: 5px solid var(--mutedGrey);
}
div.menu input:not([type="submit"]) {
width: auto;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
div.menu > ul {
list-style: none;
display: flex;
flex-direction: row;
justify-content: space-around;
}
div.menu ul li {
display: flex;
flex-direction: row;
}
div.menu ul li button.btn {
padding: initial;
border-radius: 0 0.5em 0.5em 0;
}
div.menu ul li input:not([type="submit"]):focus + button.btn,
div.menu ul li:hover button.btn,
div.menu ul li:focus button.btn {
background: var(--primaryHover);
border: 0.3215em solid var(--primaryHover);
}
div.menu ul li:hover input:not([type="submit"]),
div.menu ul li:focus input:not([type="submit"]) {
border: 0.3215em solid var(--primaryHover);
}
section.largePost { section.largePost {
/*margin: 0 5em;*/
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-evenly; justify-content: space-evenly;

View File

@ -116,6 +116,25 @@ function createLargePost(post)
*/ */
function loadHomeContent() function loadHomeContent()
{ {
let menuBar = document.createElement('div');
menuBar.classList.add('menuBar');
// language=HTML
menuBar.innerHTML = `
<div class="menu">
<ul>
<li><a href="/blog/category" class="link">categories</a></li>
<li>
<label for="search" aria-hidden="true" hidden>search</label>
<input type="search" name="search" id="search"
placeholder="Search...">
<button type="submit" class="btn btnPrimary"><i
class="fa fa-search"></i></button>
</li>
</ul>
</div>
`;
document.querySelector('#main').appendChild(menuBar);
fetch('/api/blog/post').then(res => res.json().then(json => fetch('/api/blog/post').then(res => res.json().then(json =>
{ {
for (let i = 0; i < json.length; i++) for (let i = 0; i < json.length; i++)
@ -130,10 +149,10 @@ function loadHomeContent()
featuredPost.appendChild(h1); featuredPost.appendChild(h1);
let outerContent = createLargePost(json[i]); let outerContent = createLargePost(json[i]);
featuredPost.appendChild(outerContent); featuredPost.appendChild(outerContent);
document.querySelector('#main').prepend(featuredPost); document.querySelector('#main').appendChild(featuredPost);
} }
if (i === 0) if (i === 1)
{ {
let latestPost = document.createElement('section'); let latestPost = document.createElement('section');
latestPost.classList.add('largePost'); latestPost.classList.add('largePost');
@ -143,8 +162,9 @@ function loadHomeContent()
latestPost.appendChild(h1); latestPost.appendChild(h1);
let outerContent = createLargePost(json[i]); let outerContent = createLargePost(json[i]);
latestPost.appendChild(outerContent); latestPost.appendChild(outerContent);
document.querySelector('#main').prepend(latestPost); document.querySelector('#main').appendChild(latestPost);
} }
} }
})); }));
} }

View File

@ -108,8 +108,9 @@ a.btnPrimary[disabled]:hover, button.btnPrimary[disabled]:hover {
border: 0.3215em solid var(--notAvailableHover); border: 0.3215em solid var(--notAvailableHover);
} }
a.btnPrimary:hover, button.btnPrimary:hover form input[type="submit"]:hover { a.btnPrimary:hover, button.btnPrimary:hover, form input[type="submit"]:hover {
background: var(--primaryHover); background: var(--primaryHover);
border: 0.3215em solid var(--primaryHover);
} }
a.btn:active, button.btn:active, form input[type="submit"]:active { a.btn:active, button.btn:active, form input[type="submit"]:active {
@ -129,18 +130,14 @@ a.btn:active, button.btn:active, form input[type="submit"]:active {
} }
form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid { form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid {
border: 4px solid var(--errorDefault); border: 0.3125em solid var(--errorDefault);
} }
form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus { form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus {
border: 4px solid var(--errorHover); border: 0.3125em solid var(--errorHover);
box-shadow: 0 4px 2px 0 var(--mutedBlack); box-shadow: 0 4px 2px 0 var(--mutedBlack);
} }
form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus {
border: 4px solid var(--primaryHover);
}
form .formControl input:not([type="submit"]) { form .formControl input:not([type="submit"]) {
height: 3em; height: 3em;
} }
@ -150,7 +147,6 @@ form .formControl {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
/*align-items: flex-start;*/
} }
form .formControl.passwordControl { form .formControl.passwordControl {
@ -163,13 +159,13 @@ form input[type="submit"] {
form .formControl input:not([type="submit"]), form .formControl textarea, form .formControl input:not([type="submit"]), form .formControl textarea,
form .formControl .ck.ck-editor__top .ck-sticky-panel .ck-toolbar, form .formControl .ck.ck-editor__top .ck-sticky-panel .ck-toolbar,
form .formControl .ck.ck-editor__main .ck-content { form .formControl .ck.ck-editor__main .ck-content, div.menu input:not([type="submit"]) {
width: 100%; width: 100%;
border: 4px solid var(--primaryDefault); border: 0.3125em solid var(--primaryDefault);
background: none; background: none;
outline: none; outline: none;
-webkit-border-radius: 1em; -webkit-border-radius: 0.5em;
-moz-border-radius: 1em; -moz-border-radius: 0.5em;
border-radius: 0.5em; border-radius: 0.5em;
padding: 0 0.5em; padding: 0 0.5em;
} }
@ -179,17 +175,18 @@ form .formControl textarea {
} }
form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid { form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid {
border: 4px solid var(--errorDefault); border: 0.3125em solid var(--errorDefault);
} }
form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus { form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus {
border: 4px solid var(--errorHover); border: 0.3125em solid var(--errorHover);
box-shadow: 0 4px 2px 0 var(--mutedBlack); box-shadow: 0 4px 2px 0 var(--mutedBlack);
} }
form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus, form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus,
form .formControl input:not([type="submit"]):hover, form .formControl textarea:hover { form .formControl input:not([type="submit"]):hover, form .formControl textarea:hover,
border: 4px solid var(--primaryHover); div.menu input:not([type="submit"]):focus, div.menu input:not([type="submit"]):hover {
border: 0.3125em solid var(--primaryHover);
} }
form .formControl input:not([type="submit"]) { form .formControl input:not([type="submit"]) {

View File

@ -45,7 +45,7 @@ main.editor section {
flex-direction: column; flex-direction: column;
} }
section#editPost { section#curriculumVitae {
display: flex; display: flex;
} }

View File

@ -2,6 +2,7 @@ let dateOptions = {month: 'short', year: 'numeric'};
let textareaLoaded = false; let textareaLoaded = false;
let editors = {}; let editors = {};
let posts = null; let posts = null;
const smallPaddingElements = ['figcaption', 'li'];
document.addEventListener('DOMContentLoaded', () => document.addEventListener('DOMContentLoaded', () =>
{ {
// check if the userData is logged in, if not redirect to log in // check if the userData is logged in, if not redirect to log in
@ -262,6 +263,7 @@ document.querySelector("#addPostForm").addEventListener("submit", e =>
data.append("featured", document.querySelector("#isFeatured").checked ? "1" : "0"); data.append("featured", document.querySelector("#isFeatured").checked ? "1" : "0");
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('bodyText', viewToPlainText(editors['CKEditorAddPost'].editing.view.document.getRoot()));
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.toLowerCase()); data.append('categories', document.querySelector('#postCategories').value.toLowerCase());
data.append("headerImg", document.querySelector("#headerImg").files[0]); data.append("headerImg", document.querySelector("#headerImg").files[0]);
@ -315,6 +317,7 @@ document.querySelector("#editPostForm").addEventListener("submit", e =>
data["featured"] = document.querySelector("#editIsFeatured").checked ? "1" : "0"; data["featured"] = document.querySelector("#editIsFeatured").checked ? "1" : "0";
data["abstract"] = document.querySelector("#editPostAbstract").value; data["abstract"] = document.querySelector("#editPostAbstract").value;
data["body"] = editors["CKEditorEditPost"].getData(); data["body"] = editors["CKEditorEditPost"].getData();
data['bodyText'] = viewToPlainText(editors['CKEditorEditPost'].editing.view.document.getRoot());
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.toLowerCase(); data['categories'] = document.querySelector('#editPostCategories').value.toLowerCase();
@ -364,6 +367,7 @@ document.querySelector("#editPostForm").addEventListener("submit", e =>
{ {
document.querySelector("#editPostForm").reset(); document.querySelector("#editPostForm").reset();
document.querySelector("#editPostForm input[type='submit']").id = ""; document.querySelector("#editPostForm input[type='submit']").id = "";
console.log();
editors["CKEditorEditPost"].setData(""); editors["CKEditorEditPost"].setData("");
showSuccessMessage("Post edited successfully", "editPost"); showSuccessMessage("Post edited successfully", "editPost");
return; return;
@ -468,6 +472,62 @@ function goToPage(id)
} }
/**
* Converts th CKEditor data to plain text
* @param viewItem - The CKEditor data
* @returns {string} - The plain text
*/
function viewToPlainText(viewItem)
{
let text = '';
if (viewItem.is('$text') || viewItem.is('$textProxy'))
{
// If item is `Text` or `TextProxy` simple take its text data.
text = viewItem.data;
}
else if (viewItem.is('element', 'img') && viewItem.hasAttribute('alt'))
{
// Special case for images - use alt attribute if it is provided.
text = viewItem.getAttribute('alt');
}
else if (viewItem.is('element', 'br'))
{
// A soft break should be converted into a single line break (#8045).
text = '\n';
}
else
{
// Other elements are document fragments, attribute elements or container elements.
// They don't have their own text value, so convert their children.
let prev = null;
for (const child of viewItem.getChildren())
{
const childText = viewToPlainText(child);
// Separate container element children with one or more new-line characters.
if (prev && (prev.is('containerElement') || child.is('containerElement')))
{
if (smallPaddingElements.includes(prev.name) || smallPaddingElements.includes(child.name))
{
text += '\n';
}
else
{
text += '\n\n';
}
}
text += childText;
prev = child;
}
}
return text;
}
/** /**
* Removes the active class from all nav items and adds it to the one with the given 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
@ -768,7 +828,7 @@ function createEditors(...ids)
console.warn('Build id: 1eo8ioyje2om-vgar4aghypdm'); console.warn('Build id: 1eo8ioyje2om-vgar4aghypdm');
console.error(error); console.error(error);
}); });
}) });
} }
/** /**
@ -1250,7 +1310,7 @@ function addProject(ID, isMainProject, imgLocation, title, information, projectL
<input type="checkbox" id="isMainProject${id}" name="isMainProject${id}" ${(isMainProject === "true" ? "checked=''" : "")}> <input type="checkbox" id="isMainProject${id}" name="isMainProject${id}" ${(isMainProject === "true" ? "checked=''" : "")}>
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
</div> </div>
<div class="formControl infoContainer"> <div class="formControl infoContainer">
<textarea name="info${id}" id="info${id}" disabled>${information}</textarea> <textarea name="info${id}" id="info${id}" disabled>${information}</textarea>
</div> </div>

View File

@ -44,7 +44,7 @@
<section id="about"> <section id="about">
<h1>about</h1> <h1>about</h1>
<div> <div>
<p>Hi, I'm Rohit, a computer science student at The University of Nottingham with experience in multiple <p>Hi, I'm Rohit, a Full Stack Developer at Cadonix with experience in multiple
programming languages such as Java, C#, Python, HTML, CSS, JS, PHP. Bringing forth a motivated programming languages such as Java, C#, Python, HTML, CSS, JS, PHP. Bringing forth a motivated
attitude and a variety of powerful skills. Very good at bringing a team together to get a project attitude and a variety of powerful skills. Very good at bringing a team together to get a project
finished. Below are some of my projects that I have worked on. </p> finished. Below are some of my projects that I have worked on. </p>