Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fd06953fc | ||
|
|
d3fa1733f4 | ||
|
|
54a01f0f1f | ||
|
|
a93c30c40f | ||
|
|
228cf1137b | ||
|
|
bd59f428ba | ||
|
|
693d11c506 | ||
|
|
275e6cb153 | ||
|
|
ab8f1b9ec8 | ||
|
|
0753e1867c |
+6
-3
@@ -1,6 +1,9 @@
|
|||||||
*.env
|
*.env
|
||||||
composer.lock
|
composer.lock
|
||||||
/vendor/
|
/vendor/
|
||||||
/data/profile_pictures
|
data/profile_pictures
|
||||||
/data/articles
|
data/articles
|
||||||
/data/tmp
|
data/tmp
|
||||||
|
data/neo4j
|
||||||
|
import.sql
|
||||||
|
eula.txt
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ include_once 'components/head.php';
|
|||||||
if (!isset($_SESSION['role_id']))
|
if (!isset($_SESSION['role_id']))
|
||||||
header('Location: user.php?view=login');
|
header('Location: user.php?view=login');
|
||||||
else if ($_SESSION['role_id'] >= contributor)
|
else if ($_SESSION['role_id'] >= contributor)
|
||||||
header('Location: user.php?action=view&user_id=' . $_SESSION['user_id']);
|
header('Location: user.php?view=display&user_id=' . $_SESSION['user_id']);
|
||||||
|
|
||||||
include_once 'functions/admin_functions.php';
|
include_once 'functions/admin_functions.php';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ if (isset($_POST['action']) && isset($_POST['form_id']) || isset($_FILES['csv'])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'view':
|
case 'view':
|
||||||
header('Location: user.php?action=view&user_id=' . $_POST['selected_users'][0]);
|
header('Location: user.php?view=display&user_id=' . $_POST['selected_users'][0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'functions/init.php';
|
|
||||||
require_once 'functions/functions.php';
|
|
||||||
|
|
||||||
include_once 'components/head.php';
|
|
||||||
|
|
||||||
include_once 'functions/article_functions.php';
|
|
||||||
include_once 'functions/account_functions.php';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
foreach (glob('components/article/*.php') as $file)
|
|
||||||
require_once $file;
|
|
||||||
|
|
||||||
if (isset($_POST['form_id'])) {
|
|
||||||
switch ($_POST['form_id']) {
|
|
||||||
case 'compose':
|
|
||||||
if (isset($_POST['article_id'])) {
|
|
||||||
$tags = [];
|
|
||||||
for ($i = 0; $i < count($_POST['tags']); $i++)
|
|
||||||
$tags[] = $_POST['tags'][$i];
|
|
||||||
|
|
||||||
update_article($_POST['article_id'], $_POST['title'], $_POST['excerpt'], $tags, $_POST['markdown']);
|
|
||||||
header('Location: articles.php?article_id=' . $_POST['article_id']);
|
|
||||||
} else {
|
|
||||||
$id = create_article($_SESSION['user_id'], $_POST['title'], $_POST['excerpt'], (isset($_POST['tags']) ? $_POST['tags'] : []), $_POST['markdown']);
|
|
||||||
$emails = exec_stmt('SELECT email FROM user INNER JOIN follows ON user.user_id = follows.follower_user_id WHERE follows.following_user_id = ? AND follows.notify_user = 1', 'i', $_SESSION['user_id'])->fetch_assoc();
|
|
||||||
notify_users($emails, $_SESSION['username'], $id, $_POST['title'], $_POST['excerpt']);
|
|
||||||
header('Location: articles.php?article_id=' . $id);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'follow':
|
|
||||||
follow_user($_SESSION['user_id'], $_POST['author_id'], ($_POST['follow'] == 'email_notify' ? 1 : 0));
|
|
||||||
break;
|
|
||||||
case 'unfollow':
|
|
||||||
unfollow_user($_SESSION['user_id'], $_POST['author_id']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['view']))
|
|
||||||
$view = $_GET['view'];
|
|
||||||
else
|
|
||||||
$view = 'list';
|
|
||||||
|
|
||||||
switch ($view) {
|
|
||||||
case 'list':
|
|
||||||
echo article_list();
|
|
||||||
break;
|
|
||||||
case 'filter':
|
|
||||||
if (isset($_GET['tag']))
|
|
||||||
echo article_list(article_ids_by_tag($_GET['tag']));
|
|
||||||
else if (isset($_GET['author_id']))
|
|
||||||
echo article_list(articles_ids_by_author($_GET['author_id']));
|
|
||||||
break;
|
|
||||||
case 'sort':
|
|
||||||
if (isset($_GET['sort']) && $_GET['sort'] == 'oldest')
|
|
||||||
echo article_list(article_ids_by_oldest());
|
|
||||||
else if (isset($_GET['sort']) && $_GET['sort'] == 'newest')
|
|
||||||
echo article_list(article_ids_by_newest());
|
|
||||||
break;
|
|
||||||
case 'read':
|
|
||||||
if (isset($_SESSION['user_id']) && isset($_GET['article_id'])) {
|
|
||||||
increment_read_counter($_GET['article_id']);
|
|
||||||
echo article_page_from_markdown($_GET['article_id']);
|
|
||||||
} else {
|
|
||||||
$redirect = (isset($_GET['article_id']) ? 'articles.php?view=read&article_id=' . $_GET['article_id'] : '');
|
|
||||||
if ($redirect)
|
|
||||||
header('Location: user.php?view=login&redirect=' . urlencode($redirect));
|
|
||||||
else
|
|
||||||
header('Location: articles.php');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'compose':
|
|
||||||
echo compose_view((isset($_GET['article_id']) ? $_GET['article_id'] : ''));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($view == 'list' || $view == 'sort') && (isset($_SESSION['role_id']) && $_SESSION['role_id'] <= contributor)) {
|
|
||||||
echo '
|
|
||||||
<div id="floating_compose">
|
|
||||||
<a href="articles.php?view=compose"><i class="nf nf-md-note_plus"></i></a>
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
include_once 'components/foot.php';
|
|
||||||
@@ -41,7 +41,7 @@ function admin_user_view()
|
|||||||
|
|
||||||
foreach ($results as $key => $result) {
|
foreach ($results as $key => $result) {
|
||||||
$display .= '
|
$display .= '
|
||||||
<tr data-href="user.php?action=view&user_id=' . $result['user_id'] . '">
|
<tr data-href="user.php?view=display&user_id=' . $result['user_id'] . '">
|
||||||
<td class="excluded_cell">
|
<td class="excluded_cell">
|
||||||
<label class="form_checkbox_container">
|
<label class="form_checkbox_container">
|
||||||
<input name="selected_users[]" value="' . $result['user_id'] . '" type="checkbox">
|
<input name="selected_users[]" value="' . $result['user_id'] . '" type="checkbox">
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates cards for a given article.
|
|
||||||
*/
|
|
||||||
function article_card($article_id)
|
|
||||||
{
|
|
||||||
$article = exec_stmt('SELECT * FROM article WHERE article_id = ?', 'i', $article_id)->fetch_assoc();
|
|
||||||
|
|
||||||
$tags_results = exec_stmt('SELECT tag FROM article_tags INNER JOIN tags ON article_tags.tag_id = tags.tag_id WHERE article_id = ?', 'i', $article_id);
|
|
||||||
|
|
||||||
$tags_html = '';
|
|
||||||
if ($tags_results != null) {
|
|
||||||
$tags_html .= '<div class="tag_row">';
|
|
||||||
|
|
||||||
while ($row = $tags_results->fetch_assoc())
|
|
||||||
$tags_html .= '<a href="/articles.php?view=filter&tag=' . $row['tag'] . '" class="tag">#' . $row['tag'] . '</a>';
|
|
||||||
|
|
||||||
$tags_html .= '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
$author = exec_stmt('SELECT user_id, username, profile_picture FROM user WHERE user_id = ?', 'i', $article['author_id'])->fetch_assoc();
|
|
||||||
|
|
||||||
$card = '
|
|
||||||
<div class="article_card">
|
|
||||||
<div class="row_space_between">
|
|
||||||
<h4>' . $article['title'] . '</h4>
|
|
||||||
<div class="card_info_box">
|
|
||||||
<div class="col-right">
|
|
||||||
<a href="user.php?view=display&user_id=' . $author['user_id'] . '">Written by: ' . $author['username'] . '</a>
|
|
||||||
<small>Published: ' . $article['published_at'] . '</small>
|
|
||||||
<small>Updated: ' . $article['updated_at'] . '</small>
|
|
||||||
</div>
|
|
||||||
<div class="card_pic_box">
|
|
||||||
<img src="' . $_ENV['PROFILE_IMAGES_PQ_PATH'] . $author['profile_picture'] . '" class="card_profile_picture">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<p>' . $article['excerpt'] . '</p>
|
|
||||||
<div class="row_space_between">
|
|
||||||
<a href="/articles.php?view=read&article_id=' . $article['article_id'] . '" class="button">Read More</a>
|
|
||||||
<div class="tag_box">' . $tags_html . '</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $card;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions/space.php';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates a card for a given Space.
|
||||||
|
*
|
||||||
|
* @* @param int $space_id "Retrieves information from the database using this ID."
|
||||||
|
* @* @param int $user_id "Retrieves information from the database using this ID. Default is -1, which will limit queries to a singular Open Space."
|
||||||
|
* @* @return String $html "The HTML card component for a Space."
|
||||||
|
*/
|
||||||
|
function spaceCard(Space $space)
|
||||||
|
{
|
||||||
|
$html = '';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates a card for a given Thought.
|
||||||
|
*
|
||||||
|
* @* @param Integer $post_id "Retrieves information from the database using this ID."
|
||||||
|
* @* @return String $html "The HTML card component for a Thought."
|
||||||
|
*/
|
||||||
|
function thoughtCard(Post $post)
|
||||||
|
{
|
||||||
|
$html = '';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates a card for a given Moment.
|
||||||
|
*
|
||||||
|
* @* @param Integer $post_id "Retrieves information from the database using this ID."
|
||||||
|
* @* @return String $html "The HTML card component for a Moment."
|
||||||
|
*/
|
||||||
|
function momentCard(Post $post)
|
||||||
|
{
|
||||||
|
$html = '';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates a card for a given Moment.
|
||||||
|
*
|
||||||
|
* @* @param Integer $post_id "Retrieves information from the database using this ID."
|
||||||
|
* @* @return String $html "The HTML card component for a Moment."
|
||||||
|
*/
|
||||||
|
function essayCard(Post $post)
|
||||||
|
{
|
||||||
|
$topics_html = '';
|
||||||
|
$topics_html .= '<div class="tag_row">';
|
||||||
|
$topics_html .= '<a href="/articles.php?view=filter&tag=' . $row['tag'] . '" class="tag">#' . $row['tag'] . '</a>';
|
||||||
|
$topics_html .= '</div>';
|
||||||
|
|
||||||
|
$author = exec_stmt('SELECT user_id, username, profile_picture FROM user WHERE user_id = ?', 'i', $essay['author_id'])->fetch_assoc();
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="article_card">
|
||||||
|
<div class="row_space_between">
|
||||||
|
<h4>' . $essay['title'] . '</h4>
|
||||||
|
<div class="card_info_box">
|
||||||
|
<div class="col-right">
|
||||||
|
<a href="user.php?view=display&user_id=' . $author['user_id'] . '">Written by: ' . $author['username'] . '</a>
|
||||||
|
<small>Published: ' . $essay['published_at'] . '</small>
|
||||||
|
<small>Updated: ' . $essay['updated_at'] . '</small>
|
||||||
|
</div>
|
||||||
|
<div class="card_pic_box">
|
||||||
|
<img src="' . $_ENV['PROFILE_IMAGES_PQ_PATH'] . $author['profile_picture'] . '" class="card_profile_picture">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
<p>' . $essay['excerpt'] . '</p>
|
||||||
|
<div class="row_space_between">
|
||||||
|
<a href="/articles.php?view=read&article_id=' . $essay['article_id'] . '" class="button">Read More</a>
|
||||||
|
<div class="tag_box">' . $topics_html . '</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
</body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<script src="js/util.js"></script>
|
||||||
|
<script src="js/nav.js" defer></script>
|
||||||
|
<script src="js/user.js" defer></script>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// <script src="js/theme.js" defer></script>
|
||||||
|
if ($_SERVER['SCRIPT_NAME'] == '/spaces.php' && isset($_SESSION['user'])) {
|
||||||
|
echo '
|
||||||
|
<script src="js/actionBar.js" defer></script>
|
||||||
|
<script src="js/dropdown.js" defer></script>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<link href="css/base.css" rel="stylesheet" type="text/css">
|
<link href="css/base.css" rel="stylesheet" type="text/css">
|
||||||
<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>
|
<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">';
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
$current_page = $_SERVER['SCRIPT_NAME'];
|
||||||
|
$nav = '
|
||||||
|
<nav>
|
||||||
|
<div class="row space_between no_margin full_width">
|
||||||
|
<div class="site_navigation">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/spaces.php?view=list"' . ($current_page == '/spaces.php' ? ' class="underline"' : '') . '>Spaces</a></li>
|
||||||
|
';
|
||||||
|
|
||||||
|
/* <li><a href="/index.php"' . ($current_page == '/index.php' ? ' class="underline"' : '') . '>Home</a></li> */
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
/* $nav .= ' */
|
||||||
|
/* <li><a href="/user.php?view=display&user_id=' . unserialize($_SESSION['user'])->getId() . '"' . ($current_page == '/user.php' ? ' class="underline"' : '') . '>Account</a></li> */
|
||||||
|
/* <li><a href="/todo.php?view=display"' . ($current_page == '/todo.php' ? ' class="underline"' : '') . '>Todos</a></li> */
|
||||||
|
/* '; */
|
||||||
|
|
||||||
|
if (unserialize($_SESSION['user'])->getRole() <= developer) {
|
||||||
|
$nav .= '
|
||||||
|
<li><a href="/admin.php?view=users"' . ($current_page == '/admin.php' ? ' class="underline"' : '') . '>Admin</a></li>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$nav .= '
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="nav_right_hand">
|
||||||
|
<form method="post">
|
||||||
|
';
|
||||||
|
|
||||||
|
$theme_toggle = '';
|
||||||
|
if ($_SESSION['theme'] == 'dark')
|
||||||
|
$theme_toggle = 'light';
|
||||||
|
else
|
||||||
|
$theme_toggle = 'dark';
|
||||||
|
|
||||||
|
$nav .= '
|
||||||
|
<button id="theme_toggle" value="' . $theme_toggle . '" name="theme"><i class="nf ' . ($theme_toggle == 'dark' ? 'nf-oct-moon' : 'nf-oct-sun') . '"></i></button>
|
||||||
|
</form>
|
||||||
|
';
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$nav .= '
|
||||||
|
<p class="link underline" id="logout">Log Out</p>
|
||||||
|
';
|
||||||
|
} else {
|
||||||
|
$nav .= '
|
||||||
|
<p class="link underline" id="login">Login</p>
|
||||||
|
<p class="link underline" id="signup">Sign Up</p>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
$nav .= '
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
<div id="notificationModal"></div>
|
||||||
|
<div id="stdModal"></div>
|
||||||
|
';
|
||||||
|
|
||||||
|
echo $nav;
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
</body>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<?php
|
|
||||||
function get_args_from_url()
|
|
||||||
{
|
|
||||||
$args = [];
|
|
||||||
$arr = explode('&', $_SERVER['QUERY_STRING']);
|
|
||||||
|
|
||||||
foreach ($arr as $a) {
|
|
||||||
$tmp = explode('=', $a);
|
|
||||||
$key = $tmp[0];
|
|
||||||
$value = $tmp[1];
|
|
||||||
|
|
||||||
$args[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['SCRIPT_NAME'] == '/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 '
|
|
||||||
<script src="js/override_browser_styles.js"></script>
|
|
||||||
';
|
|
||||||
?>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
$current_page = $_SERVER['SCRIPT_NAME'];
|
|
||||||
$nav = '
|
|
||||||
<nav>
|
|
||||||
<div class="row_space_between no_margin">
|
|
||||||
<div class="site_navigation">
|
|
||||||
<ul>
|
|
||||||
<li><a href="/index.php"' . ($current_page == '/index.php' ? ' class="underline"' : '') . '>Home</a></li>
|
|
||||||
<li><a href="/articles.php?view=sort&sort=newest"' . ($current_page == '/articles.php' ? ' class="underline"' : '') . '>Articles</a></li>
|
|
||||||
';
|
|
||||||
|
|
||||||
if (isset($_SESSION['role_id']) && $_SESSION['role_id'] <= admin) {
|
|
||||||
$nav .= '
|
|
||||||
<li><a href="/admin.php?view=users"' . ($current_page == '/admin.php' ? ' class="underline"' : '') . '>Admin</a></li>
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_SESSION['user_id'])) {
|
|
||||||
$nav .= '
|
|
||||||
<li><a href="/user.php?view=display&user_id=' . $_SESSION['user_id'] . '"' . ($current_page == '/user.php' ? ' class="underline"' : '') . '>Account</a></li>
|
|
||||||
<li><a href="/todo.php?view=display"' . ($current_page == '/todo.php' ? ' class="underline"' : '') . '>Todos</a></li>
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
$nav .= '
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="nav_right_hand">
|
|
||||||
<form method="post">
|
|
||||||
';
|
|
||||||
|
|
||||||
$theme_toggle = '';
|
|
||||||
if ($_SESSION['theme'] == 'dark')
|
|
||||||
$theme_toggle = 'light';
|
|
||||||
else
|
|
||||||
$theme_toggle = 'dark';
|
|
||||||
|
|
||||||
$nav .= '
|
|
||||||
<button id="theme_toggle" value="' . $theme_toggle . '" name="theme"><i class="nf ' . ($theme_toggle == 'dark' ? 'nf-oct-moon' : 'nf-oct-sun') . '"></i></button>
|
|
||||||
</form>
|
|
||||||
';
|
|
||||||
|
|
||||||
if (isset($_SESSION['username'])) {
|
|
||||||
$nav .= '
|
|
||||||
<a href="user.php?view=logout">Log Out</a>
|
|
||||||
';
|
|
||||||
} else {
|
|
||||||
$nav .= '
|
|
||||||
<a href="user.php?view=login">Login</a>
|
|
||||||
<a href="user.php?view=signup">Sign Up</a>
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
$nav .= '
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
';
|
|
||||||
|
|
||||||
echo $nav;
|
|
||||||
@@ -3,16 +3,8 @@
|
|||||||
function todo_actions()
|
function todo_actions()
|
||||||
{
|
{
|
||||||
$actions = '
|
$actions = '
|
||||||
<div class="todo_actions">
|
<div id="todo_actions">
|
||||||
<form method="post">
|
<button value="new" class="button">New</button>
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
<button name="action" value="new">New</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
|||||||
@@ -51,14 +51,12 @@ function todo_display($todos)
|
|||||||
<div class="todo_category_window">
|
<div class="todo_category_window">
|
||||||
';
|
';
|
||||||
|
|
||||||
foreach ($categories as $c)
|
|
||||||
$c = array_reverse($c);
|
|
||||||
|
|
||||||
foreach (array_keys($categories) as $key)
|
foreach (array_keys($categories) as $key)
|
||||||
$display .= todo_category($key, $categories[$key]);
|
$display .= todo_category($key, $categories[$key]);
|
||||||
|
|
||||||
$display .= '
|
$display .= '
|
||||||
</div>
|
</div>
|
||||||
|
' . todo_modal(null, 'new_todo_item') . '
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
|||||||
@@ -7,20 +7,17 @@ function todo_item($todo)
|
|||||||
<div class="row left_margin">
|
<div class="row left_margin">
|
||||||
<div class="column center">
|
<div class="column center">
|
||||||
<label class="form_checkbox_container no_margin">
|
<label class="form_checkbox_container no_margin">
|
||||||
<input type="hidden" name="todo_id" value="' . $todo['todo_id'] . '">
|
<input type="hidden" name="todo_id" value="' . (isset($todo['todo_id']) ? $todo['todo_id'] : '') . '">
|
||||||
|
|
||||||
<input type="checkbox"' . ($todo['status'] == 'completed' ? ' checked' : '') . '>
|
<input type="checkbox"' . (!empty($todo) ? ($todo['status'] == 'completed' ? ' checked' : '') : '') . '>
|
||||||
<span class="checkmark no_margin"></span>
|
<span class="checkmark no_margin"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="column center">
|
<div class="column center modal_activation_area">
|
||||||
<small>' . $todo['task'] . '</small>
|
<small>' . $todo['title'] . '</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
' . todo_modal($todo, null) . '
|
||||||
|
|
||||||
<div class="todo_item_details" style="display: none;">
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,76 @@
|
|||||||
<?php
|
<?php
|
||||||
function modal() {}
|
function todo_modal($todo = [], $id = null)
|
||||||
|
{
|
||||||
|
if (!isset($todo)) {
|
||||||
|
$todo['created_at'] = '';
|
||||||
|
$todo['updated_at'] = '';
|
||||||
|
$todo['completed_at'] = '';
|
||||||
|
$todo['todo_id'] = '';
|
||||||
|
$todo['title'] = '';
|
||||||
|
$todo['description'] = '';
|
||||||
|
$todo['priority'] = '';
|
||||||
|
$todo['due_date'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$modal = '
|
||||||
|
<div class="modal card todo_item_details"' . (isset($id) ? ' id="' . $id . '"' : '') . '>
|
||||||
|
<div id="modal_content">
|
||||||
|
<div class="form_card">
|
||||||
|
<div class="row_space_between">
|
||||||
|
<h5 class="underline">Item View</h5>
|
||||||
|
';
|
||||||
|
|
||||||
|
if (!empty($todo['title'])) {
|
||||||
|
$modal .= '
|
||||||
|
<div class="column over_right_margin">
|
||||||
|
<small>Created: ' . $todo['created_at'] . '</small>
|
||||||
|
<small>Updated: ' . $todo['updated_at'] . '</small>
|
||||||
|
<small>Completed: ' . $todo['completed_at'] . '</small>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
$modal .= '
|
||||||
|
</div>
|
||||||
|
<span class="modal_close">×</span>
|
||||||
|
<form>
|
||||||
|
<input type="hidden" name="todo_id" value="' . (!empty($todo['todo_id']) ? $todo['todo_id'] : '-1') . '">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="column full_width">
|
||||||
|
<label for="title">Title:</label>
|
||||||
|
<input id="title" name="title" value="' . $todo['title'] . '" placeholder="Task Title" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="column full_width">
|
||||||
|
<label for="description">Description:</label>
|
||||||
|
<textarea id="description" name="description">' . $todo['description'] . '</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row_space_between">
|
||||||
|
<div class="column">
|
||||||
|
<label for="priority">Priority:</label>
|
||||||
|
<select id="priority" name="priority">
|
||||||
|
<option value="low"' . ($todo['priority'] == 'low' ? ' checked' : '') . '>Low</option>
|
||||||
|
<option value="medium"' . ($todo['priority'] == 'medium' ? ' checked' : '') . '>Medium</option>
|
||||||
|
<option value="high"' . ($todo['priority'] == 'high' ? ' checked' : '') . '>High</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<label for="due_date">Due Date:</label>
|
||||||
|
<input type="datetime-local" id="due_date" name="due_date" value="' . format_date($todo['due_date']) . '">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="button" value="' . (!empty($todo['todo_id']) ? 'Update Item' : 'Create Item') . '">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $modal;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
function login_form($error_msg = '')
|
|
||||||
{
|
|
||||||
$form = '
|
|
||||||
<div class="form_card">
|
|
||||||
<h3 class="underline">Log In</h3>
|
|
||||||
<form method="post">
|
|
||||||
<input type="hidden" name="form_id" value="login_form">
|
|
||||||
|
|
||||||
<label for="username">Username / Email</label>
|
|
||||||
<input id="username" name="username" spellcheck="false" required>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</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;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
include_once 'components/article/card.php';
|
include_once 'components/core/card.php';
|
||||||
|
|
||||||
function user_view($user_id)
|
function user_view($user_id)
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-2
@@ -5,12 +5,18 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"vlucas/phpdotenv": "^5.6",
|
"vlucas/phpdotenv": "^5.6",
|
||||||
"wildbit/postmark-php": "^6.0"
|
"wildbit/postmark-php": "^6.0",
|
||||||
|
"laudis/neo4j-php-client": "^3.2"
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Josh Ashton",
|
"name": "Josh Ashton",
|
||||||
"email": "me@joshashton.dev"
|
"email": "me@joshashton.dev"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"php-http/discovery": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-394
@@ -1,6 +1,6 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Hack Nerd Font Mono';
|
font-family: "Hack Nerd Font Mono";
|
||||||
src: url('/data/resources/HackNerdFontMono-Regular.woff');
|
src: url("/data/resources/HackNerdFontMono-Regular.woff");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@@ -14,165 +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,
|
|
||||||
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: .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;
|
||||||
}
|
}
|
||||||
@@ -236,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 {
|
||||||
@@ -269,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%;
|
||||||
}
|
}
|
||||||
@@ -286,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,26 +142,11 @@ table tr td label {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.underline {
|
.divLink {
|
||||||
text-decoration: underline;
|
text-decoration: none;
|
||||||
text-decoration-thickness: 3px;
|
display: block;
|
||||||
text-decoration-color: orange;
|
height: 100%;
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
i {
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
font-size: 1.5vw;
|
|
||||||
transition-duration: 500ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
#theme_toggle {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
i:hover {
|
|
||||||
color: orange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article {
|
.article {
|
||||||
@@ -345,110 +167,14 @@ i:hover {
|
|||||||
justify-content: left;
|
justify-content: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
input[type="datetime_local"] {
|
||||||
.autofilled,
|
color: white;
|
||||||
input:-webkit-autofill,
|
|
||||||
textarea:-webkit-autofill,
|
|
||||||
input:-webkit-autofill:hover,
|
|
||||||
textarea:-webkit-autofill:hover,
|
|
||||||
input:-webkit-autofill:focus,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -548,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,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%;
|
||||||
@@ -584,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 */
|
||||||
@@ -664,7 +390,7 @@ input[type="file"] {
|
|||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
color: black;
|
color: black;
|
||||||
background: orange;
|
background: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-2x1 {
|
.grid-2x1 {
|
||||||
@@ -672,7 +398,6 @@ input[type="file"] {
|
|||||||
width: 90%;
|
width: 90%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 50% 50%;
|
grid-template-columns: 50% 50%;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-2x1 .form_card,
|
.grid-2x1 .form_card,
|
||||||
@@ -700,110 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_display {
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-top: 25px;
|
|
||||||
width: 80%;
|
|
||||||
height: 80vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_actions {
|
|
||||||
padding: 15px;
|
|
||||||
border: 2px solid white;
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_actions form {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_category_window {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_category {
|
|
||||||
display: block;
|
|
||||||
min-width: 400px;
|
|
||||||
margin-right: 50px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
overflow-x: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_category h4 {
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo_item {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 992px) {
|
@media screen and (max-width: 992px) {
|
||||||
body {
|
body {
|
||||||
padding-bottom: 160px;
|
padding-bottom: 160px;
|
||||||
|
|||||||
+9
-78
@@ -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,58 +74,10 @@ select:-webkit-autofill {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload_button {
|
.std_border,
|
||||||
border: 3px solid white;
|
.med_border,
|
||||||
}
|
.sm_border {
|
||||||
|
border-color: 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 992px) {
|
@media screen and (max-width: 992px) {
|
||||||
|
|||||||
+9
-75
@@ -14,7 +14,8 @@ h5,
|
|||||||
p,
|
p,
|
||||||
small,
|
small,
|
||||||
label,
|
label,
|
||||||
span {
|
span,
|
||||||
|
i {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,23 +33,12 @@ nav ul li a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background-color: #C6C6C6;
|
background-color: #c6c6c6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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,58 +77,10 @@ select:-webkit-autofill {
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload_button {
|
.std_border,
|
||||||
border: 3px solid black;
|
.med_border,
|
||||||
}
|
.sm_border {
|
||||||
|
border-color: 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 992px) {
|
@media screen and (max-width: 992px) {
|
||||||
|
|||||||
+537
-8
@@ -1,3 +1,258 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
small,
|
||||||
|
label,
|
||||||
|
input,
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
textarea,
|
||||||
|
a,
|
||||||
|
.modal_button,
|
||||||
|
th,
|
||||||
|
td,
|
||||||
|
li,
|
||||||
|
select,
|
||||||
|
option,
|
||||||
|
span {
|
||||||
|
font-family: "Hack Nerd Font Mono", Hack, sans-serif;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2.5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3,
|
||||||
|
h3 a {
|
||||||
|
font-size: 2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4,
|
||||||
|
h4 a {
|
||||||
|
font-size: 1.75vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5,
|
||||||
|
h5 a {
|
||||||
|
font-size: 1.5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6,
|
||||||
|
h6 a {
|
||||||
|
font-size: 1.25vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
a,
|
||||||
|
label,
|
||||||
|
li,
|
||||||
|
.modal_button,
|
||||||
|
.button,
|
||||||
|
span {
|
||||||
|
font-size: 1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.75vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
.modal_button {
|
||||||
|
color: red;
|
||||||
|
transition-duration: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
-webkit-tap-highlight-color: transparent !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.fillDiv {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition-duration: 1s;
|
||||||
|
border: 3px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: 3px;
|
||||||
|
text-decoration-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 1.5vw;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme_toggle {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** Forms ************/
|
||||||
|
|
||||||
|
input,
|
||||||
|
.autofilled,
|
||||||
|
input:-webkit-autofill,
|
||||||
|
textarea:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
textarea:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
select,
|
||||||
|
option,
|
||||||
|
textarea {
|
||||||
|
/* Try this first */
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 15px 15px 15px 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
resize: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
input:hover,
|
||||||
|
textarea:hover {
|
||||||
|
border: 3px solid red;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
border-radius: 18px;
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form_checkbox_container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 35px;
|
||||||
|
margin: 15px 15px 15px 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
height: 29px;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form_checkbox_container input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the checkmark/indicator (hidden when not checked) */
|
||||||
|
.checkmark:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the checkmark when checked */
|
||||||
|
.form_checkbox_container input:checked ~ .checkmark:after {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When the checkbox is checked, add a blue background */
|
||||||
|
.form_checkbox_container input:checked ~ .checkmark {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the checkmark/indicator */
|
||||||
|
.form_checkbox_container .checkmark:after {
|
||||||
|
left: 9px;
|
||||||
|
top: 5px;
|
||||||
|
width: 5px;
|
||||||
|
height: 10px;
|
||||||
|
border: solid white;
|
||||||
|
border-width: 0 3px 3px 0;
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** Width & Sizing **********/
|
||||||
|
.full_width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.std_width {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.med_width {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm_width {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
/********** Borders & Outlines **********/
|
/********** Borders & Outlines **********/
|
||||||
.std_border,
|
.std_border,
|
||||||
.med_border,
|
.med_border,
|
||||||
@@ -6,33 +261,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.std_border {
|
.std_border {
|
||||||
border: 3px solid white;
|
border: 3px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.med_border {
|
.med_border {
|
||||||
border: 2px solid white;
|
border: 2px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sm_border {
|
.sm_border {
|
||||||
border: 1px solid white;
|
border: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug {
|
.debug {
|
||||||
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;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row_space_between {
|
.space_between {
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
margin: 15px 0px 15px 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
@@ -41,10 +297,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
|
margin: 0 auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
/********** Margins & Padding **********/
|
/********** Margins & Padding **********/
|
||||||
.margin {
|
.margin {
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
@@ -62,6 +323,10 @@
|
|||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.over_right_margin {
|
||||||
|
margin-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.bottom_margin {
|
.bottom_margin {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
@@ -93,3 +358,267 @@
|
|||||||
.left_padding {
|
.left_padding {
|
||||||
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 ************/
|
||||||
|
#stdModal {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: none;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stdModal > * {
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass {
|
||||||
|
background-color: transparent;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#actionBar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 3.5%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -5%);
|
||||||
|
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** Animations **********/
|
||||||
|
@view-transition {
|
||||||
|
navigation: auto;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
animation: fade-out 1s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: fade-in 1s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+295
-15
@@ -1,29 +1,309 @@
|
|||||||
<?php
|
<?php
|
||||||
include 'functions/functions.php';
|
include 'functions/init.php';
|
||||||
|
|
||||||
if (isset($_POST['ajax_id'])) {
|
error_log('RECEIVED: ' . pretty_dump($_POST));
|
||||||
switch ($_POST['ajax_id']) {
|
if (isset($_POST['ajaxId'])) {
|
||||||
case 'todo':
|
switch ($_POST['ajaxId']) {
|
||||||
handle_todo();
|
case 'login':
|
||||||
|
handle_login();
|
||||||
|
break;
|
||||||
|
case 'loginHTML':
|
||||||
|
handleLoginHTML();
|
||||||
|
break;
|
||||||
|
case 'signupHTML':
|
||||||
|
handleSignupHTML();
|
||||||
|
break;
|
||||||
|
case 'usernameCheck':
|
||||||
|
handleUsernameCheck();
|
||||||
|
break;
|
||||||
|
case 'logout':
|
||||||
|
User::logout();
|
||||||
|
break;
|
||||||
|
case 'newPostHTML':
|
||||||
|
handleNewPostHTML();
|
||||||
|
break;
|
||||||
|
case 'newSpaceHTML':
|
||||||
|
handleNewSpaceHTML();
|
||||||
|
break;
|
||||||
|
case 'deletePost':
|
||||||
|
handleDeletePost();
|
||||||
|
break;
|
||||||
|
case 'deleteSpace':
|
||||||
|
handleDeleteSpace();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (isset($_POST['formID'])) {
|
||||||
|
switch ($_POST['formID']) {
|
||||||
|
case 'spaceNew':
|
||||||
|
break;
|
||||||
|
case 'signup':
|
||||||
|
handleSignup();
|
||||||
|
break;
|
||||||
|
case 'newPost':
|
||||||
|
handleNewPost();
|
||||||
|
break;
|
||||||
|
case 'newSpace':
|
||||||
|
handleNewSpace();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
echo 'Received malformed request.';
|
|
||||||
|
|
||||||
function handle_todo()
|
if (!isset($_POST['redirect']))
|
||||||
|
header('Location: .');
|
||||||
|
else
|
||||||
|
header('Location: ' . $_POST['redirect']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteSpace()
|
||||||
{
|
{
|
||||||
|
if (!isset($_POST['id'])) {
|
||||||
|
echo json_encode(['error' => 'Missing required Space ID.']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = 'DELETE FROM spaceMembers where space = ?';
|
||||||
|
exec_stmt($stmt, 'i', $_POST['id']);
|
||||||
|
|
||||||
|
$stmt = 'DELETE FROM space WHERE id = ?';
|
||||||
|
exec_stmt($stmt, 'i', $_POST['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNewSpace()
|
||||||
|
{
|
||||||
|
if (!isset($_POST['name']) || !isset($_POST['description']) || !isset($_POST['visibility'])) {
|
||||||
|
error_log('ruh roh');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = randomId(2) * -1;
|
||||||
|
/* $name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8'); */
|
||||||
|
/* $description = htmlspecialchars($_POST['description'], ENT_QUOTES, 'UTF-8'); */
|
||||||
|
$space = new Space($id, $_POST['name'], $_POST['description'], $_POST['visibility'], null, null, isset($_POST['space']) ? Space::retrieveFromDB($_POST['space']) : null, true);
|
||||||
|
error_log(pretty_dump($space));
|
||||||
|
Space::addUserToSpace(unserialize($_SESSION['user'])->getId(), $id * -1, 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNewPost()
|
||||||
|
{
|
||||||
|
if (!isset($_POST['postType']) || !isset($_POST['space']))
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch ($_POST['postType']) {
|
||||||
|
case 'Thought':
|
||||||
|
if (!isset($_POST['thought']))
|
||||||
|
return;
|
||||||
|
$id = randomId(1) * -1;
|
||||||
|
new Thought(Space::retrieveFromDB($_POST['space']), $id, unserialize($_SESSION['user']), 3, 2, urldecode($_POST['thought']));
|
||||||
|
break;
|
||||||
|
case 'Moment':
|
||||||
|
break;
|
||||||
|
case 'Essay':
|
||||||
|
new Essay(Space::retrieveFromDB($_POST['space']), null, unserialize($_SESSION['user']), 3, 2, $_POST['title'], $_POST['markdown'], $_POST['excerpt']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSignup()
|
||||||
|
{
|
||||||
|
if (!isset($_POST['username']) || !isset($_POST['password']) || !isset($_POST['vPassword']))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ($_POST['password'] != $_POST['vPassword'])
|
||||||
|
return;
|
||||||
|
|
||||||
|
$_SESSION['user'] = serialize(new User(randomId(0) * -1, null, $_POST['username'], null, null, null, 3, password_hash($_POST['password'], PASSWORD_DEFAULT)));
|
||||||
|
header('Location: .');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUsernameCheck()
|
||||||
|
{
|
||||||
|
if (!isset($_POST['username']))
|
||||||
|
return;
|
||||||
|
|
||||||
|
echo json_encode(['exists' => User::exists($_POST['username'])]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSignupHTML()
|
||||||
|
{
|
||||||
|
if (isset($_SESSION['user']))
|
||||||
|
header('Location: .');
|
||||||
|
|
||||||
|
echo json_encode(['html' => User::getSignupHTML()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLoginHTML()
|
||||||
|
{
|
||||||
|
if (isset($_SESSION['user']))
|
||||||
|
header('Location: .');
|
||||||
|
|
||||||
|
echo json_encode(['html' => User::getLoginHTML()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeletePost()
|
||||||
|
{
|
||||||
|
if (!isset($_POST['id'])) {
|
||||||
|
echo json_encode(['error' => 'Missing required Post ID.']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = 'DELETE FROM post WHERE id = ?';
|
||||||
|
exec_stmt($stmt, 'i', $_POST['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNewSpaceHTML()
|
||||||
|
{
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="med_width std_border center padding modalContent">
|
||||||
|
<h3>New Space</h3>
|
||||||
|
<form method="post" action="director.php">
|
||||||
|
<input type="hidden" name="formID" value="newSpace">
|
||||||
|
<input type="hidden" name="redirect" value="" id="redirect">
|
||||||
|
<input name="name" placeholder="Name" required autofocus>
|
||||||
|
<input name="description" placeholder="Description" required>
|
||||||
|
<select name="visibility">
|
||||||
|
<option value="1">Open</option>
|
||||||
|
<option value="2">Public</option>
|
||||||
|
<option value="3">Request</option>
|
||||||
|
<option value="4">Invite</option>
|
||||||
|
<option value="5">Private</option>
|
||||||
|
</select>
|
||||||
|
';
|
||||||
|
|
||||||
|
$spaces = $user->getAllSpaces();
|
||||||
|
foreach ($spaces as $s) {
|
||||||
|
$html .= '
|
||||||
|
<label class="form_checkbox_container">' . $s->getName() . ' <i class="nf ' . getSpaceIcon($s) . ' left_margin"></i>
|
||||||
|
<input type="radio" name="space" value="' . $s->getId() . '" id="space-' . $s->getId() . '">
|
||||||
|
<span class="checkmark"></span>
|
||||||
|
</label>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
<input class="button" type="submit" value="Create" id="create">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
echo json_encode(['html' => $html]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSpaceIcon($space)
|
||||||
|
{
|
||||||
|
switch ($space->getVisibility()) {
|
||||||
|
case 1:
|
||||||
|
return 'nf-cod-globe';
|
||||||
|
case 2:
|
||||||
|
return 'nf-fa-group';
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
return 'nf-fa-user_group';
|
||||||
|
case 5:
|
||||||
|
return 'nf-fa-lock';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNewPostHTML()
|
||||||
|
{
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
|
// TODO: Need to plumb this into the DB via AJAX.
|
||||||
|
// TODO: Need to store the current space in SESSION and use that to auto-select a Space
|
||||||
|
// TODO: List the user's spaces or let them search for a space or quick select a favorited space
|
||||||
|
$html = '
|
||||||
|
<div class="med_width std_border center padding modalContent">
|
||||||
|
<h3 id="newPostLabel">New Thought</h3>
|
||||||
|
<form method="post" action="director.php">
|
||||||
|
<input type="hidden" name="formID" value="newPost">
|
||||||
|
<input type="hidden" name="redirect" value="" id="redirect">
|
||||||
|
<input type="hidden" name="postType" value="Thought" id="postType">
|
||||||
|
<input type="hidden" name="space" value="' . $_GET['id'] . '">
|
||||||
|
|
||||||
|
<div id="newThought" class="column">
|
||||||
|
<input type="text" name="thought" placeholder="Jot a Thought." autofocus id="thought">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="newMoment" class="column" style="display: none;">
|
||||||
|
<input type="text" name="caption" placeholder="Caption goes here." autofocus id="caption">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="newEssay" class="column" style="display: none;">
|
||||||
|
<input type="text" name="title" placeholder="Title" autofocus id="title">
|
||||||
|
<textarea name="markdown" placeholder="#Heading" id="markdown"></textarea>
|
||||||
|
<input type="text" name="excerpt" placeholder="A 2-3 sentence summary." id="excerpt">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column" id="postSpaces">
|
||||||
|
';
|
||||||
|
|
||||||
|
$spaces = $user->getAllSpaces();
|
||||||
|
foreach ($spaces as $s) {
|
||||||
|
$html .= '
|
||||||
|
<label class="form_checkbox_container">' . $s->getName() . ' <i class="nf ' . getSpaceIcon($s) . ' left_margin"></i>
|
||||||
|
<input type="radio" name="space" value="' . $s->getId() . '" id="space-' . $s->getId() . '">
|
||||||
|
<span class="checkmark"></span>
|
||||||
|
</label>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input class="button" type="submit" value="Share" id="share">
|
||||||
|
</form>
|
||||||
|
<div class="line std_border"></div>
|
||||||
|
<div id="newModalNav" class="row space_between">
|
||||||
|
<span id="newNavThought" class="link">Thought</span>
|
||||||
|
<span id="newNavMoment" class="link">Moment</span>
|
||||||
|
<span id="newNavEssay" class="link">Essay</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
echo json_encode(['html' => $html]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_login()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
$response = [];
|
$response = [];
|
||||||
|
|
||||||
include 'functions/todo_functions.php';
|
if (isset($_POST['username']) && isset($_POST['password'])) { // Corrected check
|
||||||
|
// Ensure LoginCredentials and User classes are loaded
|
||||||
|
// e.g., require_once 'path/to/LoginCredentials.php';
|
||||||
|
// require_once 'path/to/User.php';
|
||||||
|
|
||||||
if (isset($_POST['todo_id']) && isset($_POST['action_id']) && isset($_POST['status'])) {
|
try {
|
||||||
switch ($_POST['action_id']) {
|
$creds = new LoginCredentials($_POST['username'], $_POST['password']);
|
||||||
case 'status':
|
$user = User::login($creds); // Assuming User::login() can return null or throw an exception
|
||||||
$response['todo_status'] = $_POST['status'];
|
|
||||||
change_status($_POST['todo_id'], $_POST['status']);
|
if ($user != null) {
|
||||||
break;
|
$response['status'] = 'success'; // Use a clear status field
|
||||||
|
$response['message'] = 'Login Successful.'; // Renamed 'log' to 'message' for clarity
|
||||||
|
$response['redirect'] = 'spaces.php?view=list';
|
||||||
|
|
||||||
|
// Serialize the user object and store it in the session
|
||||||
|
$_SESSION['user'] = serialize($user);
|
||||||
|
$spaces = $user->getSpaces();
|
||||||
|
foreach ($spaces as $s)
|
||||||
|
$_SESSION['spaces'][] = serialize($s);
|
||||||
|
} else {
|
||||||
|
$response['status'] = 'error';
|
||||||
|
$response['message'] = 'Incorrect username and/or password.';
|
||||||
}
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('Login Error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
|
||||||
|
|
||||||
|
$response['status'] = 'error';
|
||||||
|
$response['message'] = 'An internal error occurred. Please try again later.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$response['status'] = 'error';
|
||||||
|
$response['message'] = 'Invalid Request: Username and/or password not provided.';
|
||||||
}
|
}
|
||||||
|
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'db_functions.php';
|
|
||||||
require_once 'article_functions.php';
|
|
||||||
|
|
||||||
use Postmark\PostmarkClient;
|
|
||||||
|
|
||||||
function follow_user($follower_user_id, $following_user_id, $notify)
|
|
||||||
{
|
|
||||||
exec_stmt('INSERT INTO follows (follower_user_id, following_user_id, notify_user) VALUES (?, ?, ?)', 'iii', $follower_user_id, $following_user_id, $notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unfollow_user($follower_user_id, $following_user_id)
|
|
||||||
{
|
|
||||||
exec_stmt('DELETE FROM follows WHERE follower_user_id = ? AND following_user_id = ?', 'ii', $follower_user_id, $following_user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function user_id_exists($user_id)
|
|
||||||
{
|
|
||||||
$result = exec_stmt('SELECT user_id FROM user WHERE user_id = ?', 'i', $user_id)->fetch_assoc();
|
|
||||||
|
|
||||||
if ($result == null)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function username_exists($username)
|
|
||||||
{
|
|
||||||
$result = exec_stmt('SELECT username FROM user WHERE username = ?', 'i', $username)->fetch_assoc();
|
|
||||||
|
|
||||||
if ($result == null)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout()
|
|
||||||
{
|
|
||||||
foreach (array_keys($_SESSION) as $key)
|
|
||||||
unset($_SESSION[$key]);
|
|
||||||
|
|
||||||
header('Location: .');
|
|
||||||
}
|
|
||||||
|
|
||||||
function login($username, $password, $stay_logged_in)
|
|
||||||
{
|
|
||||||
$password_hash = hash('sha256', $password);
|
|
||||||
|
|
||||||
$user = exec_stmt('SELECT user_id, username, role_id FROM user WHERE username = ? OR email = ? AND password_hash = ?', 'sss', $username, $username, $password_hash)->fetch_assoc();
|
|
||||||
|
|
||||||
if ($user == null) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Username and/or Password are invalid!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION['user_id'] = $user['user_id'];
|
|
||||||
$_SESSION['username'] = $user['username'];
|
|
||||||
$_SESSION['role_id'] = $user['role_id'];
|
|
||||||
|
|
||||||
if ($stay_logged_in) {
|
|
||||||
$token = bin2hex(random_bytes(64));
|
|
||||||
// echo $token . '<br>';
|
|
||||||
// echo strlen($token);
|
|
||||||
setcookie('remember_user', $token, time() + 60 * 60 * 24 * 30, '/', '', true, true);
|
|
||||||
|
|
||||||
exec_stmt('INSERT INTO remember_user (token, user_id, remote_addr, http_forward) VALUES (?, ?, ?, ?)', 'siss', $token, $user['user_id'], hash('sha256', $_SERVER['REMOTE_ADDR']), (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? hash('sha256', $_SERVER['HTTP_X_FORWARDED_FOR']) : null));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_user($username, $email, $password, $verify_password, $role_id, $upload = null, $x = null, $y = null, $crop_width = null, $honeypot = null)
|
|
||||||
{
|
|
||||||
if (!empty($honeypot)) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
You are a bot. Leave now.
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($password != $verify_password) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Passwords do not match!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (username_exists($username)) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Username is taken!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
}
|
|
||||||
|
|
||||||
$password_hash = hash('sha256', $password);
|
|
||||||
|
|
||||||
$id = rand(1000, 999999999);
|
|
||||||
while (user_id_exists($id))
|
|
||||||
$id = rand(1000, 999999999);
|
|
||||||
|
|
||||||
if (!empty($upload['profile_picture']['tmp_name'])) {
|
|
||||||
$orig_size = getimagesize($upload['profile_picture']['tmp_name']);
|
|
||||||
$orig_width = $orig_size[0];
|
|
||||||
$orig_height = $orig_size[1];
|
|
||||||
|
|
||||||
$crop_size = min($orig_width, $orig_height) * 0.56;
|
|
||||||
|
|
||||||
crop_image($upload['profile_picture']['tmp_name'], $x, $y, $crop_size, $crop_size);
|
|
||||||
$target = upload($upload['profile_picture'], $_ENV['PROFILE_IMAGES_FQ_PATH'], $id);
|
|
||||||
} else {
|
|
||||||
$target = 'default-profile.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_stmt('INSERT INTO user (user_id, username, email, password_hash, role_id, profile_picture) VALUES (?, ?, ?, ?, ?, ?)', 'isssis', $id, $username, $email, $password_hash, $role_id, $target);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset_password($user_id, $new_password, $verify_new_password)
|
|
||||||
{
|
|
||||||
if ($new_password != $verify_new_password) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Passwords do not match!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
$password_hash = hash('sha256', $new_password);
|
|
||||||
exec_stmt('UPDATE user SET password_hash = ? WHERE user_id = ?', 'si', $password_hash, $user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_email($user_id, $new_email, $verify_new_email)
|
|
||||||
{
|
|
||||||
if ($new_email != $verify_new_email) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Emails do not match!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_stmt('UPDATE user SET email = ? WHERE user_id = ?', 'si', $new_email, $user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_username($user_id, $new_username, $verify_new_username)
|
|
||||||
{
|
|
||||||
if ($new_username != $verify_new_username) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Usernames do not match!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result = username_exists($new_username))
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
exec_stmt('UPDATE user SET username = ? WHERE user_id = ?', 'si', $new_username, $user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_profile_picture($user_id) {}
|
|
||||||
|
|
||||||
function request_role_change($user_id, $new_role_id)
|
|
||||||
{
|
|
||||||
$user = exec_stmt('SELECT user_id, username, email, roles.role, created_at FROM user INNER JOIN roles WHERE user.role_id = roles.role_id AND user_id = ?', 'i', $user_id)->fetch_assoc();
|
|
||||||
$new_role = exec_stmt('SELECT role FROM roles WHERE role_id = ?', 'i', $new_role_id)->fetch_array();
|
|
||||||
|
|
||||||
$email_html = '
|
|
||||||
<h1>User Role Change Request</h1>
|
|
||||||
<p>The following user has requested their role on vintagecoding.net to be changed:</p>
|
|
||||||
<p><strong>User ID: </strong>' . $user['user_id'] . '</p>
|
|
||||||
<p><strong>Username: </strong>' . $user['username'] . '</p>
|
|
||||||
<p><strong>Email: </strong>' . $user['email'] . '</p>
|
|
||||||
<p><strong>Current Role: </strong>' . $user['role'] . ' -> ' . $new_role[0] . '</p>
|
|
||||||
<p><strong>Created At: </strong>' . $user['created_at'] . '</p>
|
|
||||||
';
|
|
||||||
|
|
||||||
$client = new PostmarkClient($_ENV['POSTMARK_API_TOKEN']);
|
|
||||||
|
|
||||||
$client->sendEmail(
|
|
||||||
'mailer@joshashton.dev',
|
|
||||||
'me@joshashton.dev',
|
|
||||||
'User Role Change Request - ' . $user['user_id'],
|
|
||||||
$email_html
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function delete_account($user_id, $password = null, $verify_password = null)
|
|
||||||
{
|
|
||||||
if ($user_id == 2025) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
I am the owner, and I cannot delete myself...
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($password != $verify_password) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Passwords do not match!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($password && $verify_password) {
|
|
||||||
$password_hash = hash('sha256', $password);
|
|
||||||
$user = exec_stmt('SELECT user_id, profile_picture FROM user WHERE user_id = ? AND password_hash = ?', 'is', $user_id, $password_hash)->fetch_assoc();
|
|
||||||
|
|
||||||
if ($user == null) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Username and/or Password are invalid!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardcoded prevention of deleting the owner's profile picture
|
|
||||||
if (!empty($user['profile_picture']) && $user['profile_picture'] != '2025.jpg')
|
|
||||||
delete_file($_ENV['PROFILE_IMAGES_FQ_PATH'] . $user['profile_picture']);
|
|
||||||
|
|
||||||
exec_stmt('DELETE FROM user WHERE user_id = ?', 'i', $user_id);
|
|
||||||
logout();
|
|
||||||
} else if ($_SESSION['role_id'] <= admin) {
|
|
||||||
$user = exec_stmt('SELECT user_id, profile_picture FROM user WHERE user_id = ?', 'i', $user_id)->fetch_assoc();
|
|
||||||
|
|
||||||
if ($user['user_id'] == 2025) {
|
|
||||||
$error_msg = '
|
|
||||||
<div class="error">
|
|
||||||
Cannot delete the owner!
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardcoded prevention of deleting the owner's profile picture
|
|
||||||
if (!empty($user['profile_picture']) && $user['profile_picture'] != '2025.jpg')
|
|
||||||
delete_file($_ENV['PROFILE_IMAGES_FQ_PATH'] . $user['profile_picture']);
|
|
||||||
|
|
||||||
exec_stmt('DELETE FROM user WHERE user_id = ?', 'i', $user_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'db_functions.php';
|
require_once 'db.php';
|
||||||
require_once 'functions.php';
|
require_once 'functions.php';
|
||||||
require_once 'account_functions.php';
|
require_once 'account_functions.php';
|
||||||
require_once 'article_functions.php';
|
require_once 'article_functions.php';
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'db_functions.php';
|
|
||||||
|
|
||||||
function article_ids_by_newest()
|
|
||||||
{
|
|
||||||
$conn = get_connection();
|
|
||||||
$results = $conn->query('SELECT article_id FROM article ORDER BY published_at DESC');
|
|
||||||
$ids = [];
|
|
||||||
|
|
||||||
while ($row = $results->fetch_assoc())
|
|
||||||
$ids[] = $row['article_id'];
|
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
function article_ids_by_oldest()
|
|
||||||
{
|
|
||||||
$conn = get_connection();
|
|
||||||
$results = $conn->query('SELECT article_id FROM article ORDER BY published_at ASC');
|
|
||||||
$ids = [];
|
|
||||||
|
|
||||||
while ($row = $results->fetch_assoc())
|
|
||||||
$ids[] = $row['article_id'];
|
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
function articles_ids_by_author($author_id)
|
|
||||||
{
|
|
||||||
$results = exec_stmt('SELECT article_id FROM article WHERE author_id = ? ORDER BY published_at DESC', 'i', $author_id);
|
|
||||||
$ids = [];
|
|
||||||
|
|
||||||
while ($row = $results->fetch_assoc())
|
|
||||||
$ids[] = $row['article_id'];
|
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
function article_tags()
|
|
||||||
{
|
|
||||||
$conn = get_connection();
|
|
||||||
$results = $conn->query('SELECT * FROM tags');
|
|
||||||
$tags = [];
|
|
||||||
|
|
||||||
while ($row = $results->fetch_assoc())
|
|
||||||
$tags[] = $row;
|
|
||||||
|
|
||||||
return $tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
function article_ids_by_tag($tag)
|
|
||||||
{
|
|
||||||
$results = exec_stmt('SELECT article_id FROM article WHERE article.article_id IN ( SELECT article_tags.article_id FROM article_tags INNER JOIN tags ON article_tags.tag_id = tags.tag_id WHERE tag = ?) ORDER BY published_at DESC', 's', $tag);
|
|
||||||
$ids = [];
|
|
||||||
|
|
||||||
while ($row = $results->fetch_assoc())
|
|
||||||
$ids[] = $row['article_id'];
|
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_article($author_id, $title, $excerpt, $tags, $markdown_file_contents)
|
|
||||||
{
|
|
||||||
$id = exec_stmt('INSERT INTO article (author_id, title, excerpt, published_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP)', 'iss', $author_id, $title, $excerpt);
|
|
||||||
|
|
||||||
$path = $_ENV['ARTICLES_FQ_PATH'] . $id . '/';
|
|
||||||
mkdir($path);
|
|
||||||
|
|
||||||
$fs = fopen($path . 'article.md', 'a');
|
|
||||||
fwrite($fs, $markdown_file_contents);
|
|
||||||
fclose($fs);
|
|
||||||
|
|
||||||
if (count($tags) > 0) {
|
|
||||||
$tag_insert_stmt = 'INSERT INTO article_tags (article_id, tag_id) VALUES ';
|
|
||||||
$types = '';
|
|
||||||
for ($i = 0; $i < count($tags); $i++) {
|
|
||||||
$tag_insert_stmt .= '(' . $id . ', ?)';
|
|
||||||
|
|
||||||
if ($i < count($tags) - 1)
|
|
||||||
$tag_insert_stmt .= ', ';
|
|
||||||
|
|
||||||
$types .= 'i';
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_stmt($tag_insert_stmt, $types, ...$tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_article($article_id, $title, $excerpt, $tags, $markdown_file_contents)
|
|
||||||
{
|
|
||||||
exec_stmt('UPDATE article SET title = ?, excerpt = ?, updated_at = CURRENT_TIMESTAMP WHERE article_id = ?', 'ssi', $title, $excerpt, $article_id);
|
|
||||||
|
|
||||||
$path = $_ENV['ARTICLES_FQ_PATH'] . $article_id . '/';
|
|
||||||
$fs = fopen($path . 'article.md', 'w');
|
|
||||||
fwrite($fs, $markdown_file_contents);
|
|
||||||
fclose($fs);
|
|
||||||
|
|
||||||
exec_stmt('DELETE from article_tags WHERE article_id = ?', 'i', $article_id);
|
|
||||||
|
|
||||||
if (count($tags) > 0) {
|
|
||||||
$tag_insert_stmt = 'INSERT INTO article_tags (article_id, tag_id) VALUES ';
|
|
||||||
$types = '';
|
|
||||||
for ($i = 0; $i < count($tags); $i++) {
|
|
||||||
$tag_insert_stmt .= '(' . $article_id . ', ?)';
|
|
||||||
|
|
||||||
if ($i < count($tags) - 1)
|
|
||||||
$tag_insert_stmt .= ', ';
|
|
||||||
|
|
||||||
$types .= 'i';
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_stmt($tag_insert_stmt, $types, ...$tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function delete_article($article_id)
|
|
||||||
{
|
|
||||||
exec_stmt('DELETE FROM article_tags WHERE article_id = ?', 'i', $article_id);
|
|
||||||
exec_stmt('DELETE FROM article WHERE article_id = ?', 'i', $article_id);
|
|
||||||
|
|
||||||
delete_dir($_ENV['ARTICLES_FQ_PATH'] . $article_id . '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
function delete_articles_by_author($author_id)
|
|
||||||
{
|
|
||||||
exec_stmt('DELETE FROM article WHERE author_id = ?', 'i', $author_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function increment_read_counter($article_id)
|
|
||||||
{
|
|
||||||
exec_stmt('UPDATE article SET read_count = read_count + 1 WHERE article_id = ?', 'i', $article_id);
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
include_once 'functions/init.php';
|
require_once 'init.php';
|
||||||
|
|
||||||
use Postmark\PostmarkClient;
|
use Postmark\PostmarkClient;
|
||||||
|
|
||||||
if (!empty($_FILES['upload']))
|
if (!empty($_FILES['upload']))
|
||||||
upload($_FILES['upload'], $_ENV['UPLOAD_TMP_FQ_PATH'], isset($_POST['filename']) ? $_POST['filename'] : null);
|
upload($_FILES['upload'], $_ENV['UPLOAD_TMP_FQ_PATH'], isset($_POST['filename']) ? $_POST['filename'] : null);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @* @param int $mode 0 for User, 1 for Post, 2 for Space
|
||||||
|
*/
|
||||||
|
function randomId(int $mode)
|
||||||
|
{
|
||||||
|
$id = rand(10000000, 999999999);
|
||||||
|
switch ($mode) {
|
||||||
|
case 0:
|
||||||
|
if (User::exists($id))
|
||||||
|
return randomId($mode);
|
||||||
|
else
|
||||||
|
return $id;
|
||||||
|
case 1:
|
||||||
|
if (Post::exists($id))
|
||||||
|
return randomId($mode);
|
||||||
|
else
|
||||||
|
return $id;
|
||||||
|
case 2:
|
||||||
|
if (Space::exists($id))
|
||||||
|
return randomId($mode);
|
||||||
|
else
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function notify_users($emails, $auther_username, $article_id, $article_title, $excerpt)
|
function notify_users($emails, $auther_username, $article_id, $article_title, $excerpt)
|
||||||
{
|
{
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
@@ -16,7 +41,6 @@ function notify_users($emails, $auther_username, $article_id, $article_title, $e
|
|||||||
<p>' . $excerpt . '</p>
|
<p>' . $excerpt . '</p>
|
||||||
';
|
';
|
||||||
|
|
||||||
|
|
||||||
$client = new PostmarkClient($_ENV['POSTMARK_API_TOKEN']);
|
$client = new PostmarkClient($_ENV['POSTMARK_API_TOKEN']);
|
||||||
|
|
||||||
$client->sendEmail(
|
$client->sendEmail(
|
||||||
@@ -178,3 +202,8 @@ function pretty_dump($arr)
|
|||||||
echo '</pre>';
|
echo '</pre>';
|
||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function format_date($date)
|
||||||
|
{
|
||||||
|
return (explode(' ', $date))[0];
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once 'vendor/autoload.php';
|
require_once 'vendor/autoload.php';
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
$dotenv = Dotenv\Dotenv::createImmutable('/home/jashton/dev/me/weave.space/');
|
||||||
$dotenv->safeLoad();
|
$dotenv->safeLoad();
|
||||||
|
|
||||||
// Use environment variables for the database password and IP address.
|
// Use environment variables for the database password and IP address.
|
||||||
@@ -29,10 +29,15 @@ function exec_stmt($stmt, $param_types, ...$args)
|
|||||||
$stmt->bind_param($param_types, ...$args);
|
$stmt->bind_param($param_types, ...$args);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
if ($result)
|
|
||||||
|
if ($result) {
|
||||||
|
$conn->close();
|
||||||
return $result;
|
return $result;
|
||||||
else
|
} else {
|
||||||
return mysqli_insert_id($conn);
|
$id = mysqli_insert_id($conn);
|
||||||
|
$conn->close();
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_connection()
|
function get_connection()
|
||||||
+11
-51
@@ -1,16 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
header("Content-Security-Policy: default-src 'self'; font-src 'self' https://www.nerdfonts.com/assets/fonts/Symbols-2048-em%20Nerd%20Font%20Complete.woff2; style-src 'self' 'unsafe-inline' https://nerdfonts.com/assets/css/webfont.css https://www.nerdfonts.com/assets/css/webfont.css; script-src 'self' 'unsafe-inline'; img-src 'self';");
|
header("Content-Security-Policy: default-src 'self' localhost:*; font-src 'self' https://www.nerdfonts.com/assets/fonts/Symbols-2048-em%20Nerd%20Font%20Complete.woff2; style-src 'self' 'unsafe-inline' https://nerdfonts.com/assets/css/webfont.css https://www.nerdfonts.com/assets/css/webfont.css; script-src 'self' 'unsafe-inline'; img-src 'self';");
|
||||||
|
|
||||||
// Initialize Composer.
|
require_once 'functions/vendor/autoload.php';
|
||||||
require_once 'vendor/autoload.php';
|
$dotenv = Dotenv\Dotenv::createImmutable('/home/jashton/dev/me/weave.space/.env');
|
||||||
require_once 'db_functions.php';
|
|
||||||
require_once 'account_functions.php';
|
|
||||||
|
|
||||||
// Load environment variables.
|
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
|
|
||||||
$dotenv->safeLoad();
|
$dotenv->safeLoad();
|
||||||
|
|
||||||
|
require_once 'functions/db.php';
|
||||||
|
require_once 'functions/core.php';
|
||||||
|
require_once 'functions/user.php';
|
||||||
|
require_once 'functions/post.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;
|
||||||
@@ -21,46 +22,5 @@ if (!isset($_SESSION['initialized'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
define('owner', 1);
|
define('owner', 1);
|
||||||
define('admin', 2);
|
define('developer', 2);
|
||||||
define('contributor', 3);
|
define('user', 3);
|
||||||
define('reader', 4);
|
|
||||||
|
|
||||||
// Update the database and cookies to keep the user logged in.
|
|
||||||
if (isset($_SESSION['user_id'])) {
|
|
||||||
$conn = get_connection();
|
|
||||||
$stmt = $conn->prepare('UPDATE user SET is_active = true WHERE user_id = ?');
|
|
||||||
$stmt->bind_param('i', $_SESSION['user_id']);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare('UPDATE user SET last_active = CURRENT_TIMESTAMP WHERE user_id = ?');
|
|
||||||
$stmt->bind_param('i', $_SESSION['user_id']);
|
|
||||||
$stmt->execute();
|
|
||||||
} else if (isset($_COOKIE['remember_user'])) {
|
|
||||||
$conn = get_connection();
|
|
||||||
$stmt = $conn->prepare('SELECT * FROM remember_user WHERE token = ?');
|
|
||||||
$stmt->bind_param('s', $_COOKIE['remember_user']);
|
|
||||||
$stmt->execute();
|
|
||||||
$remember = $stmt->get_result()->fetch_assoc();
|
|
||||||
|
|
||||||
if (hash('sha256', $_SERVER['REMOTE_ADDR']) != $remember['remote_addr'] || (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && hash('sha256', $_SERVER['HTTP_X_FORWARDED_FOR']) != $remember['forwarded_for'])) {
|
|
||||||
// TODO: Log the attempt to use a cookie from a different browser/device than the cookie was created
|
|
||||||
echo "Naughty, you are trying to use someone else's cookie...";
|
|
||||||
} else {
|
|
||||||
$stmt = $conn->prepare('SELECT user_id, username, role_id FROM user WHERE user_id = ?');
|
|
||||||
$stmt->bind_param('s', $remember['user_id']);
|
|
||||||
$stmt->execute();
|
|
||||||
$user = $stmt->get_result()->fetch_assoc();
|
|
||||||
|
|
||||||
$_SESSION['user_id'] = $user['user_id'];
|
|
||||||
$_SESSION['username'] = $user['username'];
|
|
||||||
$_SESSION['role_id'] = $user['role_id'];
|
|
||||||
|
|
||||||
$stmt = $conn->prepare('UPDATE user SET is_active = true WHERE user_id = ?');
|
|
||||||
$stmt->bind_param('i', $_SESSION['user_id']);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare('UPDATE user SET last_active = CURRENT_TIMESTAMP WHERE user_id = ?');
|
|
||||||
$stmt->bind_param('i', $_SESSION['user_id']);
|
|
||||||
$stmt->execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,324 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// It's good practice to use namespaces, e.g.:
|
||||||
|
// namespace VintageCoding\Models;
|
||||||
|
// namespace VintageCoding\Repositories;
|
||||||
|
|
||||||
|
require_once 'functions/db.php'; // For exec_stmt() and get_connection() (though get_connection is used by exec_stmt)
|
||||||
|
require_once 'functions/user.php'; // For User and Author classes
|
||||||
|
require_once 'functions/space.php';
|
||||||
|
|
||||||
|
// -- Data Transfer Object for Topics --
|
||||||
|
class Topic
|
||||||
|
{
|
||||||
|
public int $topicId;
|
||||||
|
public string $name;
|
||||||
|
|
||||||
|
public function __construct(int $topicId, string $name)
|
||||||
|
{
|
||||||
|
$this->topicId = $topicId;
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->topicId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Post Data Models (as previously defined, with setReadCount in Post) --
|
||||||
|
class Post
|
||||||
|
{
|
||||||
|
private ?Space $space;
|
||||||
|
private ?int $id;
|
||||||
|
private User $creator;
|
||||||
|
private int $postType;
|
||||||
|
private ?int $status;
|
||||||
|
private int $seenCount;
|
||||||
|
private ?DateTime $createdAt;
|
||||||
|
private ?DateTime $updatedAt;
|
||||||
|
|
||||||
|
public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status)
|
||||||
|
{
|
||||||
|
$this->space = $space;
|
||||||
|
$this->id = $id;
|
||||||
|
$this->creator = $creator;
|
||||||
|
$this->postType = $postType;
|
||||||
|
$this->status = $status;
|
||||||
|
|
||||||
|
$this->seenCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function exists(int $id): bool
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT count(id) FROM post WHERE id = ?';
|
||||||
|
$result = exec_stmt($stmt, 'i', $id)->fetch_assoc();
|
||||||
|
if ($result['count(id)'] == 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function retrieveFromDB(int $id): ?Post
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT * FROM post WHERE id = ?';
|
||||||
|
$post = exec_stmt($stmt, 'i', $id)->fetch_assoc();
|
||||||
|
if ($post) {
|
||||||
|
$creator = User::retrieveFromDB($post['creator']);
|
||||||
|
|
||||||
|
switch ($post['postType']) {
|
||||||
|
case 1:
|
||||||
|
return new Essay(null, $id, $creator, 1, $post['status'], $post['title'], $post['markdown'], $post['excerpt']);
|
||||||
|
case 3:
|
||||||
|
return new Thought(null, $post['id'], $creator, 3, $post['status'], $post['thought']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setId(int $id): void
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreator(): User
|
||||||
|
{
|
||||||
|
return $this->creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPostType(): int
|
||||||
|
{
|
||||||
|
return $this->postType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSeenCount(): int
|
||||||
|
{
|
||||||
|
return $this->seenCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): ?DateTime
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpdatedAt(): ?DateTime
|
||||||
|
{
|
||||||
|
return $this->updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatus(): ?DateTime
|
||||||
|
{
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function incrementSeenCount(): void
|
||||||
|
{
|
||||||
|
$this->seenCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatus(?int $status): void
|
||||||
|
{
|
||||||
|
$this->status = $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSpace(Space $space): void
|
||||||
|
{
|
||||||
|
$this->space = $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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Essay extends Post
|
||||||
|
{
|
||||||
|
private string $title;
|
||||||
|
private string $markdown;
|
||||||
|
private string $excerpt;
|
||||||
|
/* private array $files; */
|
||||||
|
|
||||||
|
public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, string $title, string $markdown, string $excerpt)
|
||||||
|
{
|
||||||
|
parent::__construct($space, $id, $creator, 1, $postType, $status);
|
||||||
|
|
||||||
|
$this->title = $title;
|
||||||
|
$this->excerpt = $excerpt;
|
||||||
|
$this->markdown = $markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCardHTML(): ?string
|
||||||
|
{
|
||||||
|
/* <a href="user.php?view=display&id=' . parent::getCreator()->getId() . '">@' . parent::getCreator()->getUsername() . '</a> */
|
||||||
|
$html = '
|
||||||
|
<div class="full_width std_border padding center top_margin bottom_margin">
|
||||||
|
<a href="spaces.php?view=essay&id=' . parent::getId() . '" class="divLink">
|
||||||
|
<div class="row space_between full_width">
|
||||||
|
<div class="column std_width">
|
||||||
|
<h4>' . $this->title . '</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sm_width">
|
||||||
|
<i class="nf nf-md-dots_horizontal"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row full_width">
|
||||||
|
<p>' . $this->excerpt . '</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function allowUser(User $user): bool
|
||||||
|
{
|
||||||
|
return parent::getSpace()->allowUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): ?string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMarkdown(): ?string
|
||||||
|
{
|
||||||
|
return $this->markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExcerpt(): ?string
|
||||||
|
{
|
||||||
|
return $this->excerpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMarkdown(?string $markdown): void
|
||||||
|
{
|
||||||
|
$this->markdown = $markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExcerpt(?string $excerpt): void
|
||||||
|
{
|
||||||
|
$this->excerpt = $excerpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSpace(): ?Space
|
||||||
|
{
|
||||||
|
return parent::getSpace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Moment extends Post
|
||||||
|
{
|
||||||
|
private ?string $caption;
|
||||||
|
private ?array $images;
|
||||||
|
|
||||||
|
public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, ?array $images = null, ?string $caption = null)
|
||||||
|
{
|
||||||
|
parent::__construct($space, $id, $creator, $postType, $status);
|
||||||
|
$this->caption = $caption;
|
||||||
|
$this->images = $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCaption(): ?string
|
||||||
|
{
|
||||||
|
return $this->caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getImages(): ?array
|
||||||
|
{
|
||||||
|
return $this->images;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCaption(?string $caption): void
|
||||||
|
{
|
||||||
|
$this->caption = $caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setImages(?array $images): void
|
||||||
|
{
|
||||||
|
$this->images = $images;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Thought extends Post
|
||||||
|
{
|
||||||
|
private ?string $thought;
|
||||||
|
|
||||||
|
public function __construct(?Space $space = null, ?int $id = null, User $creator, int $postType, int $status, string $thought)
|
||||||
|
{
|
||||||
|
parent::__construct($space, $id, $creator, $postType, $status);
|
||||||
|
$this->thought = $thought;
|
||||||
|
|
||||||
|
if ($id < 0) {
|
||||||
|
$stmt = 'INSERT INTO post (id, space, creator, postType, status, thought) VALUES (?, ?, ?, ?, ?, ?)';
|
||||||
|
parent::setId($id * -1);
|
||||||
|
$space = isset($space) ? $space->getId() : $creator->getHomeSpace()->getId();
|
||||||
|
exec_stmt($stmt, 'iiiiis', $id * -1, $space, $creator->getId(), 3, 2, $this->thought);
|
||||||
|
$stmt = 'INSERT INTO spacePosts (post, space) VALUES (?, ?)';
|
||||||
|
exec_stmt($stmt, 'ii', $id * -1, $space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCardHTML(): ?string
|
||||||
|
{
|
||||||
|
$html = '
|
||||||
|
<div class="std_border padding center full_width top_margin bottom_margin rel" id="post-' . parent::getId() . '">
|
||||||
|
<div class="row space_between full_width">
|
||||||
|
<a href="user.php?view=display&id=' . parent::getCreator()->getId() . '">@' . parent::getCreator()->getUsername() . '</a>
|
||||||
|
|
||||||
|
|
||||||
|
';
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
if ($user->getId() == parent::getCreator()->getId()) {
|
||||||
|
$html .= '
|
||||||
|
<div class="dropdown" style="z-index: 0;">
|
||||||
|
<i class="nf nf-md-dots_horizontal"></i>
|
||||||
|
<div class="sm_border padding glass dropdownContent">
|
||||||
|
<a class="underline link" onclick="deletePost(' . parent::getId() . ')">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
<div class="row full_width">
|
||||||
|
<p>' . $this->thought . '</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getThought(): ?string
|
||||||
|
{
|
||||||
|
return $this->thought;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setThought(?string $thought): void
|
||||||
|
{
|
||||||
|
$this->thought = $thought;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'core.php';
|
||||||
|
require_once 'db.php';
|
||||||
|
require_once 'user.php';
|
||||||
|
require_once 'post.php';
|
||||||
|
|
||||||
|
class Space
|
||||||
|
{
|
||||||
|
private int $id;
|
||||||
|
private string $name;
|
||||||
|
private string $description;
|
||||||
|
private int $visibility;
|
||||||
|
private ?array $members;
|
||||||
|
private ?array $posts;
|
||||||
|
private ?Space $parent;
|
||||||
|
|
||||||
|
public function __construct(int $id, string $name, string $description, int $visibility, ?array $members, ?array $posts, ?Space $parent = null, bool $favorited = false)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->description = $description;
|
||||||
|
$this->visibility = $visibility;
|
||||||
|
$this->members = $members;
|
||||||
|
$this->posts[] = $posts;
|
||||||
|
$this->parent = $parent;
|
||||||
|
|
||||||
|
if ($id < 0) {
|
||||||
|
$this->id = $id * -1;
|
||||||
|
error_log('ID: ' . $this->id);
|
||||||
|
|
||||||
|
$stmt = 'INSERT INTO space (id, name, description, visibility, parent) 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 parent = ?';
|
||||||
|
$rawSubSpaces = exec_stmt($stmt, 'i', $this->id);
|
||||||
|
|
||||||
|
$subSpaces = [];
|
||||||
|
foreach ($rawSubSpaces as $ss)
|
||||||
|
$subSpaces[] = Space::retrieveFromDB($ss['id']);
|
||||||
|
|
||||||
|
return $subSpaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParentSpace(): ?Space
|
||||||
|
{
|
||||||
|
return $this->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCardHTML(): string
|
||||||
|
{
|
||||||
|
$html = '
|
||||||
|
<div class="full_width std_border padding center top_margin bottom_margin rel" id="space-' . $this->id . '">
|
||||||
|
<a href="spaces.php?view=show&id=' . $this->id . '" class="fillDiv spaceCard"></a>
|
||||||
|
<div class="row space_between full_width">
|
||||||
|
<div class="column std_width">
|
||||||
|
<h4>' . $this->name . '</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
';
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
if ($user->getId() == $this->getOwner()->getId()) {
|
||||||
|
$html .= '
|
||||||
|
<div class="dropdown">
|
||||||
|
<i class="nf nf-md-dots_horizontal"></i>
|
||||||
|
<div class="sm_border padding glass dropdownContent">
|
||||||
|
<a class="underline link" onclick="deleteSpace(' . $this->id . ')">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
|
<div class="row full_width">
|
||||||
|
<p>' . $this->description . '</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOwner(): User
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT user FROM spaceMembers WHERE space = ? AND role = ?';
|
||||||
|
$user = exec_stmt($stmt, 'ii', $this->id, 1)->fetch_assoc();
|
||||||
|
|
||||||
|
return User::retrieveFromDB($user['user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function retrieveFromDB(int $space, int $user = -1): ?Space
|
||||||
|
{
|
||||||
|
if ($user != -1) {
|
||||||
|
$stmt = 'SELECT * FROM spaceMembers WHERE space = ? AND user = ?';
|
||||||
|
$isMember = exec_stmt($stmt, 'ii', $space, $user)->fetch_assoc();
|
||||||
|
if (!$isMember)
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$stmt = 'SELECT visibility FROM space WHERE id = ?';
|
||||||
|
$isOpen = exec_stmt($stmt, 'i', $space);
|
||||||
|
if (!$isOpen)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = 'SELECT * FROM space WHERE id = ?';
|
||||||
|
$rawSpace = exec_stmt($stmt, 'i', $space)->fetch_assoc();
|
||||||
|
|
||||||
|
if ($rawSpace) {
|
||||||
|
if (!empty($rawSpace['parent']))
|
||||||
|
$parent = Space::retrieveFromDB($rawSpace['parent'], $user);
|
||||||
|
else
|
||||||
|
$parent = null;
|
||||||
|
|
||||||
|
$stmt = 'SELECT user FROM spaceMembers WHERE space = ?';
|
||||||
|
$members = [];
|
||||||
|
$rawMembers = exec_stmt($stmt, 'i', $rawSpace['id']);
|
||||||
|
foreach ($rawMembers as $m)
|
||||||
|
$members[] = User::retrieveFromDB($m['user']);
|
||||||
|
|
||||||
|
$stmt = 'SELECT post FROM spacePosts INNER JOIN post ON spacePosts.post = post.id WHERE spacePosts.space = ? ORDER BY post.createdAt DESC';
|
||||||
|
$posts = [];
|
||||||
|
$rawPosts = exec_stmt($stmt, 'i', $rawSpace['id']);
|
||||||
|
while ($row = $rawPosts->fetch_assoc())
|
||||||
|
$posts[] = Post::retrieveFromDB($row['post']);
|
||||||
|
|
||||||
|
return new Space($rawSpace['id'], $rawSpace['name'], $rawSpace['description'], $rawSpace['visibility'], $members, $posts, $parent);
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function allowUser(User $user)
|
||||||
|
{
|
||||||
|
return in_array($user, $this->members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosts()
|
||||||
|
{
|
||||||
|
return $this->posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
echo 'todo: this should be JSON';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVisibility(): int
|
||||||
|
{
|
||||||
|
return $this->visibility;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
// class Todo
|
|
||||||
// {
|
|
||||||
// private $todo_id;
|
|
||||||
// private $user_id;
|
|
||||||
// private $task;
|
|
||||||
// private $description;
|
|
||||||
// private $priority;
|
|
||||||
// private $due_date;
|
|
||||||
// private $category;
|
|
||||||
// private $status;
|
|
||||||
// private $created_at;
|
|
||||||
// private $update_at;
|
|
||||||
// private $completed_at;
|
|
||||||
// private $is_recurring;
|
|
||||||
// private $recurrence_rule;
|
|
||||||
// private $reminder_at;
|
|
||||||
//
|
|
||||||
// function __construct(
|
|
||||||
// $todo_id,
|
|
||||||
// $user_id,
|
|
||||||
// $task,
|
|
||||||
// $description,
|
|
||||||
// $priority,
|
|
||||||
// $due_date,
|
|
||||||
// $category,
|
|
||||||
// $status,
|
|
||||||
// $created_at,
|
|
||||||
// $update_at,
|
|
||||||
// $completed_at,
|
|
||||||
// $is_recurring,
|
|
||||||
// $recurrence_rule,
|
|
||||||
// $reminder_at,
|
|
||||||
// ) {
|
|
||||||
// $this->todo_id = $todo_id;
|
|
||||||
// $this->user_id = $user_id;
|
|
||||||
// $this->task = $task;
|
|
||||||
// $this->description = $description;
|
|
||||||
// $this->priority = $priority;
|
|
||||||
// $this->due_date = $due_date;
|
|
||||||
// $this->category = $category;
|
|
||||||
// $this->status = $status;
|
|
||||||
// $this->created_at = $created_at;
|
|
||||||
// $this->update_at = $update_at;
|
|
||||||
// $this->completed_at = $completed_at;
|
|
||||||
// $this->is_recurring = $is_recurring;
|
|
||||||
// $this->recurrence_rule = $recurrence_rule;
|
|
||||||
// $this->reminder_at = $reminder_at;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
function change_status($todo_id, $status)
|
|
||||||
{
|
|
||||||
exec_stmt('UPDATE todos SET status = ? WHERE todo_id = ?', 'si', $status, $todo_id);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,460 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LoginCredentials
|
||||||
|
{
|
||||||
|
private string $username;
|
||||||
|
private string $password;
|
||||||
|
|
||||||
|
public function __construct(string $username, string $password)
|
||||||
|
{
|
||||||
|
// More intensive SQL injection checks (example - adapt as needed)
|
||||||
|
if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $password)) {
|
||||||
|
throw new InvalidArgumentException('Invalid characters in username or password.');
|
||||||
|
}
|
||||||
|
// Consider using a more robust validation library
|
||||||
|
|
||||||
|
$this->username = $username;
|
||||||
|
$this->password = $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPassword(): string
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private int $id;
|
||||||
|
private ?array $spaces;
|
||||||
|
private string $username;
|
||||||
|
private bool $isActive;
|
||||||
|
private ?string $profilePicture;
|
||||||
|
private ?string $bio;
|
||||||
|
private ?string $website;
|
||||||
|
private int $role;
|
||||||
|
|
||||||
|
public function __construct(int $id = -1, ?array $spaces, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, int $role = 3, ?string $passwordHash)
|
||||||
|
{
|
||||||
|
// Basic XSS prevention on construction (can be enhanced)
|
||||||
|
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
|
||||||
|
$bio = htmlspecialchars($bio ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$website = htmlspecialchars($website ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$profilePicture = htmlspecialchars($profilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
// Basic SQL injection prevention (should primarily rely on prepared statements)
|
||||||
|
/* if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) { */
|
||||||
|
/* throw new InvalidArgumentException('Invalid characters in user data.'); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
$this->username = $username;
|
||||||
|
$this->profilePicture = $profilePicture;
|
||||||
|
$this->bio = $bio;
|
||||||
|
$this->website = $website;
|
||||||
|
$this->isActive = false;
|
||||||
|
$this->role = $role;
|
||||||
|
$this->spaces = null;
|
||||||
|
|
||||||
|
if ($id < 0) {
|
||||||
|
$this->id = $id * -1;
|
||||||
|
$stmt = 'INSERT INTO user (id, username, passwordHash, profilePicture, bio, website) VALUES (?, ?, ?, ?, ?, ?)';
|
||||||
|
exec_stmt($stmt, 'isssss', $this->id, $this->username, $passwordHash, $this->profilePicture, $this->bio, $this->website);
|
||||||
|
|
||||||
|
Space::addUserToSpace($this->id, 2025);
|
||||||
|
new Space($id, $this->username, $this->username . "'s private Space.", 5, [$this], [], null, true);
|
||||||
|
} else
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function exists($identifier): bool
|
||||||
|
{
|
||||||
|
if (is_int($identifier)) {
|
||||||
|
$stmt = 'SELECT count(id) FROM user WHERE id = ?';
|
||||||
|
$result = exec_stmt($stmt, 'i', $identifier)->fetch_assoc();
|
||||||
|
|
||||||
|
if ($result['count(id)'] == 1)
|
||||||
|
return true;
|
||||||
|
else if ($result['count(id)'] > 1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else if (is_string($identifier)) {
|
||||||
|
$stmt = 'SELECT count(id) FROM user WHERE username = ?';
|
||||||
|
$result = exec_stmt($stmt, 's', $identifier)->fetch_assoc();
|
||||||
|
|
||||||
|
if ($result['count(id)'] == 1)
|
||||||
|
return true;
|
||||||
|
else if ($result['count(id)'] > 1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function retrieveFromDB(int $userId): ?User
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT id, username, profilePicture, bio, website, role FROM user WHERE id = ?';
|
||||||
|
$user = exec_stmt($stmt, 'i', $userId)->fetch_assoc();
|
||||||
|
|
||||||
|
if ($user)
|
||||||
|
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSignupHTML()
|
||||||
|
{
|
||||||
|
$experimentalHTML = '
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
|
||||||
|
<input name="required_field" id="required_field" value="">
|
||||||
|
|
||||||
|
<input type="hidden" name="crop_x" value="" id="crop_x">
|
||||||
|
<input type="hidden" name="crop_y" value="" id="crop_y">
|
||||||
|
<input type="hidden" name="crop_width" value="" id="crop_width">
|
||||||
|
|
||||||
|
<label for="email">Email Address</label>
|
||||||
|
<input id="email" name="email" 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>
|
||||||
|
';
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="med_width std_border padding center modalContent">
|
||||||
|
<h3 class="underline">Sign Up</h3>
|
||||||
|
<div class="line"></div>
|
||||||
|
<form method="post" action="director.php">
|
||||||
|
<input type="hidden" name="formID" value="signup">
|
||||||
|
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input id="username" name="username" required autofocus>
|
||||||
|
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" name="password" type="password" required>
|
||||||
|
|
||||||
|
<label for="vPassword">Verify Password</label>
|
||||||
|
<input id="vPassword" name="vPassword" type="password" required>
|
||||||
|
|
||||||
|
<input class="button" type="submit" value="Sign Up">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="signupError"></div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLoginHTML()
|
||||||
|
{
|
||||||
|
$stayCheckedIn = '
|
||||||
|
<label class="form_checkbox_container">Stay Logged In?
|
||||||
|
<input name="stay_logged_in" type="checkbox" value="true">
|
||||||
|
<span class="checkmark"></span>
|
||||||
|
</label>
|
||||||
|
';
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="med_width std_border padding center modalContent">
|
||||||
|
<h3 class="underline">Log In</h3>
|
||||||
|
<div class="line"></div>
|
||||||
|
<form method="post" action="director.php">
|
||||||
|
<input type="hidden" name="formID" value="login">
|
||||||
|
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input id="username" name="username" spellcheck="false" required autofocus>
|
||||||
|
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" name="password" type="password" spellcheck="false" required>
|
||||||
|
|
||||||
|
<input class="button" type="submit" value="Log In">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="loginError"></div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function login(LoginCredentials $credentials): ?User
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT id, username, email, passwordHash, role, profilePicture, bio, website FROM user WHERE username = ?';
|
||||||
|
$user = exec_stmt($stmt, 's', $credentials->getUsername())->fetch_assoc();
|
||||||
|
|
||||||
|
if (!password_verify($credentials->getPassword(), $user['passwordHash']))
|
||||||
|
return null;
|
||||||
|
else {
|
||||||
|
$spaces = [];
|
||||||
|
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
|
||||||
|
$rawSpaces = exec_stmt($stmt, 'i', $user['id'])->fetch_assoc();
|
||||||
|
|
||||||
|
if ($rawSpaces)
|
||||||
|
foreach ($rawSpaces as $s)
|
||||||
|
$spaces[] = serialize(Space::retrieveFromDB($s));
|
||||||
|
|
||||||
|
return new User($user['id'], $spaces, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function logout(): void
|
||||||
|
{
|
||||||
|
foreach (array_keys($_SESSION) as $key) {
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
// Consider destroying the session cookie as well: session_destroy();
|
||||||
|
header('Location: .'); // Redirect to the homepage or login page
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHomeSpace()
|
||||||
|
{
|
||||||
|
// TODO: Need to implement. Requires fleshing out additional space types.
|
||||||
|
return Space::retrieveFromDB(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSpaces()
|
||||||
|
{
|
||||||
|
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
|
||||||
|
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
|
||||||
|
$spaces = [];
|
||||||
|
while ($r = $rawSpaces->fetch_assoc()) {
|
||||||
|
$space = Space::retrieveFromDB($r['space']);
|
||||||
|
if (!$space->getParentSpace())
|
||||||
|
$spaces[] = $space;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRole(): int
|
||||||
|
{
|
||||||
|
return $this->role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool
|
||||||
|
{
|
||||||
|
return $this->isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProfilePicture(): ?string
|
||||||
|
{
|
||||||
|
return $this->profilePicture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBio(): ?string
|
||||||
|
{
|
||||||
|
return $this->bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWebsite(): ?string
|
||||||
|
{
|
||||||
|
return $this->website;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsActive(bool $isActive): void
|
||||||
|
{
|
||||||
|
$this->isActive = $isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleActive(?mysqli $db): bool
|
||||||
|
{
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for toggleActive.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->isActive = !$this->isActive;
|
||||||
|
$stmt = $db->prepare('UPDATE user SET is_active = ? WHERE user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('ii', (int) $this->isActive, $this->id);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
return $result;
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateUsername(string $newUsername, ?mysqli $db): bool
|
||||||
|
{
|
||||||
|
$newUsername = htmlspecialchars($newUsername, ENT_QUOTES, 'UTF-8');
|
||||||
|
if (preg_match('/[\'";\-\_]/', $newUsername)) {
|
||||||
|
throw new InvalidArgumentException('Invalid characters in username.');
|
||||||
|
}
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for updateUsername.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('UPDATE user SET username = ? WHERE user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('si', $newUsername, $this->id);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
if ($result) {
|
||||||
|
$this->username = $newUsername;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePassword(string $newPassword, ?mysqli $db): bool
|
||||||
|
{
|
||||||
|
$newPasswordHash = hash('sha256', $newPassword);
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for updatePassword.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('UPDATE user SET password_hash = ? WHERE user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('si', $newPasswordHash, $this->id);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
return $result;
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateProfilePicture(?string $newProfilePicture, ?mysqli $db): bool
|
||||||
|
{
|
||||||
|
$newProfilePicture = htmlspecialchars($newProfilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
||||||
|
if (preg_match('/[\'";\-\_]/', $newProfilePicture)) {
|
||||||
|
throw new InvalidArgumentException('Invalid characters in profile picture filename.');
|
||||||
|
}
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for updateProfilePicture.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('UPDATE user SET profile_picture = ? WHERE user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('si', $newProfilePicture, $this->id);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
if ($result) {
|
||||||
|
$this->profilePicture = $newProfilePicture;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function followUser(int $followingUserId, bool $notify = false, ?mysqli $db): bool
|
||||||
|
{
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for followUser.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('INSERT INTO follows (follower_user_id, following_user_id, notify_user) VALUES (?, ?, ?)');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('iii', $this->id, $followingUserId, (int) $notify);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
return $result;
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFollowers(?mysqli $db): array
|
||||||
|
{
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for getFollowers.');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.follower_user_id = u.user_id WHERE f.following_user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('i', $this->id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$followers = [];
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$followers[] = new User(
|
||||||
|
(int) $row['user_id'],
|
||||||
|
$row['username'],
|
||||||
|
$row['profile_picture'],
|
||||||
|
$row['bio'],
|
||||||
|
$row['website']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
return $followers;
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFollowing(?mysqli $db): array
|
||||||
|
{
|
||||||
|
if (!$db) {
|
||||||
|
error_log('Database connection not provided for getFollowing.');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.following_user_id = u.user_id WHERE f.follower_user_id = ?');
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param('i', $this->id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$following = [];
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$following[] = new User(
|
||||||
|
(int) $row['user_id'],
|
||||||
|
$row['username'],
|
||||||
|
$row['profile_picture'],
|
||||||
|
$row['bio'],
|
||||||
|
$row['website']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
return $following;
|
||||||
|
} else {
|
||||||
|
error_log('Error preparing statement: ' . $db->error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Creator extends User {}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions/init.php';
|
||||||
|
require_once 'functions/core.php';
|
||||||
|
include_once 'components/core/head.php';
|
||||||
|
|
||||||
|
echo home_content();
|
||||||
|
|
||||||
|
include_once 'components/core/foot.php';
|
||||||
|
|
||||||
|
function home_content()
|
||||||
|
{
|
||||||
|
$content = '
|
||||||
|
<div class="article">
|
||||||
|
<h1>Vintage Coding</h1>
|
||||||
|
<h3>Intentional Craftsmanship Over Ephemeral Trends</h3>
|
||||||
|
<div class="line"></div>
|
||||||
|
<p>Vintage Coding, much like appreciating a well-crafted antique or a timeless piece of art, prioritizes quality, longevity, and deep understanding. It\'s about building software with a focus on robust architecture, maintainable code, and a thorough comprehension of the underlying principles. The analogy to vintage clothing is apt – these garments were often made with superior materials and construction, designed to last. Similarly, Vintage Coding aims to create software that withstands the test of time, is easier to understand and modify in the future, and is less prone to the rapid obsolescence that can plague hastily assembled codebases.</p>
|
||||||
|
<p>This site is built in collaboration with Google Gemini, and this highlights a key aspect of Vintage Coding: it\'s not about rejecting modern tools, but rather about employing them thoughtfully and strategically. Gemini, in this context, acts as a knowledgeable partner, offering insights and examples, but the ultimate direction and architectural decisions remain with the human developer. This partnership emphasizes augmented intelligence rather than complete reliance.</p>
|
||||||
|
<p>The following are the cornerstones of Vintage Coding:</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>AI is a partner for exploration and insight, with human oversight and critical evaluation remaining central.</li>
|
||||||
|
<li>Focus on core infrastructure built by humans, with AI used to identify omissions, ensuring a solid foundation.</li>
|
||||||
|
<li>AI tackles specific, well-defined issues, allowing for careful assessment of its output.</li>
|
||||||
|
<li>Emphasis on clean, concise, and readable code for long-term sustainability.</li>
|
||||||
|
</ol><br>
|
||||||
|
|
||||||
|
<p>Vibe Coding\'s potential for uncritical AI adoption and reactive development risks creating fragile, difficult-to-manage systems and hindering developer growth. However, it can serve a purpose in specific contexts where rapid prototyping, immediate experimentation, or disposable solutions are prioritized over long-term maintainability and deep understanding.</p>
|
||||||
|
<p>While seemingly opposing forces, like Yin and Yang, both Vibe Coding and Vintage Coding represent different points on the spectrum of software development. Having the wisdom and understanding to know when to prioritize rapid iteration and when to invest in deliberate craftsmanship is crucial for achieving a balanced and effective approach.</p>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
@@ -1,34 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'functions/init.php';
|
if (isset($_SESSION['user']))
|
||||||
require_once 'functions/functions.php';
|
header('Location: spaces.php?view=list');
|
||||||
include_once 'components/head.php';
|
else
|
||||||
|
header('Location: spaces.php?view=show&id=2025');
|
||||||
echo home_content();
|
|
||||||
|
|
||||||
include_once 'components/foot.php';
|
|
||||||
|
|
||||||
function home_content()
|
|
||||||
{
|
|
||||||
$content = '
|
|
||||||
<div class="article">
|
|
||||||
<h1>Vintage Coding</h1>
|
|
||||||
<h3>Intentional Craftsmanship Over Ephemeral Trends</h3>
|
|
||||||
<div class="line"></div>
|
|
||||||
<p>Vintage Coding, much like appreciating a well-crafted antique or a timeless piece of art, prioritizes quality, longevity, and deep understanding. It\'s about building software with a focus on robust architecture, maintainable code, and a thorough comprehension of the underlying principles. The analogy to vintage clothing is apt – these garments were often made with superior materials and construction, designed to last. Similarly, Vintage Coding aims to create software that withstands the test of time, is easier to understand and modify in the future, and is less prone to the rapid obsolescence that can plague hastily assembled codebases.</p>
|
|
||||||
<p>This site is built in collaboration with Google Gemini, and this highlights a key aspect of Vintage Coding: it\'s not about rejecting modern tools, but rather about employing them thoughtfully and strategically. Gemini, in this context, acts as a knowledgeable partner, offering insights and examples, but the ultimate direction and architectural decisions remain with the human developer. This partnership emphasizes augmented intelligence rather than complete reliance.</p>
|
|
||||||
<p>The following are the cornerstones of Vintage Coding:</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>AI is a partner for exploration and insight, with human oversight and critical evaluation remaining central.</li>
|
|
||||||
<li>Focus on core infrastructure built by humans, with AI used to identify omissions, ensuring a solid foundation.</li>
|
|
||||||
<li>AI tackles specific, well-defined issues, allowing for careful assessment of its output.</li>
|
|
||||||
<li>Emphasis on clean, concise, and readable code for long-term sustainability.</li>
|
|
||||||
</ol><br>
|
|
||||||
|
|
||||||
<p>Vibe Coding\'s potential for uncritical AI adoption and reactive development risks creating fragile, difficult-to-manage systems and hindering developer growth. However, it can serve a purpose in specific contexts where rapid prototyping, immediate experimentation, or disposable solutions are prioritized over long-term maintainability and deep understanding.</p>
|
|
||||||
<p>While seemingly opposing forces, like Yin and Yang, both Vibe Coding and Vintage Coding represent different points on the spectrum of software development. Having the wisdom and understanding to know when to prioritize rapid iteration and when to invest in deliberate craftsmanship is crucial for achieving a balanced and effective approach.</p>
|
|
||||||
</div>
|
|
||||||
';
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
|
|||||||
+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";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
const nav = document.querySelector("nav");
|
||||||
|
const nav_links = nav.querySelectorAll("ul li a");
|
||||||
|
const theme_toggle = nav.querySelector("button");
|
||||||
|
const back = document.querySelector("#backButton");
|
||||||
|
const links = document.querySelectorAll(".fillDiv");
|
||||||
|
|
||||||
|
if (back)
|
||||||
|
back.addEventListener("click", () => {
|
||||||
|
console.log("back");
|
||||||
|
history.back();
|
||||||
|
});
|
||||||
|
|
||||||
|
links.forEach((el) => {
|
||||||
|
el.addEventListener("click", () => {
|
||||||
|
el.preventDefault();
|
||||||
|
applyTransition(el.getAttribute("href"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
nav_links.forEach((el) => {
|
||||||
|
el.addEventListener("click", () => {
|
||||||
|
el.preventDefault();
|
||||||
|
applyTransition(el.getAttribute("href"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyTransition(href) {
|
||||||
|
if ("startViewTransition" in document) {
|
||||||
|
document.startViewTransition(() => {
|
||||||
|
window.location.href = href;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.location.href = href;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
for (let i = 0; i < todos.length; i++) {
|
for (let i = 0; i < todos.length; i++) {
|
||||||
var item = todos[i];
|
var item = todos[i];
|
||||||
|
|
||||||
var checkbox = item.querySelector('input[type="checkbox"]');
|
var checkbox = item.querySelector('input[type="checkbox"]');
|
||||||
checkbox.todo_id = item.querySelector('input[name="todo_id"]').value;
|
checkbox.todo_id = item.querySelector('input[name="todo_id"]').value;
|
||||||
checkbox.is_checked = checkbox.checked;
|
checkbox.is_checked = checkbox.checked;
|
||||||
+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";
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
USE vintagecoding;
|
|
||||||
|
|
||||||
INSERT INTO user (user_id, username, email, password_hash, role_id, profile_picture)
|
|
||||||
VALUES (5749248, 'jsmith', 'jsmith@email.com', 'f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b', 4, '2025.jpg'),
|
|
||||||
(5789203, 'dummy', 'dummy@iamdumb.com', 'f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b', 4, '2025.jpg'),
|
|
||||||
(5723948, 'writer', 'writer@something.com', 'f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b', 3, '5723948.jpg')
|
|
||||||
;
|
|
||||||
|
|
||||||
INSERT INTO article (author_id, title, excerpt, published_at)
|
|
||||||
VALUES (2025, 'Testing vintagecoding.net', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas a mauris sit amet dui pellentesque sodales. Pellentesque non viverra leo, et congue metus.', CURRENT_TIMESTAMP),
|
|
||||||
(5723948, 'Something Else', 'Here goes some random crap. Lorem ipsum akdfljskjlag IDK what to put here. I am just filling the space requirement. Blah blah blah blah, what does this look like? Is vintagecoding.net cool?', CURRENT_TIMESTAMP)
|
|
||||||
;
|
|
||||||
|
|
||||||
INSERT INTO article_tags (article_id, tag_id) VALUES (21, 2), (21, 6);
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
CREATE DATABASE vintagecoding;
|
|
||||||
USE vintagecoding;
|
|
||||||
|
|
||||||
CREATE TABLE roles (
|
|
||||||
role_id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
role VARCHAR(16) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE tags (
|
|
||||||
tag_id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
tag VARCHAR(16) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE user (
|
|
||||||
user_id INT PRIMARY KEY,
|
|
||||||
username VARCHAR(32) NOT NULL UNIQUE,
|
|
||||||
email VARCHAR(64) NOT NULL UNIQUE,
|
|
||||||
password_hash VARCHAR(256) NOT NULL,
|
|
||||||
role_id INT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
is_active BOOLEAN DEFAULT FALSE,
|
|
||||||
profile_picture VARCHAR(256),
|
|
||||||
bio VARCHAR(256),
|
|
||||||
x_username VARCHAR(32),
|
|
||||||
fb_username VARCHAR(32),
|
|
||||||
ig_username VARCHAR(32),
|
|
||||||
website VARCHAR(64),
|
|
||||||
FOREIGN KEY (role_id) REFERENCES roles (role_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE remember_user (
|
|
||||||
token VARCHAR(128) PRIMARY KEY,
|
|
||||||
user_id INT NOT NULL,
|
|
||||||
remote_addr VARCHAR(64) NOT NULL,
|
|
||||||
http_forward VARCHAR(64),
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES user (user_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE article (
|
|
||||||
article_id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
author_id INT NOT NULL,
|
|
||||||
title VARCHAR(26) NOT NULL,
|
|
||||||
slug VARCHAR(32),
|
|
||||||
read_count INT DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
published_at TIMESTAMP DEFAULT NULL,
|
|
||||||
published BOOLEAN DEFAULT TRUE,
|
|
||||||
excerpt VARCHAR(256),
|
|
||||||
FOREIGN KEY (author_id) REFERENCES user(user_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE article_tags (
|
|
||||||
article_id INT NOT NULL,
|
|
||||||
tag_id INT NOT NULL,
|
|
||||||
PRIMARY KEY (article_id, tag_id),
|
|
||||||
FOREIGN KEY (article_id) REFERENCES article(article_id),
|
|
||||||
FOREIGN KEY (tag_id) REFERENCES tags(tag_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE follows (
|
|
||||||
follower_user_id INT NOT NULL,
|
|
||||||
following_user_id INT NOT NULL,
|
|
||||||
notify_user BOOLEAN DEFAULT FALSE,
|
|
||||||
followed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (follower_user_id, following_user_id),
|
|
||||||
FOREIGN KEY (follower_user_id) REFERENCES user(user_id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (following_user_id) REFERENCES user(user_id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO roles (role)
|
|
||||||
VALUES ('owner'), ('admin'), ('contributor'), ('reader');
|
|
||||||
|
|
||||||
INSERT INTO tags (tag)
|
|
||||||
VALUES ('linux'), ('web-dev'), ('raspberry-pi'), ('self-hosting'), ('devlog'), ('testing');
|
|
||||||
|
|
||||||
INSERT INTO user (user_id, username, email, password_hash, role_id, profile_picture)
|
|
||||||
VALUES (2025, 'quaxlyqueen', 'me@joshashton.dev', '057ba03d6c44104863dc7361fe4578965d1887360f90a0895882e58a6248fc86', 1, '2025.jpg');
|
|
||||||
|
|
||||||
INSERT INTO article (author_id, title, excerpt, published_at)
|
|
||||||
VALUES (2025, 'Building vintagecoding.net', 'A devlog of the creation of vintagecoding.net and a test at the formatting of article cards and articles. Let the great experiment begin!', CURRENT_TIMESTAMP);
|
|
||||||
|
|
||||||
INSERT INTO article_tags (article_id, tag_id) VALUES (1, 2), (1, 4), (1, 5);
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
ARGS_COUNT=$#
|
|
||||||
help () {
|
|
||||||
echo "CLI Tool used to initialize the database for vintagecoding.net."
|
|
||||||
echo "With no arguments, it creates the application user in MariaDB,"
|
|
||||||
echo "and creates the following tables:"
|
|
||||||
echo "- roles (populated);"
|
|
||||||
echo "- tags (populated);"
|
|
||||||
echo "- user;"
|
|
||||||
echo "- articles;"
|
|
||||||
echo "- article_tags;"
|
|
||||||
echo ""
|
|
||||||
echo "The following flags are accepted:"
|
|
||||||
echo " init_db.sh { --help | -h }"
|
|
||||||
echo " init_db.sh { --delete | -d } { --verbose | -v }"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ $ARGS_COUNT > 0 ]]; then
|
|
||||||
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
|
||||||
help
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Delete the existing database and start over.
|
|
||||||
if [[ "$1" == "--delete" || "$1" == "-d" ]]; then
|
|
||||||
if [[ "$2" == "--verbose" || "$2" == "-v" ]]; then
|
|
||||||
echo "Deleting the existing database vintagecoding..."
|
|
||||||
fi
|
|
||||||
echo "vintage user pw"
|
|
||||||
mariadb -u vintage -p -e "DROP DATABASE vintagecoding;"
|
|
||||||
|
|
||||||
if [[ "$2" == "--verbose" || "$2" == "-v" ]]; then
|
|
||||||
echo "Deleting the vintage user..."
|
|
||||||
fi
|
|
||||||
echo "root user pw"
|
|
||||||
mariadb -u root -p -e "DROP USER vintage@localhost;"
|
|
||||||
|
|
||||||
if [[ "$2" == "--verbose" || "$2" == "-v" ]]; then
|
|
||||||
echo "Creating the vintage user..."
|
|
||||||
fi
|
|
||||||
echo "root user pw"
|
|
||||||
mariadb -u root -p < setup.sql;
|
|
||||||
|
|
||||||
if [[ "$2" == "--verbose" || "$2" == "-v" ]]; then
|
|
||||||
echo "Creating (and populating enum/static data) tables..."
|
|
||||||
fi
|
|
||||||
echo "vintage user pw"
|
|
||||||
mariadb -u vintage -p < init.sql;
|
|
||||||
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
#mariadb -u root -p < setup.sql;
|
|
||||||
mariadb -u vintage -p < init.sql;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
--This must be ran as the root user in MariaDB
|
|
||||||
CREATE USER vintage@localhost IDENTIFIED BY 'changeme';
|
|
||||||
GRANT ALL PRIVILEGES ON vintagecoding.* TO vintage@localhost IDENTIFIED BY 'changeme';
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions/init.php';
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
/* foreach (glob('components/spaces/*.php') as $file) */
|
||||||
|
/* require_once $file; */
|
||||||
|
|
||||||
|
require_once 'components/core/head.php';
|
||||||
|
|
||||||
|
if (isset($_GET['view'])) {
|
||||||
|
switch ($_GET['view']) {
|
||||||
|
case 'essay':
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
// TODO: Need another check here to verify the user is in the Essay's space.
|
||||||
|
$essay = Post::retrieveFromDB($_GET['id']);
|
||||||
|
if ($essay)
|
||||||
|
echo displayEssay($essay);
|
||||||
|
else
|
||||||
|
echo errorScreen('Essay does not exist!', 'The Essay ID provided does not exist in the database.');
|
||||||
|
} else
|
||||||
|
echo displaySpace(2025);
|
||||||
|
break;
|
||||||
|
case 'show':
|
||||||
|
// TODO: After implementing the notification modal, notify the user on success/fail auth events.
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$space = Space::retrieveFromDB($_GET['id']);
|
||||||
|
if ($space) {
|
||||||
|
if (isset($_SESSION['user']))
|
||||||
|
if ($space->allowUser(unserialize($_SESSION['user'])))
|
||||||
|
echo displaySpace($_GET['id']);
|
||||||
|
else if ($space->getVisibility() == 1)
|
||||||
|
echo displaySpace($_GET['id']);
|
||||||
|
else
|
||||||
|
echo 'TODO: Notification Modal -> User not a member';
|
||||||
|
else {
|
||||||
|
if ($space->getVisibility() == 1)
|
||||||
|
echo displaySpace($_GET['id']);
|
||||||
|
else
|
||||||
|
echo 'TODO: Notification Modal -> Space is not Open';
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
echo errorScreen('Space does not exist!', 'The Space ID provided does not exist in the database.');
|
||||||
|
} else
|
||||||
|
echo displaySpaceList();
|
||||||
|
break;
|
||||||
|
case 'list':
|
||||||
|
if (isset($_SESSION['user']))
|
||||||
|
echo displaySpaceList();
|
||||||
|
else
|
||||||
|
echo displaySpace(2025);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo displaySpaceList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo displaySpace(2025);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SESSION['user']))
|
||||||
|
echo actionBar();
|
||||||
|
|
||||||
|
if (isset($_GET['id']))
|
||||||
|
echo backButton();
|
||||||
|
|
||||||
|
require_once 'components/core/foot.php';
|
||||||
|
|
||||||
|
function backButton()
|
||||||
|
{
|
||||||
|
$html = '
|
||||||
|
<div class="link" id="backButton">
|
||||||
|
<i class="nf nf-fa-arrow_left"></i>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionBar()
|
||||||
|
{
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="std_border med_width padding glass" id="actionBar">
|
||||||
|
<div class="row space_between" id="primaryActions">
|
||||||
|
<div class="column center link med_width" id="actionSpace"><i class="nf nf-md-layers_outline"></i><p class="link">Space</p></div>
|
||||||
|
<div class="column center link med_width" id="actionPost"><i class="nf nf-md-text"></i><p class="link">Post</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="row space_between" id="postActions" style="display: none;">
|
||||||
|
<div class="column center link med_width" id="postBack"><i class="nf nf-cod-arrow_circle_left"></i><p class="link">Back</p></div>
|
||||||
|
<div class="column center link med_width" id="postNew"><i class="nf nf-md-chat_plus"></i><p class="link">New</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="row space_between" id="spaceActions" style="display: none;">
|
||||||
|
<div class="column center link med_width" id="spaceBack"><i class="nf nf-cod-arrow_circle_left"></i><p class="link">Back</p></div>
|
||||||
|
<div class="column center link med_width" id="spaceHome" href="spaces.php?view=show&id=' . $user->getId() . '"><i class="nf nf-fa-home"></i><p class="link">Home</p></div>
|
||||||
|
<div class="column center link med_width" id="spaceNew"><i class="nf nf-md-layers_plus"></i><p class="link">New</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorScreen(String $heading, String $msg)
|
||||||
|
{
|
||||||
|
$html = '
|
||||||
|
<div class="column std_border med_width padding center top_margin">
|
||||||
|
<h1>' . $heading . '</h1>
|
||||||
|
<p>' . $msg . '</p>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayEssay(Essay $e)
|
||||||
|
{
|
||||||
|
/* if (isset($_SESSION['user'])) */
|
||||||
|
/* if ($e->allowUser(unserialize($_SESSION['user'])) || $e->getSpace()->getVisibility() != 1) */
|
||||||
|
/* return errorScreen('Access Denied!', 'You do not have access to the Space that this Essay is in.'); */
|
||||||
|
|
||||||
|
$parsedown = new Parsedown();
|
||||||
|
$parsedown->setSafeMode(true);
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
|
||||||
|
' . $parsedown->parse($e->getMarkdown()) . '
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displaySpaceList()
|
||||||
|
{
|
||||||
|
$user = unserialize($_SESSION['user']);
|
||||||
|
|
||||||
|
$html = '
|
||||||
|
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
|
||||||
|
<div class="column space_between full_width">
|
||||||
|
<h1>Spaces</h4>
|
||||||
|
</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
';
|
||||||
|
|
||||||
|
$spaces = $user->getSpaces();
|
||||||
|
foreach ($spaces as $s)
|
||||||
|
$html .= $s->getCardHTML();
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displaySpace($id = 2025)
|
||||||
|
{
|
||||||
|
$space = Space::retrieveFromDB($id);
|
||||||
|
if ($space) {
|
||||||
|
$html = '
|
||||||
|
<div class="std_width std_border padding center top_margin" style="height: 77vh; overflow-y: auto;">
|
||||||
|
<div class="column space_between full_width">
|
||||||
|
<h1>' . $space->getName() . '</h4>
|
||||||
|
<p>' . $space->getDescription() . '</p>
|
||||||
|
</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
';
|
||||||
|
|
||||||
|
$subspaces = $space->getSubSpaces();
|
||||||
|
foreach ($subspaces as $ss) {
|
||||||
|
$html .= $ss->getCardHTML();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Need to investigate this bug in Posts
|
||||||
|
$posts = $space->getPosts();
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
foreach ($post as $p) {
|
||||||
|
$html .= $p->getCardHTML();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'functions/init.php';
|
|
||||||
require_once 'functions/functions.php';
|
|
||||||
require_once 'functions/todo_functions.php';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
foreach (glob('components/todo/*.php') as $file)
|
|
||||||
require_once $file;
|
|
||||||
|
|
||||||
include_once 'components/head.php';
|
|
||||||
|
|
||||||
if (!isset($_SESSION['role_id'])) {
|
|
||||||
$redirect = 'todo.php?view=display';
|
|
||||||
header('Location: user.php?view=login&redirect=' . urlencode($redirect));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['view']))
|
|
||||||
$view = $_GET['view'];
|
|
||||||
else
|
|
||||||
$view = 'display';
|
|
||||||
|
|
||||||
switch ($view) {
|
|
||||||
case 'display':
|
|
||||||
$todos = exec_stmt('SELECT * FROM todos WHERE user_id = ? ORDER BY updated_at DESC', 'i', $_SESSION['user_id']);
|
|
||||||
echo todo_display($todos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
include_once 'components/foot.php';
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'functions/init.php';
|
|
||||||
require_once 'functions/functions.php';
|
|
||||||
include_once 'functions/account_functions.php';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
foreach (glob('components/user/*.php') as $file)
|
|
||||||
require_once $file;
|
|
||||||
|
|
||||||
$msg = '';
|
|
||||||
|
|
||||||
if (isset($_POST['form_id'])) {
|
|
||||||
switch ($_POST['form_id']) {
|
|
||||||
case 'signup_form':
|
|
||||||
$msg = create_user(
|
|
||||||
$_POST['username'],
|
|
||||||
$_POST['email'],
|
|
||||||
$_POST['password'],
|
|
||||||
$_POST['verify_password'],
|
|
||||||
4, // TODO: Need to allow for other roles to be created, if the submission is from an administrator or owner.
|
|
||||||
(!empty($_FILES) ? $_FILES : ''),
|
|
||||||
(!empty($_POST['crop_x']) ? $_POST['crop_x'] : ''),
|
|
||||||
(!empty($_POST['crop_y']) ? $_POST['crop_y'] : ''),
|
|
||||||
(!empty($_POST['crop_width']) ? $_POST['crop_width'] : ''),
|
|
||||||
$_POST['required_field']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($msg === true) {
|
|
||||||
login($_POST['username'], $_POST['password'], false);
|
|
||||||
header('Location: user.php?action=view&user_id=' . $_SESSION['user_id']);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'login_form':
|
|
||||||
$msg = login(
|
|
||||||
$_POST['username'],
|
|
||||||
$_POST['password'],
|
|
||||||
(isset($_POST['stay_logged_in']) ? true : false)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($msg === true)
|
|
||||||
if (isset($_GET['redirect']))
|
|
||||||
header('Location: ' . urldecode($_GET['redirect']));
|
|
||||||
else
|
|
||||||
header('Location: .');
|
|
||||||
break;
|
|
||||||
case 'reset_pw_form':
|
|
||||||
$msg = reset_password(
|
|
||||||
($_SESSION['role_id'] <= admin && $_SESSION['user_id'] != $_POST['user_id'] ? $_POST['user_id'] : $_SESSION['user_id']),
|
|
||||||
$_POST['new_pw'],
|
|
||||||
$_POST['verify_new_pw']
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'update_email_form':
|
|
||||||
$msg = update_email(
|
|
||||||
($_SESSION['role_id'] <= admin && $_SESSION['user_id'] != $_POST['user_id'] ? $_POST['user_id'] : $_SESSION['user_id']),
|
|
||||||
$_POST['new_email'],
|
|
||||||
$_POST['verify_new_email']
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'update_username_form':
|
|
||||||
$msg = update_username(
|
|
||||||
($_SESSION['role_id'] <= admin && $_SESSION['user_id'] != $_POST['user_id'] ? $_POST['user_id'] : $_SESSION['user_id']),
|
|
||||||
$_POST['new_username'],
|
|
||||||
$_POST['verify_new_username']
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'update_profile_picture_form':
|
|
||||||
update_profile_picture($_SESSION['user_id']);
|
|
||||||
break;
|
|
||||||
case 'request_role_change_form':
|
|
||||||
request_role_change(
|
|
||||||
$_SESSION['user_id'],
|
|
||||||
$_POST['requested_role']
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'delete_account_form':
|
|
||||||
$msg = delete_account(
|
|
||||||
($_SESSION['role_id'] <= admin && $_SESSION['user_id'] != $_POST['user_id'] ? $_POST['user_id'] : $_SESSION['user_id']),
|
|
||||||
$_POST['delete_password'],
|
|
||||||
$_POST['delete_verify_password']
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'follow':
|
|
||||||
follow_user($_SESSION['user_id'], $_POST['author_id'], ($_POST['follow'] == 'email_notify' ? 1 : 0));
|
|
||||||
break;
|
|
||||||
case 'unfollow':
|
|
||||||
unfollow_user($_SESSION['user_id'], $_POST['author_id']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
echo $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
include_once 'components/head.php';
|
|
||||||
|
|
||||||
switch ($_GET['view']) {
|
|
||||||
case 'login':
|
|
||||||
echo login_form(($msg ? $msg : ''));
|
|
||||||
break;
|
|
||||||
case 'signup':
|
|
||||||
echo signup_form(($msg ? $msg : ''));
|
|
||||||
break;
|
|
||||||
case 'logout':
|
|
||||||
logout();
|
|
||||||
break;
|
|
||||||
case 'display':
|
|
||||||
echo (isset($_GET['user_id']) ? user_view($_GET['user_id']) : header('Location: .'));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
include_once 'components/foot.php';
|
|
||||||
Reference in New Issue
Block a user