Added in a cookie popup and proper newsletter functionality
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 21s

Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
2023-12-06 00:38:33 +00:00
parent e6522fb05e
commit 52614e5835
37 changed files with 2634 additions and 154 deletions
+843 -9
View File
@@ -8,11 +8,15 @@ use DOMDocument;
use PDO;
use Psr\Http\Message\UploadedFileInterface;
use DonatelloZa\RakePlus\RakePlus;
use function DI\string;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use function api\utils\dbConn;
use function api\utils\getEmailPassword;
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";
@@ -254,10 +258,10 @@ class blogData
* @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
* @param UploadedFileInterface|null $headerImg - Header image of the blog post
* @return int|string - ID of the blog post or error message
*/
public function createPost(string $title, string $abstract, string $body, string $bodyText, 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|null $headerImg): int|string
{
$conn = dbConn();
$folderID = uniqid();
@@ -289,6 +293,13 @@ class blogData
$keywords = implode(", ", RakePlus::create($bodyText)->keywords());
$latest = $this->getLatestBlogPost();
$prevTitle = $latest["title"];
$prevAbstract = $latest["abstract"];
$prevHeaderImage = $latest["headerImg"];
$headerImage = $targetFile["imgLocation"];
$stmt = $conn->prepare("INSERT INTO blog (title, dateCreated, dateModified, featured, headerImg, abstract, body, bodyText, categories, keywords, folderID)
VALUES (:title, :dateCreated, :dateModified, :featured, :headerImg, :abstract, :body, :bodyText, :categories, :keywords, :folderID);");
$stmt->bindParam(":title", $title);
@@ -296,7 +307,7 @@ class blogData
$stmt->bindParam(":dateModified", $dateCreated);
$isFeatured = $featured ? 1 : 0;
$stmt->bindParam(":featured", $isFeatured);
$stmt->bindParam(":headerImg", $targetFile["imgLocation"]);
$stmt->bindParam(":headerImg", $headerImage);
$stmt->bindParam(":abstract", $abstract);
$stmt->bindParam(":body", $newBody);
$stmt->bindParam(":bodyText", $bodyText);
@@ -304,12 +315,267 @@ class blogData
$stmt->bindParam(":keywords", $keywords);
$stmt->bindParam(":folderID", $folderID);
if ($stmt->execute())
if (!$stmt->execute())
{
return intval($conn->lastInsertId());
return "Error, couldn't create post";
}
return "Error, couldn't create post";
$stmtEmails = $conn->prepare("SELECT email FROM newsletter;");
$stmtEmails->execute();
$emails = $stmtEmails->fetchAll(PDO::FETCH_ASSOC);
$emailBody = <<<EOD
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rohit Pai's blog</title>
<style>
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/notosans/v34/o-0NIpQlx3QUlC5A4PNjXhFVZNyB.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Share Tech Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/sharetechmono/v15/J7aHnp1uDWRBEqV98dVQztYldFcLowEF.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
*{
box-sizing: border-box;
}
body, html {
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: Noto Sans KR, sans-serif;
font-style: normal;
font-weight: 500;
font-size: 22px;
line-height: 1.625rem;
min-height: 100%;
}
main, header, footer {
max-width: 768px;
margin: 0 auto;
}
a {
text-decoration: none;
text-transform: lowercase;
}
a:visited {
color: inherit;
}
h1, h2 {
font-family: Share Tech Mono, monospace;
font-style: normal;
font-weight: normal;
line-height: 2.5625rem;
text-transform: lowercase;
}
h1, nav {
font-size: 40px;
}
h2 {
font-size: 28px;
}
h3 {
font-size: 22px;
}
header {
background: hsla(80, 60%, 50%, 1);
color: #FFFFFF;
border-bottom: 5px solid hsla(0, 0%, 75%, 1);
}
header div.title, header div.byLine {
padding: 0 3em 1em;
}
header div.title h1 {
margin: 0;
padding: 1em 0 0;
font-size: 42px;
}
header div.byLine {
border-top: 5px solid hsla(80, 60%, 30%, 1);
}
a.btn {
background-color: hsla(80, 60%, 50%, 1);
text-decoration: none;
display: inline-flex;
padding: 1em 2em;
border-radius: 0.625em;
border: 0.3215em solid hsla(80, 60%, 50%, 1);
color: #FFFFFF;
text-align: center;
align-items: center;
max-height: 4em;
}
div.postContainer, div.container {
padding: 1em 2em 0;
margin: 0 auto 1em;
max-width: 768px;
}
div.postContainer ~ div.postContainer {
border-top: 5px solid hsla(0, 0%, 75%, 1);
}
div.postContainer > *, div.container > * {
margin: 0 auto;
max-width: 768px;
}
div.postContainer div.post h2, div.container div.content h2 {
margin: 0;
padding: 0;
}
div.postContainer div.image, div.container div.image {
width: 50%;
max-width: 100%;
height: auto;
}
div.postContainer div.image img {
-webkit-border-radius: 0.5em;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.container div.image img {
-webkit-border-radius: 10em;
-moz-border-radius: 10em;
border-radius: 10em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.postContainer div.image img, div.container div.image img {
min-width: 100%;
width: 100%;
}
footer {
background-color: hsla(80, 60%, 50%, 1);
margin-top: auto;
padding: 3em;
display: block;
color: #FFFFFF;
}
footer .nav {
width: 75%;
float: left;
}
footer .nav ul {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
footer .nav ul li {
display: inline-block;
margin: 0 1em;
}
footer .nav ul a {
color: #FFFFFF;
}
footer .date {
width: 25%;
float: right;
}
</style>
</head>
<body>
<header>
<div class="title">
<h1>Hey, I've got a new post!</h1>
</div>
<div class="byLine">
<p>Hello I'm Rohit an avid full-stack developer and home lab enthusiast. I love anything and everything
to do with full stack development, home labs, coffee and generally anything to do with tech.</p>
</div>
</header>
<main>
<div class="postContainer">
<h2>latest post</h2>
<div class="image">
<img src="$headerImage" alt="header image of the latest post">
</div>
<div class="post">
<h3>$title</h3>
<p>$abstract</p>
<a href="https://rohitpai.co.uk/blog/post/$title" class="btn">See Post</a>
</div>
</div>
<div class="postContainer">
<h2>in case you missed the previous post</h2>
<div class="image">
<img src="$prevHeaderImage" alt="header image of the previous post">
</div>
<div class="post">
<h3>$prevTitle</h3>
<p>$prevAbstract</p>
<a href="https://rohitpai.co.uk/blog/post/$prevTitle" class="btn">See Post</a>
</div>
</div>
</main>
<footer>
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>
</footer>
</body>
</html>
EOD;
foreach ($emails as $email)
{
$this->sendMail($email["email"], $emailBody, "Hey, Rohit's blog has a new post!");
}
return intval($conn->lastInsertId());
}
/**
@@ -616,12 +882,12 @@ class blogData
foreach ($posts as $post)
{
$items[] = array(
"id" => string($post["ID"]),
"id" => strval($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"],
"description" => $post["abstract"],
"banner_image" => "https://rohitpai.co.uk/" . rawurlencode($post["headerImg"]),
"content_html" => $post["body"]
);
@@ -655,4 +921,572 @@ class blogData
return $feed;
}
/**
* Add an email to the newsletter and send welcome email
* @param string $email - Email to add to the newsletter
* @return string|array - Success or error message
*/
public function addNewsletterEmail(string $email): string|array
{
$conn = dbConn();
$stmtCheckEmail = $conn->prepare("SELECT * FROM newsletter WHERE email = :email;");
$stmtCheckEmail->bindParam(":email", $email);
$stmtCheckEmail->execute();
$result = $stmtCheckEmail->fetch(PDO::FETCH_ASSOC);
if ($result)
{
return "Email already exists";
}
$stmt = $conn->prepare("INSERT INTO newsletter (email) VALUES (:email);");
$stmt->bindParam(":email", $email);
$stmt->execute();
$body = <<<EOD
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rohit Pai's blog</title>
<style>
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/notosans/v34/o-0NIpQlx3QUlC5A4PNjXhFVZNyB.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Share Tech Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/sharetechmono/v15/J7aHnp1uDWRBEqV98dVQztYldFcLowEF.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
*{
box-sizing: border-box;
}
body, html {
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: Noto Sans KR, sans-serif;
font-style: normal;
font-weight: 500;
font-size: 22px;
line-height: 1.625rem;
min-height: 100%;
}
main, header, footer {
max-width: 768px;
margin: 0 auto;
}
a {
text-decoration: none;
text-transform: lowercase;
}
a:visited {
color: inherit;
}
h1, h2 {
font-family: Share Tech Mono, monospace;
font-style: normal;
font-weight: normal;
line-height: 2.5625rem;
text-transform: lowercase;
}
h1, nav {
font-size: 40px;
}
h2 {
font-size: 28px;
}
h3 {
font-size: 22px;
}
header {
background: hsla(80, 60%, 50%, 1);
color: #FFFFFF;
border-bottom: 5px solid hsla(0, 0%, 75%, 1);
}
header div.title, header div.byLine {
padding: 0 3em 1em;
}
header div.title h1 {
margin: 0;
padding: 1em 0 0;
font-size: 42px;
}
header div.byLine {
border-top: 5px solid hsla(80, 60%, 30%, 1);
}
a.btn {
background-color: hsla(80, 60%, 50%, 1);
text-decoration: none;
display: inline-flex;
padding: 1em 2em;
border-radius: 0.625em;
border: 0.3215em solid hsla(80, 60%, 50%, 1);
color: #FFFFFF;
text-align: center;
align-items: center;
max-height: 4em;
}
div.postContainer, div.container {
padding: 1em 2em 0;
margin: 0 auto 1em;
max-width: 768px;
}
div.postContainer ~ div.postContainer {
border-top: 5px solid hsla(0, 0%, 75%, 1);
}
div.postContainer > *, div.container > * {
margin: 0 auto;
max-width: 768px;
}
div.postContainer div.post h2, div.container div.content h2 {
margin: 0;
padding: 0;
}
div.postContainer div.image, div.container div.image {
width: 50%;
max-width: 100%;
height: auto;
}
div.postContainer div.image img {
-webkit-border-radius: 0.5em;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.container div.image img {
-webkit-border-radius: 50em;
-moz-border-radius: 50em;
border-radius: 50em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.postContainer div.image img, div.container div.image img {
min-width: 100%;
width: 100%;
}
footer {
background-color: hsla(80, 60%, 50%, 1);
margin-top: auto;
padding: 3em;
display: block;
color: #FFFFFF;
}
footer .nav {
width: 75%;
float: left;
}
footer .nav ul {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
footer .nav ul li {
display: inline-block;
margin: 0 1em;
}
footer .nav ul a {
color: #FFFFFF;
}
footer .date {
width: 25%;
float: right;
}
</style>
</head>
<body>
<header>
<div class="title">
<h1>hello from rohit</h1>
</div>
<div class="byLine">
<p>Hello I'm Rohit an avid full-stack developer and home lab enthusiast. I love anything and everything
to do with full stack development, home labs, coffee and generally anything to do with tech.</p>
</div>
</header>
<main>
<div class="container">
<h2>hey there, i'm rohit!</h2>
<div class="image"><img src="https://rohitpai.co.uk/imgs/profile.jpg" alt=""></div>
<div class="content">
<h3>What to Expect</h3>
<p>You'll get an email from me everytime I make a new post to my blog. Sometimes, you may get a special
email on occasion, where I talk about something interesting that's not worth a full on post but I
Still want to tell you about it.</p>
<p>Don't worry, I won't spam you with emails. Well, thanks for signing up to my newsletter, hopefully
you'll hear from me soon!</p>
<p>P.S. Please consider adding this email address rohit@rohitpai.co.uk to your emails
contact list so that my emails won't get sent to span, thanks.</p>
</div>
</div>
</main>
<footer>
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>
</footer>
</body>
</html>
EOD;
return $this->sendMail($email, $body, "Hello from Rohit!");
}
/**
* Send an email to the given email address
* @param string $email - Email address to send the email to
* @param string $body - Body of the email
* @param string $subject - Subject of the email
* @return string|string[]
*/
public function sendMail(string $email, string $body, string $subject): string|array
{
$mail = new PHPMailer(true);
try
{
$mail->isSMTP();
$mail->Host = "smtp.hostinger.com";
$mail->SMTPAuth = true;
$mail->Username = "rohit@rohitpai.co.uk";
$mail->Password = getEmailPassword();
$mail->SMTPSecure = "tls";
$mail->Port = 587;
$mail->setFrom("rohit@rohitpai.co.uk", "Rohit Pai");
$mail->addAddress($email);
$mail->isHTML();
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
return "success";
}
catch (Exception $e)
{
return array("errorMessage" => "Error, couldn't send email because of " . $e);
}
}
/**
* @param string $email - Email to delete from the newsletter
* @return string - Success or error message
*/
public function deleteNewsletterEmail(string $email): string
{
$conn = dbConn();
$stmtCheckEmail = $conn->prepare("SELECT * FROM newsletter WHERE email = :email;");
$stmtCheckEmail->bindParam(":email", $email);
$stmtCheckEmail->execute();
$result = $stmtCheckEmail->fetch(PDO::FETCH_ASSOC);
if (!$result)
{
return "email not found";
}
$stmt = $conn->prepare("DELETE FROM newsletter WHERE email = :email;");
$stmt->bindParam(":email", $email);
$stmt->execute();;
return "success";
}
/**
* @param string $subject - Subject of the newsletter
* @param string $message - Message content
* @return array|string - Success or error message
*/
public function sendNewsletter(string $subject, string $message): array|string
{
$conn = dbConn();
$stmtEmails = $conn->prepare("SELECT email FROM newsletter;");
$stmtEmails->execute();
$emails = $stmtEmails->fetchAll(PDO::FETCH_ASSOC);
$msg = "";
$body = <<<EOD
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rohit Pai's blog</title>
<style>
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/notosans/v34/o-0NIpQlx3QUlC5A4PNjXhFVZNyB.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Share Tech Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/sharetechmono/v15/J7aHnp1uDWRBEqV98dVQztYldFcLowEF.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
*{
box-sizing: border-box;
}
body, html {
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: Noto Sans KR, sans-serif;
font-style: normal;
font-weight: 500;
font-size: 22px;
line-height: 1.625rem;
min-height: 100%;
}
main, header, footer {
max-width: 768px;
margin: 0 auto;
}
a {
text-decoration: none;
text-transform: lowercase;
}
a:visited {
color: inherit;
}
h1, h2 {
font-family: Share Tech Mono, monospace;
font-style: normal;
font-weight: normal;
line-height: 2.5625rem;
text-transform: lowercase;
}
h1, nav {
font-size: 40px;
}
h2 {
font-size: 28px;
}
h3 {
font-size: 22px;
}
header {
background: hsla(80, 60%, 50%, 1);
color: #FFFFFF;
border-bottom: 5px solid hsla(0, 0%, 75%, 1);
}
header div.title, header div.byLine {
padding: 0 3em 1em;
}
header div.title h1 {
margin: 0;
padding: 1em 0 0;
font-size: 42px;
}
header div.byLine {
border-top: 5px solid hsla(80, 60%, 30%, 1);
}
a.btn {
background-color: hsla(80, 60%, 50%, 1);
text-decoration: none;
display: inline-flex;
padding: 1em 2em;
border-radius: 0.625em;
border: 0.3215em solid hsla(80, 60%, 50%, 1);
color: #FFFFFF;
text-align: center;
align-items: center;
max-height: 4em;
}
div.postContainer, div.container {
padding: 1em 2em 0;
margin: 0 auto 1em;
max-width: 768px;
}
div.postContainer ~ div.postContainer {
border-top: 5px solid hsla(0, 0%, 75%, 1);
}
div.postContainer > *, div.container > * {
margin: 0 auto;
max-width: 768px;
}
div.postContainer div.post h2, div.container div.content h2 {
margin: 0;
padding: 0;
}
div.postContainer div.image, div.container div.image {
width: 50%;
max-width: 100%;
height: auto;
}
div.postContainer div.image img {
-webkit-border-radius: 0.5em;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.container div.image img {
-webkit-border-radius: 50em;
-moz-border-radius: 50em;
border-radius: 50em;
border: 4px solid hsla(0, 0%, 75%, 1);
}
div.postContainer div.image img, div.container div.image img {
min-width: 100%;
width: 100%;
}
footer {
background-color: hsla(80, 60%, 50%, 1);
margin-top: auto;
padding: 3em;
display: block;
color: #FFFFFF;
}
footer .nav {
width: 75%;
float: left;
}
footer .nav ul {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
footer .nav ul li {
display: inline-block;
margin: 0 1em;
}
footer .nav ul a {
color: #FFFFFF;
}
footer .date {
width: 25%;
float: right;
}
</style>
</head>
<body>
<header>
<div class="title">
<h1>a surprise hello from rohit</h1>
</div>
<div class="byLine">
<p>Hello I'm Rohit an avid full-stack developer and home lab enthusiast. I love anything and everything
to do with full stack development, home labs, coffee and generally anything to do with tech.</p>
</div>
</header>
<main>
<div class="container">
<h2>$subject</h2>
<div class="content">
$message
</div>
</div>
</main>
<footer>
<div class="nav">
<ul>
<li><a href="https://rohitpai.co.uk/blog">&lt;https://rohitpai.co.uk/blog&gt;</a></li>
<li><a href="https://rohitpai.co.uk/blog/unsubscribe">&lt;Unsubscribe&gt;</a></li>
</ul>
</div>
<div class="date">2023</div>
</footer>
</body>
</html>
EOD;
foreach ($emails as $email)
{
$msg = $this->sendMail($email["email"], $body, $subject);
if (is_array($msg))
{
return $msg;
}
}
return $msg;
}
}