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 & 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> 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'; ?>