Various fixes for the blog and editor. As well as finally adding in the carousel!
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 24s

Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
2024-11-04 22:17:42 +00:00
parent 7d6eeb2310
commit b28e7b2da5
13 changed files with 2287 additions and 944 deletions
+35 -4
View File
@@ -673,9 +673,17 @@ EOD;
*/
public function changeHTMLSrc(string $body, string $to, string $from): string
{
// $body = preg_replace_callback('/>([^<]+)</', function($matches) {
// // Convert special characters to HTML entities
// return '>' . htmlentities(trim($matches[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '<';
// }, $body);
$htmlDoc = new DOMDocument();
$body = mb_convert_encoding($body, "HTML-ENTITIES", "UTF-8");
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
// Load the raw HTML content into DOMDocument
@$htmlDoc->loadHTML($body, LIBXML_NOERROR);
// Get the body and process images
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
$imgs = $doc->getElementsByTagName('img');
@@ -688,9 +696,11 @@ EOD;
$srcList[] = $src;
$fileName = basename($src);
// Update the src attribute to the new location
$img->setAttribute("src", substr($to, 2) . $fileName);
}
// Rename files and clean up old ones
$files = scandir($from);
foreach ($files as $file)
{
@@ -706,15 +716,36 @@ EOD;
}
}
// Process the HTML content for output
$newBody = '';
foreach ($doc->childNodes as $node)
{
$newHTML = $htmlDoc->saveHTML($node);
$newBody .= mb_convert_encoding($newHTML, "UTF-8", mb_detect_encoding($newHTML));
// Only convert text nodes to HTML entities
if ($node->nodeType === XML_TEXT_NODE)
{
$newBody .= $this->convertToHtmlEntities($node->nodeValue); // Convert text nodes
}
else
{
$newBody .= $htmlDoc->saveHTML($node); // Keep HTML tags intact
}
}
return $newBody;
}
/**
* Convert all characters in a string to HTML entities while leaving HTML tags intact.
* @param string $text - The text to convert
* @return string - The converted text with HTML entities
*/
private function convertToHtmlEntities(string $text): string
{
// Convert characters to HTML entities using mb_encode_numericentity
return htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
/**
* Get all posts with the given category
* @param string $category - Category of the post
+1 -1
View File
@@ -181,7 +181,7 @@ class userRoutes implements routesInterface
$inactive = 60 * 60 * 48; // 2 days
$_SESSION["timeout"] = time() + $inactive;
return $response->withHeader("Location", "https://rohitpai.co.uk/editor/")->withStatus(302);
return $response->withHeader("Location", "https://rohitpai.co.uk/editor/")->withStatus(302);
}
$response->getBody()->write(json_encode(array("error" => "Unauthorised")));
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1866 -920
View File
File diff suppressed because it is too large Load Diff
+35 -4
View File
@@ -673,9 +673,17 @@ EOD;
*/
public function changeHTMLSrc(string $body, string $to, string $from): string
{
// $body = preg_replace_callback('/>([^<]+)</', function($matches) {
// // Convert special characters to HTML entities
// return '>' . htmlentities(trim($matches[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '<';
// }, $body);
$htmlDoc = new DOMDocument();
$body = mb_convert_encoding($body, "HTML-ENTITIES", "UTF-8");
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
// Load the raw HTML content into DOMDocument
@$htmlDoc->loadHTML($body, LIBXML_NOERROR);
// Get the body and process images
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
$imgs = $doc->getElementsByTagName('img');
@@ -688,9 +696,11 @@ EOD;
$srcList[] = $src;
$fileName = basename($src);
// Update the src attribute to the new location
$img->setAttribute("src", substr($to, 2) . $fileName);
}
// Rename files and clean up old ones
$files = scandir($from);
foreach ($files as $file)
{
@@ -706,15 +716,36 @@ EOD;
}
}
// Process the HTML content for output
$newBody = '';
foreach ($doc->childNodes as $node)
{
$newHTML = $htmlDoc->saveHTML($node);
$newBody .= mb_convert_encoding($newHTML, "UTF-8", mb_detect_encoding($newHTML));
// Only convert text nodes to HTML entities
if ($node->nodeType === XML_TEXT_NODE)
{
$newBody .= $this->convertToHtmlEntities($node->nodeValue); // Convert text nodes
}
else
{
$newBody .= $htmlDoc->saveHTML($node); // Keep HTML tags intact
}
}
return $newBody;
}
/**
* Convert all characters in a string to HTML entities while leaving HTML tags intact.
* @param string $text - The text to convert
* @return string - The converted text with HTML entities
*/
private function convertToHtmlEntities(string $text): string
{
// Convert characters to HTML entities using mb_encode_numericentity
return htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
/**
* Get all posts with the given category
* @param string $category - Category of the post
+99
View File
@@ -123,3 +123,102 @@ section.largePost .outerContent .postContent a {
display: table-cell;
vertical-align: middle;
}
section#olderPosts {
/*max-width: 90%;*/
margin: auto;
}
section#olderPosts .carousel {
position: relative;
margin: auto;
}
section#olderPosts .arrow {
position: absolute;
top: 50%;
display: flex;
width: 2.5em;
height: 2.5em;
justify-content: center;
align-items: center;
border-radius: 50%;
z-index: 1;
font-size: 1.625em;
color: white;
background: var(--mutedBlack);
cursor: pointer;
}
section#olderPosts .arrow:hover {
background: var(--grey);
}
section#olderPosts #prev {
left: -4em;
}
section#olderPosts #next {
right: -4em;
}
section#olderPosts .carouselOuter {
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
overflow: hidden;
position: relative;
margin: auto;
}
section#olderPosts #allCarouselItems {
display: none;
}
section#olderPosts #carouselInner {
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: center;
gap: 1em;
transition: transform 0.5s ease-in-out;
left: 0;
height: 50%;
}
section#olderPosts #carouselInner .cardItem {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
border: 2px solid var(--primaryDefault);
-webkit-border-radius: 0.625rem;
-moz-border-radius: 0.625rem;
border-radius: 0.625rem;
width: 30em;
height: 40rem;
}
section#olderPosts #carouselInner .cardItem img {
width: 100%;
height: 300px;
object-fit: cover;
object-position: left;
-webkit-border-radius: 0.625rem;
-moz-border-radius: 0.625rem;
border-radius: 0.625rem;
color: #FFFFFF;
}
section#olderPosts #carouselInner .cardItem .content {
height: auto;
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
background: #FFFFFF;
margin: 0 2em 1.5em;
padding: 0;
}
+49 -1
View File
@@ -9,6 +9,12 @@
@import "home.css";
@import "category.css";
div.menuBar a.link::before, main a.link::before,
div.menuBar a.link::after, main a.link::after {
margin-top: -1px;
}
/* Modal Styling */
.policy {
display: flex;
flex-direction: column;
@@ -72,7 +78,12 @@
/**** Media Queries *****/
@media screen and (max-width: 90em) {
section#olderPosts #carouselInner .cardItem {
width: 25em;
}
/***** Individual Blog Posts ***/
div.mainContent {
@@ -124,6 +135,25 @@
width: 90%;
}
section#olderPosts .arrow {
width: 2em;
height: 2em;
font-size: 1.25em;
}
section#olderPosts #prev {
left: -3em;
}
section#olderPosts #next {
right: -3em;
}
section#olderPosts #carouselInner .cardItem {
width: 20em;
}
/***** Individual Blog Posts ***/
section#individualPost {
flex-direction: column-reverse;
@@ -219,6 +249,24 @@
max-width: 75%;
}
section#olderPosts .arrow {
width: 1.5em;
height: 1.5em;
font-size: 1em;
}
section#olderPosts #prev {
left: -1.75em;
}
section#olderPosts #next {
right: -1.75em;
}
section#olderPosts #carouselInner .cardItem {
width: 15em;
}
/***** Individual Blog Posts ***/
aside.sideContent > div.authorInfo {
+190 -2
View File
@@ -41,7 +41,6 @@ function goToURL(url)
if (url === '/blog' || url === 'blog' || url === '/blog/')
{
loadHomeContent();
// window.history.pushState(null, null, url);
return;
}
@@ -162,6 +161,10 @@ function submitNewsletter()
}));
}
/**
* unsubscribe by email
* @param email the email to unsubscribe
*/
function unsubscribe(email)
{
fetch(`/api/blog/newsletter/${email}`, {
@@ -243,6 +246,32 @@ function createLargePost(post)
return outerContent;
}
/**
* Creates a card post element
* @param post the object
* @returns {HTMLDivElement} the outer content of the post
*/
function createCardPost(post)
{
let cardItem = document.createElement('div');
cardItem.classList.add('cardItem');
cardItem.id = 'post' + post.ID;
let img = document.createElement('img');
img.className = 'cardImg';
img.src = post.headerImg.replaceAll('%2F', '/');
img.alt = post.title;
cardItem.appendChild(img);
let content = document.createElement('div');
content.classList.add('content');
content.innerHTML = `
<h2>${post.title}</h2>
<h3>Last updated: ${createFormattedDate(post.dateModified)}</h3>
<a href="/blog/post/${post.title}" class="btn btnPrimary">See Post</a>
`;
cardItem.appendChild(content);
return cardItem;
}
/**
* Loads the home content
*/
@@ -250,6 +279,30 @@ function loadHomeContent()
{
fetch('/api/blog/post').then(res => res.json().then(json =>
{
// older posts outside the carousel and loop
let olderPosts = document.createElement('section');
olderPosts.classList.add('largePost');
olderPosts.id = 'olderPosts';
let h1 = document.createElement('h1');
h1.innerHTML = 'older posts';
olderPosts.appendChild(h1);
let carousel = document.createElement('div');
carousel.classList.add('carousel');
carousel.innerHTML += `<div class="arrow" id="prev"><i class="fa-solid fa-chevron-left"></i></div>
<div class="arrow" id="next"><i class="fa-solid fa-chevron-right"></i></div>
`;
let carouselOuter = document.createElement('div');
let carouselInner = document.createElement('div');
let allCarouselItems = document.createElement('div');
carouselOuter.classList.add('carouselOuter');
carouselInner.id = 'carouselInner';
allCarouselItems.id = 'allCarouselItems';
carouselOuter.appendChild(allCarouselItems);
carouselOuter.appendChild(carouselInner);
carousel.appendChild(carouselOuter);
olderPosts.appendChild(carousel);
for (let i = 0; i < json.length; i++)
{
if (json[i].featured === 1)
@@ -278,10 +331,142 @@ function loadHomeContent()
document.querySelector('#main').appendChild(latestPost);
}
if (i > 1)
{
allCarouselItems.appendChild(createCardPost(json[i]));
}
}
document.querySelector('#main').appendChild(olderPosts);
//carousel loop
carouselLoop(carouselInner, allCarouselItems);
}));
}
/**
* Creates the loop for the carousel
* @param {HTMLDivElement} carouselInner
* @param {HTMLDivElement} allItems
*/
function carouselLoop(carouselInner, allItems)
{
const prev = document.querySelector('#prev');
const next = document.querySelector('#next');
const mediaBig = window.matchMedia('(max-width: 75em)');
const mediaSmall = window.matchMedia('(max-width: 30em)');
let cards = document.querySelectorAll('#allCarouselItems .cardItem');
let visibleCardsCount = 3;
if (mediaBig.matches)
{
visibleCardsCount = 2; // only show 2 cards if on a slightly smaller screen e.g. tablet/laptop
}
if (mediaSmall.matches)
{
visibleCardsCount = 1; // only show 1 card if on a mobile, although it'll only work on portrait
}
let visibleCards = [];
// put the first n (3, 2, or 1) cards in the carousel
for (let i = 0; i < visibleCardsCount; i++)
{
carouselInner.appendChild(cards[i]);
visibleCards.push(cards[i]);
}
next.addEventListener('click', () =>
{
const firstCard = visibleCards.shift();
firstCard.style.visibility = 'hidden'; // hide the first card
// add the next card to the end and off the screen
const nextCard = allItems.querySelector('.cardItem');
nextCard.style.transform = 'translateX(100%)';
nextCard.style.transition = 'none';
carouselInner.appendChild(nextCard);
visibleCards.push(nextCard);
// transition all the cards to the left after 50ms
// i.e. after the next card is added hence the setTimeout
setTimeout(() =>
{
// move all the cards to the left
for (let i = 0; i < carouselInner.children.length; i++)
{
carouselInner.children[i].style.transition = 'transform 0.5s ease-out';
carouselInner.children[i].style.transform = 'translateX(-100%)';
}
}, 50);
// after 500ms move all cards back to the center and move
// the first card to the hidden div
setTimeout(() =>
{
// move the first card back to the center
// instantly and move it to the hidden div
firstCard.style.transition = 'none';
firstCard.style.transform = 'translateX(0)';
allItems.appendChild(firstCard);
firstCard.style.visibility = 'visible'; // make it visible again
// for the remaining cards, reset them
for (let i = 0; i < carouselInner.children.length; i++)
{
carouselInner.children[i].style.transition = 'none';
carouselInner.children[i].style.transform = 'translateX(0%)';
}
}, 500);
});
// everything is done in reverse
prev.addEventListener('click', () =>
{
const lastCard = visibleCards.pop();
lastCard.style.visibility = 'hidden'; // hide the last card
// add the previous card to the beginning and off the screen
const prevCard = allItems.querySelector('.cardItem:last-child');
prevCard.style.transform = 'translateX(-100%)';
prevCard.style.transition = 'none';
carouselInner.insertBefore(prevCard, carouselInner.firstChild);
visibleCards.unshift(prevCard);
// transition all the cards to the right after 50ms
// i.e. after the previous card is added hence the setTimeout
setTimeout(() =>
{
// move all the cards to the right
for (let i = 0; i < carouselInner.children.length; i++)
{
carouselInner.children[i].style.transition = 'transform 0.5s ease-out';
carouselInner.children[i].style.transform = 'translateX(100%)';
}
}, 50);
// after 500ms move all cards back to the center and move
// the last card to the hidden div
setTimeout(() =>
{
// move the last card back to the center
// instantly and move it to the hidden div
lastCard.style.transition = 'none';
lastCard.style.transform = 'translateX(0)';
allItems.insertBefore(lastCard, allItems.firstChild);
lastCard.style.visibility = 'visible';
// for the remaining cards reset them
for (let i = 0; i < carouselInner.children.length; i++)
{
carouselInner.children[i].style.transition = 'none';
carouselInner.children[i].style.transform = 'translateX(0%)';
}
}, 500);
});
}
/**
* Gets the latest and featured posts
* @returns {Promise<any[]>} the latest and featured posts
@@ -390,7 +575,7 @@ async function createSideContent()
<div class="authorInfo">
<div class="picture">
<img src="/imgs/profile.jpg"
alt="My professional picture taken in brighton near
alt="My professional picture taken in brighton near
north street at night wearing a beige jacket and checkered shirt"
class="profile">
<p>Rohit Pai</p>
@@ -848,6 +1033,9 @@ function loadPrivacyPolicy()
`;
}
/**
* Loads the cookie policy
*/
function loadCookiePolicy()
{
document.querySelector('#main').innerHTML = `
+7 -7
View File
@@ -876,7 +876,7 @@ function createEditors(...ids)
return (
`<iframe width="560" height="315" style="text-align:center;" src="https://www.youtube.com/embed/${id}${time ? `?start=${time}` : ''}" ` +
'frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>' +
'allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>' +
'</iframe>'
);
},
@@ -1229,8 +1229,8 @@ function updateProjectItem(id, e)
data['gitLink'] = document.querySelector(`#git${id}proj`).value;
let imgData = new FormData();
imgData.append("img", document.querySelector(`#img${id}`).files[0]);
imgData.append('img', document.querySelector(`#img${id}proj`).files[0]);
fetch("/api/projectData/" + id, {
method: "PATCH",
body: JSON.stringify(data),
@@ -1254,8 +1254,8 @@ function updateProjectItem(id, e)
}
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
document.querySelector(`#title${id}`).setAttribute("disabled", "");
document.querySelector(`#info${id}`).setAttribute("disabled", "");
document.querySelector(`#title${id}proj`).setAttribute('disabled', '');
document.querySelector(`#info${id}proj`).setAttribute('disabled', '');
return;
}
console.log("updating image")
@@ -1293,8 +1293,8 @@ function updateProjectItem(id, e)
}
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
document.querySelector(`#title${id}`).setAttribute("disabled", "");
document.querySelector(`#info${id}`).setAttribute("disabled", "");
document.querySelector(`#title${id}proj`).setAttribute('disabled', '');
document.querySelector(`#info${id}proj`).setAttribute('disabled', '');
document.querySelector(`#projectImage${id}`).src = updatedProjectImage.imgLocation;
return;
}