- [index.php](/Users/tyemeclifford/Documents/GH/MyKeyser/index.php): events now appear before featured businesses, plus forum pretty-route fallback.
[events.php](/Users/tyemeclifford/Documents/GH/MyKeyser/events.php) and [admin/events.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/events.php): event image uploads. [menu.php](/Users/tyemeclifford/Documents/GH/MyKeyser/menu.php), [admin/menus.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/menus.php), and [business.php](/Users/tyemeclifford/Documents/GH/MyKeyser/business.php): one configurable menu item image slot. [account.php](/Users/tyemeclifford/Documents/GH/MyKeyser/account.php): circular avatar upload and crop controls. [forum.php](/Users/tyemeclifford/Documents/GH/MyKeyser/forum.php), [admin/forum.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/forum.php), [.htaccess](/Users/tyemeclifford/Documents/GH/MyKeyser/.htaccess), and [forum/index.php](/Users/tyemeclifford/Documents/GH/MyKeyser/forum/index.php): forum, categories, topic/reply uploads, permalinks, toggle/moderation. [includes/db.php](/Users/tyemeclifford/Documents/GH/MyKeyser/includes/db.php) and [includes/functions.php](/Users/tyemeclifford/Documents/GH/MyKeyser/includes/functions.php): schema upgrades and shared upload/image helpers. [assets/css/style.css](/Users/tyemeclifford/Documents/GH/MyKeyser/assets/css/style.css): neon styling for forum, images, avatars, attachments.
This commit is contained in:
@@ -25,6 +25,7 @@ $_pendingClaims = (int)qval("SELECT COUNT(*) FROM business_claim_requests WHERE
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
<div class="admin-side-title" style="margin-top:1rem">COMMUNITY</div>
|
||||
<a class="asl" href="/admin/forum.php">💬 Forum</a>
|
||||
<a class="asl" href="/admin/events.php">📅 Events</a>
|
||||
<a class="asl" href="/admin/attractions.php">🗺️ Attractions</a>
|
||||
<a class="asl" href="/admin/reports.php">🚩 Reports</a>
|
||||
|
||||
+40
-7
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__).'/includes/boot.php';
|
||||
requireAdmin();
|
||||
$adminTitle = 'Events';
|
||||
include __DIR__.'/admin_header.php';
|
||||
|
||||
if (isPost()) {
|
||||
csrfCheck();
|
||||
@@ -12,18 +13,36 @@ if (isPost()) {
|
||||
if ($act === 'feature') { qrun("UPDATE events SET is_featured=1-is_featured WHERE id=?",[(int)p('id')]); go('/admin/events.php'); }
|
||||
|
||||
if ($act === 'add' || $act === 'edit') {
|
||||
$id = (int)p('id');
|
||||
$existing = $act === 'edit' ? qone("SELECT image_path FROM events WHERE id=?", [$id]) : null;
|
||||
$imagePath = (string)($existing['image_path'] ?? '');
|
||||
if (p('remove_image','0') === '1') $imagePath = '';
|
||||
$upload = storeSingleUpload(
|
||||
'event_image',
|
||||
'events',
|
||||
imageExts(),
|
||||
mbToBytes(setting('event_upload_max_mb','5'), 5),
|
||||
[
|
||||
'image_only' => true,
|
||||
'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb','1'), 1),
|
||||
'image_max_width' => (int)setting('forum_image_max_width','1600'),
|
||||
'image_quality' => (int)setting('forum_image_quality','82'),
|
||||
]
|
||||
);
|
||||
if (!$upload['ok']) { flash($upload['error'], 'error'); go('/admin/events.php'.($id ? '?edit='.$id : '')); }
|
||||
if (!empty($upload['path'])) $imagePath = (string)$upload['path'];
|
||||
|
||||
$f=['title'=>ps('title'),'description'=>ps('description'),'venue'=>ps('venue'),'address'=>ps('address'),
|
||||
'event_date'=>ps('event_date'),'event_time'=>ps('event_time'),'end_date'=>ps('end_date')?:null,
|
||||
'end_time'=>ps('end_time'),'cost'=>ps('cost'),'website'=>ps('website'),'contact_name'=>ps('contact_name'),
|
||||
'contact_email'=>ps('contact_email'),'contact_phone'=>ps('contact_phone'),
|
||||
'contact_email'=>ps('contact_email'),'contact_phone'=>ps('contact_phone'),'image_path'=>$imagePath,
|
||||
'status'=>p('status','approved'),'is_featured'=>(int)p('is_featured',0)];
|
||||
if (!$f['title'] || !$f['event_date']) { flash('Title and date required.','error'); go('/admin/events.php'); }
|
||||
if ($act === 'add') {
|
||||
qrun("INSERT INTO events(title,description,venue,address,event_date,event_time,end_date,end_time,cost,website,contact_name,contact_email,contact_phone,status,is_featured,submitted_by)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",array_merge(array_values($f),[uid()]));
|
||||
qrun("INSERT INTO events(title,description,venue,address,event_date,event_time,end_date,end_time,cost,website,contact_name,contact_email,contact_phone,image_path,status,is_featured,submitted_by)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",array_merge(array_values($f),[uid()]));
|
||||
flash('Event added!','success');
|
||||
} else {
|
||||
$id = (int)p('id');
|
||||
qrun("UPDATE events SET title=?,description=?,venue=?,address=?,event_date=?,event_time=?,end_date=?,end_time=?,cost=?,website=?,contact_name=?,contact_email=?,contact_phone=?,status=?,is_featured=? WHERE id=?",array_merge(array_values($f),[$id]));
|
||||
qrun("UPDATE events SET title=?,description=?,venue=?,address=?,event_date=?,event_time=?,end_date=?,end_time=?,cost=?,website=?,contact_name=?,contact_email=?,contact_phone=?,image_path=?,status=?,is_featured=? WHERE id=?",array_merge(array_values($f),[$id]));
|
||||
flash('Event updated!','success');
|
||||
}
|
||||
go('/admin/events.php');
|
||||
@@ -35,12 +54,13 @@ $editId = iget('edit');
|
||||
$editing = $editId ? qone("SELECT * FROM events WHERE id=?",[$editId]) : null;
|
||||
$where = match($filter) { 'pending'=>"WHERE status='pending'",'approved'=>"WHERE status='approved'", default=>"" };
|
||||
$events = qall("SELECT e.*,u.username FROM events e LEFT JOIN users u ON u.id=e.submitted_by $where ORDER BY CASE status WHEN 'pending' THEN 0 ELSE 1 END,event_date DESC");
|
||||
include __DIR__.'/admin_header.php';
|
||||
?>
|
||||
<?php adminTitle('Manage Events') ?>
|
||||
|
||||
<div class="ibox" style="border-color:var(--gold-d);margin-bottom:2rem">
|
||||
<div class="ibox-title"><?=$editing ? '✏️ EDIT EVENT: '.strtoupper(e($editing['title'])) : '➕ ADD EVENT'?></div>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="<?=$editing?'edit':'add'?>">
|
||||
<form method="POST" enctype="multipart/form-data"><?=csrfField()?><input type="hidden" name="_act" value="<?=$editing?'edit':'add'?>">
|
||||
<?php if($editing): ?><input type="hidden" name="id" value="<?=$editing['id']?>"><?php endif; ?>
|
||||
<div class="fg"><label class="flabel">TITLE *</label><input type="text" name="title" class="finput" value="<?=e($editing['title']??'')?>" required></div>
|
||||
<div class="fg"><label class="flabel">DESCRIPTION</label><textarea name="description" class="ftextarea" style="min-height:80px"><?=e($editing['description']??'')?></textarea></div>
|
||||
@@ -60,6 +80,19 @@ $events = qall("SELECT e.*,u.username FROM events e LEFT JOIN users u ON u.id=e.
|
||||
<div class="fg"><label class="flabel">COST</label><input type="text" name="cost" class="finput" value="<?=e($editing['cost']??'Free')?>"></div>
|
||||
<div class="fg"><label class="flabel">WEBSITE</label><input type="text" name="website" class="finput" value="<?=e($editing['website']??'')?>"></div>
|
||||
</div>
|
||||
<div class="fg">
|
||||
<label class="flabel">EVENT IMAGE</label>
|
||||
<?php if(!empty($editing['image_path'])): ?>
|
||||
<div class="event-admin-image">
|
||||
<img src="<?=e($editing['image_path'])?>" alt="<?=e($editing['title'] ?? 'Event image')?>">
|
||||
<label style="display:flex;align-items:center;gap:.45rem;font-size:.85rem;color:var(--text-m);cursor:pointer">
|
||||
<input type="checkbox" name="remove_image" value="1"> Remove current image
|
||||
</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="event_image" class="finput" accept="image/*">
|
||||
<div class="fhint">Optional JPG, PNG, GIF, or WebP. Max <?=e(setting('event_upload_max_mb','5'))?> MB.</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">CONTACT NAME</label><input type="text" name="contact_name" class="finput" value="<?=e($editing['contact_name']??'')?>"></div>
|
||||
<div class="fg"><label class="flabel">CONTACT PHONE</label><input type="text" name="contact_phone" class="finput" value="<?=e($editing['contact_phone']??'')?>"></div>
|
||||
@@ -90,7 +123,7 @@ $events = qall("SELECT e.*,u.username FROM events e LEFT JOIN users u ON u.id=e.
|
||||
<tbody>
|
||||
<?php foreach($events as $ev): ?>
|
||||
<tr>
|
||||
<td class="td-name" style="max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"><?=e($ev['title'])?></td>
|
||||
<td class="td-name" style="max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"><?=e($ev['title'])?><?=!empty($ev['image_path'])?' <span class="badge badge-blue">Image</span>':''?></td>
|
||||
<td style="font-size:.8rem"><?=fdate($ev['event_date'])?></td>
|
||||
<td style="font-size:.8rem"><?=e($ev['username']??'Admin')?></td>
|
||||
<td><?php $sc=['approved'=>'badge-green','pending'=>'badge-gold','rejected'=>'badge-red']; ?><span class="badge <?=$sc[$ev['status']]??"badge-gray"?>"><?=strtoupper($ev['status'])?></span></td>
|
||||
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__).'/includes/boot.php';
|
||||
requireAdmin();
|
||||
|
||||
function forumCategorySlugForAdmin(string $name, int $id = 0): string {
|
||||
$base = makeSlug($name) ?: 'category';
|
||||
$slug = $base;
|
||||
$i = 1;
|
||||
while (qval("SELECT id FROM forum_categories WHERE slug=? AND id!=?", [$slug, $id])) {
|
||||
$slug = $base.'-'.$i++;
|
||||
}
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if (isPost()) {
|
||||
csrfCheck();
|
||||
$act = p('_act');
|
||||
|
||||
if ($act === 'settings') {
|
||||
setSetting('forum_enabled', p('forum_enabled','0') === '1' ? '1' : '0');
|
||||
setSetting('forum_upload_max_mb', (string)max(1, (float)p('forum_upload_max_mb','5')));
|
||||
$allowed = strtolower(ps('forum_allowed_filetypes'));
|
||||
$allowed = implode(',', uploadAllowedExts($allowed, ['jpg','jpeg','png','gif','webp','pdf','txt','doc','docx']));
|
||||
setSetting('forum_allowed_filetypes', $allowed);
|
||||
setSetting('forum_image_resize_threshold_mb', (string)max(0.5, (float)p('forum_image_resize_threshold_mb','1')));
|
||||
setSetting('forum_image_max_width', (string)max(640, min(3200, (int)p('forum_image_max_width','1600'))));
|
||||
setSetting('forum_image_quality', (string)max(40, min(95, (int)p('forum_image_quality','82'))));
|
||||
flash('Forum settings saved.', 'success');
|
||||
go('/admin/forum.php');
|
||||
}
|
||||
|
||||
if ($act === 'add_category') {
|
||||
$name = ps('name');
|
||||
if ($name !== '') {
|
||||
qrun("INSERT INTO forum_categories(name,slug,description,sort_order,is_active)VALUES(?,?,?,?,?)",
|
||||
[$name, forumCategorySlugForAdmin($name), ps('description'), (int)p('sort_order',0), (int)p('is_active',1)]);
|
||||
flash('Category added.', 'success');
|
||||
}
|
||||
go('/admin/forum.php#categories');
|
||||
}
|
||||
|
||||
if ($act === 'edit_category') {
|
||||
$id = (int)p('id');
|
||||
$name = ps('name');
|
||||
if ($id && $name !== '') {
|
||||
qrun("UPDATE forum_categories SET name=?,slug=?,description=?,sort_order=?,is_active=? WHERE id=?",
|
||||
[$name, forumCategorySlugForAdmin($name, $id), ps('description'), (int)p('sort_order',0), (int)p('is_active',1), $id]);
|
||||
flash('Category updated.', 'success');
|
||||
}
|
||||
go('/admin/forum.php#categories');
|
||||
}
|
||||
|
||||
if ($act === 'delete_category') {
|
||||
$id = (int)p('id');
|
||||
$topicCount = (int)qval("SELECT COUNT(*) FROM forum_topics WHERE category_id=?", [$id]);
|
||||
if ($topicCount > 0) {
|
||||
qrun("UPDATE forum_categories SET is_active=0 WHERE id=?", [$id]);
|
||||
flash('Category has topics, so it was deactivated instead of deleted.', 'warning');
|
||||
} else {
|
||||
qrun("DELETE FROM forum_categories WHERE id=?", [$id]);
|
||||
flash('Category deleted.', 'success');
|
||||
}
|
||||
go('/admin/forum.php#categories');
|
||||
}
|
||||
|
||||
if (in_array($act, ['toggle_pin','toggle_lock','delete_topic'], true)) {
|
||||
$id = (int)p('id');
|
||||
if ($act === 'toggle_pin') {
|
||||
qrun("UPDATE forum_topics SET is_pinned=1-is_pinned WHERE id=?", [$id]);
|
||||
flash('Topic pin status updated.', 'success');
|
||||
} elseif ($act === 'toggle_lock') {
|
||||
qrun("UPDATE forum_topics SET is_locked=1-is_locked WHERE id=?", [$id]);
|
||||
flash('Topic lock status updated.', 'success');
|
||||
} else {
|
||||
qrun("DELETE FROM forum_topics WHERE id=?", [$id]);
|
||||
flash('Topic deleted.', 'success');
|
||||
}
|
||||
go('/admin/forum.php#topics');
|
||||
}
|
||||
}
|
||||
|
||||
$adminTitle = 'Forum';
|
||||
include __DIR__.'/admin_header.php';
|
||||
|
||||
$forumEnabled = setting('forum_enabled','1');
|
||||
$forumUploadMax = setting('forum_upload_max_mb','5');
|
||||
$forumAllowed = setting('forum_allowed_filetypes','jpg,jpeg,png,gif,webp,pdf,txt,doc,docx');
|
||||
$resizeThreshold = setting('forum_image_resize_threshold_mb','1');
|
||||
$resizeMaxWidth = setting('forum_image_max_width','1600');
|
||||
$resizeQuality = setting('forum_image_quality','82');
|
||||
$counts = [
|
||||
'Topics' => qval("SELECT COUNT(*) FROM forum_topics"),
|
||||
'Replies' => qval("SELECT COUNT(*) FROM forum_replies"),
|
||||
'Categories' => qval("SELECT COUNT(*) FROM forum_categories"),
|
||||
'Attachments' => qval("SELECT COUNT(*) FROM forum_attachments"),
|
||||
];
|
||||
$categories = qall("
|
||||
SELECT fc.*, COUNT(ft.id) topic_count
|
||||
FROM forum_categories fc
|
||||
LEFT JOIN forum_topics ft ON ft.category_id=fc.id
|
||||
GROUP BY fc.id
|
||||
ORDER BY fc.sort_order, fc.name
|
||||
");
|
||||
$topics = qall("
|
||||
SELECT ft.*, fc.name category_name, u.username,
|
||||
(SELECT COUNT(*) FROM forum_replies fr WHERE fr.topic_id=ft.id) reply_count
|
||||
FROM forum_topics ft
|
||||
JOIN forum_categories fc ON fc.id=ft.category_id
|
||||
JOIN users u ON u.id=ft.user_id
|
||||
ORDER BY ft.is_pinned DESC, datetime(ft.updated_at) DESC
|
||||
LIMIT 60
|
||||
");
|
||||
?>
|
||||
<?php adminTitle('Forum Controls', 'Enable the forum, manage upload rules, categories, and recent topics.') ?>
|
||||
|
||||
<div class="stat-cards">
|
||||
<?php foreach($counts as $label=>$value): ?>
|
||||
<div class="stat-card"><div class="sc-n"><?=$value?></div><div class="sc-l"><?=e($label)?></div></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="g2" style="gap:1.5rem">
|
||||
<div class="ibox" style="border-color:var(--gold-d)">
|
||||
<div class="ibox-title">FORUM SETTINGS</div>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="settings">
|
||||
<div class="fg">
|
||||
<label class="flabel">FORUM VISIBILITY</label>
|
||||
<div style="display:flex;gap:1rem;flex-wrap:wrap">
|
||||
<label style="display:flex;gap:.45rem;align-items:center;color:var(--green-l);cursor:pointer"><input type="radio" name="forum_enabled" value="1"<?=$forumEnabled==='1'?' checked':''?>> Enabled for members</label>
|
||||
<label style="display:flex;gap:.45rem;align-items:center;color:var(--red-l);cursor:pointer"><input type="radio" name="forum_enabled" value="0"<?=$forumEnabled==='0'?' checked':''?>> Disabled for non-admins</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">FILE MAX MB</label><input type="number" name="forum_upload_max_mb" class="finput" min="1" step="0.5" value="<?=e($forumUploadMax)?>"></div>
|
||||
<div class="fg"><label class="flabel">ALLOWED FILE TYPES</label><input type="text" name="forum_allowed_filetypes" class="finput" value="<?=e($forumAllowed)?>"><div class="fhint">Comma-separated extensions.</div></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">RESIZE IMAGES OVER MB</label><input type="number" name="forum_image_resize_threshold_mb" class="finput" min="0.5" step="0.5" value="<?=e($resizeThreshold)?>"></div>
|
||||
<div class="fg"><label class="flabel">MAX IMAGE WIDTH</label><input type="number" name="forum_image_max_width" class="finput" min="640" max="3200" step="80" value="<?=e($resizeMaxWidth)?>"></div>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">IMAGE QUALITY</label><input type="number" name="forum_image_quality" class="finput" min="40" max="95" value="<?=e($resizeQuality)?>"></div>
|
||||
<button class="btn btn-primary">Save Forum Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ibox" id="categories" style="border-color:var(--gold-d)">
|
||||
<div class="ibox-title">ADD CATEGORY</div>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="add_category">
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">NAME</label><input type="text" name="name" class="finput" required></div>
|
||||
<div class="fg"><label class="flabel">SORT ORDER</label><input type="number" name="sort_order" class="finput" value="0"></div>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="description" class="finput"></div>
|
||||
<div class="fg"><label class="flabel">STATUS</label><select name="is_active" class="fselect"><option value="1">Active</option><option value="0">Inactive</option></select></div>
|
||||
<button class="btn btn-primary btn-sm">Add Category</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-sec-title" style="margin-top:1.5rem">CATEGORIES</div>
|
||||
<div class="tbl-wrap" style="margin-bottom:2rem">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>Name</th><th>Description</th><th>Topics</th><th>Status</th><th>Order</th><th>Actions</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($categories as $cat): ?>
|
||||
<tr>
|
||||
<td class="td-name"><?=e($cat['name'])?></td>
|
||||
<td style="font-size:.8rem"><?=e($cat['description'])?></td>
|
||||
<td><?=$cat['topic_count']?></td>
|
||||
<td><?=$cat['is_active']?'<span class="badge badge-green">Active</span>':'<span class="badge badge-gray">Inactive</span>'?></td>
|
||||
<td><?=$cat['sort_order']?></td>
|
||||
<td>
|
||||
<details>
|
||||
<summary class="btn btn-xs btn-outline" style="display:inline-flex">Edit</summary>
|
||||
<form method="POST" class="admin-inline-editor">
|
||||
<?=csrfField()?><input type="hidden" name="_act" value="edit_category"><input type="hidden" name="id" value="<?=$cat['id']?>">
|
||||
<div class="form-row">
|
||||
<input type="text" name="name" class="finput" value="<?=e($cat['name'])?>" required>
|
||||
<input type="number" name="sort_order" class="finput" value="<?=$cat['sort_order']?>">
|
||||
</div>
|
||||
<input type="text" name="description" class="finput" value="<?=e($cat['description'])?>">
|
||||
<select name="is_active" class="fselect"><option value="1"<?=$cat['is_active']?' selected':''?>>Active</option><option value="0"<?=!$cat['is_active']?' selected':''?>>Inactive</option></select>
|
||||
<button class="btn btn-success btn-xs">Save</button>
|
||||
</form>
|
||||
</details>
|
||||
<form method="POST" style="display:inline"><?=csrfField()?><input type="hidden" name="_act" value="delete_category"><input type="hidden" name="id" value="<?=$cat['id']?>"><button class="btn btn-xs btn-danger" data-confirm="Delete this category?">Delete</button></form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="admin-sec-title" id="topics">RECENT TOPICS</div>
|
||||
<div class="tbl-wrap">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>Topic</th><th>Category</th><th>By</th><th>Replies</th><th>Status</th><th>Actions</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($topics as $topic): ?>
|
||||
<tr>
|
||||
<td><a class="td-name" href="<?=e(forumTopicUrl($topic))?>" target="_blank"><?=e($topic['title'])?></a><div style="font-size:.75rem;color:var(--text-d)"><?=ago($topic['updated_at'])?></div></td>
|
||||
<td><?=e($topic['category_name'])?></td>
|
||||
<td><?=e($topic['username'])?></td>
|
||||
<td><?=$topic['reply_count']?></td>
|
||||
<td>
|
||||
<?php if($topic['is_pinned']): ?><span class="badge badge-gold">Pinned</span><?php endif; ?>
|
||||
<?php if($topic['is_locked']): ?><span class="badge badge-gray">Locked</span><?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex-row" style="gap:.3rem">
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="toggle_pin"><input type="hidden" name="id" value="<?=$topic['id']?>"><button class="btn btn-xs btn-outline"><?=$topic['is_pinned']?'Unpin':'Pin'?></button></form>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="toggle_lock"><input type="hidden" name="id" value="<?=$topic['id']?>"><button class="btn btn-xs btn-secondary"><?=$topic['is_locked']?'Unlock':'Lock'?></button></form>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="delete_topic"><input type="hidden" name="id" value="<?=$topic['id']?>"><button class="btn btn-xs btn-danger" data-confirm="Delete this topic and replies?">Delete</button></form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if(!$topics): ?><tr><td colspan="6" style="text-align:center;color:var(--text-d)">No forum topics yet.</td></tr><?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__.'/admin_footer.php'; ?>
|
||||
@@ -9,6 +9,7 @@ $stats = [
|
||||
'Pending Events' => qval("SELECT COUNT(*) FROM events WHERE status='pending'"),
|
||||
'Pending Claims' => qval("SELECT COUNT(*) FROM business_claim_requests WHERE status='pending'"),
|
||||
'Pending Edits' => qval("SELECT COUNT(*) FROM business_edit_requests WHERE status='pending'"),
|
||||
'Forum Topics' => qval("SELECT COUNT(*) FROM forum_topics"),
|
||||
'Open Reports' => qval("SELECT COUNT(*) FROM reports WHERE status='open'"),
|
||||
'Active Alerts' => qval("SELECT COUNT(*) FROM alerts WHERE is_active=1"),
|
||||
];
|
||||
@@ -34,6 +35,15 @@ $pendingEdits = qall("
|
||||
ORDER BY ber.created_at DESC
|
||||
LIMIT 10
|
||||
");
|
||||
$recentForumTopics = qall("
|
||||
SELECT ft.*, fc.name category_name, u.username,
|
||||
(SELECT COUNT(*) FROM forum_replies fr WHERE fr.topic_id=ft.id) reply_count
|
||||
FROM forum_topics ft
|
||||
JOIN forum_categories fc ON fc.id=ft.category_id
|
||||
JOIN users u ON u.id=ft.user_id
|
||||
ORDER BY datetime(ft.updated_at) DESC
|
||||
LIMIT 5
|
||||
");
|
||||
?>
|
||||
<?php adminTitle('Site Overview','Welcome to the Discover Keyser WV administration panel.') ?>
|
||||
|
||||
@@ -156,6 +166,28 @@ $pendingEdits = qall("
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if($recentForumTopics): ?>
|
||||
<div style="margin-bottom:2rem">
|
||||
<div class="admin-sec-title">FORUM ACTIVITY</div>
|
||||
<div class="tbl-wrap">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>TOPIC</th><th>CATEGORY</th><th>BY</th><th>REPLIES</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($recentForumTopics as $topic): ?>
|
||||
<tr>
|
||||
<td><a href="<?=e(forumTopicUrl($topic))?>" target="_blank" class="td-name"><?=e($topic['title'])?></a><div style="font-size:.75rem;color:var(--text-d)"><?=ago($topic['updated_at'])?></div></td>
|
||||
<td><?=e($topic['category_name'])?></td>
|
||||
<td><?=e($topic['username'])?></td>
|
||||
<td><?=$topic['reply_count']?></td>
|
||||
<td><a href="/admin/forum.php#topics" class="btn btn-xs btn-primary">Moderate</a></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="g2">
|
||||
<div>
|
||||
<div class="admin-sec-title">🏢 RECENT BUSINESSES</div>
|
||||
|
||||
+62
-6
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__).'/includes/boot.php';
|
||||
requireAdmin();
|
||||
$adminTitle = 'Menus';
|
||||
include __DIR__.'/admin_header.php';
|
||||
|
||||
$bizId = iget('biz_id');
|
||||
$biz = $bizId ? qone("SELECT * FROM businesses WHERE id=?",[$bizId]) : null;
|
||||
$menuImagesEnabled = max(0, min(1, (int)setting('menu_item_image_limit','1'))) > 0;
|
||||
|
||||
if (isPost()) {
|
||||
csrfCheck();
|
||||
@@ -31,15 +33,51 @@ if (isPost()) {
|
||||
$sid = (int)p('section_id');
|
||||
$name = ps('item_name');
|
||||
if ($sid && $name) {
|
||||
$imagePath = '';
|
||||
if ($menuImagesEnabled) {
|
||||
$upload = storeSingleUpload(
|
||||
'item_image',
|
||||
'menus',
|
||||
imageExts(),
|
||||
mbToBytes(setting('menu_upload_max_mb','5'), 5),
|
||||
[
|
||||
'image_only' => true,
|
||||
'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb','1'), 1),
|
||||
'image_max_width' => (int)setting('forum_image_max_width','1600'),
|
||||
'image_quality' => (int)setting('forum_image_quality','82'),
|
||||
]
|
||||
);
|
||||
if (!$upload['ok']) { flash($upload['error'], 'error'); go("/admin/menus.php?biz_id=$bizId"); }
|
||||
$imagePath = (string)($upload['path'] ?? '');
|
||||
}
|
||||
$ord = (int)qval("SELECT COALESCE(MAX(sort_order),0)+1 FROM menu_items WHERE section_id=?",[$sid]);
|
||||
qrun("INSERT INTO menu_items(section_id,name,description,price,is_available,sort_order)VALUES(?,?,?,?,?,?)",[$sid,$name,ps('item_desc'),(float)p('item_price',0),(int)p('is_available',1),$ord]);
|
||||
qrun("INSERT INTO menu_items(section_id,name,description,price,image_path,is_available,sort_order)VALUES(?,?,?,?,?,?,?)",[$sid,$name,ps('item_desc'),(float)p('item_price',0),$imagePath,(int)p('is_available',1),$ord]);
|
||||
flash('Item added!','success');
|
||||
}
|
||||
go("/admin/menus.php?biz_id=$bizId");
|
||||
}
|
||||
if ($act === 'edit_item') {
|
||||
$iid = (int)p('iid');
|
||||
qrun("UPDATE menu_items SET name=?,description=?,price=?,is_available=?,sort_order=? WHERE id=?",[ps('item_name'),ps('item_desc'),(float)p('item_price',0),(int)p('is_available',1),(int)p('item_order'),$iid]);
|
||||
$item = qone("SELECT * FROM menu_items WHERE id=?", [$iid]);
|
||||
$imagePath = (string)($item['image_path'] ?? '');
|
||||
if ($menuImagesEnabled && p('remove_item_image','0') === '1') $imagePath = '';
|
||||
if ($menuImagesEnabled) {
|
||||
$upload = storeSingleUpload(
|
||||
'item_image',
|
||||
'menus',
|
||||
imageExts(),
|
||||
mbToBytes(setting('menu_upload_max_mb','5'), 5),
|
||||
[
|
||||
'image_only' => true,
|
||||
'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb','1'), 1),
|
||||
'image_max_width' => (int)setting('forum_image_max_width','1600'),
|
||||
'image_quality' => (int)setting('forum_image_quality','82'),
|
||||
]
|
||||
);
|
||||
if (!$upload['ok']) { flash($upload['error'], 'error'); go("/admin/menus.php?biz_id=$bizId"); }
|
||||
if (!empty($upload['path'])) $imagePath = (string)$upload['path'];
|
||||
}
|
||||
qrun("UPDATE menu_items SET name=?,description=?,price=?,image_path=?,is_available=?,sort_order=? WHERE id=?",[ps('item_name'),ps('item_desc'),(float)p('item_price',0),$imagePath,(int)p('is_available',1),(int)p('item_order'),$iid]);
|
||||
flash('Item updated!','success'); go("/admin/menus.php?biz_id=$bizId");
|
||||
}
|
||||
if ($act === 'del_item') {
|
||||
@@ -55,6 +93,7 @@ if ($biz) {
|
||||
$items = [];
|
||||
foreach ($sections as $s) $items[$s['id']] = qall("SELECT * FROM menu_items WHERE section_id=? ORDER BY sort_order",[$s['id']]);
|
||||
}
|
||||
include __DIR__.'/admin_header.php';
|
||||
?>
|
||||
<?php adminTitle('Manage Menus','Edit menus, sections, and items for any business.') ?>
|
||||
|
||||
@@ -117,11 +156,12 @@ if ($biz) {
|
||||
<?php if($items[$sec['id']]): ?>
|
||||
<div class="tbl-wrap" style="margin-bottom:.85rem">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>ITEM NAME</th><th>DESCRIPTION</th><th>PRICE</th><th>AVAIL.</th><th>ORDER</th><th></th></tr></thead>
|
||||
<thead><tr><th>ITEM NAME</th><th>IMAGE</th><th>DESCRIPTION</th><th>PRICE</th><th>AVAIL.</th><th>ORDER</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($items[$sec['id']] as $item): ?>
|
||||
<tr>
|
||||
<td class="td-name"><?=e($item['name'])?></td>
|
||||
<td><?php if(!empty($item['image_path'])): ?><img src="<?=e($item['image_path'])?>" alt="<?=e($item['name'])?>" class="admin-menu-thumb"><?php else: ?>—<?php endif; ?></td>
|
||||
<td style="font-size:.8rem;max-width:180px"><?=e(substr($item['description'],0,80))?></td>
|
||||
<td style="color:var(--gold);font-family:'Oswald',sans-serif"><?=$item['price']>0?money($item['price']):'—'?></td>
|
||||
<td><?=$item['is_available']?'<span class="badge badge-green">Yes</span>':'<span class="badge badge-gray">No</span>'?></td>
|
||||
@@ -132,12 +172,25 @@ if ($biz) {
|
||||
<form method="POST" style="display:inline"><?=csrfField()?><input type="hidden" name="_act" value="del_item"><input type="hidden" name="iid" value="<?=$item['id']?>"><button class="btn btn-xs btn-danger" data-confirm="Delete item?">🗑️</button></form>
|
||||
</div>
|
||||
<div id="eif<?=$item['id']?>" class="hidden" style="background:var(--bg2);border:1px solid var(--bdr);border-radius:4px;padding:.75rem;margin-top:.4rem;min-width:320px">
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="edit_item"><input type="hidden" name="iid" value="<?=$item['id']?>">
|
||||
<form method="POST" enctype="multipart/form-data"><?=csrfField()?><input type="hidden" name="_act" value="edit_item"><input type="hidden" name="iid" value="<?=$item['id']?>">
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">NAME *</label><input type="text" name="item_name" class="finput" value="<?=e($item['name'])?>" required></div>
|
||||
<div class="fg"><label class="flabel">PRICE</label><input type="number" name="item_price" class="finput" value="<?=$item['price']?>" step="0.01" min="0"></div>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="item_desc" class="finput" value="<?=e($item['description'])?>"></div>
|
||||
<?php if($menuImagesEnabled): ?>
|
||||
<div class="fg">
|
||||
<label class="flabel">ITEM IMAGE</label>
|
||||
<?php if(!empty($item['image_path'])): ?>
|
||||
<div class="menu-admin-image">
|
||||
<img src="<?=e($item['image_path'])?>" alt="<?=e($item['name'])?>">
|
||||
<label style="display:flex;align-items:center;gap:.45rem;font-size:.82rem;color:var(--text-m);cursor:pointer"><input type="checkbox" name="remove_item_image" value="1"> Remove current image</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="item_image" class="finput" accept="image/*">
|
||||
<div class="fhint">One image allowed. Max <?=e(setting('menu_upload_max_mb','5'))?> MB.</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">AVAILABLE</label><select name="is_available" class="fselect"><option value="1"<?=$item['is_available']?' selected':''?>>Yes</option><option value="0"<?=!$item['is_available']?' selected':''?>>No</option></select></div>
|
||||
<div class="fg"><label class="flabel">SORT ORDER</label><input type="number" name="item_order" class="finput" value="<?=$item['sort_order']?>"></div>
|
||||
@@ -156,12 +209,15 @@ if ($biz) {
|
||||
<!-- Add item -->
|
||||
<details><summary style="cursor:pointer;font-size:.82rem;color:var(--gold);font-family:'Oswald',sans-serif;letter-spacing:.08em">➕ Add Item to "<?=e($sec['name'])?>"</summary>
|
||||
<div style="padding:.75rem 0">
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="add_item"><input type="hidden" name="section_id" value="<?=$sec['id']?>">
|
||||
<form method="POST" enctype="multipart/form-data"><?=csrfField()?><input type="hidden" name="_act" value="add_item"><input type="hidden" name="section_id" value="<?=$sec['id']?>">
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">ITEM NAME *</label><input type="text" name="item_name" class="finput" required placeholder="e.g. Spaghetti Carbonara"></div>
|
||||
<div class="fg"><label class="flabel">PRICE ($)</label><input type="number" name="item_price" class="finput" value="0" step="0.01" min="0"></div>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="item_desc" class="finput" placeholder="Brief description…"></div>
|
||||
<?php if($menuImagesEnabled): ?>
|
||||
<div class="fg"><label class="flabel">ITEM IMAGE</label><input type="file" name="item_image" class="finput" accept="image/*"><div class="fhint">Optional. One image allowed, max <?=e(setting('menu_upload_max_mb','5'))?> MB.</div></div>
|
||||
<?php endif; ?>
|
||||
<div class="fg"><label class="flabel">AVAILABLE</label><select name="is_available" class="fselect"><option value="1">Yes</option><option value="0">No</option></select></div>
|
||||
<button type="submit" class="btn btn-primary btn-sm">Add Item</button>
|
||||
</form>
|
||||
|
||||
+59
-1
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__).'/includes/boot.php';
|
||||
requireAdmin();
|
||||
$adminTitle = 'Site Settings';
|
||||
include __DIR__.'/admin_header.php';
|
||||
|
||||
if (isPost()) {
|
||||
csrfCheck();
|
||||
@@ -13,6 +14,15 @@ if (isPost()) {
|
||||
setSetting('registration_enabled',p('registration_enabled','0'));
|
||||
setSetting('events_require_approval',p('events_require_approval','0'));
|
||||
setSetting('home_random_interval',p('home_random_interval','2hours') === 'day' ? 'day' : '2hours');
|
||||
if (array_key_exists('event_upload_max_mb', $_POST)) {
|
||||
setSetting('event_upload_max_mb', (string)max(1, (float)p('event_upload_max_mb','5')));
|
||||
setSetting('menu_item_image_limit', (string)max(0, min(1, (int)p('menu_item_image_limit','1'))));
|
||||
setSetting('menu_upload_max_mb', (string)max(1, (float)p('menu_upload_max_mb','5')));
|
||||
setSetting('avatar_upload_max_mb', (string)max(1, (float)p('avatar_upload_max_mb','3')));
|
||||
setSetting('forum_image_resize_threshold_mb', (string)max(0.5, (float)p('forum_image_resize_threshold_mb','1')));
|
||||
setSetting('forum_image_max_width', (string)max(640, min(3200, (int)p('forum_image_max_width','1600'))));
|
||||
setSetting('forum_image_quality', (string)max(40, min(95, (int)p('forum_image_quality','82'))));
|
||||
}
|
||||
if (array_key_exists('mailgun_domain', $_POST)) {
|
||||
setSetting('site_base_url', rtrim(ps('site_base_url'), '/'));
|
||||
setSetting('mailgun_region', p('mailgun_region','us') === 'eu' ? 'eu' : 'us');
|
||||
@@ -55,7 +65,15 @@ $mailgunFromName = setting('mailgun_from_name','Discover Keyser WV');
|
||||
$mailgunFromEmail = setting('mailgun_from_email','');
|
||||
$mailgunSendMode = setting('mailgun_send_mode','html');
|
||||
$mailgunHasKey = setting('mailgun_api_key','') !== '';
|
||||
$eventUploadMax = setting('event_upload_max_mb','5');
|
||||
$menuImageLimit = setting('menu_item_image_limit','1');
|
||||
$menuUploadMax = setting('menu_upload_max_mb','5');
|
||||
$avatarUploadMax = setting('avatar_upload_max_mb','3');
|
||||
$resizeThreshold = setting('forum_image_resize_threshold_mb','1');
|
||||
$resizeMaxWidth = setting('forum_image_max_width','1600');
|
||||
$resizeQuality = setting('forum_image_quality','82');
|
||||
$me = qone("SELECT * FROM users WHERE id=?",[uid()]);
|
||||
include __DIR__.'/admin_header.php';
|
||||
?>
|
||||
<?php adminTitle('Site Settings') ?>
|
||||
|
||||
@@ -119,6 +137,39 @@ $me = qone("SELECT * FROM users WHERE id=?",[uid()]);
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ibox" style="border-color:var(--gold-d)">
|
||||
<div class="ibox-title">🖼️ UPLOAD & IMAGE SETTINGS</div>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="settings">
|
||||
<input type="hidden" name="site_name" value="<?=e($siteName)?>">
|
||||
<input type="hidden" name="site_tagline" value="<?=e($siteTagline)?>">
|
||||
<input type="hidden" name="contact_email" value="<?=e($contactEmail)?>">
|
||||
<input type="hidden" name="registration_enabled" value="<?=e($regEnabled)?>">
|
||||
<input type="hidden" name="events_require_approval" value="<?=e($evtApproval)?>">
|
||||
<input type="hidden" name="home_random_interval" value="<?=e($homeRandomInterval)?>">
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">EVENT IMAGE MAX MB</label><input type="number" name="event_upload_max_mb" class="finput" min="1" step="0.5" value="<?=e($eventUploadMax)?>"></div>
|
||||
<div class="fg"><label class="flabel">AVATAR MAX MB</label><input type="number" name="avatar_upload_max_mb" class="finput" min="1" step="0.5" value="<?=e($avatarUploadMax)?>"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="fg">
|
||||
<label class="flabel">MENU ITEM IMAGE LIMIT</label>
|
||||
<select name="menu_item_image_limit" class="fselect">
|
||||
<option value="1"<?=$menuImageLimit==='1'?' selected':''?>>One image per item</option>
|
||||
<option value="0"<?=$menuImageLimit==='0'?' selected':''?>>No menu item images</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">MENU IMAGE MAX MB</label><input type="number" name="menu_upload_max_mb" class="finput" min="1" step="0.5" value="<?=e($menuUploadMax)?>"></div>
|
||||
</div>
|
||||
<hr class="divider" style="margin:1rem 0">
|
||||
<div class="form-row">
|
||||
<div class="fg"><label class="flabel">RESIZE IMAGES OVER MB</label><input type="number" name="forum_image_resize_threshold_mb" class="finput" min="0.5" step="0.5" value="<?=e($resizeThreshold)?>"></div>
|
||||
<div class="fg"><label class="flabel">MAX IMAGE WIDTH</label><input type="number" name="forum_image_max_width" class="finput" min="640" max="3200" step="80" value="<?=e($resizeMaxWidth)?>"></div>
|
||||
</div>
|
||||
<div class="fg"><label class="flabel">IMAGE QUALITY</label><input type="number" name="forum_image_quality" class="finput" min="40" max="95" value="<?=e($resizeQuality)?>"><div class="fhint">Used when large uploaded images are re-saved.</div></div>
|
||||
<button type="submit" class="btn btn-primary">Save Upload Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ibox" style="border-color:var(--gold-d)">
|
||||
<div class="ibox-title">✉️ EMAIL VERIFICATION & MAILGUN</div>
|
||||
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="settings">
|
||||
@@ -128,6 +179,13 @@ $me = qone("SELECT * FROM users WHERE id=?",[uid()]);
|
||||
<input type="hidden" name="registration_enabled" value="<?=e($regEnabled)?>">
|
||||
<input type="hidden" name="events_require_approval" value="<?=e($evtApproval)?>">
|
||||
<input type="hidden" name="home_random_interval" value="<?=e($homeRandomInterval)?>">
|
||||
<input type="hidden" name="event_upload_max_mb" value="<?=e($eventUploadMax)?>">
|
||||
<input type="hidden" name="menu_item_image_limit" value="<?=e($menuImageLimit)?>">
|
||||
<input type="hidden" name="menu_upload_max_mb" value="<?=e($menuUploadMax)?>">
|
||||
<input type="hidden" name="avatar_upload_max_mb" value="<?=e($avatarUploadMax)?>">
|
||||
<input type="hidden" name="forum_image_resize_threshold_mb" value="<?=e($resizeThreshold)?>">
|
||||
<input type="hidden" name="forum_image_max_width" value="<?=e($resizeMaxWidth)?>">
|
||||
<input type="hidden" name="forum_image_quality" value="<?=e($resizeQuality)?>">
|
||||
|
||||
<div class="fg">
|
||||
<label class="flabel">SITE BASE URL</label>
|
||||
|
||||
Reference in New Issue
Block a user