xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

admin/settings.php

1 <?php
2 require_once __DIR__ . '/../includes/bootstrap.php';
3 must_admin_only();
4 $PAGE_TITLE = 'Settings';
5 $ADMIN_PAGE = 'settings';
6 $saved = false;
7 
8 if ($_SERVER['REQUEST_METHOD']==='POST' && csrf_ok()) {
9     // Checkboxes (default 0 if unchecked)
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 = [];
20 foreach (DB::rows('SELECT key,value FROM settings') as $r) $s[$r['key']] = $r['value'];
21 include __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 &amp; 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 &lt;head&gt; 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>
154 function 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'; ?>