Created feeds and UI for feeds and newsletter
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 19s
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 19s
Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
Vendored
+132
-12
@@ -2,13 +2,18 @@
|
||||
|
||||
namespace api\blog;
|
||||
|
||||
use api\utils\feedGenerator\FeedWriter;
|
||||
use api\utils\imgUtils;
|
||||
use DOMDocument;
|
||||
use PDO;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use function DI\string;
|
||||
use const api\utils\feedGenerator\ATOM;
|
||||
use const api\utils\feedGenerator\RSS2;
|
||||
|
||||
require_once __DIR__ . "/../utils/config.php";
|
||||
require_once __DIR__ . "/../utils/imgUtils.php";
|
||||
require_once __DIR__ . "/../utils/feedGenerator/FeedWriter.php";
|
||||
|
||||
/**
|
||||
* Blog Data Class
|
||||
@@ -18,12 +23,15 @@ class blogData
|
||||
{
|
||||
/**
|
||||
* Get all blog posts
|
||||
* @return array - Array of all blog posts or error message
|
||||
* @return array<array> - Array of all blog posts or error message
|
||||
*/
|
||||
public function getBlogPosts(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT * FROM blog ORDER BY featured DESC, dateCreated DESC;");
|
||||
$stmt = $conn->prepare("SELECT ID, title, DATE_FORMAT(dateCreated, '%Y-%m-%dT%TZ') AS dateCreated,
|
||||
DATE_FORMAT(dateModified, '%Y-%m-%dT%TZ') AS dateModified, featured, abstract,
|
||||
headerImg, body, bodyText, categories, folderID FROM blog ORDER BY featured DESC,
|
||||
dateCreated DESC;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@@ -40,12 +48,15 @@ class blogData
|
||||
/**
|
||||
* Get a blog post with the given ID
|
||||
* @param string $title - Title of the blog post
|
||||
* @return array - Array of all blog posts or error message
|
||||
* @return array - Array of blog post or error message
|
||||
*/
|
||||
public function getBlogPost(string $title): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT * FROM blog WHERE title = :title;");
|
||||
$stmt = $conn->prepare("SELECT ID, title, DATE_FORMAT(dateCreated, '%Y-%m-%dT%TZ') AS dateCreated,
|
||||
DATE_FORMAT(dateModified, '%Y-%m-%dT%TZ') AS dateModified, featured, abstract,
|
||||
headerImg, body, bodyText, categories, folderID FROM blog WHERE
|
||||
title = :title;");
|
||||
$stmt->bindParam(":title", $title);
|
||||
$stmt->execute();
|
||||
|
||||
@@ -67,7 +78,10 @@ class blogData
|
||||
public function getLatestBlogPost(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT * FROM blog ORDER BY dateCreated DESC LIMIT 1;");
|
||||
$stmt = $conn->prepare("SELECT ID, title, DATE_FORMAT(dateCreated, '%Y-%m-%dT%TZ') AS dateCreated,
|
||||
DATE_FORMAT(dateModified, '%Y-%m-%dT%TZ') AS dateModified, featured, abstract,
|
||||
headerImg, body, bodyText, categories, folderID FROM blog ORDER BY
|
||||
dateCreated DESC LIMIT 1;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@@ -88,7 +102,9 @@ class blogData
|
||||
public function getFeaturedBlogPost(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT * FROM blog WHERE featured = 1;");
|
||||
$stmt = $conn->prepare("SELECT ID, title, DATE_FORMAT(dateCreated, '%Y-%m-%dT%TZ') AS dateCreated,
|
||||
DATE_FORMAT(dateModified, '%Y-%m-%dT%TZ') AS dateModified, featured, abstract,
|
||||
headerImg, body, bodyText, categories, folderID FROM blog WHERE featured = 1;");
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
@@ -399,11 +415,10 @@ class blogData
|
||||
if (!in_array($from . $file, $srcList))
|
||||
{
|
||||
unlink($from . $file);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rename($from . $file, $to . $file);
|
||||
}
|
||||
|
||||
rename($from . $file, $to . $file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +433,7 @@ class blogData
|
||||
/**
|
||||
* Get all posts with the given category
|
||||
* @param string $category - Category of the post
|
||||
* @return array - Array of all posts with the given category or error message
|
||||
* @return array<array> - Array of all posts with the given category or error message
|
||||
*/
|
||||
public function getPostsByCategory(string $category): array
|
||||
{
|
||||
@@ -432,7 +447,7 @@ class blogData
|
||||
/**
|
||||
* 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
|
||||
* @return array<array> - Array of all posts with the given search term or error message
|
||||
*/
|
||||
public function searchBlog(string $searchTerm): array
|
||||
{
|
||||
@@ -527,4 +542,109 @@ class blogData
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the XML feed
|
||||
* @param mixed $type - Type of feed
|
||||
* @return array|string - Error message or the XML feed
|
||||
*/
|
||||
private function generateXMLFeed(mixed $type): array|string
|
||||
{
|
||||
ob_start();
|
||||
$feed = new FeedWriter($type);
|
||||
$feed->setTitle("Rohit Pai's Blog");
|
||||
$feed->setLink('https://rohitpai.co.uk/blog');
|
||||
$feed->setFeedURL('https://rohitpai.co.uk/api/blog/feed/atom');
|
||||
$feed->setChannelElement('updated', date(DATE_ATOM, time()));
|
||||
$feed->setChannelElement('author', ['name' => 'Rohit Pai']);
|
||||
$posts = $this->getBlogPosts();
|
||||
|
||||
if (isset($posts["errorMessage"]))
|
||||
{
|
||||
return $posts;
|
||||
}
|
||||
|
||||
foreach ($posts as $post)
|
||||
{
|
||||
$newItem = $feed->createNewItem();
|
||||
$newItem->setTitle($post["title"]);
|
||||
$newItem->setLink("https://rohitpai.co.uk/blog/post/" . rawurlencode($post["title"]) . "#disqus_thread");
|
||||
$newItem->setDate($post["dateModified"]);
|
||||
$newItem->setDescription($post["body"]);
|
||||
$feed->addItem($newItem);
|
||||
}
|
||||
|
||||
$feed->generateFeed();
|
||||
$atom = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $atom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the JSON feed
|
||||
* @return array|array[] - Error message or the JSON feed
|
||||
*/
|
||||
private function generateJSONFeed(): array
|
||||
{
|
||||
$posts = $this->getBlogPosts();
|
||||
|
||||
if (isset($posts["errorMessage"]))
|
||||
{
|
||||
return $posts;
|
||||
}
|
||||
|
||||
$json = array();
|
||||
$json["version"] = "https://jsonfeed.org/version/1.1";
|
||||
$json["title"] = "Rohit Pai's Blog";
|
||||
$json["home_page_url"] = "https://rohitpai.co.uk/blog";
|
||||
$json["feed_url"] = "https://rohitpai.co.uk/api/blog/feed/json";
|
||||
$json["description"] = "Rohit Pai's personal blog on all things self hosting and various other tech topics";
|
||||
$json["author"] = array(
|
||||
"name" => "Rohit Pai",
|
||||
"url" => "https://rohitpai.co.uk",
|
||||
"avatar" => "https://rohitpai.co.uk/imgs/profile.jpg"
|
||||
);
|
||||
$items = array();
|
||||
foreach ($posts as $post)
|
||||
{
|
||||
$items[] = array(
|
||||
"id" => string($post["ID"]),
|
||||
"url" => "https://rohitpai.co.uk/blog/post/" . rawurlencode($post["title"]) . "#disqus_thread",
|
||||
"title" => $post["title"],
|
||||
"date_published" => date($post["dateCreated"]),
|
||||
"date_modified" => date($post["dateModified"]),
|
||||
// "description" => $post["abstract"],
|
||||
"banner_image" => "https://rohitpai.co.uk/" . rawurlencode($post["headerImg"]),
|
||||
"content_html" => $post["body"]
|
||||
);
|
||||
}
|
||||
$json["items"] = $items;
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the RSS feed based on type
|
||||
* @param string $type - Type of feed
|
||||
* @return string|array - RSS feed or an error message
|
||||
*/
|
||||
public function getFeed(string $type): string|array
|
||||
{
|
||||
$feed = "";
|
||||
if ($type == "atom")
|
||||
{
|
||||
$feed = $this->generateXMLFeed(ATOM);
|
||||
}
|
||||
|
||||
if ($type == "rss")
|
||||
{
|
||||
$feed = $this->generateXMLFeed(RSS2);
|
||||
}
|
||||
|
||||
if ($type == "json")
|
||||
{
|
||||
$feed = $this->generateJSONFeed();
|
||||
}
|
||||
|
||||
return $feed;
|
||||
}
|
||||
}
|
||||
Vendored
+171
-131
@@ -4,6 +4,7 @@ namespace api\blog;
|
||||
require_once __DIR__ . "/../utils/routesInterface.php";
|
||||
require_once "blogData.php";
|
||||
|
||||
|
||||
use api\utils\routesInterface;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
@@ -80,35 +81,15 @@ class blogRoutes implements routesInterface
|
||||
|
||||
$app->get("/blog/post/{type}", function (Request $request, Response $response, $args)
|
||||
{
|
||||
if ($args["type"] != null)
|
||||
if ($args["type"] == null)
|
||||
{
|
||||
if ($args["type"] == "latest")
|
||||
{
|
||||
$post = $this->blogData->getLatestBlogPost();
|
||||
if (array_key_exists("errorMessage", $post))
|
||||
{
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide a title")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($args["type"] == "featured")
|
||||
{
|
||||
$post = $this->blogData->getFeaturedBlogPost();
|
||||
if (array_key_exists("errorMessage", $post))
|
||||
{
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response;
|
||||
}
|
||||
|
||||
$post = $this->blogData->getBlogPost($args["type"]);
|
||||
if ($args["type"] == "latest")
|
||||
{
|
||||
$post = $this->blogData->getLatestBlogPost();
|
||||
if (array_key_exists("errorMessage", $post))
|
||||
{
|
||||
$response->getBody()->write(json_encode($post));
|
||||
@@ -119,114 +100,173 @@ class blogRoutes implements routesInterface
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide a title")));
|
||||
if ($args["type"] == "featured")
|
||||
{
|
||||
$post = $this->blogData->getFeaturedBlogPost();
|
||||
if (array_key_exists("errorMessage", $post))
|
||||
{
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response;
|
||||
}
|
||||
|
||||
$post = $this->blogData->getBlogPost($args["type"]);
|
||||
if (array_key_exists("errorMessage", $post))
|
||||
{
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($post));
|
||||
return $response;
|
||||
|
||||
});
|
||||
|
||||
$app->get("/blog/feed/{type}", function (Request $request, Response $response, $args)
|
||||
{
|
||||
if ($args["type"] == null)
|
||||
{
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide a title")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$feed = $this->blogData->getFeed($args["type"]);
|
||||
if (is_array($feed))
|
||||
{
|
||||
$response->getBody()->write(json_encode($feed));
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
|
||||
if ($args["type"] == "atom")
|
||||
{
|
||||
$response->getBody()->write($feed);
|
||||
return $response->withHeader("Content-Type", "application/atom+xml");
|
||||
}
|
||||
|
||||
if ($args["type"] == "rss")
|
||||
{
|
||||
$response->getBody()->write($feed);
|
||||
return $response->withHeader("Content-Type", "application/rss+xml");
|
||||
}
|
||||
|
||||
if ($args["type"] == "json")
|
||||
{
|
||||
$response->getBody()->write(json_encode($feed));
|
||||
return $response->withHeader("Content-Type", "application/feed+json");
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Invalid feed type")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->get("/blog/search/{searchTerm}", function (Request $request, $response, $args)
|
||||
{
|
||||
if ($args["searchTerm"] != null)
|
||||
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);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide a search term")));
|
||||
return $response->withStatus(400);
|
||||
$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;
|
||||
|
||||
});
|
||||
|
||||
$app->patch("/blog/post/{id}", function (Request $request, Response $response, $args)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
if ($args["id"] != null)
|
||||
if ($args["id"] == null)
|
||||
{
|
||||
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
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["abstract"], $data["body"], $data["bodyText"], $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);
|
||||
}
|
||||
|
||||
$response->withStatus(201);
|
||||
return $response;
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
|
||||
return $response->withStatus(400);
|
||||
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
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (!preg_match('/[a-zA-Z0-9 ]+, |\w+/mx', $data["categories"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Categories must be in a CSV format")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$message = $this->blogData->updatePost($args["id"], $data["title"], intval($data["featured"]), $data["abstract"], $data["body"], $data["bodyText"], $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);
|
||||
}
|
||||
|
||||
$response->withStatus(201);
|
||||
return $response;
|
||||
|
||||
});
|
||||
|
||||
$app->delete("/blog/post/{id}", function (Request $request, Response $response, $args)
|
||||
{
|
||||
if ($args["id"] != null)
|
||||
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);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
|
||||
return $response->withStatus(400);
|
||||
$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;
|
||||
});
|
||||
|
||||
$app->post("/blog/post", function (Request $request, Response $response)
|
||||
@@ -290,28 +330,28 @@ class blogRoutes implements routesInterface
|
||||
{
|
||||
$files = $request->getUploadedFiles();
|
||||
|
||||
if ($args["id"] != null)
|
||||
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);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
|
||||
return $response->withStatus(400);
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user