domma-cms 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/config/plugins.json +0 -8
  2. package/package.json +1 -1
  3. package/plugins/job-board/admin/templates/application-detail.html +0 -40
  4. package/plugins/job-board/admin/templates/applications.html +0 -10
  5. package/plugins/job-board/admin/templates/companies.html +0 -24
  6. package/plugins/job-board/admin/templates/dashboard.html +0 -36
  7. package/plugins/job-board/admin/templates/job-editor.html +0 -17
  8. package/plugins/job-board/admin/templates/jobs.html +0 -15
  9. package/plugins/job-board/admin/templates/profile.html +0 -17
  10. package/plugins/job-board/admin/views/application-detail.js +0 -62
  11. package/plugins/job-board/admin/views/applications.js +0 -47
  12. package/plugins/job-board/admin/views/companies.js +0 -104
  13. package/plugins/job-board/admin/views/dashboard.js +0 -88
  14. package/plugins/job-board/admin/views/job-editor.js +0 -86
  15. package/plugins/job-board/admin/views/jobs.js +0 -53
  16. package/plugins/job-board/admin/views/profile.js +0 -47
  17. package/plugins/job-board/config.js +0 -6
  18. package/plugins/job-board/plugin.js +0 -466
  19. package/plugins/job-board/plugin.json +0 -40
  20. package/plugins/job-board/schemas/jb-agent-companies.json +0 -17
  21. package/plugins/job-board/schemas/jb-applications.json +0 -20
  22. package/plugins/job-board/schemas/jb-candidate-profiles.json +0 -20
  23. package/plugins/job-board/schemas/jb-companies.json +0 -21
  24. package/plugins/job-board/schemas/jb-jobs.json +0 -23
  25. package/plugins/theme-roller/admin/templates/theme-roller.html +0 -71
  26. package/plugins/theme-roller/admin/views/theme-roller-view.js +0 -403
  27. package/plugins/theme-roller/config.js +0 -1
  28. package/plugins/theme-roller/plugin.js +0 -233
  29. package/plugins/theme-roller/plugin.json +0 -31
  30. package/plugins/theme-roller/public/active-theme.css +0 -0
  31. package/plugins/theme-roller/public/inject-head-late.html +0 -1
@@ -1,233 +0,0 @@
1
- /**
2
- * Theme Roller Plugin — Server
3
- * Manages custom themes: CRUD on JSON files, CSS generation, and site config updates.
4
- *
5
- * Endpoints (prefix: /api/plugins/theme-roller):
6
- * GET /themes — admin: list saved themes
7
- * GET /themes/:name — admin: get single theme
8
- * POST /themes — admin: save new theme
9
- * PUT /themes/:name — admin: update existing theme
10
- * DELETE /themes/:name — admin: delete theme
11
- * POST /themes/:name/activate — admin: activate theme (generate CSS + update site config)
12
- * POST /deactivate — admin: revert to a built-in theme
13
- */
14
- import fs from 'node:fs/promises';
15
- import path from 'node:path';
16
- import {fileURLToPath} from 'url';
17
- import {getConfig, saveConfig} from '../../server/config.js';
18
-
19
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
- const THEMES_DIR = path.join(__dirname, 'data', 'themes');
21
- const CSS_FILE = path.join(__dirname, 'public', 'active-theme.css');
22
-
23
- // ---------------------------------------------------------------------------
24
- // Helpers
25
- // ---------------------------------------------------------------------------
26
-
27
- function themePath(name) {
28
- return path.join(THEMES_DIR, `${name}.json`);
29
- }
30
-
31
- async function readTheme(name) {
32
- return JSON.parse(await fs.readFile(themePath(name), 'utf8'));
33
- }
34
-
35
- async function writeTheme(name, data) {
36
- await fs.writeFile(themePath(name), JSON.stringify(data, null, 4) + '\n', 'utf8');
37
- }
38
-
39
- async function listThemes() {
40
- let entries;
41
- try {
42
- entries = await fs.readdir(THEMES_DIR);
43
- } catch {
44
- return [];
45
- }
46
- const themes = [];
47
- for (const f of entries) {
48
- if (!f.endsWith('.json')) continue;
49
- try {
50
- themes.push(JSON.parse(await fs.readFile(path.join(THEMES_DIR, f), 'utf8')));
51
- } catch { /* skip corrupt files */ }
52
- }
53
- return themes.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
54
- }
55
-
56
- /**
57
- * Generate CSS for a custom theme.
58
- * Produces `.dm-theme-{name} { --var: value; }` matching exportCSS() format.
59
- *
60
- * @param {Object} themeData - The theme JSON object
61
- * @returns {string} CSS string
62
- */
63
- function generateCSS(themeData) {
64
- const {name, changes} = themeData;
65
- if (!changes || Object.keys(changes).length === 0) return '';
66
-
67
- const date = new Date().toISOString().split('T')[0];
68
- let css = `/**\n * Custom Domma Theme: ${name}\n * Generated: ${date}\n */\n\n`;
69
- // Use :root so overrides apply regardless of which built-in theme class is on <body>.
70
- // active-theme.css loads last (headInjectLate), so same-specificity :root wins by source order.
71
- css += `:root {\n`;
72
- for (const [variable, value] of Object.entries(changes)) {
73
- css += ` ${variable}: ${value};\n`;
74
- }
75
- css += '}\n';
76
- return css;
77
- }
78
-
79
- /**
80
- * Write the active-theme.css file.
81
- *
82
- * @param {string} css - CSS content (empty string = no custom theme)
83
- */
84
- async function writeActiveCSS(css) {
85
- const content = css || '/* Domma CMS — active custom theme (empty until a custom theme is activated) */\n';
86
- await fs.writeFile(CSS_FILE, content, 'utf8');
87
- }
88
-
89
- // ---------------------------------------------------------------------------
90
- // Plugin registration
91
- // ---------------------------------------------------------------------------
92
-
93
- export default async function themeRollerPlugin(fastify, opts) {
94
- const {authenticate, requireAdmin} = opts.auth;
95
-
96
- // -----------------------------------------------------------------------
97
- // GET /themes — list all saved themes
98
- // -----------------------------------------------------------------------
99
- fastify.get('/themes', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
100
- return listThemes();
101
- });
102
-
103
- // -----------------------------------------------------------------------
104
- // GET /themes/:name — get single theme
105
- // -----------------------------------------------------------------------
106
- fastify.get('/themes/:name', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
107
- try {
108
- return await readTheme(req.params.name);
109
- } catch {
110
- return reply.code(404).send({error: 'Theme not found'});
111
- }
112
- });
113
-
114
- // -----------------------------------------------------------------------
115
- // POST /themes — save a new theme
116
- // -----------------------------------------------------------------------
117
- fastify.post('/themes', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
118
- const {name, baseTheme, changes} = req.body || {};
119
-
120
- if (!name || typeof name !== 'string') {
121
- return reply.code(400).send({error: 'name is required'});
122
- }
123
-
124
- // Sanitise name: lowercase alphanumeric + hyphens only
125
- const safeName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
126
- if (!safeName) return reply.code(400).send({error: 'Invalid theme name'});
127
-
128
- const now = new Date().toISOString();
129
- const theme = {
130
- name: safeName,
131
- baseTheme: baseTheme || 'charcoal-dark',
132
- changes: changes || {},
133
- createdAt: now,
134
- updatedAt: now
135
- };
136
-
137
- await writeTheme(safeName, theme);
138
- return reply.code(201).send(theme);
139
- });
140
-
141
- // -----------------------------------------------------------------------
142
- // PUT /themes/:name — update an existing theme
143
- // -----------------------------------------------------------------------
144
- fastify.put('/themes/:name', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
145
- const {name} = req.params;
146
- let existing;
147
- try {
148
- existing = await readTheme(name);
149
- } catch {
150
- return reply.code(404).send({error: 'Theme not found'});
151
- }
152
-
153
- const {baseTheme, changes} = req.body || {};
154
- const updated = {
155
- ...existing,
156
- baseTheme: baseTheme ?? existing.baseTheme,
157
- changes: changes ?? existing.changes,
158
- updatedAt: new Date().toISOString()
159
- };
160
-
161
- await writeTheme(name, updated);
162
-
163
- // If this is the currently active theme, regenerate CSS
164
- const siteConfig = getConfig('site');
165
- if (siteConfig.theme === name) {
166
- await writeActiveCSS(generateCSS(updated));
167
- }
168
-
169
- return updated;
170
- });
171
-
172
- // -----------------------------------------------------------------------
173
- // DELETE /themes/:name — delete a theme
174
- // -----------------------------------------------------------------------
175
- fastify.delete('/themes/:name', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
176
- const {name} = req.params;
177
- try {
178
- await fs.unlink(themePath(name));
179
- } catch {
180
- return reply.code(404).send({error: 'Theme not found'});
181
- }
182
-
183
- // If deleting the active theme, revert to charcoal-dark
184
- const siteConfig = getConfig('site');
185
- if (siteConfig.theme === name) {
186
- await writeActiveCSS('');
187
- await saveConfig('site', {...siteConfig, theme: 'charcoal-dark'});
188
- }
189
-
190
- return {success: true};
191
- });
192
-
193
- // -----------------------------------------------------------------------
194
- // POST /themes/:name/activate — activate a custom theme
195
- // -----------------------------------------------------------------------
196
- fastify.post('/themes/:name/activate', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
197
- const {name} = req.params;
198
- let theme;
199
- try {
200
- theme = await readTheme(name);
201
- } catch {
202
- return reply.code(404).send({error: 'Theme not found'});
203
- }
204
-
205
- // Generate and write CSS
206
- const css = generateCSS(theme);
207
- await writeActiveCSS(css);
208
-
209
- // Update site config so renderer applies the theme class to <body>
210
- // baseTheme is saved so the renderer can apply both the base and custom class
211
- const siteConfig = getConfig('site');
212
- await saveConfig('site', {...siteConfig, theme: name, baseTheme: theme.baseTheme});
213
-
214
- return {success: true, theme: name, css};
215
- });
216
-
217
- // -----------------------------------------------------------------------
218
- // POST /deactivate — revert to a built-in theme
219
- // -----------------------------------------------------------------------
220
- fastify.post('/deactivate', {preHandler: [authenticate, requireAdmin]}, async (req, reply) => {
221
- const {theme: builtInTheme = 'charcoal-dark'} = req.body || {};
222
-
223
- // Clear custom CSS
224
- await writeActiveCSS('');
225
-
226
- // Update site config to the chosen built-in theme, clearing custom baseTheme
227
- const siteConfig = getConfig('site');
228
- const {baseTheme: _, ...restConfig} = siteConfig;
229
- await saveConfig('site', {...restConfig, theme: builtInTheme});
230
-
231
- return {success: true, theme: builtInTheme};
232
- });
233
- }
@@ -1,31 +0,0 @@
1
- {
2
- "name": "theme-roller",
3
- "displayName": "Theme Roller",
4
- "version": "1.0.0",
5
- "description": "Visual theme customisation with live preview. Create, save, and apply custom themes.",
6
- "author": "Darryl Waterhouse",
7
- "date": "2026-03-06",
8
- "icon": "palette",
9
- "admin": {
10
- "sidebar": [{
11
- "id": "theme-roller",
12
- "text": "Theme Roller",
13
- "icon": "palette",
14
- "url": "#/plugins/theme-roller",
15
- "section": "#/plugins/theme-roller",
16
- "countKey": "themes"
17
- }],
18
- "routes": [
19
- { "path": "/plugins/theme-roller", "view": "plugin-theme-roller", "title": "Theme Roller - Domma CMS" }
20
- ],
21
- "views": {
22
- "plugin-theme-roller": {
23
- "entry": "theme-roller/admin/views/theme-roller-view.js",
24
- "exportName": "themeRollerView"
25
- }
26
- }
27
- },
28
- "inject": {
29
- "headLate": "public/inject-head-late.html"
30
- }
31
- }
File without changes
@@ -1 +0,0 @@
1
- <link rel="stylesheet" href="/plugins/theme-roller/public/active-theme.css">