xgit simple git

nexus

nexus

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

admin/themes.php

1 <?php
2 require_once __DIR__ . '/../includes/bootstrap.php';
3 must_admin_only();
4 $PAGE_TITLE = 'Themes & Templates';
5 $ADMIN_PAGE = 'themes';
6 $flash = null;
7 
8 if ($_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 
44 include __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 */
139 body {
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>
205 var 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 
260 function applyPreset(name) {
261   document.getElementById('cssEditor').value = presets[name] || '';
262 }
263 
264 function 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'; ?>