implemented spaces, fixed login and signup, thought creation and space creation.

This commit is contained in:
2025-05-26 13:00:12 -06:00
parent 54a01f0f1f
commit d3fa1733f4
32 changed files with 1499 additions and 1132 deletions
+8 -65
View File
@@ -1,74 +1,17 @@
</body>
<footer>
<script src="js/util.js"></script>
<script src="js/nav.js" defer></script>
<script src="js/user.js" defer></script>
<?php
function get_args_from_url()
{
$args = [];
$arr = explode('&', $_SERVER['QUERY_STRING']);
foreach ($arr as $a) {
$tmp = explode('=', $a);
$key = $tmp[0];
$value = $tmp[1];
$args[$key] = $value;
}
return $args;
}
if ($_SERVER['SCRIPT_NAME'] == '/spaces.php')
echo '<script src="js/actionButton.js" defer></script>';
function extra()
{
if ($_SERVER['SCRIPT_NAME'] == '/user.php') {
$args = get_args_from_url();
if (isset($args['view'])) {
if ($args['view'] == 'signup') {
// <script src="js/theme.js" defer></script>
if ($_SERVER['SCRIPT_NAME'] == '/spaces.php' && isset($_SESSION['user'])) {
echo '
<script src="js/profile_picture_cropper_modal.js"></script>
<script src="js/upload.js"></script>
<script src="js/actionBar.js" defer></script>
<script src="js/dropdown.js" defer></script>
';
} else if ($args['view'] == 'display') {
echo '
<script src="js/user_actions_modal.js"></script>
<script src="js/upload.js"></script>
';
}
}
} else if ($_SERVER['SCRIPT_NAME'] == '/admin.php') {
echo '
<script src="js/clickable_rows.js"></script>
';
$args = get_args_from_url();
if (isset($args['view'])) {
if ($args['view'] == 'users') {
echo '
<script src="js/upload.js"></script>
';
}
}
} else if ($_SERVER['SCRIPT_NAME'] == '/articles.php') {
$args = get_args_from_url();
if (isset($args['view'])) {
if ($args['view'] == 'compose') {
echo '
<script src="js/md_preview.js"></script>
';
}
}
} else if ($_SERVER['SCRIPT_NAME'] == '/todo.php') {
echo '<script src="js/todo.js"></script>';
}
echo '
<script src="js/override_browser_styles.js"></script>
';
}
?>
</footer>
+1
View File
@@ -6,6 +6,7 @@
<link href="css/rewrite.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://nerdfonts.com/assets/css/webfont.css">
<link rel="icon" type="image/x-icon" href="data/vintagecoding-favicon.png">
<?php
if (!isset($_SESSION['theme']) || $_SESSION['theme'] == 'dark')
echo '<link href="css/dark.css" rel="stylesheet" type="text/css">';
-2
View File
@@ -55,8 +55,6 @@ $nav .= '
</div>
</div>
<script src="js/nav.js" defer></script>
<script src="js/login.js" defer></script>
</nav>
<div id="notificationModal"></div>
<div id="stdModal"></div>
-31
View File
@@ -1,31 +0,0 @@
<?php
function login_form($error_msg = '')
{
$form = '
<div class="form_card">
<h3 class="underline">Log In</h3>
<form method="post" id="login_form">
<input type="hidden" name="form_id" value="login_form">
<input type="hidden" name="password_hash" value="" id="password_hash">
<label for="username">Username / Email</label>
<input id="username" name="username" spellcheck="false" required autofocus>
<label for="password">Password</label>
<input id="password" name="password" type="password" spellcheck="false" required>
<label class="form_checkbox_container">Stay Logged In?
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
<input class="button" type="submit" value="Log In">
' . $error_msg . '
</form>
<script src="js/login.js" defer></script>
</div>
';
return $form;
}
-50
View File
@@ -1,50 +0,0 @@
<?php
function signup_form($error_msg = '')
{
$form = '
<div class="form_card">
<h3 class="underline">Sign Up</h3>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="form_id" value="signup_form">
<input type="hidden" name="crop_x" value="" id="crop_x">
<input type="hidden" name="crop_y" value="" id="crop_y">
<input type="hidden" name="crop_width" value="" id="crop_width">
<input name="required_field" id="required_field" value="">
<label for="username">Username</label>
<input id="username" name="username" required>
<label for="email">Email Address</label>
<input id="email" name="email" required>
<label for="password">Password</label>
<input id="password" name="password" type="password" required>
<label for="verify_password">Verify Password</label>
<input id="verify_password" name="verify_password" type="password" required>
<label for="profile_picture_input" class="upload_button modal_button" id="profile_cropper_button">Upload a Profile Picture</label>
<input id="profile_picture_input" class="file_input" name="profile_picture" type="file" accept="image/png, image/jpeg, image/jpg">
<small class="file_input_feedback">No file selected.</small>
<label class="form_checkbox_container">Stay Logged In?
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
<input class="button" type="submit" value="Sign Up">
' . $error_msg . '
</form>
<div id="modal" class="modal">
<div id="display_modal"></div>
</div>
<div style="display: none;">
<div id="profile_cropper" class="modal_form">' . profile_cropper() . '</div>
</div>
</div>
';
return $form;
}
+6 -345
View File
@@ -14,167 +14,11 @@ body {
padding-bottom: 50px;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
small,
label,
input,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea,
a,
.modal_button,
th,
td,
li,
select,
option,
span {
font-family: "Hack Nerd Font Mono", Hack, sans-serif;
padding: 0;
margin: 0;
}
h1 {
font-size: 3vw;
}
h2 {
font-size: 2.5vw;
}
h3,
h3 a {
font-size: 2vw;
}
h4,
h4 a {
font-size: 1.75vw;
}
h5,
h5 a {
font-size: 1.5vw;
}
h6,
h6 a {
font-size: 1.25vw;
}
p,
a,
label,
li,
.modal_button,
.button,
span {
font-size: 1vw;
}
p {
margin-bottom: 15px;
}
small {
font-size: 0.75vw;
}
a,
.modal_button {
color: orange;
transition-duration: 1s;
}
a {
-webkit-tap-highlight-color: transparent !important;
outline: none !important;
}
a.fill_div {
display: block;
height: 100%;
width: 100%;
text-decoration: none;
}
nav {
display: flex;
flex-direction: row;
position: sticky;
width: 100%;
padding: 0;
margin: 0;
backdrop-filter: blur(50px);
z-index: 100;
}
nav ul {
padding: 0;
margin: 0;
width: 100%;
}
nav ul li {
display: inline-block;
}
nav ul li a {
font-size: 2vw;
font-weight: 800;
text-decoration: none;
display: block;
height: 100%;
width: 100%;
padding: 15px;
transition-duration: 500ms;
}
nav ul li a:hover {
color: orange;
}
nav form {
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.nav_right_hand {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.nav_right_hand * {
margin: 0px 15px 0px 15px;
}
code {
padding: 1px;
border-radius: 4px;
}
.line {
height: 1px;
width: 100%;
margin-top: 5px;
margin-bottom: 5px;
}
.center-h {
margin: 0 auto;
}
@@ -238,7 +82,7 @@ tbody tr {
}
tbody tr:hover {
outline: solid 3px orange;
outline: solid 3px red;
}
tbody tr td {
@@ -271,15 +115,6 @@ table tr td label {
justify-content: center;
}
.button {
background-color: transparent;
padding: 15px;
text-decoration: none;
border-radius: 12px;
transition-duration: 1s;
border: 3px solid orange;
}
.narrow {
width: 20%;
}
@@ -288,7 +123,7 @@ table tr td label {
margin: 15px;
padding: 10px;
border-radius: 18px;
border: 2px solid orange;
border: 2px solid red;
text-decoration: none;
}
@@ -307,11 +142,6 @@ table tr td label {
border-radius: 50%;
}
.link {
color: orange;
cursor: pointer;
}
.divLink {
text-decoration: none;
display: block;
@@ -319,28 +149,6 @@ table tr td label {
width: 100%;
}
.underline {
text-decoration: underline;
text-decoration-thickness: 3px;
text-decoration-color: orange;
}
i {
border: none;
background: transparent;
font-size: 1.5vw;
transition-duration: 500ms;
}
#theme_toggle {
background: none;
border: none;
}
i:hover {
color: orange;
}
.article {
width: 60%;
margin: 0 auto;
@@ -359,115 +167,14 @@ i:hover {
justify-content: left;
}
input,
.autofilled,
input:-webkit-autofill,
textarea:-webkit-autofill,
input:-webkit-autofill:hover,
textarea:-webkit-autofill:hover,
input:-webkit-autofill:focus,
select,
option,
textarea {
/* Try this first */
border-radius: 12px;
width: 100%;
padding: 15px;
margin: 15px 15px 15px 0px;
box-sizing: border-box;
transition-duration: 500ms;
resize: none;
background-color: transparent;
}
input[type="datetime_local"] {
color: white;
}
input:focus,
textarea:focus,
input:hover,
textarea:hover {
border: 3px solid orange;
outline: none;
}
textarea {
border-radius: 18px;
height: 70%;
}
.checkboxes {
height: max-content;
}
input[type="radio"] {
display: none;
}
.form_checkbox_container {
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding-left: 35px;
margin: 15px 15px 15px 0px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
height: 29px;
width: max-content;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
}
.form_checkbox_container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.form_checkbox_container input:checked~.checkmark:after {
display: block;
}
/* When the checkbox is checked, add a blue background */
.form_checkbox_container input:checked~.checkmark {
background-color: orange;
}
/* Style the checkmark/indicator */
.form_checkbox_container .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
input[type="file"] {
display: none;
}
@@ -567,7 +274,7 @@ input[type="file"] {
.modal_close:hover,
.modal_close:focus {
color: orange;
color: red;
cursor: pointer;
}
@@ -591,7 +298,7 @@ input[type="file"] {
height: 128px;
width: 128px;
outline: dashed 4px orange;
outline: dashed 4px red;
position: fixed;
left: 50%;
@@ -603,7 +310,7 @@ input[type="file"] {
position: absolute;
width: 10px;
height: 10px;
background-color: orange;
background-color: red;
border-radius: 50%;
cursor: nwse-resize;
/* Default cursor for resizing */
@@ -683,7 +390,7 @@ input[type="file"] {
::selection {
color: black;
background: orange;
background: red;
}
.grid-2x1 {
@@ -718,52 +425,6 @@ hr {
margin: 15px 0px 15px 0px;
}
::-webkit-scrollbar {
background-color: transparent;
border-radius: 18px;
}
::-webkit-scrollbar-button {
display: none;
}
::-webkit-scrollbar-track {
display: none;
}
::-webkit-scrollbar-thumb {
background-color: orange;
border-radius: 9px;
}
.dropdown {
position: relative;
display: block;
margin-right: 15px;
padding: 15px;
border-radius: 9px;
}
.dropdown_content {
display: none;
position: absolute;
min-width: 50px;
padding: 15px;
z-index: 2;
}
.dropdown:hover .dropdown_content {
display: block;
margin-left: -15px;
margin-top: 15px;
border-radius: 12px;
}
.dropdown_content form * {
margin-top: 5px;
margin-bottom: 5px;
}
@media screen and (max-width: 992px) {
body {
padding-bottom: 160px;
+5 -80
View File
@@ -11,10 +11,12 @@ h2,
h3,
h4,
h5,
h6,
p,
small,
label,
span {
span,
i {
color: white;
}
@@ -31,24 +33,9 @@ nav ul li a {
color: white;
}
code {
background-color: #2f2f2f;
}
.line {
background-color: white;
border-top: 1px solid white;
}
.article_card,
.form_card,
.view_card {
border: 5px solid white;
background-color: black;
}
table tr {
outline: solid 3px white;
border-top: 2px solid white;
}
.button {
@@ -57,21 +44,13 @@ table tr {
.button:hover,
.button:hover i {
color: orange;
}
.tag {
color: white;
color: red;
}
#theme_toggle {
color: white;
}
.article ul li {
color: white;
}
input,
textarea {
background-color: transparent;
@@ -95,60 +74,6 @@ select:-webkit-autofill {
background-color: white;
}
.upload_button {
border: 3px solid white;
}
/* .form_card:hover, */
/* .form_card:focus, */
/* .article_card:hover, */
/* .article_card:focus { */
/* box-shadow: 0 2px 4px 0 white, 0 4px 8px 0 white; */
/* } */
.modal {
background-color: black;
background-color: rgba(0, 0, 0, 0.9);
}
.modal_close {
color: white;
}
.user_actions {
border: 3px solid white;
}
#floating_compose {
border: 2px solid white;
}
#floating_compose,
#floating_compose * {
background-color: white;
color: black;
transition-duration: 500ms;
}
#floating_compose:hover,
#floating_compose:hover * {
background-color: black;
color: orange;
}
#floating_compose:hover {
border: 2px solid orange;
}
.dropdown {
border: 2px solid white;
}
.dropdown_content {
background-color: black;
border: 2px solid white;
}
.std_border,
.med_border,
.sm_border {
+4 -76
View File
@@ -14,7 +14,8 @@ h5,
p,
small,
label,
span {
span,
i {
color: black;
}
@@ -37,18 +38,7 @@ code {
.line {
background-color: black;
border-top: 1px solid black;
}
.article_card,
.form_card,
.view_card {
border: 5px solid black;
background-color: white;
}
table tr {
outline: solid 3px black;
border-top: 2px solid black;
}
.button {
@@ -57,21 +47,13 @@ table tr {
.button:hover,
.button:hover * {
color: orange;
}
.tag {
color: black;
color: red;
}
#theme_toggle {
color: black;
}
.article ul li {
color: black;
}
input,
textarea {
background-color: transparent;
@@ -95,60 +77,6 @@ select:-webkit-autofill {
background-color: black;
}
.upload_button {
border: 3px solid black;
}
/* .form_card:hover, */
/* .form_card:focus, */
/* .article_card:hover, */
/* .article_card:focus { */
/* box-shadow: 0 2px 4px 0 black, 0 4px 8px 0 black; */
/* } */
.modal {
background-color: white;
background-color: rgba(256, 256, 256, 0.9);
}
.modal_close {
color: black;
}
.user_actions {
border: 3px solid black;
}
#floating_compose {
border: 2px solid black;
}
#floating_compose,
#floating_compose * {
background-color: black;
color: white;
transition-duration: 500ms;
}
#floating_compose:hover,
#floating_compose:hover * {
background-color: white;
color: orange;
}
#floating_compose:hover {
border: 2px solid orange;
}
.dropdown {
border: 2px solid black;
}
.dropdown_content {
background-color: white;
border: 2px solid black;
}
.std_border,
.med_border,
.sm_border {
+446 -44
View File
@@ -10,6 +10,232 @@ body {
box-sizing: border-box;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
small,
label,
input,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea,
a,
.modal_button,
th,
td,
li,
select,
option,
span {
font-family: "Hack Nerd Font Mono", Hack, sans-serif;
padding: 0;
margin: 0;
}
h1 {
font-size: 3vw;
}
h2 {
font-size: 2.5vw;
}
h3,
h3 a {
font-size: 2vw;
}
h4,
h4 a {
font-size: 1.75vw;
}
h5,
h5 a {
font-size: 1.5vw;
}
h6,
h6 a {
font-size: 1.25vw;
}
p,
a,
label,
li,
.modal_button,
.button,
span {
font-size: 1vw;
}
small {
font-size: 0.75vw;
}
a,
.modal_button {
color: red;
transition-duration: 1s;
}
a {
-webkit-tap-highlight-color: transparent !important;
outline: none !important;
}
a.fillDiv {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
}
.button {
background-color: transparent;
padding: 15px;
text-decoration: none;
border-radius: 12px;
transition-duration: 1s;
border: 3px solid red;
}
.link {
cursor: pointer;
}
.underline {
text-decoration: underline;
text-decoration-thickness: 3px;
text-decoration-color: red;
}
i {
border: none;
background: transparent;
font-size: 1.5vw;
transition-duration: 500ms;
}
#theme_toggle {
background: none;
border: none;
}
i:hover {
color: red;
}
/********** Forms ************/
input,
.autofilled,
input:-webkit-autofill,
textarea:-webkit-autofill,
input:-webkit-autofill:hover,
textarea:-webkit-autofill:hover,
input:-webkit-autofill:focus,
select,
option,
textarea {
/* Try this first */
border-radius: 12px;
width: 100%;
padding: 15px;
margin: 15px 15px 15px 0px;
box-sizing: border-box;
transition-duration: 500ms;
resize: none;
background-color: transparent;
}
input:focus,
textarea:focus,
input:hover,
textarea:hover {
border: 3px solid red;
outline: none;
}
textarea {
border-radius: 18px;
height: 70%;
}
input[type="radio"] {
display: none;
}
.form_checkbox_container {
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding-left: 35px;
margin: 15px 15px 15px 0px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
height: 29px;
width: max-content;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
}
.form_checkbox_container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.form_checkbox_container input:checked ~ .checkmark:after {
display: block;
}
/* When the checkbox is checked, add a blue background */
.form_checkbox_container input:checked ~ .checkmark {
background-color: red;
}
/* Style the checkmark/indicator */
.form_checkbox_container .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
/********** Width & Sizing **********/
.full_width {
width: 100%;
@@ -50,6 +276,11 @@ body {
border: 1px solid red;
}
.line {
margin-top: 5px;
margin-bottom: 5px;
}
/********** Rows & Columns **********/
.row {
display: flex;
@@ -71,6 +302,10 @@ body {
justify-content: center;
}
.rel {
position: relative;
}
/********** Margins & Padding **********/
.margin {
margin: 15px;
@@ -124,6 +359,83 @@ body {
padding-left: 15px;
}
/*********** Nav **********/
nav {
display: flex;
flex-direction: row;
position: sticky;
width: 100%;
padding: 0;
margin: 0;
backdrop-filter: blur(50px);
z-index: 100;
}
nav ul {
padding: 0;
margin: 0;
width: 100%;
}
nav ul li {
display: inline-block;
}
nav ul li a {
font-size: 2vw;
font-weight: 800;
text-decoration: none;
display: block;
height: 100%;
width: 100%;
padding: 15px;
transition-duration: 500ms;
}
nav ul li a:hover {
color: red;
}
nav form {
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.nav_right_hand {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.nav_right_hand * {
margin: 0px 15px 0px 15px;
}
/*********** Dropdowns **********/
.dropdown {
position: relative;
display: inline-block;
z-index: 0;
}
.dropdownContent {
display: none;
position: absolute;
right: 0;
}
/******* Space Selector *********/
#postSpaces {
overflow: hidden;
}
/************ Modals ************/
#stdModal {
position: absolute;
@@ -136,55 +448,25 @@ body {
justify-content: center;
align-items: center;
z-index: 10;
}
#stdModal>* {}
/******** Action Buttons ********/
.circleLayout {
width: 350px;
/* Adjust as needed */
height: 350px;
border-radius: 50%;
/* Important for positioning children */
position: fixed;
bottom: 50px;
right: 50px;
overflow: hidden;
#stdModal > * {
}
.actionButton {
.glass {
background-color: transparent;
backdrop-filter: blur(5px);
}
#actionBar {
position: absolute;
border-radius: 50%;
width: 75px;
height: 75px;
background-color: orange;
color: black;
}
#actionButton {
top: 50%;
bottom: 3.5%;
left: 50%;
transform: translate(-50%, -50%);
}
transform: translate(-50%, -5%);
#newPost {
top: 50%;
left: 0%;
transform: translate(0%, -50%);
}
#newSpace {
top: 15%;
left: 15%;
transform: translate(-15%, -15%);
}
#filterSort {
top: 0%;
left: 50%;
transform: translate(-50%, 0%);
z-index: 10;
}
/********** Animations **********/
@@ -194,11 +476,11 @@ body {
}
::view-transition-old(root) {
animation: fade-out 750ms forwards;
animation: fade-out 1s forwards;
}
::view-transition-new(root) {
animation: fade-in 750ms forwards;
animation: fade-in 1s forwards;
}
@keyframes fade-in {
@@ -220,3 +502,123 @@ body {
opacity: 0;
}
}
/********** Misc. **********/
::-webkit-scrollbar {
background-color: transparent;
border-radius: 18px;
width: 10px;
}
::-webkit-scrollbar-button {
display: none;
}
::-webkit-scrollbar-track {
display: none;
}
::-webkit-scrollbar-thumb {
background-color: red;
border-radius: 9px;
}
#backButton {
position: absolute;
top: 11%;
left: 8%;
width: 100px;
height: 100px;
}
#backButton:hover > i {
color: red;
}
/********** Mobile **********/
@media screen and (max-width: 1200px) {
h1 {
font-size: 6vw;
}
h2 {
font-size: 5vw;
}
h3 {
font-size: 4vw;
}
h4,
h5,
h6 {
font-size: 3.5vw;
}
p,
a,
nav ul li a,
li,
i,
span,
input,
label,
input[type="submit"] {
font-size: 2.5vw;
}
.std_width,
.stdWidth {
width: 95%;
}
.med_width,
.medWidth {
width: 70%;
}
nav {
position: fixed;
bottom: -2px;
width: 100%;
margin: 0;
padding: 25px 0px 25px 0px;
backdrop-filter: blur(50px);
}
nav ul li a {
font-size: 3vw;
}
.site_navigation ul {
display: flex;
flex-direction: row-reverse;
}
nav .row_space_between .no_margin {
flex-direction: row-reverse;
}
.nav_right_hand {
flex-direction: row-reverse;
align-items: center;
justify-content: center;
}
.nav_right_hand * {
margin: 0px 0px 0px 15px;
}
#backButton {
display: none;
}
#actionBar {
bottom: 125px;
}
.modalContent {
max-height: 600px;
overflow-y: auto;
}
}
+209 -18
View File
@@ -7,41 +7,218 @@ if (isset($_POST['ajaxId'])) {
case 'login':
handle_login();
break;
case 'loginHTML':
handleLoginHTML();
break;
case 'signupHTML':
handleSignupHTML();
break;
case 'usernameCheck':
handleUsernameCheck();
break;
case 'logout':
User::logout();
break;
case 'newPostHTML':
handleNewPostHTML();
break;
case 'newThought':
handleNewThought();
case 'newSpaceHTML':
handleNewSpaceHTML();
break;
case 'deletePost':
handleDeletePost();
break;
case 'deleteSpace':
handleDeleteSpace();
break;
}
} else if (isset($_POST['formID'])) {
switch ($_POST['formID']) {
case 'spaceNew':
break;
case 'signup':
handleSignup();
break;
case 'newPost':
handleNewPost();
break;
case 'newSpace':
handleNewSpace();
break;
}
} else {
$response = [
'error' => pretty_dump($_POST),
];
echo json_encode($response);
if (!isset($_POST['redirect']))
header('Location: .');
else
header('Location: ' . $_POST['redirect']);
}
function handleNewThought()
function handleDeleteSpace()
{
$thought = new Thought(Space::retrieveFromDB($_POST['space']), null, unserialize($_SESSION['user']), 3, 2, urldecode($_POST['thought']));
echo json_encode(['log' => 'success']);
if (!isset($_POST['id'])) {
echo json_encode(['error' => 'Missing required Space ID.']);
return;
}
$stmt = 'DELETE FROM spaceMembers where space = ?';
exec_stmt($stmt, 'i', $_POST['id']);
$stmt = 'DELETE FROM space WHERE id = ?';
exec_stmt($stmt, 'i', $_POST['id']);
}
function handleNewSpace()
{
if (!isset($_POST['name']) || !isset($_POST['description']) || !isset($_POST['visibility'])) {
error_log('ruh roh');
return;
}
$id = randomId(2) * -1;
/* $name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8'); */
/* $description = htmlspecialchars($_POST['description'], ENT_QUOTES, 'UTF-8'); */
$space = new Space($id, $_POST['name'], $_POST['description'], $_POST['visibility'], null, null, isset($_POST['space']) ? Space::retrieveFromDB($_POST['space']) : null, true);
error_log(pretty_dump($space));
Space::addUserToSpace(unserialize($_SESSION['user'])->getId(), $id * -1, 1, true);
}
function handleNewPost()
{
if (!isset($_POST['postType']) || !isset($_POST['space']))
return;
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']));
break;
case 'Moment':
break;
case 'Essay':
new Essay(Space::retrieveFromDB($_POST['space']), null, unserialize($_SESSION['user']), 3, 2, $_POST['title'], $_POST['markdown'], $_POST['excerpt']);
break;
}
}
function handleSignup()
{
if (!isset($_POST['username']) || !isset($_POST['password']) || !isset($_POST['vPassword']))
return;
if ($_POST['password'] != $_POST['vPassword'])
return;
$_SESSION['user'] = serialize(new User(randomId(0) * -1, null, $_POST['username'], null, null, null, 3, password_hash($_POST['password'], PASSWORD_DEFAULT)));
header('Location: .');
}
function handleUsernameCheck()
{
if (!isset($_POST['username']))
return;
echo json_encode(['exists' => User::exists($_POST['username'])]);
}
function handleSignupHTML()
{
if (isset($_SESSION['user']))
header('Location: .');
echo json_encode(['html' => User::getSignupHTML()]);
}
function handleLoginHTML()
{
if (isset($_SESSION['user']))
header('Location: .');
echo json_encode(['html' => User::getLoginHTML()]);
}
function handleDeletePost()
{
if (!isset($_POST['id'])) {
echo json_encode(['error' => 'Missing required Post ID.']);
return;
}
$stmt = 'DELETE FROM post WHERE id = ?';
exec_stmt($stmt, 'i', $_POST['id']);
}
function handleNewSpaceHTML()
{
$user = unserialize($_SESSION['user']);
$html = '
<div class="med_width std_border center padding modalContent">
<h3>New Space</h3>
<form method="post" action="director.php">
<input type="hidden" name="formID" value="newSpace">
<input type="hidden" name="redirect" value="" id="redirect">
<input name="name" placeholder="Name" required autofocus>
<input name="description" placeholder="Description" required>
<select name="visibility">
<option value="1">Open</option>
<option value="2">Public</option>
<option value="3">Request</option>
<option value="4">Invite</option>
<option value="5">Private</option>
</select>
';
$spaces = $user->getAllSpaces();
foreach ($spaces as $s) {
$html .= '
<label class="form_checkbox_container">' . $s->getName() . ' <i class="nf ' . getSpaceIcon($s) . ' left_margin"></i>
<input type="radio" name="space" value="' . $s->getId() . '" id="space-' . $s->getId() . '">
<span class="checkmark"></span>
</label>
';
}
$html .= '
<input class="button" type="submit" value="Create" id="create">
</form>
</div>
';
echo json_encode(['html' => $html]);
}
function getSpaceIcon($space)
{
switch ($space->getVisibility()) {
case 1:
return 'nf-cod-globe';
case 2:
return 'nf-fa-group';
case 3:
case 4:
return 'nf-fa-user_group';
case 5:
return 'nf-fa-lock';
}
}
function handleNewPostHTML()
{
$user = unserialize($_SESSION['user']);
// 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 = '
<div class="med_width std_border center padding">
<div class="med_width std_border center padding modalContent">
<h3 id="newPostLabel">New Thought</h3>
<form method="post" class="center">
<input type="hidden" name="userID" value="' . unserialize($_SESSION['user'])->getId() . '">
<form method="post" action="director.php">
<input type="hidden" name="formID" value="newPost">
<input type="hidden" name="redirect" value="" id="redirect">
<input type="hidden" name="postType" value="Thought" id="postType">
<input type="hidden" name="space" value="' . $_GET['id'] . '">
<div id="newThought" class="column">
<input type="text" name="thought" placeholder="Jot a Thought." autofocus id="thought">
@@ -57,6 +234,22 @@ function handleNewPostHTML()
<input type="text" name="excerpt" placeholder="A 2-3 sentence summary." id="excerpt">
</div>
<div class="column" id="postSpaces">
';
$spaces = $user->getAllSpaces();
foreach ($spaces as $s) {
$html .= '
<label class="form_checkbox_container">' . $s->getName() . ' <i class="nf ' . getSpaceIcon($s) . ' left_margin"></i>
<input type="radio" name="space" value="' . $s->getId() . '" id="space-' . $s->getId() . '">
<span class="checkmark"></span>
</label>
';
}
$html .= '
</div>
<input class="button" type="submit" value="Share" id="share">
</form>
<div class="line std_border"></div>
@@ -93,20 +286,18 @@ function handle_login()
// Serialize the user object and store it in the session
$_SESSION['user'] = serialize($user);
// You might want to store specific user data instead of the whole serialized object,
// e.g., $_SESSION['user_id'] = $user->getId(); $_SESSION['username'] = $user->getUsername();
$spaces = $user->getSpaces();
foreach ($spaces as $s)
$_SESSION['spaces'][] = serialize($s);
} else {
$response['status'] = 'error';
$response['message'] = 'Incorrect username and/or password.';
}
} catch (Exception $e) {
// Catch potential exceptions from LoginCredentials or User::login()
// Log the detailed error for server-side debugging
error_log('Login Error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
$response['status'] = 'error';
$response['message'] = 'An internal error occurred. Please try again later.';
// Optionally, for development, you could include $e->getMessage() but not in production.
}
} else {
$response['status'] = 'error';
+4 -2
View File
@@ -23,9 +23,11 @@ function randomId(int $mode)
return randomId($mode);
else
return $id;
break;
case 2:
break;
if (Space::exists($id))
return randomId($mode);
else
return $id;
}
}
-1
View File
@@ -12,7 +12,6 @@ require_once 'functions/user.php';
require_once 'functions/post.php';
require_once 'functions/space.php';
/**/
// Set SESSION variable.
if (!isset($_SESSION['initialized'])) {
$_SESSION['initialized'] = true;
+45 -34
View File
@@ -60,10 +60,6 @@ class Post
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
if ($result['count(id)'] == 1)
return true;
else if ($result['count(id)'] > 1) {
// TODO: Add additional logging here, since that means multiple posts have the same ID.
return true;
}
return false;
}
@@ -80,10 +76,9 @@ class Post
return new Essay(null, $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']);
case 3:
return new Thought(null, $post['id'], $creator, 3, $post['status'], $post['thought']);
default:
return null;
}
} else
}
return null;
}
@@ -92,11 +87,6 @@ class Post
$this->id = $id;
}
public function authUser(User $user): bool
{
return true;
}
public function getId(): int
{
return $this->id;
@@ -149,6 +139,10 @@ class Post
public function getSpace(): ?Space
{
if (!isset($this->space)) {
$stmt = 'SELECT space from spacePosts WHERE post = ?';
$rawSpace = exec_stmt($stmt, 'i', $this->id)->fetch_assoc();
}
return $this->space;
}
}
@@ -156,21 +150,17 @@ class Post
class Essay extends Post
{
private string $title;
private string $html;
private string $markdown;
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)
{
parent::__construct($space, $id, $creator, 1, $postType, $status);
require_once 'vendor/autoload.php';
$parsedown = new Parsedown();
$parsedown->setSafeMode(true);
$this->title = $title;
$this->excerpt = $excerpt;
$this->html = $parsedown->text($markdown);
$this->markdown = $markdown;
}
public function getCardHTML(): ?string
@@ -184,8 +174,8 @@ class Essay extends Post
<h4>' . $this->title . '</h4>
</div>
<div class="">
<span>Something</span>
<div class="sm_width">
<i class="nf nf-md-dots_horizontal"></i>
</div>
</div>
<div class="row full_width">
@@ -198,14 +188,19 @@ class Essay extends Post
return $html;
}
public function allowUser(User $user): bool
{
return parent::getSpace()->allowUser($user);
}
public function getTitle(): ?string
{
return $this->title;
}
public function getHTML(): ?string
public function getMarkdown(): ?string
{
return $this->html;
return $this->markdown;
}
public function getExcerpt(): ?string
@@ -215,16 +210,18 @@ class Essay extends Post
public function setMarkdown(?string $markdown): void
{
require_once 'vendor/autoload.php';
$parsedown = new Parsedown();
$parsedown->setSafeMode(true);
$this->html = $parsedown->text($markdown);
$this->markdown = $markdown;
}
public function setExcerpt(?string $excerpt): void
{
$this->excerpt = $excerpt;
}
public function getSpace(): ?Space
{
return parent::getSpace();
}
}
class Moment extends Post
@@ -269,28 +266,42 @@ class Thought extends Post
parent::__construct($space, $id, $creator, $postType, $status);
$this->thought = $thought;
if ($id == null) {
if ($id < 0) {
$stmt = 'INSERT INTO post (id, parentSpace, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)';
$newID = randomId(1);
parent::setId($newID);
parent::setId($id * -1);
$space = isset($space) ? $space->getId() : $creator->getHomeSpace()->getId();
exec_stmt($stmt, 'iiiiis', $newID, $space, $creator->getId(), 3, 2, $this->thought);
exec_stmt($stmt, 'iiiiis', $id * -1, $space, $creator->getId(), 3, 2, $this->thought);
$stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)';
exec_stmt($stmt, 'ii', $newID, $space);
exec_stmt($stmt, 'ii', $id * -1, $space);
}
}
public function getCardHTML(): ?string
{
$html = '
<div class="std_border padding center full_width top_margin bottom_margin">
<div class="std_border padding center full_width top_margin bottom_margin rel" id="post-' . parent::getId() . '">
<div class="row space_between full_width">
<a href="user.php?view=display&id=' . parent::getCreator()->getId() . '">@' . parent::getCreator()->getUsername() . '</a>
<div class="">
<span>Something</span>
';
if (isset($_SESSION['user'])) {
$user = unserialize($_SESSION['user']);
if ($user->getId() == parent::getCreator()->getId()) {
$html .= '
<div class="dropdown" style="z-index: 0;">
<i class="nf nf-md-dots_horizontal"></i>
<div class="sm_border padding glass dropdownContent">
<a class="underline link" onclick="deletePost(' . parent::getId() . ')">Delete</a>
</div>
</div>
';
}
}
$html .= '
</div>
<div class="line"></div>
<div class="row full_width">
<p>' . $this->thought . '</p>
+85 -27
View File
@@ -10,13 +10,12 @@ class Space
private string $name;
private string $description;
private int $visibility;
private array $members;
private array $posts;
private ?array $members;
private ?array $posts;
private ?Space $parent;
public function __construct(?int $id, string $name, string $description, int $visibility, array $members, array $posts, ?Space $parent = null)
public function __construct(int $id, string $name, string $description, int $visibility, ?array $members, ?array $posts, ?Space $parent = null, bool $favorited = false)
{
$this->id = $id ?? randomId(2);
$this->name = $name;
$this->description = $description;
$this->visibility = $visibility;
@@ -24,33 +23,101 @@ class Space
$this->posts[] = $posts;
$this->parent = $parent;
/* $stmt = 'INSERT INTO spaces (spaceId, name, description, parentSpaceIdk)'; */
if ($id < 0) {
$this->id = $id * -1;
error_log('ID: ' . $this->id);
$stmt = 'INSERT INTO space (id, name, description, visibility, parentSpace) VALUES (?, ?, ?, ?, ?)';
exec_stmt($stmt, 'issii', $this->id, $name, $description, $visibility, (isset($parent) ? $parent->getId() : null));
error_log('Added space in DB');
} else
$this->id = $id;
}
public static function exists(int $id): bool
{
$stmt = 'SELECT count(id) FROM space WHERE id = ?';
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
if ($result['count(id)'] == 1)
return true;
else if ($result['count(id)'] > 1)
return true;
else
return false;
return true;
}
public static function addUserToSpace(int $user, int $space, int $role = 3, bool $favorited = false)
{
$stmt = 'INSERT INTO spaceMembers (user, space, role, favorited) VALUES (?, ?, ?, ?)';
exec_stmt($stmt, 'iiii', $user, $space, $role, $favorited);
}
public function getSubSpaces(): ?array
{
$stmt = 'SELECT id FROM space WHERE parentSpace = ?';
$rawSubSpaces = exec_stmt($stmt, 'i', $this->id);
$subSpaces = [];
foreach ($rawSubSpaces as $ss) {
$subSpaces[] = Space::retrieveFromDB($ss['id']);
}
return $subSpaces;
}
public function getParentSpace(): ?Space
{
return $this->parent;
}
public function getCardHTML(): string
{
$html = '
<div class="full_width std_border padding center top_margin bottom_margin">
<a href="spaces.php?view=show&id=' . $this->id . '" class="divLink">
<div class="full_width std_border padding center top_margin bottom_margin rel" id="space-' . $this->id . '">
<a href="spaces.php?view=show&id=' . $this->id . '" class="fillDiv spaceCard"></a>
<div class="row space_between full_width">
<div class="column std_width">
<h4>' . $this->name . '</h4>
</div>
<div class="">
<span>Something</span>
';
if (isset($_SESSION['user'])) {
$user = unserialize($_SESSION['user']);
if ($user->getId() == $this->getOwner()->getId()) {
$html .= '
<div class="dropdown">
<i class="nf nf-md-dots_horizontal"></i>
<div class="sm_border padding glass dropdownContent">
<a class="underline link" onclick="deleteSpace(' . $this->id . ')">Delete</a>
</div>
</div>
';
}
}
$html .= '
</div>
<div class="row full_width">
<p>' . $this->description . '</p>
</div>
</a>
</div>
';
return $html;
}
public function getOwner(): User
{
$stmt = 'SELECT user FROM spaceMembers WHERE space = ? AND role = ?';
$user = exec_stmt($stmt, 'ii', $this->id, 1)->fetch_assoc();
return User::retrieveFromDB($user['user']);
}
public static function retrieveFromDB(int $space, int $user = -1): ?Space
{
if ($user != -1) {
@@ -68,6 +135,7 @@ class Space
$stmt = 'SELECT * FROM space WHERE id = ?';
$rawSpace = exec_stmt($stmt, 'i', $space)->fetch_assoc();
if ($rawSpace) {
if (!empty($rawSpace['parentSpace']))
$parent = Space::retrieveFromDB($rawSpace['parentSpace'], $user);
else
@@ -76,33 +144,23 @@ class Space
$stmt = 'SELECT user FROM spaceMembers WHERE space = ?';
$members = [];
$rawMembers = exec_stmt($stmt, 'i', $rawSpace['id']);
foreach ($rawMembers as $m) {
foreach ($rawMembers as $m)
$members[] = User::retrieveFromDB($m['user']);
}
$stmt = 'SELECT post FROM spacePosts WHERE space = ?';
$stmt = 'SELECT post FROM spacePosts INNER JOIN post ON spacePosts.post = post.id WHERE space = ? ORDER BY post.createdAt DESC';
$posts = [];
$rawPosts = exec_stmt($stmt, 'i', $rawSpace['id']);
while ($row = $rawPosts->fetch_assoc()) {
while ($row = $rawPosts->fetch_assoc())
$posts[] = Post::retrieveFromDB($row['post']);
}
$space = new Space($rawSpace['id'], $rawSpace['name'], $rawSpace['description'], $rawSpace['visibility'], $members, $posts, $parent);
foreach ($posts as $p)
$p->setSpace($space);
return $space;
return new Space($rawSpace['id'], $rawSpace['name'], $rawSpace['description'], $rawSpace['visibility'], $members, $posts, $parent);
} else
return null;
}
public function allowUser(User $user)
{
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$result = exec_stmt($stmt, 'i', $user->getId())->fetch_assoc();
if ($result['space'] == $this->id)
return true;
else
return false;
return in_array($user, $this->members);
}
public function getPosts()
+96 -75
View File
@@ -33,15 +33,13 @@ class User
private int $id;
private ?array $spaces;
private string $username;
private ?DateTime $createdAt;
private ?DateTime $lastActive;
private bool $isActive;
private ?string $profilePicture;
private ?string $bio;
private ?string $website;
private int $role;
public function __construct(int $id = -1, ?array $spaces, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, int $role = 3)
public function __construct(int $id = -1, ?array $spaces, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, int $role = 3, ?string $passwordHash)
{
// Basic XSS prevention on construction (can be enhanced)
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
@@ -50,32 +48,44 @@ class User
$profilePicture = htmlspecialchars($profilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
// Basic SQL injection prevention (should primarily rely on prepared statements)
if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) {
throw new InvalidArgumentException('Invalid characters in user data.');
}
/* if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) { */
/* throw new InvalidArgumentException('Invalid characters in user data.'); */
/* } */
$this->username = $username;
$this->profilePicture = $profilePicture;
$this->bio = $bio;
$this->website = $website;
$this->createdAt = new DateTime();
$this->lastActive = null;
$this->isActive = false;
$this->role = $role;
$this->spaces = null;
if ($id == -1) {
$this->id = randomId(0);
$stmt = 'INSERT INTO user (userId, username, profilePicture, bio, website) VALUES (?, ?, ?, ?, ?)';
exec_stmt($stmt, 'issss', $this->id, $this->username, $this->profilePicture, $this->bio, $this->website);
if ($id < 0) {
$this->id = $id * -1;
$stmt = 'INSERT INTO user (id, username, passwordHash, profilePicture, bio, website) VALUES (?, ?, ?, ?, ?, ?)';
exec_stmt($stmt, 'isssss', $this->id, $this->username, $passwordHash, $this->profilePicture, $this->bio, $this->website);
Space::addUserToSpace($this->id, 2025);
new Space($id, $this->username, $this->username . "'s private Space.", 5, [$this], [], null, true);
} else
$this->id = $id;
}
public static function exists(int $id): bool
public static function exists($identifier): bool
{
if (is_int($identifier)) {
$stmt = 'SELECT count(id) FROM user WHERE id = ?';
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
$result = exec_stmt($stmt, 'i', $identifier)->fetch_assoc();
if ($result['count(id)'] == 1)
return true;
else if ($result['count(id)'] > 1)
return true;
else
return false;
} else if (is_string($identifier)) {
$stmt = 'SELECT count(id) FROM user WHERE username = ?';
$result = exec_stmt($stmt, 's', $identifier)->fetch_assoc();
if ($result['count(id)'] == 1)
return true;
@@ -85,42 +95,34 @@ class User
return false;
}
return true;
}
public static function retrieveFromDB(int $userId): ?User
{
$stmt = 'SELECT id, username, profilePicture, bio, website FROM user WHERE id = ?';
$stmt = 'SELECT id, username, profilePicture, bio, website, role FROM user WHERE id = ?';
$user = exec_stmt($stmt, 'i', $userId)->fetch_assoc();
if ($user) {
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website']);
} else {
if ($user)
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
else
return null;
}
}
public static function getSignupHTML()
{
$html = '
<div class="form_card">
<h3 class="underline">Sign Up</h3>
$experimentalHTML = '
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="form_id" value="signup_form">
<input name="required_field" id="required_field" value="">
<input type="hidden" name="crop_x" value="" id="crop_x">
<input type="hidden" name="crop_y" value="" id="crop_y">
<input type="hidden" name="crop_width" value="" id="crop_width">
<input name="required_field" id="required_field" value="">
<label for="username">Username</label>
<input id="username" name="username" required>
<label for="email">Email Address</label>
<input id="email" name="email" required>
<label for="password">Password</label>
<input id="password" name="password" type="password" required>
<label for="verify_password">Verify Password</label>
<input id="verify_password" name="verify_password" type="password" required>
<label for="profile_picture_input" class="upload_button modal_button" id="profile_cropper_button">Upload a Profile Picture</label>
<input id="profile_picture_input" class="file_input" name="profile_picture" type="file" accept="image/png, image/jpeg, image/jpg">
<small class="file_input_feedback">No file selected.</small>
@@ -129,17 +131,28 @@ class User
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
';
$html = '
<div class="med_width std_border padding center modalContent">
<h3 class="underline">Sign Up</h3>
<div class="line"></div>
<form method="post" action="director.php">
<input type="hidden" name="formID" value="signup">
<label for="username">Username</label>
<input id="username" name="username" required autofocus>
<label for="password">Password</label>
<input id="password" name="password" type="password" required>
<label for="vPassword">Verify Password</label>
<input id="vPassword" name="vPassword" type="password" required>
<input class="button" type="submit" value="Sign Up">
</form>
<div id="modal" class="modal">
<div id="display_modal"></div>
</div>
<div style="display: none;">
<div id="profile_cropper" class="modal_form">' . profile_cropper() . '</div>
</div>
<div id="signupError"></div>
</div>
';
@@ -148,28 +161,30 @@ class User
public static function getLoginHTML()
{
$html = '
<div class="form_card">
<h3 class="underline">Log In</h3>
<form method="post" id="login_form" action="director.php">
<input type="hidden" name="form_id" value="login_form">
<input type="hidden" name="password_hash" value="" id="password_hash">
$stayCheckedIn = '
<label class="form_checkbox_container">Stay Logged In?
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
';
<label for="username">Username / Email</label>
$html = '
<div class="med_width std_border padding center modalContent">
<h3 class="underline">Log In</h3>
<div class="line"></div>
<form method="post" action="director.php">
<input type="hidden" name="formID" value="login">
<label for="username">Username</label>
<input id="username" name="username" spellcheck="false" required autofocus>
<label for="password">Password</label>
<input id="password" name="password" type="password" spellcheck="false" required>
<label class="form_checkbox_container">Stay Logged In?
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
<input class="button" type="submit" value="Log In">
</form>
<script src="js/login.js" defer></script>
<div id="loginError"></div>
</div>
';
@@ -183,8 +198,17 @@ class User
if (!password_verify($credentials->getPassword(), $user['passwordHash']))
return null;
else
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website']);
else {
$spaces = [];
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $user['id'])->fetch_assoc();
if ($rawSpaces)
foreach ($rawSpaces as $s)
$spaces[] = serialize(Space::retrieveFromDB($s));
return new User($user['id'], $spaces, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
}
}
public static function logout(): void
@@ -205,14 +229,26 @@ class User
public function getSpaces()
{
$stmt = 'SELECT * FROM spaceMembers WHERE user = ?';
$spaces = [];
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
while ($row = $rawSpaces->fetch_assoc()) {
$spaces[] = Space::retrieveFromDB($row['space'], $row['user']);
$spaces = [];
while ($r = $rawSpaces->fetch_assoc()) {
$space = Space::retrieveFromDB($r['space']);
if (!$space->getParentSpace())
$spaces[] = $space;
}
$this->spaces = $spaces;
return $spaces;
}
public function getAllSpaces()
{
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
$spaces = [];
while ($r = $rawSpaces->fetch_assoc())
$spaces[] = Space::retrieveFromDB($r['space']);
return $spaces;
}
@@ -231,16 +267,6 @@ class User
return $this->username;
}
public function getCreatedAt(): ?DateTime
{
return $this->createdAt;
}
public function getLastActive(): ?DateTime
{
return $this->lastActive;
}
public function isActive(): bool
{
return $this->isActive;
@@ -261,11 +287,6 @@ class User
return $this->website;
}
public function setLastActive(?DateTime $lastActive): void
{
$this->lastActive = $lastActive;
}
public function setIsActive(bool $isActive): void
{
$this->isActive = $isActive;
+4 -1
View File
@@ -1,2 +1,5 @@
<?php
header('Location: spaces.php?view=list');
if (isset($_SESSION['user']))
header('Location: spaces.php?view=list');
else
header('Location: spaces.php?view=show&id=2025');
+104
View File
@@ -0,0 +1,104 @@
const bar = document.querySelector("#actionBar");
const actionSpace = bar.querySelector("#actionSpace");
const actionPost = bar.querySelector("#actionPost");
actionSpace.addEventListener("click", () => {
document.querySelector("#primaryActions").style.display = "none";
document.querySelector("#spaceActions").style.display = "flex";
document.querySelector("#spaceBack").addEventListener("click", () => {
document.querySelector("#primaryActions").style.display = "flex";
document.querySelector("#spaceActions").style.display = "none";
});
let home = document.querySelector("#spaceHome");
home.addEventListener("click", () => {
applyTransition(home.getAttribute("href"));
});
document.querySelector("#spaceNew").addEventListener("click", () => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=newSpaceHTML",
})
.then((response) => {
return response.json();
})
.then((data) => {
openModal(data["html"]);
document.querySelector("#redirect").value = window.location.href;
});
});
});
actionPost.addEventListener("click", () => {
document.querySelector("#primaryActions").style.display = "none";
document.querySelector("#postActions").style.display = "flex";
document.querySelector("#postBack").addEventListener("click", () => {
document.querySelector("#primaryActions").style.display = "flex";
document.querySelector("#postActions").style.display = "none";
});
document.querySelector("#postNew").addEventListener("click", (e) => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=newPostHTML",
})
.then((response) => {
return response.json();
})
.then((data) => {
openModal(data["html"]);
document.querySelector("#redirect").value = window.location.href;
let newNavThought = document.querySelector("#newNavThought");
let newThought = document.querySelector("#newThought");
let newNavMoment = document.querySelector("#newNavMoment");
let newMoment = document.querySelector("#newMoment");
let newNavEssay = document.querySelector("#newNavEssay");
let newEssay = document.querySelector("#newEssay");
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";
newThought.style.display = "flex";
newPostLabel.innerText = "New Thought";
newThought.querySelector('input[type="text"]').focus();
postType.value = "Thought";
});
newNavMoment.addEventListener("click", () => {
newThought.style.display = "none";
newEssay.style.display = "none";
newMoment.style.display = "flex";
newPostLabel.innerText = "New Moment";
newMoment.querySelector('input[type="text"]').focus();
postType.value = "Moment";
});
newNavEssay.addEventListener("click", () => {
newThought.style.display = "none";
newMoment.style.display = "none";
newEssay.style.display = "flex";
newPostLabel.innerText = "New Essay";
newEssay.querySelector('input[type="text"]').focus();
postType.value = "Essay";
});
});
});
});
-118
View File
@@ -1,118 +0,0 @@
let actionButton = document.querySelector("#actionButton");
let newPostButton = document.querySelector("#newPost");
let newSpaceButton = document.querySelector("#newSpace");
let filterSortButton = document.querySelector("#filterSort");
let circle = document.querySelector(".circleLayout");
let modal = document.querySelector("#stdModal");
let state = "hide";
actionButton.addEventListener("click", () => {
if (state == "hide") {
state = "show";
newPostButton.style.display = "flex";
newSpaceButton.style.display = "flex";
filterSortButton.style.display = "flex";
circle.style.backgroundColor = "#FFFFFF";
newPostButton.addEventListener("click", () => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=newPostHTML",
})
.then((response) => {
return response.json();
})
.then((data) => {
modal.innerHTML = data["html"];
modal.style.display = "flex";
let newNavThought = document.querySelector("#newNavThought");
let newThought = document.querySelector("#newThought");
let newNavMoment = document.querySelector("#newNavMoment");
let newMoment = document.querySelector("#newMoment");
let newNavEssay = document.querySelector("#newNavEssay");
let newEssay = document.querySelector("#newEssay");
let newPostLabel = document.querySelector("#newPostLabel");
let postType = document.querySelector("#postType");
let share = document.querySelector("#share");
share.addEventListener("click", (e) => {
e.preventDefault();
switch (postType.value) {
case "Thought":
let thought = document.querySelector("#thought");
// TODO: Retrieve space dynamically.
fetch("director.php", {
method: "POST",
// TODO: Add Authorization header with JWT
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body:
"ajaxId=newThought&space=1&thought=" +
encodeURIComponent(thought.value),
}).then((response) => {
console.log(response.json());
modal.style.display = "none";
});
break;
case "Moment":
break;
case "Essay":
break;
}
});
newNavThought.addEventListener("click", () => {
newMoment.style.display = "none";
newEssay.style.display = "none";
newThought.style.display = "flex";
newPostLabel.innerText = "New Thought";
newThought.querySelector('input[type="text"]').focus();
postType.value = "Thought";
});
newNavMoment.addEventListener("click", () => {
newThought.style.display = "none";
newEssay.style.display = "none";
newMoment.style.display = "flex";
newPostLabel.innerText = "New Moment";
newMoment.querySelector('input[type="text"]').focus();
postType.value = "Moment";
});
newNavEssay.addEventListener("click", () => {
newThought.style.display = "none";
newMoment.style.display = "none";
newEssay.style.display = "flex";
newPostLabel.innerText = "New Essay";
newEssay.querySelector('input[type="text"]').focus();
postType.value = "Essay";
});
return data;
});
});
newSpaceButton.addEventListener("click", () => { });
filterSortButton.addEventListener("click", () => { });
} else if (state == "show") {
state = "hide";
newPostButton.style.display = "none";
newSpaceButton.style.display = "none";
filterSortButton.style.display = "none";
circle.style.backgroundColor = "transparent";
}
});
+47
View File
@@ -0,0 +1,47 @@
let dropdowns = document.querySelectorAll(".dropdown");
dropdowns.forEach((el) => {
el.addEventListener("click", (e) => {
e.stopPropagation();
let dropContent = el.querySelector(".dropdownContent");
dropContent.style.display = "block";
dropContent.addEventListener("click", (e) => {
e.stopPropagation();
});
});
});
window.addEventListener("click", () => {
document.querySelectorAll(".dropdownContent").forEach((dc) => {
dc.style.display = "none";
});
});
function deleteSpace(id) {
document.querySelector("#space-" + id).remove();
fetch("director.php", {
method: "POST",
// TODO: Add Authorization header with JWT
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=deleteSpace&id=" + id,
});
}
function deletePost(id) {
document.querySelector("#post-" + id).remove();
fetch("director.php", {
method: "POST",
// TODO: Add Authorization header with JWT
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=deletePost&id=" + id,
});
}
-93
View File
@@ -1,93 +0,0 @@
const login = document.querySelector("#login");
const logout = document.querySelector("#logout");
const stdModal = document.querySelector("#stdModal");
if (logout) {
logout.addEventListener("click", () => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=logout",
}).then(() => {
if ("startViewTransition" in document) {
document.startViewTransition(() => {
window.location.href = "spaces.php?view=list";
});
} else {
window.location.href = "spaces.php?view=list";
}
});
});
}
if (login) {
login.addEventListener("click", () => {
console.log("click");
stdModal.innerHTML = `
<div class="form_card">
<h3 class="underline">Log In</h3>
<form method="post" id="login_form" action="director.php">
<input type="hidden" name="form_id" value="login_form">
<input type="hidden" name="password_hash" value="" id="password_hash">
<label for="username">Username / Email</label>
<input id="username" name="username" spellcheck="false" required autofocus>
<label for="password">Password</label>
<input id="password" name="password" type="password" spellcheck="false" required>
<label class="form_checkbox_container">Stay Logged In?
<input name="stay_logged_in" type="checkbox" value="true">
<span class="checkmark"></span>
</label>
<input class="button" type="submit" value="Log In">
</form>
<script src="js/login.js" defer></script>
</div>
`;
stdModal.style.display = "flex";
const form = document.querySelector("#login_form");
const username_field = form.querySelector("#username");
const passwordField = form.querySelector("#password");
const submit_button = form.querySelector('input[type="submit"]');
submit_button.addEventListener("click", async (e) => {
e.preventDefault();
requestAuth(username_field.value, passwordField.value);
});
function requestAuth(username, password) {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=login&username=" + username + "&password=" + password,
})
.then((response) => {
if (!response.ok) {
console.log("Login Failed!");
}
return response.json();
})
.then((data) => {
console.log(data);
if ("startViewTransition" in document) {
document.startViewTransition(() => {
window.location.href = data["redirect"];
});
} else {
// Fallback for browsers that don't support View Transitions
window.location.href = data["redirect"];
}
});
}
});
}
+31 -12
View File
@@ -1,16 +1,35 @@
const nav = document.querySelector('nav');
const nav_links = nav.querySelectorAll('ul li a');
const theme_toggle = nav.querySelector('button');
const nav = document.querySelector("nav");
const nav_links = nav.querySelectorAll("ul li a");
const theme_toggle = nav.querySelector("button");
const back = document.querySelector("#backButton");
const links = document.querySelectorAll(".fillDiv");
nav_links.forEach((el) => {
el.addEventListener('click', () => {
el.preventDefault();
if ('startViewTransition' in document) {
document.startViewTransition(() => {
window.location.href = el.getAttribute('href');
if (back)
back.addEventListener("click", () => {
console.log("back");
history.back();
});
} else {
window.location.href = el.getAttribute('href');
}
links.forEach((el) => {
el.addEventListener("click", () => {
el.preventDefault();
applyTransition(el.getAttribute("href"));
});
});
nav_links.forEach((el) => {
el.addEventListener("click", () => {
el.preventDefault();
applyTransition(el.getAttribute("href"));
});
});
function applyTransition(href) {
if ("startViewTransition" in document) {
document.startViewTransition(() => {
window.location.href = href;
});
} else {
window.location.href = href;
}
}
View File
View File
View File
+34
View File
@@ -0,0 +1,34 @@
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
if (darkThemeMq.matches) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "css/dark.css";
document.head.appendChild(link);
// Theme set to dark.
} else {
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "css/light.css";
document.head.appendChild(link);
// Theme set to light.
}
darkThemeMq.addEventListener("change", (e) => {
if (e.matches) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "css/dark.css";
document.head.appendChild(link);
// Theme set to dark.
} else {
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "css/light.css";
document.head.appendChild(link);
// Theme set to light.
}
});
+245
View File
@@ -0,0 +1,245 @@
const login = document.querySelector("#login");
const logout = document.querySelector("#logout");
const signup = document.querySelector("#signup");
if (logout) {
logout.addEventListener("click", () => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=logout",
}).then(() => {
if ("startViewTransition" in document) {
document.startViewTransition(() => {
window.location.href = "spaces.php?view=list";
});
} else {
window.location.href = "spaces.php?view=list";
}
});
});
}
if (login) {
login.addEventListener("click", () => {
console.log("click");
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=loginHTML",
})
.then((response) => {
return response.json();
})
.then((data) => {
openModal(data["html"]);
const username_field = modalContent.querySelector("#username");
const passwordField = modalContent.querySelector("#password");
const submit_button = modalContent.querySelector(
'input[type="submit"]',
);
submit_button.addEventListener("click", async (e) => {
e.preventDefault();
requestAuth(username_field.value, passwordField.value);
});
function requestAuth(username, password) {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=login&username=" + username + "&password=" + password,
})
.then((response) => {
if (!response.ok) {
console.log("Login Failed!");
}
return response.json();
})
.then((data) => {
if ("startViewTransition" in document) {
document.startViewTransition(() => {
window.location.href = data["redirect"];
});
} else {
// Fallback for browsers that don't support View Transitions
window.location.href = data["redirect"];
}
});
}
});
});
}
if (signup) {
signup.addEventListener("click", () => {
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=signupHTML",
})
.then((response) => {
return response.json();
})
.then((data) => {
openModal(data["html"]);
const submit = modalContent.querySelector('input[type="submit"]');
submit.disabled = true;
const signupErrorArea = modalContent.querySelector("#signupError");
const usernameField = modalContent.querySelector("#username");
usernameField.addEventListener("blur", checkUsername);
const passwordField = modalContent.querySelector("#password");
passwordField.addEventListener("input", checkPW);
const vPasswordField = modalContent.querySelector("#vPassword");
vPasswordField.addEventListener("input", matchPW);
submit.addEventListener("click", (e) => {
e.preventDefault();
checkUsername();
checkPW();
matchPW();
if (
usernameField.value != "" &&
passwordField.value != "" &&
vPasswordField.value != "" &&
signupErrorArea.innerHTML == ""
)
modalContent.querySelector("form").submit();
});
function checkUsername() {
const username = usernameField.value;
fetch("director.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "ajaxId=usernameCheck&username=" + username,
})
.then((response) => {
if (!response.ok) {
console.log("Failed to check if username exists!");
}
return response.json(); // Parse response body as JSON
})
.then((data) => {
if (data["exists"]) {
console.log("user exists");
if (!document.querySelector("#userExists")) {
let p = document.createElement("p");
p.id = "userExists";
p.innerText = "Username is taken.";
signupErrorArea.append(p);
submit.disabled = true;
}
} else {
let p = signupErrorArea.querySelector("#userExists");
if (p) p.remove();
matchPW();
if (
signupErrorArea.innerHTML == "" &&
passwordField.value != "" &&
vPasswordField.value != ""
)
submit.disabled = false;
}
});
}
function checkPW() {
let pw = passwordField.value;
if (pw.length < 8) {
if (!document.querySelector("#pwLength")) {
let p = document.createElement("p");
p.id = "pwLength";
p.innerText =
"Password length must be greater than 8 characters.";
signupErrorArea.append(p);
}
} else {
let p = signupErrorArea.querySelector("#pwLength");
if (p) p.remove();
matchPW();
if (
signupErrorArea.innerHTML == "" &&
passwordField.value != "" &&
vPasswordField.value != ""
)
submit.disabled = false;
}
const num_regex = /\d/;
if (!num_regex.test(pw)) {
if (!document.querySelector("#pwNum")) {
let p = document.createElement("p");
p.id = "pwNum";
p.innerText = "Password must have at least 1 digit.";
signupErrorArea.append(p);
submit.disabled = true;
}
} else {
let p = signupErrorArea.querySelector("#pwNum");
if (p) p.remove();
matchPW();
if (
signupErrorArea.innerHTML == "" &&
passwordField.value != "" &&
vPasswordField.value != ""
)
submit.disabled = false;
}
}
function matchPW() {
let pw = passwordField.value;
let vPW = vPasswordField.value;
if (pw != vPW) {
if (!document.querySelector("#pwMatch")) {
let p = document.createElement("p");
p.id = "pwMatch";
p.innerText = "Passwords must match.";
signupErrorArea.append(p);
submit.disabled = true;
}
} else {
let p = signupErrorArea.querySelector("#pwMatch");
if (p) p.remove();
if (
signupErrorArea.innerHTML == "" &&
passwordField.value != "" &&
vPasswordField.value != ""
)
submit.disabled = false;
}
}
});
});
}
+21
View File
@@ -0,0 +1,21 @@
const stdModal = document.querySelector("#stdModal");
let modalContent = document.querySelector(".modalContent");
function openModal(html) {
stdModal.innerHTML = html;
stdModal.style.display = "flex";
modalContent = document.querySelector(".modalContent");
window.addEventListener("click", () => {
closeModal();
});
modalContent.addEventListener("click", (e) => {
e.stopPropagation();
});
}
function closeModal() {
stdModal.innerHTML = "";
stdModal.style.display = "none";
}
+67 -21
View File
@@ -12,15 +12,11 @@ if (isset($_GET['view'])) {
case 'essay':
if (isset($_GET['id'])) {
// TODO: Need another check here to verify the user is in the Essay's space.
$essay = Essay::retrieveFromDB($_GET['id']);
$space = $essay->getSpace();
if (isset($_SESSION['user']) && $space->allowUser($_SESSION['user']))
echo displayEssay($essay);
else if ($space->getVisibility() == 1)
$essay = Post::retrieveFromDB($_GET['id']);
if ($essay)
echo displayEssay($essay);
else
echo displaySpace(2025);
echo errorScreen('Essay does not exist!', 'The Essay ID provided does not exist in the database.');
} else
echo displaySpace(2025);
break;
@@ -28,6 +24,7 @@ if (isset($_GET['view'])) {
// TODO: After implementing the notification modal, notify the user on success/fail auth events.
if (isset($_GET['id'])) {
$space = Space::retrieveFromDB($_GET['id']);
if ($space) {
if (isset($_SESSION['user']))
if ($space->allowUser(unserialize($_SESSION['user'])))
echo displaySpace($_GET['id']);
@@ -41,6 +38,8 @@ if (isset($_GET['view'])) {
else
echo 'TODO: Notification Modal -> Space is not Open';
}
} else
echo errorScreen('Space does not exist!', 'The Space ID provided does not exist in the database.');
} else
echo displaySpaceList();
break;
@@ -60,19 +59,55 @@ if (isset($_GET['view'])) {
}
if (isset($_SESSION['user']))
echo actionButton();
echo actionBar();
if (isset($_GET['id']))
echo backButton();
require_once 'components/core/foot.php';
function actionButton()
function backButton()
{
// TODO: Position these on the arc of a circle.
$html = '
<div class="circleLayout">
<div id="newPost" class="actionButton" style="display: none;"><i class="nf nf-cod-new_file"></i></div>
<div id="newSpace" class="actionButton" style="display: none;"><i class="nf nf-cod-new_folder"></i></div>
<div id="filterSort" class="actionButton" style="display: none;"><i class="nf nf-oct-filter"></i></div>
<div id="actionButton" class="actionButton"><i class="nf nf-cod-symbol_property"></i></div>
<div class="link" id="backButton">
<i class="nf nf-fa-arrow_left"></i>
</div>
';
return $html;
}
function actionBar()
{
$user = unserialize($_SESSION['user']);
$html = '
<div class="std_border med_width padding glass" id="actionBar">
<div class="row space_between" id="primaryActions">
<div class="column center link med_width" id="actionSpace"><i class="nf nf-md-layers_outline"></i><p class="link">Space</p></div>
<div class="column center link med_width" id="actionPost"><i class="nf nf-md-text"></i><p class="link">Post</p></div>
</div>
<div class="row space_between" id="postActions" style="display: none;">
<div class="column center link med_width" id="postBack"><i class="nf nf-cod-arrow_circle_left"></i><p class="link">Back</p></div>
<div class="column center link med_width" id="postNew"><i class="nf nf-md-chat_plus"></i><p class="link">New</p></div>
</div>
<div class="row space_between" id="spaceActions" style="display: none;">
<div class="column center link med_width" id="spaceBack"><i class="nf nf-cod-arrow_circle_left"></i><p class="link">Back</p></div>
<div class="column center link med_width" id="spaceHome" href="spaces.php?view=show&id=' . $user->getId() . '"><i class="nf nf-fa-home"></i><p class="link">Home</p></div>
<div class="column center link med_width" id="spaceNew"><i class="nf nf-md-layers_plus"></i><p class="link">New</p></div>
</div>
</div>
';
return $html;
}
function errorScreen(String $heading, String $msg)
{
$html = '
<div class="column std_border med_width padding center top_margin">
<h1>' . $heading . '</h1>
<p>' . $msg . '</p>
</div>
';
@@ -81,9 +116,16 @@ function actionButton()
function displayEssay(Essay $e)
{
/* if (isset($_SESSION['user'])) */
/* if ($e->allowUser(unserialize($_SESSION['user'])) || $e->getSpace()->getVisibility() != 1) */
/* return errorScreen('Access Denied!', 'You do not have access to the Space that this Essay is in.'); */
$parsedown = new Parsedown();
$parsedown->setSafeMode(true);
$html = '
<div class="std_width std_border padding center top_margin" style="height: 87%; overflow-y: auto;">
' . $e->getHTML() . '
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
' . $parsedown->parse($e->getMarkdown()) . '
</div>
';
@@ -95,7 +137,7 @@ function displaySpaceList()
$user = unserialize($_SESSION['user']);
$html = '
<div class="std_width std_border padding center top_margin" style="height: 87%; overflow-y: auto;">
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
<div class="column space_between full_width">
<h1>Spaces</h4>
</div>
@@ -103,9 +145,8 @@ function displaySpaceList()
';
$spaces = $user->getSpaces();
foreach ($spaces as $s) {
foreach ($spaces as $s)
$html .= $s->getCardHTML();
}
$html .= '
</div>
@@ -119,7 +160,7 @@ function displaySpace($id = 2025)
$space = Space::retrieveFromDB($id);
if ($space) {
$html = '
<div class="std_width std_border padding center top_margin" style="max-height: 89vh; overflow-y: auto;">
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
<div class="column space_between full_width">
<h1>' . $space->getName() . '</h4>
<p>' . $space->getDescription() . '</p>
@@ -127,6 +168,11 @@ function displaySpace($id = 2025)
<div class="line"></div>
';
$subspaces = $space->getSubSpaces();
foreach ($subspaces as $ss) {
$html .= $ss->getCardHTML();
}
// TODO: Need to investigate this bug in Posts
$posts = $space->getPosts();
foreach ($posts as $post) {