1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3must_admin_only();
4$PAGE_TITLE = 'Settings';
5$ADMIN_PAGE = 'settings';
6$saved = false;
7
8if ($_SERVER['REQUEST_METHOD']==='POST' && csrf_ok()) {
9
10 $checkboxes = ['allow_reg','rate_limit_enabled','post_captcha_enabled','topic_captcha_enabled','post_layout_horizontal'];
11 $textfields = ['site_name','site_desc','logo_url','topics_per_page','posts_per_page',
12 'footer_text','custom_head','rate_limit_count','rate_limit_window',
13 'max_upload_mb'];
14 foreach ($checkboxes as $k) cfg_set($k, isset($_POST[$k]) ? '1' : '0');
15 foreach ($textfields as $k) cfg_set($k, post($k));
16 $saved = true;
17}
18
19$s = [];
20foreach (DB::rows('SELECT key,value FROM settings') as $r) $s[$r['key']] = $r['value'];
21include __DIR__ . '/../views/partials/admin_layout.php';
22?>
23<?php if ($saved): ?><div class="alert ok">โ
Settings saved!</div><?php endif; ?>
24
25<form method="POST">
26 <?= csrf_input() ?>
27
28 <!-- Site Identity -->
29 <div class="acard" style="margin-bottom:18px">
30 <div class="acard-head"><h2>๐ Site Identity</h2></div>
31 <div class="acard-body">
32 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
33 <div class="fg"><label>Site Name</label>
34 <input type="text" name="site_name" class="fi" value="<?= e($s['site_name']??'Nexus Forum') ?>"></div>
35 <div class="fg"><label>Logo URL <small>(blank = text logo)</small></label>
36 <input type="text" name="logo_url" class="fi" value="<?= e($s['logo_url']??'') ?>" placeholder="/public/uploads/logo.png"></div>
37 <div class="fg" style="grid-column:1/-1"><label>Description</label>
38 <textarea name="site_desc" class="fi" rows="2"><?= e($s['site_desc']??'') ?></textarea></div>
39 <div class="fg" style="grid-column:1/-1"><label>Footer Text <small>(HTML allowed)</small></label>
40 <input type="text" name="footer_text" class="fi" value="<?= e($s['footer_text']??'') ?>"></div>
41 </div>
42 </div>
43 </div>
44
45 <!-- Registration -->
46 <div class="acard" style="margin-bottom:18px">
47 <div class="acard-head"><h2>๐ฅ Registration</h2></div>
48 <div class="acard-body">
49 <label class="setting-toggle">
50 <div><strong>Allow Registration</strong><p class="hint">Let new users create accounts</p></div>
51 <label class="toggle-sw"><input type="checkbox" name="allow_reg" <?= ($s['allow_reg']??'1')==='1'?'checked':'' ?>><span class="toggle-knob"></span></label>
52 </label>
53 </div>
54 </div>
55
56 <!-- Content -->
57 <div class="acard" style="margin-bottom:18px">
58 <div class="acard-head"><h2>๐ Pagination</h2></div>
59 <div class="acard-body">
60 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
61 <div class="fg"><label>Topics Per Page</label>
62 <input type="number" name="topics_per_page" class="fi" value="<?= e($s['topics_per_page']??'30') ?>" min="5" max="100"></div>
63 <div class="fg"><label>Posts Per Page</label>
64 <input type="number" name="posts_per_page" class="fi" value="<?= e($s['posts_per_page']??'20') ?>" min="5" max="100"></div>
65
66 <div class="arow">
67 <div>
68 <label class="form-label">Max Image Upload Size (MB)</label>
69 <small style="color:var(--muted);display:block">Maximum file size per uploaded image. Range: 1โ50 MB.</small>
70 </div>
71 <input type="number" name="max_upload_mb" class="fi" value="<?= e($s['max_upload_mb']??'5') ?>" min="1" max="50" style="width:80px"></div>
72 </div>
73 </div>
74 </div>
75
76 <!-- Rate Limiting -->
77 <div class="acard" style="margin-bottom:18px">
78 <div class="acard-head"><h2>โฑ๏ธ Post Rate Limiting</h2></div>
79 <div class="acard-body">
80 <label class="setting-toggle" style="margin-bottom:16px">
81 <div><strong>Enable Rate Limiting</strong><p class="hint">Prevent users from posting too fast. Admins & moderators are exempt.</p></div>
82 <label class="toggle-sw"><input type="checkbox" name="rate_limit_enabled" id="rlToggle"
83 <?= ($s['rate_limit_enabled']??'0')==='1'?'checked':'' ?>
84 onchange="document.getElementById('rlSettings').style.display=this.checked?'':'none'">
85 <span class="toggle-knob"></span></label>
86 </label>
87 <div id="rlSettings" style="<?= ($s['rate_limit_enabled']??'0')==='1'?'':'display:none' ?>;background:#f8fafc;border:1px solid var(--border);border-radius:8px;padding:16px">
88 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
89 <div class="fg">
90 <label>Max Posts Per Window</label>
91 <input type="number" name="rate_limit_count" class="fi" value="<?= e($s['rate_limit_count']??'3') ?>" min="1" max="100">
92 <span class="hint">Number of posts allowed in the time window</span>
93 </div>
94 <div class="fg">
95 <label>Time Window (seconds)</label>
96 <input type="number" name="rate_limit_window" class="fi" value="<?= e($s['rate_limit_window']??'60') ?>" min="5" max="86400">
97 <span class="hint">E.g. 60 = 1 minute, 3600 = 1 hour</span>
98 </div>
99 </div>
100 <div class="rate-presets">
101 <span style="font-size:12px;color:#94a3b8;margin-right:6px">Presets:</span>
102 <button type="button" class="btn-ghost btn-sm" onclick="setRL(3,60)">Relaxed (3/min)</button>
103 <button type="button" class="btn-ghost btn-sm" onclick="setRL(1,30)">Moderate (1/30s)</button>
104 <button type="button" class="btn-ghost btn-sm" onclick="setRL(1,120)">Strict (1/2min)</button>
105 <button type="button" class="btn-ghost btn-sm" onclick="setRL(5,300)">5 per 5 mins</button>
106 </div>
107 </div>
108 </div>
109 </div>
110
111 <!-- Post/Reply Captcha -->
112 <div class="acard" style="margin-bottom:18px">
113 <div class="acard-head"><h2>๐ Post/Reply Security Captcha</h2></div>
114 <div class="acard-body">
115 <label class="setting-toggle" style="margin-bottom:12px">
116 <div>
117 <strong>Require Math Captcha to Reply</strong>
118 <p class="hint">Users must solve a simple math question before posting a reply to any topic</p>
119 </div>
120 <label class="toggle-sw"><input type="checkbox" name="post_captcha_enabled"
121 <?= ($s['post_captcha_enabled']??'0')==='1'?'checked':'' ?>><span class="toggle-knob"></span></label>
122 </label>
123 <label class="setting-toggle">
124 <div>
125 <strong>Require Math Captcha to Create Topic</strong>
126 <p class="hint">Users must solve a math question when creating a new topic</p>
127 </div>
128 <label class="toggle-sw"><input type="checkbox" name="topic_captcha_enabled"
129 <?= ($s['topic_captcha_enabled']??'0')==='1'?'checked':'' ?>><span class="toggle-knob"></span></label>
130 </label>
131 </div>
132 </div>
133
134 <!-- Advanced -->
135 <div class="acard" style="margin-bottom:18px">
136 <div class="acard-head"><h2>๐ง Advanced</h2></div>
137 <div class="acard-body">
138 <div class="fg">
139 <label>Custom <head> HTML <small>(analytics, meta tags)</small></label>
140 <textarea name="custom_head" class="fi" rows="4" style="font-family:var(--mono);font-size:13px"
141 placeholder="<!-- Google Analytics, custom meta, etc. -->"><?= e($s['custom_head']??'') ?></textarea>
142 </div>
143 </div>
144 </div>
145
146 <button type="submit" class="btn-primary btn-lg">๐พ Save Settings</button>
147</form>
148
149<style>
150.setting-toggle { display:flex; align-items:center; justify-content:space-between; padding:6px 0; }
151.rate-presets { display:flex; flex-wrap:wrap; gap:6px; margin-top:12px; align-items:center; }
152</style>
153<script>
154function setRL(count,window){
155 document.querySelector('[name=rate_limit_count]').value = count;
156 document.querySelector('[name=rate_limit_window]').value = window;
157}
158</script>
159<?php include __DIR__ . '/../views/partials/admin_layout_end.php'; ?>