xgit simple git

nexus

nexus

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

admin/themes.php


1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3must_admin_only();
4$PAGE_TITLE = 'Themes & Templates';
5$ADMIN_PAGE = 'themes';
6$flash = null;
7 
8if ($_SERVER['REQUEST_METHOD']==='POST' && csrf_ok()) {
9    $action = post('action');
10 
11    if ($action === 'create' || $action === 'update') {
12        $name = post('name');
13        $css  = post('css');
14        if ($name) {
15            if ($action === 'create') {
16                $slug = unique_slug($name, 'themes');
17                DB::insert('INSERT INTO themes (name,slug,css) VALUES (?,?,?)', [$name,$slug,$css]);
18                $flash = ['ok','Theme created!'];
19            } else {
20                $tid = (int)post('id');
21                DB::run('UPDATE themes SET name=?,css=? WHERE id=?', [$name,$css,$tid]);
22                $flash = ['ok','Theme updated!'];
23            }
24        }
25    } elseif ($action === 'activate') {
26        $tid = (int)post('id');
27        DB::run('UPDATE themes SET is_active=0');
28        DB::run('UPDATE themes SET is_active=1 WHERE id=?', [$tid]);
29        $flash = ['ok','Theme activated!'];
30    } elseif ($action === 'deactivate') {
31        DB::run('UPDATE themes SET is_active=0');
32        $flash = ['ok','Default theme restored.'];
33    } elseif ($action === 'delete') {
34        $tid = (int)post('id');
35        DB::run('DELETE FROM themes WHERE id=?', [$tid]);
36        $flash = ['ok','Theme deleted.'];
37    }
38}
39 
40$themes = DB::rows('SELECT * FROM themes ORDER BY id');
41$editId = (int)get('edit');
42$editTheme = $editId ? DB::row('SELECT * FROM themes WHERE id=?', [$editId]) : null;
43 
44include __DIR__ . '/../views/partials/admin_layout.php';
45?>
46 
47<?php if ($flash): ?>
48  <div class="alert <?= $flash[0] ?>"><?= e($flash[1]) ?></div>
49<?php endif; ?>
50 
51<div class="admin-two-col">
52  <!-- Theme List -->
53  <div class="acard">
54    <div class="acard-head">
55      <h2>๐ŸŽจ Themes</h2>
56      <span style="font-size:12px;color:#94a3b8">One theme active at a time</span>
57    </div>
58    <div class="acard-body" style="padding:0">
59      <?php if (empty($themes)): ?>
60        <p style="padding:20px;color:#94a3b8;text-align:center">No themes yet. Create one below.</p>
61      <?php else: ?>
62        <?php foreach ($themes as $th): ?>
63          <div class="theme-row <?= $th['is_active'] ? 'theme-active' : '' ?>">
64            <div class="theme-info">
65              <?php if ($th['is_active']): ?>
66                <span class="stag active" style="margin-right:8px">โ— Active</span>
67              <?php endif; ?>
68              <strong><?= e($th['name']) ?></strong>
69              <span style="font-size:12px;color:#94a3b8;margin-left:8px"><?= strlen($th['css']) ?> chars CSS</span>
70            </div>
71            <div class="theme-btns">
72              <a href="?edit=<?= $th['id'] ?>" class="btn-ghost btn-sm">Edit</a>
73              <?php if (!$th['is_active']): ?>
74                <form method="POST" style="display:inline"><?= csrf_input() ?>
75                  <input type="hidden" name="action" value="activate">
76                  <input type="hidden" name="id" value="<?= $th['id'] ?>">
77                  <button class="btn-primary btn-sm">Activate</button>
78                </form>
79              <?php else: ?>
80                <form method="POST" style="display:inline"><?= csrf_input() ?>
81                  <input type="hidden" name="action" value="deactivate">
82                  <button class="btn-ghost btn-sm">Deactivate</button>
83                </form>
84              <?php endif; ?>
85              <form method="POST" style="display:inline"><?= csrf_input() ?>
86                <input type="hidden" name="action" value="delete">
87                <input type="hidden" name="id" value="<?= $th['id'] ?>">
88                <button class="btn-danger btn-sm" onclick="return confirm('Delete theme?')">Delete</button>
89              </form>
90            </div>
91          </div>
92        <?php endforeach; ?>
93      <?php endif; ?>
94    </div>
95  </div>
96 
97  <!-- Create / Edit -->
98  <div class="acard">
99    <div class="acard-head">
100      <h2><?= $editTheme ? 'โœ๏ธ Edit: '.e($editTheme['name']) : '+ New Theme' ?></h2>
101      <?php if ($editTheme): ?><a href="<?= u('admin/themes.php') ?>" class="btn-ghost btn-sm">Cancel</a><?php endif; ?>
102    </div>
103    <div class="acard-body">
104      <form method="POST">
105        <?= csrf_input() ?>
106        <input type="hidden" name="action" value="<?= $editTheme ? 'update' : 'create' ?>">
107        <?php if ($editTheme): ?>
108          <input type="hidden" name="id" value="<?= $editTheme['id'] ?>">
109        <?php endif; ?>
110 
111        <div class="fg">
112          <label>Theme Name</label>
113          <input type="text" name="name" class="fi" required placeholder="My Custom Theme"
114                 value="<?= e($editTheme['name'] ?? '') ?>">
115        </div>
116 
117        <div class="fg">
118          <label>Custom CSS</label>
119          <div class="css-editor-wrap">
120            <div class="css-presets">
121              <span style="font-size:12px;color:#94a3b8;margin-right:6px">Presets:</span>
122              <button type="button" class="btn-ghost btn-sm" onclick="applyPreset('dark')">๐ŸŒ™ Dark</button>
123              <button type="button" class="btn-ghost btn-sm" onclick="applyPreset('warm')">๐Ÿ‚ Warm</button>
124              <button type="button" class="btn-ghost btn-sm" onclick="applyPreset('green')">๐ŸŒฟ Green</button>
125              <button type="button" class="btn-ghost btn-sm" onclick="applyPreset('purple')">๐Ÿ’œ Purple</button>
126            </div>
127            <textarea name="css" id="cssEditor" class="fi css-ta" rows="18" spellcheck="false"
128                      placeholder="/* Write your custom CSS here */
129/* Override any CSS variables or rules */
130 
131/* Example: Change primary color */
132:root {
133  --blue: #e74c3c;
134  --blue-d: #c0392b;
135  --blue-l: #fdf2f2;
136}
137 
138/* Example: Custom background */
139body {
140  background: #1a1a2e;
141  color: #e0e0e0;
142}"><?= e($editTheme['css'] ?? '') ?></textarea>
143          </div>
144        </div>
145 
146        <div style="display:flex;gap:10px;align-items:center">
147          <button type="submit" class="btn-primary">๐Ÿ’พ Save Theme</button>
148          <button type="button" class="btn-ghost" onclick="previewTheme()">๐Ÿ‘๏ธ Preview</button>
149          <span style="font-size:12px;color:#94a3b8">Preview opens forum in new tab</span>
150        </div>
151      </form>
152    </div>
153  </div>
154</div>
155 
156<!-- CSS Variable Reference -->
157<div class="acard">
158  <div class="acard-head"><h2>๐Ÿ“– CSS Variables Reference</h2></div>
159  <div class="acard-body">
160    <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;font-size:13px">
161      <?php
162      $vars = [
163        ['--blue','Primary color (links, buttons)','#3b82f6'],
164        ['--blue-d','Primary hover color','#2563eb'],
165        ['--blue-l','Primary light background','#eff6ff'],
166        ['--bg','Page background','#f1f5f9'],
167        ['--surface','Card/panel background','#ffffff'],
168        ['--border','Border color','#e2e8f0'],
169        ['--text','Main text color','#0f172a'],
170        ['--muted','Secondary text','#64748b'],
171        ['--green','Success/accent color','#22c55e'],
172        ['--red','Danger color','#ef4444'],
173        ['--amber','Warning color','#f59e0b'],
174        ['--header','Header height','56px'],
175        ['--sidebar','Sidebar width','210px'],
176        ['--font','Font family','Inter'],
177        ['--mono','Monospace font','JetBrains Mono'],
178        ['--r','Border radius','8px'],
179      ];
180      foreach ($vars as [$var,$desc,$val]): ?>
181        <div style="background:#f8fafc;padding:8px 10px;border-radius:6px;border:1px solid #e2e8f0">
182          <code style="color:#6366f1;font-size:12px"><?= e($var) ?></code>
183          <div style="font-size:11px;color:#94a3b8;margin-top:2px"><?= e($desc) ?></div>
184          <div style="font-size:11px;color:#64748b">Default: <em><?= e($val) ?></em></div>
185        </div>
186      <?php endforeach; ?>
187    </div>
188  </div>
189</div>
190 
191<style>
192.theme-row{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-bottom:1px solid #f1f5f9;transition:background .18s}
193.theme-row:hover{background:#f8fafc}
194.theme-row:last-child{border-bottom:none}
195.theme-active{background:#eff6ff}
196.theme-info{display:flex;align-items:center;flex:1}
197.theme-btns{display:flex;gap:6px;align-items:center}
198.css-editor-wrap{background:#1e293b;border-radius:8px;overflow:hidden}
199.css-presets{display:flex;align-items:center;gap:4px;padding:8px 12px;background:#0f172a}
200.css-ta{background:#1e293b!important;color:#e2e8f0!important;font-family:var(--mono)!important;font-size:13px!important;border-radius:0!important;border:none!important;padding:14px 16px!important;min-height:300px;line-height:1.6}
201.css-ta:focus{box-shadow:none!important}
202</style>
203 
204<script>
205var presets = {
206  dark: `:root {
207  --blue: #6366f1;
208  --blue-d: #4f46e5;
209  --blue-l: #1e1b4b;
210  --bg: #0f172a;
211  --surface: #1e293b;
212  --border: #334155;
213  --border-l: #1e293b;
214  --text: #f1f5f9;
215  --muted: #94a3b8;
216  --faint: #64748b;
217}
218.site-header { background: #1e293b; border-color: #334155; }
219.sidebar { background: #1e293b; border-color: #334155; }
220.site-footer { background: #1e293b; border-color: #334155; }
221.cat-card, .topic-row, .post { background: #1e293b; border-color: #334155; }
222.reply-box { background: #1e293b; border-color: #334155; }
223.tl-hdr { background: #0f172a; }
224.topic-list { background: #1e293b; border-color: #334155; }`,
225 
226  warm: `:root {
227  --blue: #ea580c;
228  --blue-d: #c2410c;
229  --blue-l: #fff7ed;
230  --bg: #fdf4ec;
231  --surface: #fffaf5;
232  --border: #fed7aa;
233  --text: #431407;
234  --muted: #9a3412;
235}`,
236 
237  green: `:root {
238  --blue: #16a34a;
239  --blue-d: #15803d;
240  --blue-l: #f0fdf4;
241  --bg: #f0fdf4;
242  --surface: #ffffff;
243  --border: #bbf7d0;
244  --text: #14532d;
245  --muted: #166534;
246}`,
247 
248  purple: `:root {
249  --blue: #9333ea;
250  --blue-d: #7e22ce;
251  --blue-l: #faf5ff;
252  --bg: #faf5ff;
253  --surface: #ffffff;
254  --border: #e9d5ff;
255  --text: #3b0764;
256  --muted: #6b21a8;
257}`
258};
259 
260function applyPreset(name) {
261  document.getElementById('cssEditor').value = presets[name] || '';
262}
263 
264function previewTheme() {
265  var css = document.getElementById('cssEditor').value;
266  var key = 'nx_preview_css';
267  localStorage.setItem(key, css);
268  var w = window.open('<?= u('/') ?>', '_blank');
269  w.addEventListener('load', function() {
270    w.postMessage({ type: 'nx_preview', css: css }, '*');
271  });
272  alert('Preview tip: the theme preview uses postMessage.\nActivate the theme to see it properly in all browsers.');
273}
274</script>
275 
276<?php include __DIR__ . '/../views/partials/admin_layout_end.php'; ?>