slight reorganization

This commit is contained in:
Conner Harkness 2025-07-11 09:44:51 -06:00
parent e29d9c6577
commit 544d08cb00
12 changed files with 484 additions and 166 deletions

View File

@ -1,4 +1,4 @@
CREATE table if not exists settings ( CREATE table if not exists settings (
id integer primary key auto_increment, id integer primary key auto_increment,
setting text not null, setting text not null,
value text not null) value text null)

View File

@ -1,4 +1,4 @@
CREATE table if not exists settings ( CREATE table if not exists settings (
id integer primary key autoincrement, id integer primary key autoincrement,
setting text not null, setting text not null,
value text not null) value text null)

View File

@ -11,10 +11,30 @@
} }
?> ?>
<?php if (count($varFooterLinks) > 0): ?> <div class="mb-5">
<hr /> <hr />
<div class="container mb-5"> <?php
$strSidebarContent = Settings::get("footer_content", "Copyright ©", true);
?>
<?php if (strlen($strSidebarContent) > 0): ?>
<div class="container">
<div class="row">
<div class="col-lg-8">
<div>
<?php
$varParsedown = new Parsedown();
echo $varParsedown->text($strSidebarContent);
?>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php if (count($varFooterLinks) > 0): ?>
<div class="container">
<div class="row"> <div class="row">
<div class="col-lg-4"> <div class="col-lg-4">
<?php foreach ($varFooterLinks as $varLink): ?> <?php foreach ($varFooterLinks as $varLink): ?>
@ -26,6 +46,7 @@
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div>
<script> <script>
$(function() { $(function() {

View File

@ -13,10 +13,25 @@
<div class="offcanvas offcanvas-start" id="sidebar"> <div class="offcanvas offcanvas-start" id="sidebar">
<div class="offcanvas-body"> <div class="offcanvas-body">
<?php
$strSidebarContent = Settings::get("sidebar_content", "#### Sidebar Navigation", true);
?>
<?php if (strlen($strSidebarContent) > 0): ?>
<div class="mt-5">
<?php
$varParsedown = new Parsedown();
echo $varParsedown->text($strSidebarContent);
?>
</div>
<?php endif; ?>
<?php foreach ($varSidebarLinks as $varLink): ?> <?php foreach ($varSidebarLinks as $varLink): ?>
<?php if (!UserAuth::visible($varLink["visibility"])) continue; ?> <?php if (!UserAuth::visible($varLink["visibility"])) continue; ?>
<a class="btn btn-outline-secondary d-block w-100 mb-2" href="<?= $varLink["url"]; ?>"><i class="fa fa-fw fa-<?= $varLink["icon"]; ?> pe-2"></i> <?= $varLink["label"]; ?></a> <div class="mb-2">
<a class="btn btn-outline-secondary w-100" href="<?= $varLink["url"]; ?>"><i class="fa fa-fw fa-<?= $varLink["icon"]; ?> pe-2"></i> <?= $varLink["label"]; ?></a>
</div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
</div> </div>

158
init.php
View File

@ -35,163 +35,13 @@
('Edit Links', '/edit/links', 'link', 'sidebar', 'admin'), ('Edit Links', '/edit/links', 'link', 'sidebar', 'admin'),
('Edit CSS', '/settings/css', 'code', 'sidebar', 'admin'), ('Edit CSS', '/settings/css', 'code', 'sidebar', 'admin'),
('Edit JS', '/settings/js', 'code', 'sidebar', 'admin'), ('Edit JS', '/settings/js', 'code', 'sidebar', 'admin'),
('Edit sidebar content', '/settings/sidebar_content', 'comment', 'sidebar', 'admin'),
('Edit footer content', '/settings/footer_content', 'comment', 'sidebar', 'admin'),
('Copyright © 2025', '/', 'building', 'footer', '')" ('Go home', '/', 'home', 'footer', ''),
('Search', '/search', 'search', 'footer', '')"
); );
} }
} }
class Settings
{
public static function get($strSettingName, $strDefault="", $intSave=0)
{
global $c;
$varExisting = $c->query("
SELECT *
from settings
where
setting like ?
order by
id desc",
$strSettingName);
if (count($varExisting) > 0)
return $varExisting[0]["value"];
if ($intSave)
Settings::set($strSettingName, $strDefault);
return $strDefault;
}
public static function set($strSettingName, $strValue)
{
global $c;
$varExisting = $c->query("
SELECT *
from settings
where
setting like ?
order by
id desc",
$strSettingName);
if (count($varExisting) !== 1)
{
$c->query("DELETE from settings where setting like ?", $strSettingName);
$c->query("INSERT into settings (setting, value) values (?, ?)", $strSettingName, $strValue);
}
$c->query(
"UPDATE settings
set
value = ?
where setting like ?",
$strValue,
$strSettingName);
}
}
class UserAuth
{
public static function getUser()
{
global $c;
try
{
$strToken = Cookie::get("token");
if ($strToken !== null)
if (strlen($strToken) > 0)
{
$varSessions = $c->query(
"SELECT *
from sessions as s
join users as u on u.username = s.username
where
s.token = ?
and (
s.expires is null
or s.expires > current_timestamp
)",
$strToken);
if (count($varSessions) == 1)
return $varSessions[0];
}
}
catch (Exception $x) {}
return null;
}
public static function has($strColumnName)
{
global $c;
$varUser = UserAuth::getUser();
if ($varUser == null)
return false;
if (array_key_exists($strColumnName, $varUser))
if (intval($varUser[$strColumnName]) > 0)
return true;
return false;
}
public static function require($strColumnName)
{
if (!UserAuth::has($strColumnName))
{
BootstrapRender::message("You do not have permission to do that, please sign into an account that does.", "warning");
Respond::redirect("/user/signin");
}
}
public static function visible($strVisibility)
{
global $c;
if (UserAuth::has("is_admin"))
return true;
$varUser = UserAuth::getUser();
$strUsername = $varUser["username"] ?? null;
$varRegex = [
["/user/i", ($varUser == null)],
["/admin/i", (!UserAuth::has("is_admin"))],
];
// Support arrays with username and visibility keys:
if (is_array($strVisibility))
{
if (array_key_exists("username", $strVisibility))
if ($strVisibility["username"] == $strUsername)
return true;
if (!array_key_exists("visibility", $strVisibility))
return false;
$strVisibility = $strVisibility["visibility"];
}
if (preg_match("/{$strUsername}/i", $strVisibility)) return true;
if (preg_match("/(every|any|all)/i", $strVisibility)) return true;
$intExit = 0;
foreach ($varRegex as $re)
if (preg_match($re[0], $strVisibility))
if ($re[1])
$intExit = 1;
if ($intExit == 1)
return false;
return true;
}
}
?> ?>

62
lib/Settings.php Normal file
View File

@ -0,0 +1,62 @@
<?php
class Settings
{
private static $varValues = null;
public static function get($strSettingName=null, $strDefault="", $intSave=0)
{
global $c;
if (Settings::$varValues == null)
{
$varRows = $c->query("SELECT * from settings order by setting");
$varValues = [];
foreach ($varRows as $r)
$varValues[$r["setting"]] = $r["value"];
Settings::$varValues = $varValues;
}
if ($strSettingName == null)
return Settings::$varValues;
if (array_key_exists($strSettingName, Settings::$varValues))
return Settings::$varValues[$strSettingName];
if ($intSave)
Settings::set($strSettingName, $strDefault);
return $strDefault;
}
public static function set($strSettingName, $strValue)
{
Settings::$varValues = null;
global $c;
$varExisting = $c->query("
SELECT *
from settings
where
setting like ?
order by
id desc",
$strSettingName);
if (count($varExisting) !== 1)
{
$c->query("DELETE from settings where setting like ?", $strSettingName);
$c->query("INSERT into settings (setting, value) values (?, ?)", $strSettingName, $strValue);
}
$c->query(
"UPDATE settings
set
value = ?
where setting like ?",
$strValue,
$strSettingName);
}
}
?>

107
lib/UserAuth.php Normal file
View File

@ -0,0 +1,107 @@
<?php
class UserAuth
{
public static function getUser()
{
global $c;
try
{
$strToken = Cookie::get("token");
if ($strToken !== null)
if (strlen($strToken) > 0)
{
$varSessions = $c->query(
"SELECT *
from sessions as s
join users as u on u.username = s.username
where
s.token = ?
and (
s.expires is null
or s.expires > current_timestamp
)",
$strToken);
if (count($varSessions) == 1)
return $varSessions[0];
}
}
catch (Exception $x) {}
return null;
}
public static function has($strColumnName)
{
global $c;
$varUser = UserAuth::getUser();
if ($varUser == null)
return false;
if (array_key_exists($strColumnName, $varUser))
if (intval($varUser[$strColumnName]) > 0)
return true;
return false;
}
public static function require($strColumnName)
{
if (!UserAuth::has($strColumnName))
{
BootstrapRender::message("You do not have permission to do that, please sign into an account that does.", "warning");
Respond::redirect("/user/signin");
}
}
public static function visible($strVisibility)
{
global $c;
if (UserAuth::has("is_admin"))
return true;
$varUser = UserAuth::getUser();
$strUsername = $varUser["username"] ?? null;
$varRegex = [
["/user/i", ($varUser == null)],
["/admin/i", (!UserAuth::has("is_admin"))],
];
// Support arrays with username and visibility keys:
if (is_array($strVisibility))
{
if (array_key_exists("username", $strVisibility))
if ($strVisibility["username"] == $strUsername)
return true;
if (!array_key_exists("visibility", $strVisibility))
return false;
$strVisibility = $strVisibility["visibility"];
}
// Handle hiding the post from non-admins:
if (preg_match("/^(admin|hid(e|den)|invisible|no(ne|body)|private)$/i", $strVisibility))
return false;
if (preg_match("/{$strUsername}/i", $strVisibility)) return true;
// Handle showing the post to everyone:
if (preg_match("/^(|(every|any)(body|one))|all|public)$/i", $strVisibility))
return true;
$intExit = 0;
foreach ($varRegex as $re)
if (preg_match($re[0], $strVisibility))
if ($re[1])
$intExit = 1;
if ($intExit == 1)
return false;
return true;
}
}
?>

46
pages/directory.php Normal file
View File

@ -0,0 +1,46 @@
<?php
global $c;
$varPostLocations = $c->query("SELECT distinct location from posts order by location");
$varLinks = $c->query("SELECT * from links order by sort");
?>
<div class="navbar navbar-expand bg-body-tertiary d-flex px-3 sticky-top">
<div class="container justify-content-between">
<div class="navbar-nav d-inline-flex align-items-center">
<span class="navbar-brand">Directory</span>
</div>
<div class="navbar-nav d-inline-flex">
</div>
</div>
</div>
<div class="container my-5">
<div class="row mb-4">
<div class="col-lg-6">
<h5 class="mb-3"><i class="fa fa-fw fa-comment pe-2"></i> Posts</h5>
<?php foreach ($varPostLocations as $i): ?>
<?php
$intPostCount = $c->query("SELECT count(*) as c from posts where location = ?", $i["location"])[0]["c"];
?>
<div class="border p-2 mb-2">
<a class="link-underline link-underline-opacity-0" href="<?= $i["location"]; ?>"><i class="fa fa-fw fa-file pe-2"></i> <?= $i["location"]; ?></a>
<small class="text-muted">&mdash; <?= $intPostCount !== 1? "{$intPostCount} posts": "{$intPostCount} post"; ?></small>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-6">
<h5 class="mb-3"><i class="fa fa-fw fa-link pe-2"></i> Links</h5>
<?php foreach ($varLinks as $i): ?>
<?php if (!UserAuth::visible($i["visibility"])) continue; ?>
<div class="border p-2 mb-2">
<a class="link-underline link-underline-opacity-0" href="<?= $i["url"]; ?>"><i class="fa fa-fw fa-<?= $i["icon"]; ?> pe-2"></i> <?= $i["label"]; ?></a>
<small class="text-muted">&mdash; <?= $i["position"]; ?></small>
</div>
<?php endforeach; ?>
</div>
</div>
</div>

163
pages/edit/settings.php Normal file
View File

@ -0,0 +1,163 @@
<?php
global $c;
UserAuth::require("is_admin");
$varUser = UserAuth::getUser();
$varRows = Settings::get();
$strInput = file_get_contents("php://input");
if (strlen($strInput) > 0)
{
$a = json_decode($strInput, true);
$output = [];
foreach ($a as $r)
{
$strSetting = $r["setting"];
$strValue = $r["value"];
Settings::set($strSetting, $strValue);
}
Respond::json(["message" => "success", "output" => $output]);
}
?>
<div class="navbar navbar-expand bg-body-tertiary d-flex px-3 sticky-top">
<div class="container justify-content-between">
<div class="navbar-nav d-inline-flex">
<span class="navbar-brand">Options</span>
<a class="btn btn-outline-success" onclick="fnSave();"><i class="fa fa-fw fa-save"></i> Save</a>
</div>
<div class="navbar-nav d-inline-flex">
</div>
</div>
</div>
<style>
/* https://github.com/twbs/bootstrap/issues/37184 */
.dropdown-menu {
z-index: 1040 !important;
}
/*
th:first-child,
th:last-child {
width: 7.5%;
background: #F00 !important;
}*/
.w-1 { width: 1%; }
.table-responsive {
overflow-x: scroll;
}
.w-input {
width: 15em !important;
}
tr td:first-child input[type="text"]
{
width: 5em !important;
}
</style>
<div class="container">
<div class="row my-5">
<div class="col-lg-12">
<?php if (count($varRows) > 0): ?>
<div class="table-responsive">
<table class="table table-borderless">
<thead>
<tr>
<th>Setting</th>
<th>Value<th>
</tr>
</thead>
<tbody>
<?php foreach ($varRows as $k => $v): ?>
<tr>
<td>
<div class="input-group">
<input type="text" class="form-control w-input" name="setting" value="<?= $k; ?>" />
</div>
</td>
<td>
<?php
$strClass = "";
if (preg_match("/\n/", $v))
$strClass = "disabled readonly";
?>
<div class="input-group">
<input type="text" class="form-control w-input <?= $strClass; ?>" name="value" value="<?= $v; ?>" <?= $strClass; ?> />
</div>
<div>
<small>
<a class="link-underline link-underline-opacity-0" href="/edit/setting/<?= $k; ?>">Edit in multi-line editor</a>
</small>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script>
$(function() {
$("[name='setting']").each(function(i, x) {
x = $(x);
x.attr("readonly", 1);
});
fnSerialize = function() {
var a = [];
$("table tbody tr").each(function(i, x) {
x = $(x);
var valueInput = x.find("[name='value']").first();
var o = {};
o["setting"] = x.find("[name='setting']").first().val().trim();
o["value"] = valueInput.val();
if (valueInput.attr("disabled"))
return;
a.push(o);
});
console.log(a);
return a;
};
fnSave = function()
{
var data = fnSerialize();
$.ajax({
url: "",
method: "post",
data: JSON.stringify(data),
success: function(r)
{
console.log(r);
window.location.href = window.location.href;
}
});
};
});
</script>

54
pages/search.php Normal file
View File

@ -0,0 +1,54 @@
<?php
global $c;
$strPath = "/";
$strPath .= implode("/", Request::getPathParts());
$varPosts = [];
$strQuery = Request::getParam("q");
if ($strQuery !== null && strlen($strQuery) > 0)
{
$varPosts = $c->query(
"SELECT *
from posts as p
where
content like concat('%', ?, '%')
order by
created desc",
$strQuery);
$i = 0;
for ($i = 0; $i < count($varPosts); $i++)
{
$varOld = $varPosts[$i];
$varOld["content"] = preg_replace("/({$strQuery})/i", "<mark>$1</mark>", $varOld["content"]);
$varPosts[$i] = $varOld;
}
}
?>
<form method="get">
<div class="navbar navbar-expand bg-body-tertiary d-flex px-3 sticky-top">
<div class="container justify-content-between">
<div class="navbar-nav d-inline-flex align-items-center">
<span class="navbar-brand">Search</span>
<input class="form-control me-2" type="text" name="q" value="<?= $strQuery; ?>" />
<button type="submit" class="btn btn-outline-info text-nowrap"><i class="fa fa-fw fa-search"></i> Go</a>
</div>
<div class="navbar-nav d-inline-flex"></div>
</div>
</div>
</form>
<div class="container">
<div class="row">
<div class="col-lg-4">
<?php BootstrapRender::message(); ?>
</div>
</div>
</div>
<?php PostRender::rows($varPosts); ?>