xgit simple git

nexus

nexus

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

admin/settings.php


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    // 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 = [];
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 &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>
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'; ?>