implemented spaces, fixed login and signup, thought creation and space creation.
This commit is contained in:
@@ -1,74 +1,17 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
<script src="js/util.js"></script>
|
||||||
|
<script src="js/nav.js" defer></script>
|
||||||
|
<script src="js/user.js" defer></script>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
function get_args_from_url()
|
// <script src="js/theme.js" defer></script>
|
||||||
{
|
if ($_SERVER['SCRIPT_NAME'] == '/spaces.php' && isset($_SESSION['user'])) {
|
||||||
$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') {
|
|
||||||
echo '
|
|
||||||
<script src="js/profile_picture_cropper_modal.js"></script>
|
|
||||||
<script src="js/upload.js"></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 '
|
echo '
|
||||||
<script src="js/override_browser_styles.js"></script>
|
<script src="js/actionBar.js" defer></script>
|
||||||
';
|
<script src="js/dropdown.js" defer></script>
|
||||||
|
';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<link href="css/rewrite.css" rel="stylesheet" type="text/css">
|
<link href="css/rewrite.css" rel="stylesheet" type="text/css">
|
||||||
<link rel="stylesheet" href="https://nerdfonts.com/assets/css/webfont.css">
|
<link rel="stylesheet" href="https://nerdfonts.com/assets/css/webfont.css">
|
||||||
<link rel="icon" type="image/x-icon" href="data/vintagecoding-favicon.png">
|
<link rel="icon" type="image/x-icon" href="data/vintagecoding-favicon.png">
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if (!isset($_SESSION['theme']) || $_SESSION['theme'] == 'dark')
|
if (!isset($_SESSION['theme']) || $_SESSION['theme'] == 'dark')
|
||||||
echo '<link href="css/dark.css" rel="stylesheet" type="text/css">';
|
echo '<link href="css/dark.css" rel="stylesheet" type="text/css">';
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ $nav .= '
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/nav.js" defer></script>
|
|
||||||
<script src="js/login.js" defer></script>
|
|
||||||
</nav>
|
</nav>
|
||||||
<div id="notificationModal"></div>
|
<div id="notificationModal"></div>
|
||||||
<div id="stdModal"></div>
|
<div id="stdModal"></div>
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
@@ -14,167 +14,11 @@ body {
|
|||||||
padding-bottom: 50px;
|
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 {
|
code {
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line {
|
|
||||||
height: 1px;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-h {
|
.center-h {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
@@ -238,7 +82,7 @@ tbody tr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:hover {
|
tbody tr:hover {
|
||||||
outline: solid 3px orange;
|
outline: solid 3px red;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr td {
|
tbody tr td {
|
||||||
@@ -271,15 +115,6 @@ table tr td label {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 15px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 12px;
|
|
||||||
transition-duration: 1s;
|
|
||||||
border: 3px solid orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.narrow {
|
.narrow {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
@@ -288,7 +123,7 @@ table tr td label {
|
|||||||
margin: 15px;
|
margin: 15px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
border: 2px solid orange;
|
border: 2px solid red;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,11 +142,6 @@ table tr td label {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
|
||||||
color: orange;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divLink {
|
.divLink {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -319,28 +149,6 @@ table tr td label {
|
|||||||
width: 100%;
|
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 {
|
.article {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -359,115 +167,14 @@ i:hover {
|
|||||||
justify-content: left;
|
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"] {
|
input[type="datetime_local"] {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus,
|
|
||||||
textarea:focus,
|
|
||||||
input:hover,
|
|
||||||
textarea:hover {
|
|
||||||
border: 3px solid orange;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
border-radius: 18px;
|
|
||||||
height: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkboxes {
|
.checkboxes {
|
||||||
height: max-content;
|
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"] {
|
input[type="file"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -567,7 +274,7 @@ input[type="file"] {
|
|||||||
|
|
||||||
.modal_close:hover,
|
.modal_close:hover,
|
||||||
.modal_close:focus {
|
.modal_close:focus {
|
||||||
color: orange;
|
color: red;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,7 +298,7 @@ input[type="file"] {
|
|||||||
height: 128px;
|
height: 128px;
|
||||||
width: 128px;
|
width: 128px;
|
||||||
|
|
||||||
outline: dashed 4px orange;
|
outline: dashed 4px red;
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -603,7 +310,7 @@ input[type="file"] {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background-color: orange;
|
background-color: red;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: nwse-resize;
|
cursor: nwse-resize;
|
||||||
/* Default cursor for resizing */
|
/* Default cursor for resizing */
|
||||||
@@ -683,7 +390,7 @@ input[type="file"] {
|
|||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
color: black;
|
color: black;
|
||||||
background: orange;
|
background: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-2x1 {
|
.grid-2x1 {
|
||||||
@@ -718,52 +425,6 @@ hr {
|
|||||||
margin: 15px 0px 15px 0px;
|
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) {
|
@media screen and (max-width: 992px) {
|
||||||
body {
|
body {
|
||||||
padding-bottom: 160px;
|
padding-bottom: 160px;
|
||||||
|
|||||||
+5
-80
@@ -11,10 +11,12 @@ h2,
|
|||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
|
h6,
|
||||||
p,
|
p,
|
||||||
small,
|
small,
|
||||||
label,
|
label,
|
||||||
span {
|
span,
|
||||||
|
i {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,24 +33,9 @@ nav ul li a {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #2f2f2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-top: 1px solid white;
|
border-top: 2px solid white;
|
||||||
}
|
|
||||||
|
|
||||||
.article_card,
|
|
||||||
.form_card,
|
|
||||||
.view_card {
|
|
||||||
border: 5px solid white;
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr {
|
|
||||||
outline: solid 3px white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@@ -57,21 +44,13 @@ table tr {
|
|||||||
|
|
||||||
.button:hover,
|
.button:hover,
|
||||||
.button:hover i {
|
.button:hover i {
|
||||||
color: orange;
|
color: red;
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#theme_toggle {
|
#theme_toggle {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article ul li {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea {
|
textarea {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -95,60 +74,6 @@ select:-webkit-autofill {
|
|||||||
background-color: white;
|
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,
|
.std_border,
|
||||||
.med_border,
|
.med_border,
|
||||||
.sm_border {
|
.sm_border {
|
||||||
|
|||||||
+4
-76
@@ -14,7 +14,8 @@ h5,
|
|||||||
p,
|
p,
|
||||||
small,
|
small,
|
||||||
label,
|
label,
|
||||||
span {
|
span,
|
||||||
|
i {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,18 +38,7 @@ code {
|
|||||||
|
|
||||||
.line {
|
.line {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
border-top: 1px solid black;
|
border-top: 2px solid black;
|
||||||
}
|
|
||||||
|
|
||||||
.article_card,
|
|
||||||
.form_card,
|
|
||||||
.view_card {
|
|
||||||
border: 5px solid black;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr {
|
|
||||||
outline: solid 3px black;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@@ -57,21 +47,13 @@ table tr {
|
|||||||
|
|
||||||
.button:hover,
|
.button:hover,
|
||||||
.button:hover * {
|
.button:hover * {
|
||||||
color: orange;
|
color: red;
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
color: black;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#theme_toggle {
|
#theme_toggle {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article ul li {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea {
|
textarea {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -95,60 +77,6 @@ select:-webkit-autofill {
|
|||||||
background-color: black;
|
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,
|
.std_border,
|
||||||
.med_border,
|
.med_border,
|
||||||
.sm_border {
|
.sm_border {
|
||||||
|
|||||||
+446
-44
@@ -10,6 +10,232 @@ body {
|
|||||||
box-sizing: border-box;
|
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 **********/
|
/********** Width & Sizing **********/
|
||||||
.full_width {
|
.full_width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -50,6 +276,11 @@ body {
|
|||||||
border: 1px solid red;
|
border: 1px solid red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
/********** Rows & Columns **********/
|
/********** Rows & Columns **********/
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -71,6 +302,10 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
/********** Margins & Padding **********/
|
/********** Margins & Padding **********/
|
||||||
.margin {
|
.margin {
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
@@ -124,6 +359,83 @@ body {
|
|||||||
padding-left: 15px;
|
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 ************/
|
/************ Modals ************/
|
||||||
#stdModal {
|
#stdModal {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -136,55 +448,25 @@ body {
|
|||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stdModal>* {}
|
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionButton {
|
.glass {
|
||||||
|
background-color: transparent;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#actionBar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
bottom: 3.5%;
|
||||||
border-radius: 50%;
|
|
||||||
width: 75px;
|
|
||||||
height: 75px;
|
|
||||||
background-color: orange;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#actionButton {
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -5%);
|
||||||
}
|
|
||||||
|
|
||||||
#newPost {
|
z-index: 10;
|
||||||
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%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/********** Animations **********/
|
/********** Animations **********/
|
||||||
@@ -194,11 +476,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::view-transition-old(root) {
|
::view-transition-old(root) {
|
||||||
animation: fade-out 750ms forwards;
|
animation: fade-out 1s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
::view-transition-new(root) {
|
::view-transition-new(root) {
|
||||||
animation: fade-in 750ms forwards;
|
animation: fade-in 1s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade-in {
|
@keyframes fade-in {
|
||||||
@@ -220,3 +502,123 @@ body {
|
|||||||
opacity: 0;
|
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
@@ -7,41 +7,218 @@ if (isset($_POST['ajaxId'])) {
|
|||||||
case 'login':
|
case 'login':
|
||||||
handle_login();
|
handle_login();
|
||||||
break;
|
break;
|
||||||
|
case 'loginHTML':
|
||||||
|
handleLoginHTML();
|
||||||
|
break;
|
||||||
|
case 'signupHTML':
|
||||||
|
handleSignupHTML();
|
||||||
|
break;
|
||||||
|
case 'usernameCheck':
|
||||||
|
handleUsernameCheck();
|
||||||
|
break;
|
||||||
case 'logout':
|
case 'logout':
|
||||||
User::logout();
|
User::logout();
|
||||||
break;
|
break;
|
||||||
case 'newPostHTML':
|
case 'newPostHTML':
|
||||||
handleNewPostHTML();
|
handleNewPostHTML();
|
||||||
break;
|
break;
|
||||||
case 'newThought':
|
case 'newSpaceHTML':
|
||||||
handleNewThought();
|
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;
|
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']));
|
if (!isset($_POST['id'])) {
|
||||||
echo json_encode(['log' => 'success']);
|
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()
|
function handleNewPostHTML()
|
||||||
{
|
{
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
// TODO: Need to plumb this into the DB via AJAX.
|
// 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: 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
|
// TODO: List the user's spaces or let them search for a space or quick select a favorited space
|
||||||
$html = '
|
$html = '
|
||||||
<div class="med_width std_border center padding">
|
<div class="med_width std_border center padding modalContent">
|
||||||
<h3 id="newPostLabel">New Thought</h3>
|
<h3 id="newPostLabel">New Thought</h3>
|
||||||
<form method="post" class="center">
|
<form method="post" action="director.php">
|
||||||
<input type="hidden" name="userID" value="' . unserialize($_SESSION['user'])->getId() . '">
|
<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="postType" value="Thought" id="postType">
|
||||||
|
<input type="hidden" name="space" value="' . $_GET['id'] . '">
|
||||||
|
|
||||||
<div id="newThought" class="column">
|
<div id="newThought" class="column">
|
||||||
<input type="text" name="thought" placeholder="Jot a Thought." autofocus id="thought">
|
<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">
|
<input type="text" name="excerpt" placeholder="A 2-3 sentence summary." id="excerpt">
|
||||||
</div>
|
</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">
|
<input class="button" type="submit" value="Share" id="share">
|
||||||
</form>
|
</form>
|
||||||
<div class="line std_border"></div>
|
<div class="line std_border"></div>
|
||||||
@@ -93,20 +286,18 @@ function handle_login()
|
|||||||
|
|
||||||
// Serialize the user object and store it in the session
|
// Serialize the user object and store it in the session
|
||||||
$_SESSION['user'] = serialize($user);
|
$_SESSION['user'] = serialize($user);
|
||||||
// You might want to store specific user data instead of the whole serialized object,
|
$spaces = $user->getSpaces();
|
||||||
// e.g., $_SESSION['user_id'] = $user->getId(); $_SESSION['username'] = $user->getUsername();
|
foreach ($spaces as $s)
|
||||||
|
$_SESSION['spaces'][] = serialize($s);
|
||||||
} else {
|
} else {
|
||||||
$response['status'] = 'error';
|
$response['status'] = 'error';
|
||||||
$response['message'] = 'Incorrect username and/or password.';
|
$response['message'] = 'Incorrect username and/or password.';
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} 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());
|
error_log('Login Error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
|
||||||
|
|
||||||
$response['status'] = 'error';
|
$response['status'] = 'error';
|
||||||
$response['message'] = 'An internal error occurred. Please try again later.';
|
$response['message'] = 'An internal error occurred. Please try again later.';
|
||||||
// Optionally, for development, you could include $e->getMessage() but not in production.
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$response['status'] = 'error';
|
$response['status'] = 'error';
|
||||||
|
|||||||
+4
-2
@@ -23,9 +23,11 @@ function randomId(int $mode)
|
|||||||
return randomId($mode);
|
return randomId($mode);
|
||||||
else
|
else
|
||||||
return $id;
|
return $id;
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
break;
|
if (Space::exists($id))
|
||||||
|
return randomId($mode);
|
||||||
|
else
|
||||||
|
return $id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -12,14 +12,13 @@ require_once 'functions/user.php';
|
|||||||
require_once 'functions/post.php';
|
require_once 'functions/post.php';
|
||||||
require_once 'functions/space.php';
|
require_once 'functions/space.php';
|
||||||
|
|
||||||
/**/
|
|
||||||
// Set SESSION variable.
|
// Set SESSION variable.
|
||||||
if (!isset($_SESSION['initialized'])) {
|
if (!isset($_SESSION['initialized'])) {
|
||||||
$_SESSION['initialized'] = true;
|
$_SESSION['initialized'] = true;
|
||||||
$_SESSION['theme'] = 'dark';
|
$_SESSION['theme'] = 'dark';
|
||||||
} else if (isset($_POST['theme'])) {
|
} else if (isset($_POST['theme'])) {
|
||||||
$_SESSION['theme'] = $_POST['theme'];
|
$_SESSION['theme'] = $_POST['theme'];
|
||||||
unset($_POST['theme']);
|
unset($_POST['theme']);
|
||||||
}
|
}
|
||||||
|
|
||||||
define('owner', 1);
|
define('owner', 1);
|
||||||
|
|||||||
+46
-35
@@ -60,10 +60,6 @@ class Post
|
|||||||
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
|
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
|
||||||
if ($result['count(id)'] == 1)
|
if ($result['count(id)'] == 1)
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -80,11 +76,10 @@ class Post
|
|||||||
return new Essay(null, $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']);
|
return new Essay(null, $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']);
|
||||||
case 3:
|
case 3:
|
||||||
return new Thought(null, $post['id'], $creator, 3, $post['status'], $post['thought']);
|
return new Thought(null, $post['id'], $creator, 3, $post['status'], $post['thought']);
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
return null;
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setId(int $id): void
|
public function setId(int $id): void
|
||||||
@@ -92,11 +87,6 @@ class Post
|
|||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authUser(User $user): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId(): int
|
public function getId(): int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
@@ -149,6 +139,10 @@ class Post
|
|||||||
|
|
||||||
public function getSpace(): ?Space
|
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;
|
return $this->space;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,21 +150,17 @@ class Post
|
|||||||
class Essay extends Post
|
class Essay extends Post
|
||||||
{
|
{
|
||||||
private string $title;
|
private string $title;
|
||||||
private string $html;
|
private string $markdown;
|
||||||
private string $excerpt;
|
private string $excerpt;
|
||||||
/* private array $files; */
|
/* 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(?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);
|
parent::__construct($space, $id, $creator, 1, $postType, $status);
|
||||||
require_once 'vendor/autoload.php';
|
|
||||||
|
|
||||||
$parsedown = new Parsedown();
|
|
||||||
$parsedown->setSafeMode(true);
|
|
||||||
|
|
||||||
$this->title = $title;
|
$this->title = $title;
|
||||||
$this->excerpt = $excerpt;
|
$this->excerpt = $excerpt;
|
||||||
$this->html = $parsedown->text($markdown);
|
$this->markdown = $markdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCardHTML(): ?string
|
public function getCardHTML(): ?string
|
||||||
@@ -184,8 +174,8 @@ class Essay extends Post
|
|||||||
<h4>' . $this->title . '</h4>
|
<h4>' . $this->title . '</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="">
|
<div class="sm_width">
|
||||||
<span>Something</span>
|
<i class="nf nf-md-dots_horizontal"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row full_width">
|
<div class="row full_width">
|
||||||
@@ -198,14 +188,19 @@ class Essay extends Post
|
|||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function allowUser(User $user): bool
|
||||||
|
{
|
||||||
|
return parent::getSpace()->allowUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
public function getTitle(): ?string
|
public function getTitle(): ?string
|
||||||
{
|
{
|
||||||
return $this->title;
|
return $this->title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHTML(): ?string
|
public function getMarkdown(): ?string
|
||||||
{
|
{
|
||||||
return $this->html;
|
return $this->markdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getExcerpt(): ?string
|
public function getExcerpt(): ?string
|
||||||
@@ -215,16 +210,18 @@ class Essay extends Post
|
|||||||
|
|
||||||
public function setMarkdown(?string $markdown): void
|
public function setMarkdown(?string $markdown): void
|
||||||
{
|
{
|
||||||
require_once 'vendor/autoload.php';
|
$this->markdown = $markdown;
|
||||||
$parsedown = new Parsedown();
|
|
||||||
$parsedown->setSafeMode(true);
|
|
||||||
$this->html = $parsedown->text($markdown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setExcerpt(?string $excerpt): void
|
public function setExcerpt(?string $excerpt): void
|
||||||
{
|
{
|
||||||
$this->excerpt = $excerpt;
|
$this->excerpt = $excerpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSpace(): ?Space
|
||||||
|
{
|
||||||
|
return parent::getSpace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Moment extends Post
|
class Moment extends Post
|
||||||
@@ -269,27 +266,41 @@ class Thought extends Post
|
|||||||
parent::__construct($space, $id, $creator, $postType, $status);
|
parent::__construct($space, $id, $creator, $postType, $status);
|
||||||
$this->thought = $thought;
|
$this->thought = $thought;
|
||||||
|
|
||||||
if ($id == null) {
|
if ($id < 0) {
|
||||||
$stmt = 'INSERT INTO post (id, parentSpace, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)';
|
$stmt = 'INSERT INTO post (id, parentSpace, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)';
|
||||||
$newID = randomId(1);
|
parent::setId($id * -1);
|
||||||
parent::setId($newID);
|
|
||||||
$space = isset($space) ? $space->getId() : $creator->getHomeSpace()->getId();
|
$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 (?, ?)';
|
$stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)';
|
||||||
exec_stmt($stmt, 'ii', $newID, $space);
|
exec_stmt($stmt, 'ii', $id * -1, $space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCardHTML(): ?string
|
public function getCardHTML(): ?string
|
||||||
{
|
{
|
||||||
$html = '
|
$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">
|
<div class="row space_between full_width">
|
||||||
<a href="user.php?view=display&id=' . parent::getCreator()->getId() . '">@' . parent::getCreator()->getUsername() . '</a>
|
<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>
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
</div>
|
</div>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<div class="row full_width">
|
<div class="row full_width">
|
||||||
|
|||||||
+96
-38
@@ -10,13 +10,12 @@ class Space
|
|||||||
private string $name;
|
private string $name;
|
||||||
private string $description;
|
private string $description;
|
||||||
private int $visibility;
|
private int $visibility;
|
||||||
private array $members;
|
private ?array $members;
|
||||||
private array $posts;
|
private ?array $posts;
|
||||||
private ?Space $parent;
|
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->name = $name;
|
||||||
$this->description = $description;
|
$this->description = $description;
|
||||||
$this->visibility = $visibility;
|
$this->visibility = $visibility;
|
||||||
@@ -24,33 +23,101 @@ class Space
|
|||||||
$this->posts[] = $posts;
|
$this->posts[] = $posts;
|
||||||
$this->parent = $parent;
|
$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
|
public function getCardHTML(): string
|
||||||
{
|
{
|
||||||
$html = '
|
$html = '
|
||||||
<div class="full_width std_border padding center top_margin bottom_margin">
|
<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="divLink">
|
<a href="spaces.php?view=show&id=' . $this->id . '" class="fillDiv spaceCard"></a>
|
||||||
<div class="row space_between full_width">
|
<div class="row space_between full_width">
|
||||||
<div class="column std_width">
|
<div class="column std_width">
|
||||||
<h4>' . $this->name . '</h4>
|
<h4>' . $this->name . '</h4>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
<div class="row full_width">
|
<div class="row full_width">
|
||||||
<p>' . $this->description . '</p>
|
<p>' . $this->description . '</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
return $html;
|
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
|
public static function retrieveFromDB(int $space, int $user = -1): ?Space
|
||||||
{
|
{
|
||||||
if ($user != -1) {
|
if ($user != -1) {
|
||||||
@@ -68,41 +135,32 @@ class Space
|
|||||||
$stmt = 'SELECT * FROM space WHERE id = ?';
|
$stmt = 'SELECT * FROM space WHERE id = ?';
|
||||||
$rawSpace = exec_stmt($stmt, 'i', $space)->fetch_assoc();
|
$rawSpace = exec_stmt($stmt, 'i', $space)->fetch_assoc();
|
||||||
|
|
||||||
if (!empty($rawSpace['parentSpace']))
|
if ($rawSpace) {
|
||||||
$parent = Space::retrieveFromDB($rawSpace['parentSpace'], $user);
|
if (!empty($rawSpace['parentSpace']))
|
||||||
else
|
$parent = Space::retrieveFromDB($rawSpace['parentSpace'], $user);
|
||||||
$parent = null;
|
else
|
||||||
|
$parent = null;
|
||||||
|
|
||||||
$stmt = 'SELECT user FROM spaceMembers WHERE space = ?';
|
$stmt = 'SELECT user FROM spaceMembers WHERE space = ?';
|
||||||
$members = [];
|
$members = [];
|
||||||
$rawMembers = exec_stmt($stmt, 'i', $rawSpace['id']);
|
$rawMembers = exec_stmt($stmt, 'i', $rawSpace['id']);
|
||||||
foreach ($rawMembers as $m) {
|
foreach ($rawMembers as $m)
|
||||||
$members[] = User::retrieveFromDB($m['user']);
|
$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 = [];
|
$posts = [];
|
||||||
$rawPosts = exec_stmt($stmt, 'i', $rawSpace['id']);
|
$rawPosts = exec_stmt($stmt, 'i', $rawSpace['id']);
|
||||||
while ($row = $rawPosts->fetch_assoc()) {
|
while ($row = $rawPosts->fetch_assoc())
|
||||||
$posts[] = Post::retrieveFromDB($row['post']);
|
$posts[] = Post::retrieveFromDB($row['post']);
|
||||||
}
|
|
||||||
|
|
||||||
$space = new Space($rawSpace['id'], $rawSpace['name'], $rawSpace['description'], $rawSpace['visibility'], $members, $posts, $parent);
|
return new Space($rawSpace['id'], $rawSpace['name'], $rawSpace['description'], $rawSpace['visibility'], $members, $posts, $parent);
|
||||||
|
} else
|
||||||
foreach ($posts as $p)
|
return null;
|
||||||
$p->setSpace($space);
|
|
||||||
|
|
||||||
return $space;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function allowUser(User $user)
|
public function allowUser(User $user)
|
||||||
{
|
{
|
||||||
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
|
return in_array($user, $this->members);
|
||||||
$result = exec_stmt($stmt, 'i', $user->getId())->fetch_assoc();
|
|
||||||
if ($result['space'] == $this->id)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPosts()
|
public function getPosts()
|
||||||
|
|||||||
+103
-82
@@ -33,15 +33,13 @@ class User
|
|||||||
private int $id;
|
private int $id;
|
||||||
private ?array $spaces;
|
private ?array $spaces;
|
||||||
private string $username;
|
private string $username;
|
||||||
private ?DateTime $createdAt;
|
|
||||||
private ?DateTime $lastActive;
|
|
||||||
private bool $isActive;
|
private bool $isActive;
|
||||||
private ?string $profilePicture;
|
private ?string $profilePicture;
|
||||||
private ?string $bio;
|
private ?string $bio;
|
||||||
private ?string $website;
|
private ?string $website;
|
||||||
private int $role;
|
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)
|
// Basic XSS prevention on construction (can be enhanced)
|
||||||
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
|
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
|
||||||
@@ -50,77 +48,81 @@ class User
|
|||||||
$profilePicture = htmlspecialchars($profilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
$profilePicture = htmlspecialchars($profilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
// Basic SQL injection prevention (should primarily rely on prepared statements)
|
// Basic SQL injection prevention (should primarily rely on prepared statements)
|
||||||
if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) {
|
/* if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) { */
|
||||||
throw new InvalidArgumentException('Invalid characters in user data.');
|
/* throw new InvalidArgumentException('Invalid characters in user data.'); */
|
||||||
}
|
/* } */
|
||||||
|
|
||||||
$this->username = $username;
|
$this->username = $username;
|
||||||
$this->profilePicture = $profilePicture;
|
$this->profilePicture = $profilePicture;
|
||||||
$this->bio = $bio;
|
$this->bio = $bio;
|
||||||
$this->website = $website;
|
$this->website = $website;
|
||||||
$this->createdAt = new DateTime();
|
|
||||||
$this->lastActive = null;
|
|
||||||
$this->isActive = false;
|
$this->isActive = false;
|
||||||
$this->role = $role;
|
$this->role = $role;
|
||||||
$this->spaces = null;
|
$this->spaces = null;
|
||||||
|
|
||||||
if ($id == -1) {
|
if ($id < 0) {
|
||||||
$this->id = randomId(0);
|
$this->id = $id * -1;
|
||||||
$stmt = 'INSERT INTO user (userId, username, profilePicture, bio, website) VALUES (?, ?, ?, ?, ?)';
|
$stmt = 'INSERT INTO user (id, username, passwordHash, profilePicture, bio, website) VALUES (?, ?, ?, ?, ?, ?)';
|
||||||
exec_stmt($stmt, 'issss', $this->id, $this->username, $this->profilePicture, $this->bio, $this->website);
|
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
|
} else
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function exists(int $id): bool
|
public static function exists($identifier): bool
|
||||||
{
|
{
|
||||||
$stmt = 'SELECT count(id) FROM user WHERE id = ?';
|
if (is_int($identifier)) {
|
||||||
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
|
$stmt = 'SELECT count(id) FROM user WHERE id = ?';
|
||||||
|
$result = exec_stmt($stmt, 'i', $identifier)->fetch_assoc();
|
||||||
|
|
||||||
if ($result['count(id)'] == 1)
|
if ($result['count(id)'] == 1)
|
||||||
return true;
|
return true;
|
||||||
else if ($result['count(id)'] > 1)
|
else if ($result['count(id)'] > 1)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
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;
|
||||||
|
else if ($result['count(id)'] > 1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function retrieveFromDB(int $userId): ?User
|
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();
|
$user = exec_stmt($stmt, 'i', $userId)->fetch_assoc();
|
||||||
|
|
||||||
if ($user) {
|
if ($user)
|
||||||
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website']);
|
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
|
||||||
} else {
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSignupHTML()
|
public static function getSignupHTML()
|
||||||
{
|
{
|
||||||
$html = '
|
$experimentalHTML = '
|
||||||
<div class="form_card">
|
|
||||||
<h3 class="underline">Sign Up</h3>
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
<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_x" value="" id="crop_x">
|
||||||
<input type="hidden" name="crop_y" value="" id="crop_y">
|
<input type="hidden" name="crop_y" value="" id="crop_y">
|
||||||
<input type="hidden" name="crop_width" value="" id="crop_width">
|
<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>
|
<label for="email">Email Address</label>
|
||||||
<input id="email" name="email" required>
|
<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>
|
<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">
|
<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>
|
<small class="file_input_feedback">No file selected.</small>
|
||||||
@@ -129,17 +131,28 @@ class User
|
|||||||
<input name="stay_logged_in" type="checkbox" value="true">
|
<input name="stay_logged_in" type="checkbox" value="true">
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
</label>
|
</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">
|
<input class="button" type="submit" value="Sign Up">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="modal" class="modal">
|
<div id="signupError"></div>
|
||||||
<div id="display_modal"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: none;">
|
|
||||||
<div id="profile_cropper" class="modal_form">' . profile_cropper() . '</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
@@ -148,28 +161,30 @@ class User
|
|||||||
|
|
||||||
public static function getLoginHTML()
|
public static function getLoginHTML()
|
||||||
{
|
{
|
||||||
$html = '
|
$stayCheckedIn = '
|
||||||
<div class="form_card">
|
<label class="form_checkbox_container">Stay Logged In?
|
||||||
<h3 class="underline">Log In</h3>
|
<input name="stay_logged_in" type="checkbox" value="true">
|
||||||
<form method="post" id="login_form" action="director.php">
|
<span class="checkmark"></span>
|
||||||
<input type="hidden" name="form_id" value="login_form">
|
</label>
|
||||||
<input type="hidden" name="password_hash" value="" id="password_hash">
|
';
|
||||||
|
|
||||||
<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>
|
<input id="username" name="username" spellcheck="false" required autofocus>
|
||||||
|
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
<input id="password" name="password" type="password" spellcheck="false" required>
|
<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">
|
<input class="button" type="submit" value="Log In">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script src="js/login.js" defer></script>
|
<div id="loginError"></div>
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
@@ -183,8 +198,17 @@ class User
|
|||||||
|
|
||||||
if (!password_verify($credentials->getPassword(), $user['passwordHash']))
|
if (!password_verify($credentials->getPassword(), $user['passwordHash']))
|
||||||
return null;
|
return null;
|
||||||
else
|
else {
|
||||||
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website']);
|
$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
|
public static function logout(): void
|
||||||
@@ -205,14 +229,26 @@ class User
|
|||||||
|
|
||||||
public function getSpaces()
|
public function getSpaces()
|
||||||
{
|
{
|
||||||
$stmt = 'SELECT * FROM spaceMembers WHERE user = ?';
|
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
|
||||||
$spaces = [];
|
|
||||||
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
|
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
|
||||||
while ($row = $rawSpaces->fetch_assoc()) {
|
$spaces = [];
|
||||||
$spaces[] = Space::retrieveFromDB($row['space'], $row['user']);
|
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;
|
return $spaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,16 +267,6 @@ class User
|
|||||||
return $this->username;
|
return $this->username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): ?DateTime
|
|
||||||
{
|
|
||||||
return $this->createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLastActive(): ?DateTime
|
|
||||||
{
|
|
||||||
return $this->lastActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isActive(): bool
|
public function isActive(): bool
|
||||||
{
|
{
|
||||||
return $this->isActive;
|
return $this->isActive;
|
||||||
@@ -261,11 +287,6 @@ class User
|
|||||||
return $this->website;
|
return $this->website;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLastActive(?DateTime $lastActive): void
|
|
||||||
{
|
|
||||||
$this->lastActive = $lastActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setIsActive(bool $isActive): void
|
public function setIsActive(bool $isActive): void
|
||||||
{
|
{
|
||||||
$this->isActive = $isActive;
|
$this->isActive = $isActive;
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
<?php
|
<?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
@@ -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";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -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
@@ -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"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,35 @@
|
|||||||
const nav = document.querySelector('nav');
|
const nav = document.querySelector("nav");
|
||||||
const nav_links = nav.querySelectorAll('ul li a');
|
const nav_links = nav.querySelectorAll("ul li a");
|
||||||
const theme_toggle = nav.querySelector('button');
|
const theme_toggle = nav.querySelector("button");
|
||||||
|
const back = document.querySelector("#backButton");
|
||||||
|
const links = document.querySelectorAll(".fillDiv");
|
||||||
|
|
||||||
nav_links.forEach((el) => {
|
if (back)
|
||||||
el.addEventListener('click', () => {
|
back.addEventListener("click", () => {
|
||||||
|
console.log("back");
|
||||||
|
history.back();
|
||||||
|
});
|
||||||
|
|
||||||
|
links.forEach((el) => {
|
||||||
|
el.addEventListener("click", () => {
|
||||||
el.preventDefault();
|
el.preventDefault();
|
||||||
if ('startViewTransition' in document) {
|
applyTransition(el.getAttribute("href"));
|
||||||
document.startViewTransition(() => {
|
|
||||||
window.location.href = el.getAttribute('href');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
window.location.href = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+34
@@ -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
@@ -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
@@ -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";
|
||||||
|
}
|
||||||
+80
-34
@@ -12,15 +12,11 @@ if (isset($_GET['view'])) {
|
|||||||
case 'essay':
|
case 'essay':
|
||||||
if (isset($_GET['id'])) {
|
if (isset($_GET['id'])) {
|
||||||
// TODO: Need another check here to verify the user is in the Essay's space.
|
// TODO: Need another check here to verify the user is in the Essay's space.
|
||||||
$essay = Essay::retrieveFromDB($_GET['id']);
|
$essay = Post::retrieveFromDB($_GET['id']);
|
||||||
$space = $essay->getSpace();
|
if ($essay)
|
||||||
|
|
||||||
if (isset($_SESSION['user']) && $space->allowUser($_SESSION['user']))
|
|
||||||
echo displayEssay($essay);
|
|
||||||
else if ($space->getVisibility() == 1)
|
|
||||||
echo displayEssay($essay);
|
echo displayEssay($essay);
|
||||||
else
|
else
|
||||||
echo displaySpace(2025);
|
echo errorScreen('Essay does not exist!', 'The Essay ID provided does not exist in the database.');
|
||||||
} else
|
} else
|
||||||
echo displaySpace(2025);
|
echo displaySpace(2025);
|
||||||
break;
|
break;
|
||||||
@@ -28,19 +24,22 @@ if (isset($_GET['view'])) {
|
|||||||
// TODO: After implementing the notification modal, notify the user on success/fail auth events.
|
// TODO: After implementing the notification modal, notify the user on success/fail auth events.
|
||||||
if (isset($_GET['id'])) {
|
if (isset($_GET['id'])) {
|
||||||
$space = Space::retrieveFromDB($_GET['id']);
|
$space = Space::retrieveFromDB($_GET['id']);
|
||||||
if (isset($_SESSION['user']))
|
if ($space) {
|
||||||
if ($space->allowUser(unserialize($_SESSION['user'])))
|
if (isset($_SESSION['user']))
|
||||||
echo displaySpace($_GET['id']);
|
if ($space->allowUser(unserialize($_SESSION['user'])))
|
||||||
else if ($space->getVisibility() == 1)
|
echo displaySpace($_GET['id']);
|
||||||
echo displaySpace($_GET['id']);
|
else if ($space->getVisibility() == 1)
|
||||||
else
|
echo displaySpace($_GET['id']);
|
||||||
echo 'TODO: Notification Modal -> User not a member';
|
else
|
||||||
else {
|
echo 'TODO: Notification Modal -> User not a member';
|
||||||
if ($space->getVisibility() == 1)
|
else {
|
||||||
echo displaySpace($_GET['id']);
|
if ($space->getVisibility() == 1)
|
||||||
else
|
echo displaySpace($_GET['id']);
|
||||||
echo 'TODO: Notification Modal -> Space is not Open';
|
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
|
} else
|
||||||
echo displaySpaceList();
|
echo displaySpaceList();
|
||||||
break;
|
break;
|
||||||
@@ -60,19 +59,55 @@ if (isset($_GET['view'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SESSION['user']))
|
if (isset($_SESSION['user']))
|
||||||
echo actionButton();
|
echo actionBar();
|
||||||
|
|
||||||
|
if (isset($_GET['id']))
|
||||||
|
echo backButton();
|
||||||
|
|
||||||
require_once 'components/core/foot.php';
|
require_once 'components/core/foot.php';
|
||||||
|
|
||||||
function actionButton()
|
function backButton()
|
||||||
{
|
{
|
||||||
// TODO: Position these on the arc of a circle.
|
|
||||||
$html = '
|
$html = '
|
||||||
<div class="circleLayout">
|
<div class="link" id="backButton">
|
||||||
<div id="newPost" class="actionButton" style="display: none;"><i class="nf nf-cod-new_file"></i></div>
|
<i class="nf nf-fa-arrow_left"></i>
|
||||||
<div id="newSpace" class="actionButton" style="display: none;"><i class="nf nf-cod-new_folder"></i></div>
|
</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>
|
|
||||||
|
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>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
@@ -81,9 +116,16 @@ function actionButton()
|
|||||||
|
|
||||||
function displayEssay(Essay $e)
|
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 = '
|
$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;">
|
||||||
' . $e->getHTML() . '
|
' . $parsedown->parse($e->getMarkdown()) . '
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
@@ -95,7 +137,7 @@ function displaySpaceList()
|
|||||||
$user = unserialize($_SESSION['user']);
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
$html = '
|
$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">
|
<div class="column space_between full_width">
|
||||||
<h1>Spaces</h4>
|
<h1>Spaces</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,9 +145,8 @@ function displaySpaceList()
|
|||||||
';
|
';
|
||||||
|
|
||||||
$spaces = $user->getSpaces();
|
$spaces = $user->getSpaces();
|
||||||
foreach ($spaces as $s) {
|
foreach ($spaces as $s)
|
||||||
$html .= $s->getCardHTML();
|
$html .= $s->getCardHTML();
|
||||||
}
|
|
||||||
|
|
||||||
$html .= '
|
$html .= '
|
||||||
</div>
|
</div>
|
||||||
@@ -119,7 +160,7 @@ function displaySpace($id = 2025)
|
|||||||
$space = Space::retrieveFromDB($id);
|
$space = Space::retrieveFromDB($id);
|
||||||
if ($space) {
|
if ($space) {
|
||||||
$html = '
|
$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">
|
<div class="column space_between full_width">
|
||||||
<h1>' . $space->getName() . '</h4>
|
<h1>' . $space->getName() . '</h4>
|
||||||
<p>' . $space->getDescription() . '</p>
|
<p>' . $space->getDescription() . '</p>
|
||||||
@@ -127,6 +168,11 @@ function displaySpace($id = 2025)
|
|||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
$subspaces = $space->getSubSpaces();
|
||||||
|
foreach ($subspaces as $ss) {
|
||||||
|
$html .= $ss->getCardHTML();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Need to investigate this bug in Posts
|
// TODO: Need to investigate this bug in Posts
|
||||||
$posts = $space->getPosts();
|
$posts = $space->getPosts();
|
||||||
foreach ($posts as $post) {
|
foreach ($posts as $post) {
|
||||||
|
|||||||
Reference in New Issue
Block a user