diff --git a/components/core/slideshow.php b/components/core/slideshow.php new file mode 100644 index 0000000..e5e3474 --- /dev/null +++ b/components/core/slideshow.php @@ -0,0 +1,3 @@ + $essay->getHTML()]); +} + function handleDeleteSpace() { if (!isset($_POST['id'])) { @@ -84,20 +107,28 @@ function handleNewSpace() function handleNewPost() { - if (!isset($_POST['postType']) || !isset($_POST['space'])) + if (!isset($_POST['postType']) || !isset($_POST['space']) || !isset($_SESSION['user'])) return; + // TODO: Better input validation for SQL Inj. and XSS switch ($_POST['postType']) { case 'Thought': if (!isset($_POST['thought'])) return; $id = randomId(1) * -1; - new Thought(Space::retrieveFromDB($_POST['space']), $id, unserialize($_SESSION['user']), 3, 2, urldecode($_POST['thought'])); + new Thought($_POST['space'], $id, unserialize($_SESSION['user']), 3, 2, urldecode($_POST['thought'])); break; case 'Moment': + if (!isset($_FILES['image']) || !isset($_POST['caption'])) + return; + $id = randomId(1) * -1; + new Moment($_POST['space'], $id, unserialize($_SESSION['user']), 2, 2, $_FILES['image'], $_POST['caption']); break; case 'Essay': - new Essay(Space::retrieveFromDB($_POST['space']), null, unserialize($_SESSION['user']), 3, 2, $_POST['title'], $_POST['markdown'], $_POST['excerpt']); + if (!isset($_POST['title']) || !isset($_POST['markdown']) || !isset($_POST['excerpt'])) + return; + $id = randomId(1) * -1; + new Essay($_POST['space'], $id, unserialize($_SESSION['user']), 3, 2, $_POST['title'], $_POST['markdown'], $_POST['excerpt']); break; } } @@ -204,64 +235,46 @@ function getSpaceIcon($space) } } -function handleNewPostHTML() +function handleEditPost() { - $user = unserialize($_SESSION['user']); + if (!isset($_POST['id']) || !isset($_POST['postType']) || !isset($_POST['space'])) + return; - // TODO: Need to plumb this into the DB via AJAX. - // TODO: Need to store the current space in SESSION and use that to auto-select a Space - // TODO: List the user's spaces or let them search for a space or quick select a favorited space - $html = ' -
-

New Thought

-
- - - - - -
- -
- - - - - -
- '; - - $spaces = $user->getAllSpaces(); - foreach ($spaces as $s) { - $html .= ' - - '; + switch ($_POST['postType']) { + case 1: + if (!isset($_POST['title']) || !isset($_POST['markdown']) || !isset($_POST['excerpt']) || !isset($_POST['space'])) + return; + Essay::edit($_POST['id'], $_POST['title'], $_POST['markdown'], $_POST['excerpt'], $_POST['space']); + break; + case 2: + if (!isset($_POST['caption'])) + return; + Moment::edit($_POST['id'], $_POST['caption'], $_POST['space']); + break; + case 3: + if (!isset($_POST['thought'])) + return; + Thought::edit($_POST['id'], $_POST['thought'], $_POST['space']); + break; } - $html .= ' -
+ if (isset($_POST['redirect'])) + header('Location: ' . $_POST['redirect']); + else + header('Location: .'); +} - -
-
-
- Thought - Moment - Essay -
-
- '; +function handleEditPostHTML() +{ + if (!isset($_POST['id'])) + return; - echo json_encode(['html' => $html]); + echo json_encode(['html' => Post::getEditHTML($_POST['id'])]); +} + +function handleNewPostHTML() +{ + echo json_encode(['html' => Post::getComposeHTML()]); } function handle_login() diff --git a/functions/db.php b/functions/db.php index fc7baf1..5529adb 100644 --- a/functions/db.php +++ b/functions/db.php @@ -1,7 +1,7 @@ safeLoad(); // Use environment variables for the database password and IP address. diff --git a/functions/init.php b/functions/init.php index 3c556b7..aef4482 100644 --- a/functions/init.php +++ b/functions/init.php @@ -3,7 +3,7 @@ session_start(); header("Content-Security-Policy: default-src 'self' localhost:*; font-src 'self' https://www.nerdfonts.com/assets/fonts/Symbols-2048-em%20Nerd%20Font%20Complete.woff2; style-src 'self' 'unsafe-inline' https://nerdfonts.com/assets/css/webfont.css https://www.nerdfonts.com/assets/css/webfont.css; script-src 'self' 'unsafe-inline'; img-src 'self';"); require_once 'functions/vendor/autoload.php'; -$dotenv = Dotenv\Dotenv::createImmutable('/home/prod/vintagecoding.net/'); +$dotenv = Dotenv\Dotenv::createImmutable('/home/jashton/dev/me/weave.space/'); $dotenv->safeLoad(); require_once 'functions/db.php'; @@ -19,6 +19,8 @@ if (!isset($_SESSION['initialized'])) { } else if (isset($_POST['theme'])) { $_SESSION['theme'] = $_POST['theme']; unset($_POST['theme']); +} else { + $_SESSION['theme'] = 'light'; } define('owner', 1); diff --git a/functions/post.php b/functions/post.php index 8519d43..1c3d2e4 100644 --- a/functions/post.php +++ b/functions/post.php @@ -34,7 +34,7 @@ class Topic // -- Post Data Models (as previously defined, with setReadCount in Post) -- class Post { - private ?Space $space; + private int $space; private ?int $id; private User $creator; private int $postType; @@ -43,7 +43,7 @@ class Post private ?DateTime $createdAt; private ?DateTime $updatedAt; - public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status) + public function __construct(int $space = 2025, ?int $id = null, User $creator, int $postType, int $status) { $this->space = $space; $this->id = $id; @@ -73,15 +73,159 @@ class Post switch ($post['postType']) { case 1: - return new Essay(null, $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']); + return new Essay($post['space'], $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']); + case 2: + return new Moment($post['space'], $id, $creator, 2, $post['status'], $post['images'], $post['caption']); case 3: - return new Thought(null, $post['id'], $creator, 3, $post['status'], $post['thought']); + return new Thought($post['space'], $id, $creator, 3, $post['status'], $post['thought']); } } return null; } + public static function getComposeHTML(): string + { + $user = unserialize($_SESSION['user']); + + // TODO: Need to store the current space in SESSION and use that to auto-select a Space + // TODO: List the user's spaces or let them search for a space or quick select a favorited space + $html = ' +
+

New Thought

+
+ + + + +
+ +
+ + + + + +
+ '; + + $spaces = $user->getAllSpaces(); + foreach ($spaces as $s) { + $html .= ' + + '; + } + + $html .= ' +
+ + +
+
+
+ Thought + Moment + Essay +
+
+ '; + + return $html; + } + + public static function getEditHTML($id) + { + $user = unserialize($_SESSION['user']); + $post = Post::retrieveFromDB($id); + if (!$post) + return ''; + + // TODO: Need to plumb this into the DB via AJAX. + // TODO: Need to store the current space in SESSION and use that to auto-select a Space + // TODO: List the user's spaces or let them search for a space or quick select a favorited space + $html = ' +
+

New Thought

+
+ + + + + + '; + + switch ($post->getPostType()) { + case 1: + if (!is_a($post, 'Essay')) + return; + + $html .= ' + + + +
+ '; + + $spaces = $user->getAllSpaces(); + foreach ($spaces as $s) { + $html .= ' + + '; + } + + $html .= ' +
+ '; + break; + case 2: + if (!is_a($post, 'Moment')) + return; + + $html .= ' + + '; + break; + case 3: + if (!is_a($post, 'Thought')) + return; + + $html .= ' +
+ +
+ '; + break; + } + + $html .= ' + +
+
+
+ '; + + return $html; + } + public function setId(int $id): void { $this->id = $id; @@ -132,12 +276,12 @@ class Post $this->status = $status; } - public function setSpace(Space $space): void + public function setSpace(int $space): void { $this->space = $space; } - public function getSpace(): ?Space + public function getSpace(): int { if (!isset($this->space)) { $stmt = 'SELECT space from spacePosts WHERE post = ?'; @@ -154,13 +298,37 @@ class Essay extends Post private string $excerpt; /* private array $files; */ - public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, string $title, string $markdown, string $excerpt) + public function __construct(int $space, ?int $id = null, User $creator, int $postType, int $status, string $title, string $markdown, string $excerpt) { parent::__construct($space, $id, $creator, 1, $postType, $status); $this->title = $title; $this->excerpt = $excerpt; $this->markdown = $markdown; + + if ($id < 0) { + parent::setId($id * -1); + $stmt = 'INSERT INTO post (id, space, creator, postType, status, title, markdown, excerpt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'; + $space = isset($space) ? $space : $creator->getHomeSpace()->getId(); + exec_stmt($stmt, 'iiiiisss', $id * -1, $space, $creator->getId(), 1, 2, $title, $markdown, $excerpt); + $stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)'; + exec_stmt($stmt, 'ii', $id * -1, $space); + } + } + + public static function edit(int $id, string $title, string $markdown, string $excerpt, int $space) + { + $post = Post::retrieveFromDB($id); + if (!is_a($post, 'Essay')) + return; + + $post->setTitle($title); + $post->setMarkdown($markdown); + $post->setExcerpt($excerpt); + $post->setSpace($space); + + $stmt = 'UPDATE post SET title = ?, markdown = ?, excerpt = ?, space = ? WHERE id = ?'; + exec_stmt($stmt, 'sssii', $title, $markdown, $excerpt, $space, $id); } public function getCardHTML(): ?string @@ -168,16 +336,32 @@ class Essay extends Post /* @' . parent::getCreator()->getUsername() . ' */ $html = '
- +

' . $this->title . '

-
+

' . $this->excerpt . '

@@ -188,9 +372,9 @@ class Essay extends Post return $html; } - public function allowUser(User $user): bool + public function setTitle(string $title) { - return parent::getSpace()->allowUser($user); + $this->title = $title; } public function getTitle(): ?string @@ -198,79 +382,57 @@ class Essay extends Post return $this->title; } + public function setMarkdown(string $markdown) + { + $this->markdown = $markdown; + } + public function getMarkdown(): ?string { return $this->markdown; } + public function getHTML(): string + { + $parsedown = new Parsedown(); + $parsedown->setSafeMode(true); + return $parsedown->parse($this->markdown); + } + + public function setExcerpt(string $excerpt) + { + $this->excerpt = $excerpt; + } + public function getExcerpt(): ?string { return $this->excerpt; } - - public function setMarkdown(?string $markdown): void - { - $this->markdown = $markdown; - } - - public function setExcerpt(?string $excerpt): void - { - $this->excerpt = $excerpt; - } - - public function getSpace(): ?Space - { - return parent::getSpace(); - } } class Moment extends Post { private ?string $caption; - private ?array $images; + private $image; - public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, ?array $images = null, ?string $caption = null) + public function __construct(int $space, ?int $id = null, User $creator, int $postType, int $status, $image, string $caption) { parent::__construct($space, $id, $creator, $postType, $status); $this->caption = $caption; - $this->images = $images; - } - - public function getCaption(): ?string - { - return $this->caption; - } - - public function getImages(): ?array - { - return $this->images; - } - - public function setCaption(?string $caption): void - { - $this->caption = $caption; - } - - public function setImages(?array $images): void - { - $this->images = $images; - } -} - -class Thought extends Post -{ - private ?string $thought; - - public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, string $thought) - { - parent::__construct($space, $id, $creator, $postType, $status); - $this->thought = $thought; + $this->image = $image; if ($id < 0) { - $stmt = 'INSERT INTO post (id, parentSpace, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)'; + if (!isset($image['tmp_name'])) + return; + mkdir($_ENV['IMG_UPLOAD'] . ($id * -1)); + move_uploaded_file($image['tmp_name'], $_ENV['IMG_UPLOAD'] . ($id * -1) . '/' . $image['name']); + + $this->image = $image['name']; + + $stmt = 'INSERT INTO post (id, space, creator, postType, status, images, caption) VALUES (?, ?, ?, ?, ?, ?, ?)'; parent::setId($id * -1); - $space = isset($space) ? $space->getId() : $creator->getHomeSpace()->getId(); - exec_stmt($stmt, 'iiiiis', $id * -1, $space, $creator->getId(), 3, 2, $this->thought); + $space = isset($space) ? $space : $creator->getHomeSpace()->getId(); + exec_stmt($stmt, 'iiiiiss', $id * -1, $space, $creator->getId(), 2, 2, $this->image, $this->caption); $stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)'; exec_stmt($stmt, 'ii', $id * -1, $space); } @@ -290,10 +452,118 @@ class Thought extends Post $user = unserialize($_SESSION['user']); if ($user->getId() == parent::getCreator()->getId()) { $html .= ' - +
+
+ +
+

' . $this->caption . '

+
+
+ '; + + return $html; + } + + public static function edit(int $id, string $caption, int $space) + { + $post = Post::retrieveFromDB($id); + if (!is_a($post, 'Moment')) + return; + + $post->setCaption($caption); + $post->setSpace($space); + + $stmt = 'UPDATE post SET caption = ?, space = ? WHERE id = ?'; + exec_stmt($stmt, 'sii', $caption, $space, $id); + } + + public function getCaption(): ?string + { + return $this->caption; + } + + public function getImages(): ?array + { + return $this->image; + } + + public function setCaption(?string $caption): void + { + $this->caption = $caption; + } + + public function setImages(?array $image): void + { + $this->image = $image; + } +} + +class Thought extends Post +{ + private ?string $thought; + + public function __construct(int $space, ?int $id = null, User $creator, int $postType, int $status, string $thought) + { + parent::__construct($space, $id, $creator, $postType, $status); + $this->thought = $thought; + + if ($id < 0) { + $stmt = 'INSERT INTO post (id, space, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)'; + parent::setId($id * -1); + $space = isset($space) ? $space : $creator->getHomeSpace()->getId(); + exec_stmt($stmt, 'iiiiis', $id * -1, $space, $creator->getId(), 3, 2, $this->thought); + $stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)'; + exec_stmt($stmt, 'ii', $id * -1, $space); + } + } + + public static function edit(int $id, string $thought, int $space) + { + error_log('Updating post ' . $id . ' to have thought = ' . $thought); + $post = Post::retrieveFromDB($id); + if (!is_a($post, 'Thought')) + return; + + $post->setThought($thought); + $post->setSpace($space); + + $stmt = 'UPDATE post SET thought = ?, space = ? WHERE id = ?'; + exec_stmt($stmt, 'sii', $thought, $space, $id); + } + + public function getCardHTML(): ?string + { + $html = ' +
+
+ @' . parent::getCreator()->getUsername() . ' + + + '; + + if (isset($_SESSION['user'])) { + $user = unserialize($_SESSION['user']); + if ($user->getId() == parent::getCreator()->getId()) { + $html .= ' + '; diff --git a/functions/space.php b/functions/space.php index 630072b..ed8f338 100644 --- a/functions/space.php +++ b/functions/space.php @@ -147,7 +147,7 @@ class Space foreach ($rawMembers as $m) $members[] = User::retrieveFromDB($m['user']); - $stmt = 'SELECT post FROM spacePosts INNER JOIN post ON spacePosts.post = post.id WHERE space = ? ORDER BY post.createdAt DESC'; + $stmt = 'SELECT post FROM spacePosts INNER JOIN post ON spacePosts.post = post.id WHERE post.space = ? ORDER BY post.createdAt DESC'; $posts = []; $rawPosts = exec_stmt($stmt, 'i', $rawSpace['id']); while ($row = $rawPosts->fetch_assoc()) diff --git a/js/actionBar.js b/js/actionBar.js index 5561673..b64aaa6 100644 --- a/js/actionBar.js +++ b/js/actionBar.js @@ -55,6 +55,7 @@ actionPost.addEventListener("click", () => { return response.json(); }) .then((data) => { + console.log(data["html"]); openModal(data["html"]); document.querySelector("#redirect").value = window.location.href; @@ -68,8 +69,6 @@ actionPost.addEventListener("click", () => { let newPostLabel = document.querySelector("#newPostLabel"); let postType = document.querySelector("#postType"); - let share = document.querySelector("#share"); - newNavThought.addEventListener("click", () => { newMoment.style.display = "none"; newEssay.style.display = "none"; @@ -86,7 +85,6 @@ actionPost.addEventListener("click", () => { newMoment.style.display = "flex"; newPostLabel.innerText = "New Moment"; - newMoment.querySelector('input[type="text"]').focus(); postType.value = "Moment"; }); diff --git a/js/dropdown.js b/js/dropdown.js index a209f94..09077d0 100644 --- a/js/dropdown.js +++ b/js/dropdown.js @@ -18,6 +18,25 @@ window.addEventListener("click", () => { }); }); +function editPost(id) { + fetch("director.php", { + method: "POST", + + // TODO: Add Authorization header with JWT + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: "ajaxId=editPostHTML&id=" + id, + }) + .then((response) => { + return response.json(); + }) + .then((data) => { + openModal(data["html"]); + document.querySelector("#redirect").value = window.location.href; + }); +} + function deleteSpace(id) { document.querySelector("#space-" + id).remove(); diff --git a/js/handleEssayModal.js b/js/handleEssayModal.js new file mode 100644 index 0000000..be78544 --- /dev/null +++ b/js/handleEssayModal.js @@ -0,0 +1,24 @@ +let essays = document.querySelectorAll(".divLink"); + +essays.forEach((e) => { + let link = e.querySelector("a").href; + let id = link.split("=")[2]; + + e.addEventListener("click", () => { + fetch("director.php", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: "ajaxId=viewEssay&id=" + id, + }) + .then((response) => { + return response.json(); + }) + .then((data) => { + if (!data["html"]) return; + + openModal(data["html"]); + }); + }); +}); diff --git a/js/user.js b/js/user.js index 1856fe1..2f78b01 100644 --- a/js/user.js +++ b/js/user.js @@ -24,8 +24,6 @@ if (logout) { if (login) { login.addEventListener("click", () => { - console.log("click"); - fetch("director.php", { method: "POST", headers: {