@reldens/cms 0.47.0 → 0.49.0
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.
- package/.claude/advanced-usage-guide.md +98 -0
- package/.claude/api-reference.md +146 -0
- package/.claude/configuration-guide.md +126 -0
- package/.claude/database-schema.md +32 -0
- package/.claude/forms-system-guide.md +193 -0
- package/.claude/installation-guide.md +223 -0
- package/.claude/multi-domain-i18n-guide.md +178 -0
- package/.claude/password-management-guide.md +426 -0
- package/.claude/search-guide.md +66 -0
- package/.claude/templating-system-guide.md +349 -0
- package/CLAUDE.md +92 -3
- package/README.md +71 -1447
- package/bin/reldens-cms-update-password.js +246 -0
- package/lib/admin-manager/router-contents.js +14 -2
- package/lib/admin-manager/router.js +1 -0
- package/lib/manager.js +70 -8
- package/lib/password-encryption-handler.js +94 -0
- package/lib/template-engine.js +8 -2
- package/package.json +5 -4
- package/templates/.env.dist +1 -1
- package/templates/page.html +15 -11
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ A powerful, flexible Content Management System built with Node.js, featuring an
|
|
|
59
59
|
- **Modular service architecture** with specialized classes for better maintainability
|
|
60
60
|
- **Event-driven system** with hooks for customization
|
|
61
61
|
- **Extensible authentication** (database users or custom callbacks)
|
|
62
|
+
- **Password encryption** with PBKDF2 (100k iterations, SHA-512) - automatic on save
|
|
62
63
|
- **File security** with path validation and dangerous key filtering
|
|
63
64
|
- **Internationalization support** with translation files
|
|
64
65
|
|
|
@@ -150,1502 +151,125 @@ cms.start();
|
|
|
150
151
|
|
|
151
152
|
## Configuration
|
|
152
153
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
RELDENS_ADMIN_ROUTE_PATH=/admin
|
|
158
|
-
RELDENS_ADMIN_SECRET=your-secret-key
|
|
159
|
-
|
|
160
|
-
RELDENS_DB_CLIENT=mysql
|
|
161
|
-
RELDENS_DB_HOST=localhost
|
|
162
|
-
RELDENS_DB_PORT=3306
|
|
163
|
-
RELDENS_DB_NAME=cms_db
|
|
164
|
-
RELDENS_DB_USER=username
|
|
165
|
-
RELDENS_DB_PASSWORD=password
|
|
166
|
-
RELDENS_STORAGE_DRIVER=prisma
|
|
167
|
-
|
|
168
|
-
RELDENS_DEFAULT_DOMAIN=example.com
|
|
169
|
-
RELDENS_DOMAIN_MAPPING={"dev.example.com":"development"}
|
|
170
|
-
RELDENS_SITE_KEY_MAPPING={"example.com":"main"}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Template Reloading Configuration
|
|
174
|
-
Configure template reloading for development environments:
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
const cms = new Manager({
|
|
178
|
-
// Development: reload templates on every request when changes detected
|
|
179
|
-
reloadTime: -1,
|
|
180
|
-
|
|
181
|
-
// Production: disable template reloading (default)
|
|
182
|
-
reloadTime: 0,
|
|
183
|
-
|
|
184
|
-
// Interval-based: reload every 5 seconds when changes detected
|
|
185
|
-
reloadTime: 5000
|
|
186
|
-
});
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Template Reloading Options:**
|
|
190
|
-
- **`reloadTime: 0`** (default) - Template reloading disabled. Templates load once at startup.
|
|
191
|
-
- **`reloadTime: -1`** - Reload templates on every request when file changes are detected. Best for active development.
|
|
192
|
-
- **`reloadTime: > 0`** - Check for template changes at specified interval (milliseconds) and reload when needed. Good for development with lower overhead.
|
|
154
|
+
**Environment variables:** Database, server, admin, multi-domain settings
|
|
155
|
+
**Template reloading:** Configure for development (-1) or production (0)
|
|
156
|
+
**Entity configuration:** Custom entity properties, validation, and relationships
|
|
157
|
+
**Manager options:** Authentication, caching, domain mapping
|
|
193
158
|
|
|
194
|
-
**
|
|
195
|
-
- Tracks file modification times for admin and frontend templates
|
|
196
|
-
- Only reloads templates that have actually changed
|
|
197
|
-
- Automatically updates admin contents and frontend template cache
|
|
198
|
-
- Works with both admin panel templates and frontend templates/partials
|
|
199
|
-
- Zero performance impact when disabled (`reloadTime: 0`)
|
|
200
|
-
|
|
201
|
-
### Custom Entity Configuration
|
|
202
|
-
```javascript
|
|
203
|
-
const entityConfig = {
|
|
204
|
-
articles: {
|
|
205
|
-
listProperties: ['title', 'status', 'created_at'],
|
|
206
|
-
showProperties: ['title', 'content', 'author', 'status'],
|
|
207
|
-
editProperties: ['title', 'content', 'author_id', 'status'],
|
|
208
|
-
filterProperties: ['status', 'author_id'],
|
|
209
|
-
titleProperty: 'title',
|
|
210
|
-
parentItemLabel: 'Content',
|
|
211
|
-
properties: {
|
|
212
|
-
title: { type: 'string', isRequired: true },
|
|
213
|
-
content: { type: 'text' },
|
|
214
|
-
author_id: { type: 'reference', reference: 'users' },
|
|
215
|
-
featured_image: {
|
|
216
|
-
type: 'string',
|
|
217
|
-
isUpload: true,
|
|
218
|
-
allowedTypes: 'image',
|
|
219
|
-
bucket: 'uploads'
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const cms = new Manager({
|
|
226
|
-
entitiesConfig: entityConfig
|
|
227
|
-
});
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Advanced Installation Features
|
|
159
|
+
**Full documentation:** See `.claude/configuration-guide.md`
|
|
231
160
|
|
|
232
|
-
|
|
233
|
-
The installer now supports complex operations through subprocess management:
|
|
161
|
+
## CLI Commands
|
|
234
162
|
|
|
235
|
-
|
|
236
|
-
const { Installer } = require('@reldens/cms');
|
|
237
|
-
|
|
238
|
-
const installer = new Installer({
|
|
239
|
-
projectRoot: process.cwd(),
|
|
240
|
-
subprocessMaxAttempts: 1800, // 3 minutes timeout
|
|
241
|
-
postInstallCallback: async (props) => {
|
|
242
|
-
// Custom initialization after installation
|
|
243
|
-
console.log('Entities loaded:', Object.keys(props.loadedEntities.rawRegisteredEntities));
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// The installer automatically handles:
|
|
249
|
-
// - Package dependency checking and installation
|
|
250
|
-
// - Database schema creation via subprocess
|
|
251
|
-
// - Prisma client generation with progress tracking
|
|
252
|
-
// - Entity generation with validation
|
|
253
|
-
// - Environment file creation
|
|
254
|
-
// - Directory structure setup
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Enhanced Manager Initialization
|
|
258
|
-
The Manager class now provides comprehensive service initialization:
|
|
259
|
-
|
|
260
|
-
```javascript
|
|
261
|
-
const cms = new Manager({
|
|
262
|
-
// Server validation - automatically validates provided instances
|
|
263
|
-
app: customExpressApp, // Optional: provide your own Express app
|
|
264
|
-
appServer: customAppServer, // Optional: provide your own HTTP server
|
|
265
|
-
dataServer: customDataServer, // Optional: provide your own database driver
|
|
266
|
-
adminManager: customAdmin, // Optional: provide your own admin manager
|
|
267
|
-
frontend: customFrontend, // Optional: provide your own frontend handler
|
|
268
|
-
|
|
269
|
-
// Configuration validation
|
|
270
|
-
adminRoleId: 99, // Admin role ID for authentication
|
|
271
|
-
authenticationMethod: 'db-users', // or 'custom'
|
|
272
|
-
authenticationCallback: async (email, password, roleId) => {
|
|
273
|
-
// Custom authentication logic
|
|
274
|
-
return await yourAuthService.validate(email, password, roleId);
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
// Performance configuration
|
|
278
|
-
cache: true, // Enable caching
|
|
279
|
-
reloadTime: -1, // Template reloading for development
|
|
280
|
-
|
|
281
|
-
// Multi-domain configuration
|
|
282
|
-
defaultDomain: 'example.com',
|
|
283
|
-
domainMapping: {'dev.example.com': 'development'},
|
|
284
|
-
siteKeyMapping: {'example.com': 'main'}
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Manager automatically:
|
|
288
|
-
// - Validates all provided instances
|
|
289
|
-
// - Initializes missing services
|
|
290
|
-
// - Sets up entity access control
|
|
291
|
-
// - Generates admin entities
|
|
292
|
-
// - Configures template reloading
|
|
293
|
-
```
|
|
163
|
+
### Start CMS
|
|
294
164
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
The CMS automatically detects development environments based on domain patterns. Domain mapping keys are **no longer automatically treated as development domains**.
|
|
298
|
-
|
|
299
|
-
**Default Development Patterns:**
|
|
300
|
-
```javascript
|
|
301
|
-
const patterns = [
|
|
302
|
-
'localhost',
|
|
303
|
-
'127.0.0.1',
|
|
304
|
-
'.local', // Domains ending with .local
|
|
305
|
-
'.test', // Domains ending with .test
|
|
306
|
-
'.dev', // Domains ending with .dev
|
|
307
|
-
'.acc', // Domains ending with .acc
|
|
308
|
-
'.staging', // Domains ending with .staging
|
|
309
|
-
'local.', // Domains starting with local.
|
|
310
|
-
'test.', // Domains starting with test.
|
|
311
|
-
'dev.', // Domains starting with dev.
|
|
312
|
-
'acc.', // Domains starting with acc.
|
|
313
|
-
'staging.' // Domains starting with staging.
|
|
314
|
-
];
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
**Override Development Patterns:**
|
|
318
|
-
```javascript
|
|
319
|
-
const cms = new Manager({
|
|
320
|
-
// Only these patterns will trigger development mode
|
|
321
|
-
developmentPatterns: [
|
|
322
|
-
'localhost',
|
|
323
|
-
'127.0.0.1',
|
|
324
|
-
'.local'
|
|
325
|
-
],
|
|
326
|
-
domainMapping: {
|
|
327
|
-
// These are just aliases - NOT automatically development
|
|
328
|
-
'www.example.com': 'example.com',
|
|
329
|
-
'new.example.com': 'example.com'
|
|
330
|
-
}
|
|
331
|
-
});
|
|
165
|
+
```bash
|
|
166
|
+
npx reldens-cms
|
|
332
167
|
```
|
|
333
168
|
|
|
334
|
-
|
|
335
|
-
- The bug in pattern matching where domains with common substrings (e.g., "reldens" in both "acc.reldens.com" and "reldens.new") incorrectly triggered development mode has been fixed.
|
|
336
|
-
- Domain patterns now only match at the start or end of domains, not arbitrary positions.
|
|
337
|
-
- Override `developmentPatterns` in production to prevent staging/acc domains from enabling development mode.
|
|
338
|
-
|
|
339
|
-
### Security Configuration
|
|
340
|
-
|
|
341
|
-
Configure Content Security Policy and security headers through the app server config:
|
|
169
|
+
Runs the installer if not installed, otherwise starts the CMS server.
|
|
342
170
|
|
|
343
|
-
|
|
171
|
+
### Generate Entities
|
|
344
172
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const cms = new Manager({
|
|
349
|
-
appServerConfig: {
|
|
350
|
-
developmentExternalDomains: {
|
|
351
|
-
// Both formats work - choose whichever you prefer
|
|
352
|
-
'scriptSrc': ['https://cdn.example.com'], // camelCase
|
|
353
|
-
'script-src': ['https://analytics.example.com'], // kebab-case (auto-converted)
|
|
354
|
-
'styleSrc': ['https://fonts.googleapis.com'], // camelCase
|
|
355
|
-
'font-src': ['https://fonts.gstatic.com'] // kebab-case (auto-converted)
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
});
|
|
173
|
+
```bash
|
|
174
|
+
npx reldens-cms-generate-entities
|
|
175
|
+
npx reldens-cms-generate-entities --override
|
|
359
176
|
```
|
|
360
177
|
|
|
361
|
-
|
|
362
|
-
- Converts kebab-case keys to camelCase (e.g., `'script-src'` → `scriptSrc`)
|
|
363
|
-
- Adds domains to both the base directive and the `-elem` variant (e.g., `scriptSrc` and `scriptSrcElem`)
|
|
364
|
-
|
|
365
|
-
#### CSP Directive Merging vs Override
|
|
366
|
-
|
|
367
|
-
By default, custom CSP directives are **merged** with security defaults:
|
|
178
|
+
Generate entity classes from database schema.
|
|
368
179
|
|
|
369
|
-
|
|
370
|
-
const cms = new Manager({
|
|
371
|
-
appServerConfig: {
|
|
372
|
-
helmetConfig: {
|
|
373
|
-
contentSecurityPolicy: {
|
|
374
|
-
// Default: merge with base directives
|
|
375
|
-
directives: {
|
|
376
|
-
scriptSrc: ['https://cdn.example.com']
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
});
|
|
180
|
+
### Update User Password
|
|
382
181
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
**Default Base Directives:**
|
|
387
|
-
```javascript
|
|
388
|
-
{
|
|
389
|
-
defaultSrc: ["'self'"],
|
|
390
|
-
scriptSrc: ["'self'"],
|
|
391
|
-
scriptSrcElem: ["'self'"],
|
|
392
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
393
|
-
styleSrcElem: ["'self'", "'unsafe-inline'"],
|
|
394
|
-
imgSrc: ["'self'", "data:", "https:"],
|
|
395
|
-
fontSrc: ["'self'"],
|
|
396
|
-
connectSrc: ["'self'"],
|
|
397
|
-
frameAncestors: ["'none'"],
|
|
398
|
-
baseUri: ["'self'"],
|
|
399
|
-
formAction: ["'self'"]
|
|
400
|
-
}
|
|
182
|
+
```bash
|
|
183
|
+
npx reldens-cms-update-password --email=admin@example.com
|
|
184
|
+
npx reldens-cms-update-password --username=admin
|
|
401
185
|
```
|
|
402
186
|
|
|
403
|
-
|
|
187
|
+
Securely update user passwords via CLI. Password will be prompted if not provided.
|
|
404
188
|
|
|
405
|
-
|
|
406
|
-
const cms = new Manager({
|
|
407
|
-
appServerConfig: {
|
|
408
|
-
helmetConfig: {
|
|
409
|
-
contentSecurityPolicy: {
|
|
410
|
-
overrideDirectives: true, // Replace defaults entirely
|
|
411
|
-
directives: {
|
|
412
|
-
defaultSrc: ["'self'"],
|
|
413
|
-
scriptSrc: ["'self'", "https://trusted-cdn.com"],
|
|
414
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
415
|
-
imgSrc: ["'self'", "data:", "https:"],
|
|
416
|
-
fontSrc: ["'self'"],
|
|
417
|
-
connectSrc: ["'self'"],
|
|
418
|
-
frameAncestors: ["'none'"],
|
|
419
|
-
baseUri: ["'self'"],
|
|
420
|
-
formAction: ["'self'"]
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
```
|
|
189
|
+
## Password Management
|
|
427
190
|
|
|
428
|
-
|
|
191
|
+
**Automatic encryption:** PBKDF2 with 100k iterations, SHA-512
|
|
192
|
+
**CLI password update:** `npx reldens-cms-update-password --email=user@example.com`
|
|
193
|
+
**Event-driven:** Encrypts passwords automatically on save via admin panel
|
|
194
|
+
**Storage format:** salt:hash (192 characters total)
|
|
195
|
+
**Enabled by default:** Set `enablePasswordEncryption: false` to disable
|
|
429
196
|
|
|
430
|
-
|
|
197
|
+
**Full documentation:** See `.claude/password-management-guide.md`
|
|
431
198
|
|
|
432
|
-
|
|
433
|
-
const cms = new Manager({
|
|
434
|
-
appServerConfig: {
|
|
435
|
-
helmetConfig: {
|
|
436
|
-
// HTTP Strict Transport Security
|
|
437
|
-
hsts: {
|
|
438
|
-
maxAge: 31536000, // 1 year in seconds
|
|
439
|
-
includeSubDomains: true,
|
|
440
|
-
preload: true
|
|
441
|
-
},
|
|
442
|
-
// Cross-Origin-Opener-Policy
|
|
443
|
-
crossOriginOpenerPolicy: {
|
|
444
|
-
policy: "same-origin"
|
|
445
|
-
},
|
|
446
|
-
// Cross-Origin-Resource-Policy
|
|
447
|
-
crossOriginResourcePolicy: {
|
|
448
|
-
policy: "same-origin"
|
|
449
|
-
},
|
|
450
|
-
// Cross-Origin-Embedder-Policy
|
|
451
|
-
crossOriginEmbedderPolicy: {
|
|
452
|
-
policy: "require-corp"
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
```
|
|
199
|
+
## Advanced Installation Features
|
|
458
200
|
|
|
459
|
-
**
|
|
201
|
+
**Subprocess handling** for complex operations with progress tracking
|
|
202
|
+
**Enhanced Manager** with service validation and automatic initialization
|
|
203
|
+
**Development mode detection** with configurable patterns
|
|
204
|
+
**Security configuration** with CSP, Helmet headers, and external domain support
|
|
460
205
|
|
|
461
|
-
**
|
|
462
|
-
```javascript
|
|
463
|
-
requireTrustedTypesFor: ["'script'"]
|
|
464
|
-
```
|
|
465
|
-
However, this requires updating all JavaScript code to use the Trusted Types API.
|
|
206
|
+
**Full documentation:** See `.claude/installation-guide.md`
|
|
466
207
|
|
|
467
208
|
## Dynamic Forms System
|
|
468
209
|
|
|
469
|
-
|
|
470
|
-
Create forms in your templates using the `<cmsForm>` tag:
|
|
471
|
-
|
|
472
|
-
```html
|
|
473
|
-
<!-- Render all form fields -->
|
|
474
|
-
<cmsForm key="contactForm"/>
|
|
475
|
-
|
|
476
|
-
<!-- Render specific fields only -->
|
|
477
|
-
<cmsForm key="contactForm" fields="name,email,subject,message"/>
|
|
478
|
-
|
|
479
|
-
<!-- Custom form attributes -->
|
|
480
|
-
<cmsForm key="newsletterSignup"
|
|
481
|
-
fields="email,name"
|
|
482
|
-
submitButtonText="Subscribe Now"
|
|
483
|
-
cssClass="newsletter-form"
|
|
484
|
-
successRedirect="/thank-you"
|
|
485
|
-
errorRedirect="/contact-error"/>
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### Form Configuration in Database
|
|
489
|
-
Forms are configured in the `cms_forms` table via the admin panel:
|
|
490
|
-
|
|
491
|
-
```sql
|
|
492
|
-
-- Example form configuration
|
|
493
|
-
INSERT INTO cms_forms (form_key, fields_schema, enabled) VALUES
|
|
494
|
-
('contactForm', '[
|
|
495
|
-
{
|
|
496
|
-
"name": "name",
|
|
497
|
-
"type": "text",
|
|
498
|
-
"label": "Full Name",
|
|
499
|
-
"required": true,
|
|
500
|
-
"maxLength": 100,
|
|
501
|
-
"placeholder": "Enter your full name"
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
"name": "email",
|
|
505
|
-
"type": "email",
|
|
506
|
-
"label": "Email Address",
|
|
507
|
-
"required": true,
|
|
508
|
-
"placeholder": "your@email.com"
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
"name": "subject",
|
|
512
|
-
"type": "select",
|
|
513
|
-
"label": "Subject",
|
|
514
|
-
"required": true,
|
|
515
|
-
"options": [
|
|
516
|
-
{"value": "general", "label": "General Inquiry"},
|
|
517
|
-
{"value": "support", "label": "Technical Support"},
|
|
518
|
-
{"value": "sales", "label": "Sales Question"}
|
|
519
|
-
]
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
"name": "message",
|
|
523
|
-
"type": "textarea",
|
|
524
|
-
"label": "Message",
|
|
525
|
-
"required": true,
|
|
526
|
-
"maxLength": 1000,
|
|
527
|
-
"placeholder": "Enter your message here..."
|
|
528
|
-
}
|
|
529
|
-
]', 1);
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### Supported Field Types
|
|
533
|
-
The forms system supports various field types with validation:
|
|
534
|
-
|
|
535
|
-
- **text** - Basic text input with maxLength, pattern validation
|
|
536
|
-
- **email** - Email input with built-in email validation
|
|
537
|
-
- **number** - Numeric input with min/max validation
|
|
538
|
-
- **textarea** - Multi-line text with maxLength
|
|
539
|
-
- **select** - Dropdown with options array
|
|
540
|
-
- **password** - Password input (masked)
|
|
541
|
-
- **tel** - Phone number input
|
|
542
|
-
- **url** - URL input with validation
|
|
543
|
-
- **date** - Date picker input
|
|
544
|
-
|
|
545
|
-
### Field Schema Properties
|
|
546
|
-
Each field in the `fields_schema` JSON supports:
|
|
547
|
-
|
|
548
|
-
```json
|
|
549
|
-
{
|
|
550
|
-
"name": "fieldName", // Required: Field identifier
|
|
551
|
-
"type": "text", // Required: Field type
|
|
552
|
-
"label": "Field Label", // Display label
|
|
553
|
-
"required": true, // Validation: required field
|
|
554
|
-
"placeholder": "Enter text...", // Input placeholder
|
|
555
|
-
"helpText": "Additional help", // Help text below field
|
|
556
|
-
"maxLength": 100, // String length limit
|
|
557
|
-
"minLength": 3, // Minimum string length
|
|
558
|
-
"pattern": "^[A-Za-z]+$", // Regex validation pattern
|
|
559
|
-
"min": 0, // Number minimum value
|
|
560
|
-
"max": 100, // Number maximum value
|
|
561
|
-
"step": 1, // Number step increment
|
|
562
|
-
"defaultValue": "default", // Default field value
|
|
563
|
-
"options": [ // Select/radio options
|
|
564
|
-
{"value": "val1", "label": "Option 1"},
|
|
565
|
-
{"value": "val2", "label": "Option 2"}
|
|
566
|
-
]
|
|
567
|
-
}
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
### Security Features
|
|
571
|
-
The form system includes comprehensive security measures:
|
|
572
|
-
|
|
573
|
-
#### 1. Honeypot Protection
|
|
574
|
-
Automatic bot detection using invisible fields:
|
|
575
|
-
```html
|
|
576
|
-
<!-- Automatically added to all forms -->
|
|
577
|
-
<div class="hidden">
|
|
578
|
-
<input type="text" name="website_url" value="" />
|
|
579
|
-
</div>
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
#### 2. Server-Side Validation
|
|
583
|
-
- **SchemaValidator integration** - Uses `@reldens/utils` SchemaValidator
|
|
584
|
-
- **Required field validation** - Ensures all required fields are provided
|
|
585
|
-
- **Type validation** - Email, number, string validation with patterns
|
|
586
|
-
- **Length limits** - Configurable per field via schema
|
|
587
|
-
- **Custom validation** - Extensible validation rules
|
|
588
|
-
|
|
589
|
-
#### 3. Data Sanitization
|
|
590
|
-
- **XSS protection** - Handled by `@reldens/server-utils` SecurityConfigurer
|
|
591
|
-
- **Input normalization** - Type-specific data processing
|
|
592
|
-
- **Length truncation** - Based on field schema maxLength
|
|
593
|
-
|
|
594
|
-
#### 4. Rate Limiting
|
|
595
|
-
- **AppServerFactory integration** - Uses existing rate limiting from server-utils
|
|
596
|
-
- **No duplicate implementation** - Leverages proven security measures
|
|
597
|
-
|
|
598
|
-
### Template Customization
|
|
599
|
-
Forms use a domain-aware template fallback system:
|
|
600
|
-
|
|
601
|
-
```
|
|
602
|
-
templates/
|
|
603
|
-
├── domains/
|
|
604
|
-
│ └── example.com/
|
|
605
|
-
│ └── cms_forms/
|
|
606
|
-
│ ├── form.html # Domain-specific form wrapper
|
|
607
|
-
│ ├── field_text.html # Domain-specific text field
|
|
608
|
-
│ └── field_email.html # Domain-specific email field
|
|
609
|
-
└── cms_forms/ # Default templates
|
|
610
|
-
├── form.html # Main form wrapper
|
|
611
|
-
├── field_text.html # Text input template
|
|
612
|
-
├── field_email.html # Email input template
|
|
613
|
-
├── field_textarea.html # Textarea template
|
|
614
|
-
├── field_select.html # Select dropdown template
|
|
615
|
-
└── field_number.html # Number input template
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
### Custom Field Templates
|
|
619
|
-
Create custom field templates for specific types:
|
|
620
|
-
|
|
621
|
-
**templates/cms_forms/field_text.html:**
|
|
622
|
-
```html
|
|
623
|
-
<div class="form-field {{errorClass}} {{requiredClass}}">
|
|
624
|
-
<label for="{{fieldName}}" class="form-label">
|
|
625
|
-
{{fieldLabel}}{{#isRequired}} <span class="required-indicator">*</span>{{/isRequired}}
|
|
626
|
-
</label>
|
|
627
|
-
<input type="{{fieldType}}"
|
|
628
|
-
name="submittedValues[{{fieldName}}]"
|
|
629
|
-
id="{{fieldName}}"
|
|
630
|
-
value="{{fieldValue}}"
|
|
631
|
-
class="form-control {{#hasError}}is-invalid{{/hasError}}"
|
|
632
|
-
{{#isRequired}}required{{/isRequired}}
|
|
633
|
-
{{#placeholder}}placeholder="{{placeholder}}"{{/placeholder}}
|
|
634
|
-
{{#maxLength}}maxlength="{{maxLength}}"{{/maxLength}}
|
|
635
|
-
{{#pattern}}pattern="{{pattern}}"{{/pattern}} />
|
|
636
|
-
{{#helpText}}<div class="form-text">{{helpText}}</div>{{/helpText}}
|
|
637
|
-
{{#hasError}}<div class="invalid-feedback">{{fieldError}}</div>{{/hasError}}
|
|
638
|
-
</div>
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
**templates/cms_forms/form.html:**
|
|
642
|
-
```html
|
|
643
|
-
<form method="POST" action="{{submitUrl}}" class="{{cssClass}}">
|
|
644
|
-
<input type="hidden" name="formKey" value="{{formKey}}" />
|
|
645
|
-
<input type="hidden" name="successRedirect" value="{{successRedirect}}" />
|
|
646
|
-
<input type="hidden" name="errorRedirect" value="{{errorRedirect}}" />
|
|
647
|
-
<div class="hidden">
|
|
648
|
-
<input type="text" name="{{honeypotFieldName}}" value="" />
|
|
649
|
-
</div>
|
|
650
|
-
{{&formFields}}
|
|
651
|
-
<div class="form-submit">
|
|
652
|
-
<button type="submit" class="btn btn-primary">{{submitButtonText}}</button>
|
|
653
|
-
</div>
|
|
654
|
-
</form>
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
### Forms with System Variables
|
|
658
|
-
Forms can access system variables and enhanced data in templates:
|
|
659
|
-
|
|
660
|
-
```html
|
|
661
|
-
<!-- Form with the current user context -->
|
|
662
|
-
<cmsForm key="userProfile" fields="name,email,bio"/>
|
|
663
|
-
|
|
664
|
-
<!-- In the form template, access system variables: -->
|
|
665
|
-
<form method="POST" action="{{submitUrl}}" class="{{cssClass}}">
|
|
666
|
-
<h2>Update Profile for {{currentRequest.host}}</h2>
|
|
667
|
-
<p>Current time: {{systemInfo.timestamp}}</p>
|
|
668
|
-
{{&formFields}}
|
|
669
|
-
<button type="submit">Update Profile</button>
|
|
670
|
-
</form>
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
### Event System Integration
|
|
674
|
-
The forms system provides comprehensive event hooks:
|
|
675
|
-
|
|
676
|
-
```javascript
|
|
677
|
-
// Listen for form events
|
|
678
|
-
cms.events.on('reldens.formsTransformer.beforeRender', (eventData) => {
|
|
679
|
-
console.log('Rendering form:', eventData.formKey);
|
|
680
|
-
// Modify form attributes or fields before rendering
|
|
681
|
-
eventData.formAttributes.cssClass += ' custom-form';
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
cms.events.on('reldens.dynamicForm.beforeValidation', (eventData) => {
|
|
685
|
-
console.log('Validating form:', eventData.formKey);
|
|
686
|
-
// Add custom validation logic
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
cms.events.on('reldens.dynamicForm.afterSave', (eventData) => {
|
|
690
|
-
console.log('Form saved:', eventData.result.id);
|
|
691
|
-
// Send notifications, trigger workflows, etc.
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
cms.events.on('reldens.dynamicFormRequestHandler.beforeSave', (eventData) => {
|
|
695
|
-
// Modify prepared values before saving
|
|
696
|
-
eventData.preparedValues.submissionDate = new Date().toISOString();
|
|
697
|
-
});
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### Available Form Events
|
|
701
|
-
- `reldens.formsTransformer.beforeRender` - Before form rendering
|
|
702
|
-
- `reldens.formsTransformer.afterRender` - After form rendering
|
|
703
|
-
- `reldens.dynamicForm.beforeValidation` - Before form validation
|
|
704
|
-
- `reldens.dynamicForm.afterValidation` - After form validation
|
|
705
|
-
- `reldens.dynamicForm.beforeSave` - Before saving to the database
|
|
706
|
-
- `reldens.dynamicForm.afterSave` - After successful save
|
|
707
|
-
- `reldens.dynamicFormRenderer.beforeFieldsRender` - Before rendering fields
|
|
708
|
-
- `reldens.dynamicFormRenderer.afterFieldsRender` - After rendering fields
|
|
709
|
-
- `reldens.dynamicFormRequestHandler.beforeValidation` - Before request validation
|
|
710
|
-
- `reldens.dynamicFormRequestHandler.beforeSave` - Before save process
|
|
711
|
-
- `reldens.dynamicFormRequestHandler.afterSave` - After successful save
|
|
712
|
-
|
|
713
|
-
### Database Tables
|
|
714
|
-
The forms system uses two main tables:
|
|
715
|
-
|
|
716
|
-
#### cms_forms Table
|
|
717
|
-
Store form configurations:
|
|
718
|
-
```sql
|
|
719
|
-
CREATE TABLE `cms_forms` (
|
|
720
|
-
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
721
|
-
`form_key` VARCHAR(255) NOT NULL UNIQUE,
|
|
722
|
-
`fields_schema` JSON NOT NULL,
|
|
723
|
-
`enabled` TINYINT UNSIGNED NOT NULL DEFAULT '0',
|
|
724
|
-
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
725
|
-
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
726
|
-
PRIMARY KEY (`id`)
|
|
727
|
-
);
|
|
728
|
-
```
|
|
210
|
+
Create database-driven forms with `<cmsForm key="contactForm"/>` tags in templates. Features:
|
|
729
211
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
`form_id` INT UNSIGNED NOT NULL,
|
|
736
|
-
`submitted_values` JSON NOT NULL,
|
|
737
|
-
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
738
|
-
PRIMARY KEY (`id`),
|
|
739
|
-
FOREIGN KEY (`form_id`) REFERENCES `cms_forms`(`id`)
|
|
740
|
-
);
|
|
741
|
-
```
|
|
212
|
+
- Schema-based validation with multiple field types (text, email, select, etc.)
|
|
213
|
+
- Security: Honeypot protection, rate limiting, XSS protection
|
|
214
|
+
- Domain-aware template customization
|
|
215
|
+
- Event hooks for custom processing
|
|
216
|
+
- Database storage in cms_forms and cms_forms_submitted tables
|
|
742
217
|
|
|
743
|
-
|
|
744
|
-
1. **Template Processing** - `FormsTransformer` finds `<cmsForm>` tags
|
|
745
|
-
2. **Form Loading** - Loads form configuration from database
|
|
746
|
-
3. **Field Filtering** - Applies field filter if specified
|
|
747
|
-
4. **Template Rendering** - Renders form using domain-aware templates
|
|
748
|
-
5. **Form Submission** - POST request to `/dynamic-form` endpoint
|
|
749
|
-
6. **Validation** - Honeypot, required fields, and schema validation
|
|
750
|
-
7. **Data Processing** - Input sanitization and normalization
|
|
751
|
-
8. **Database Storage** - Save to `cms_forms_submitted` table
|
|
752
|
-
9. **Response** - Redirect with success/error parameters
|
|
753
|
-
|
|
754
|
-
### Advanced Form Usage
|
|
755
|
-
|
|
756
|
-
#### Multi-Step Forms
|
|
757
|
-
```html
|
|
758
|
-
<!-- Step 1: Basic info -->
|
|
759
|
-
<cmsForm key="applicationForm" fields="name,email,phone"/>
|
|
760
|
-
|
|
761
|
-
<!-- Step 2: Details (separate form) -->
|
|
762
|
-
<cmsForm key="applicationDetails" fields="experience,portfolio"/>
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
#### Conditional Field Display
|
|
766
|
-
Use JavaScript to show/hide fields based on selections:
|
|
767
|
-
```html
|
|
768
|
-
<cmsForm key="surveyForm" fields="age,experience,expertise"/>
|
|
769
|
-
|
|
770
|
-
<script>
|
|
771
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
772
|
-
const ageField = document.getElementById('age');
|
|
773
|
-
const experienceField = document.getElementById('experience');
|
|
774
|
-
|
|
775
|
-
ageField.addEventListener('change', function() {
|
|
776
|
-
if(parseInt(this.value) >= 18) {
|
|
777
|
-
experienceField.parentElement.style.display = 'block';
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
experienceField.parentElement.style.display = 'none';
|
|
781
|
-
});
|
|
782
|
-
});
|
|
783
|
-
</script>
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
#### AJAX Form Submissions
|
|
787
|
-
Enable JSON responses for AJAX handling:
|
|
788
|
-
```javascript
|
|
789
|
-
const cms = new Manager({
|
|
790
|
-
enableJsonResponse: true // Enable JSON responses for forms
|
|
791
|
-
});
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
```javascript
|
|
795
|
-
// Frontend AJAX handling
|
|
796
|
-
document.querySelector('.dynamic-form').addEventListener('submit', async function(e) {
|
|
797
|
-
e.preventDefault();
|
|
798
|
-
|
|
799
|
-
const response = await fetch('/dynamic-form', {method: 'POST', body: new FormData(this)});
|
|
800
|
-
|
|
801
|
-
const result = await response.json();
|
|
802
|
-
if(result.success) {
|
|
803
|
-
alert('Form submitted successfully!');
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
alert('Error: ' + result.error);
|
|
807
|
-
});
|
|
808
|
-
```
|
|
218
|
+
**Full documentation:** See `.claude/forms-system-guide.md`
|
|
809
219
|
|
|
810
220
|
## Search Functionality
|
|
811
221
|
|
|
812
|
-
|
|
813
|
-
```bash
|
|
814
|
-
# Simple search
|
|
815
|
-
/search?search=technology
|
|
816
|
-
|
|
817
|
-
# Entity-specific search with custom limit
|
|
818
|
-
/search?search=javascript&limit=20
|
|
819
|
-
|
|
820
|
-
# Custom template rendering
|
|
821
|
-
/search?search=news&renderPartial=newsListView&renderLayout=minimal
|
|
822
|
-
```
|
|
222
|
+
Multi-entity search with pagination and template data support.
|
|
823
223
|
|
|
824
|
-
|
|
825
|
-
```bash
|
|
826
|
-
# Pass custom template variables
|
|
827
|
-
/search?search=articles&templateData[columnsClass]=col-md-4&templateData[showExcerpt]=true
|
|
828
|
-
|
|
829
|
-
# Multiple template variables
|
|
830
|
-
/search?search=technology&templateData[columnsClass]=col-lg-6&templateData[cardClass]=shadow-sm&templateData[showAuthor]=false
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
### Search Template Variables
|
|
834
|
-
Templates receive dynamic data through URL parameters:
|
|
835
|
-
|
|
836
|
-
**URL:** `/search?search=tech&templateData[columnsClass]=col-md-6&templateData[showDate]=true`
|
|
837
|
-
|
|
838
|
-
**Template (entriesListView.html):**
|
|
839
|
-
```html
|
|
840
|
-
<div class="{{columnsClass}}">
|
|
841
|
-
<div class="card">
|
|
842
|
-
<h3>{{row.title}}</h3>
|
|
843
|
-
<p>{{row.content}}</p>
|
|
844
|
-
{{#showDate}}
|
|
845
|
-
<span class="date">{{row.created_at}}</span>
|
|
846
|
-
{{/showDate}}
|
|
847
|
-
</div>
|
|
848
|
-
</div>
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
**Default Values:**
|
|
852
|
-
- `columnsClass` defaults to `col-lg-6` if not provided or empty
|
|
853
|
-
- Custom variables can be added via `templateData[variableName]=value`
|
|
854
|
-
|
|
855
|
-
### Search Configuration
|
|
856
|
-
```javascript
|
|
857
|
-
// Custom search sets in Manager configuration
|
|
858
|
-
const searchSets = {
|
|
859
|
-
articlesSearch: {
|
|
860
|
-
entities: [{
|
|
861
|
-
name: 'articles',
|
|
862
|
-
fields: ['title', 'content', 'summary'],
|
|
863
|
-
relations: 'authors'
|
|
864
|
-
}],
|
|
865
|
-
pagination: {active: true, limit: 15, sortBy: 'created_at', sortDirection: 'desc'}
|
|
866
|
-
}
|
|
867
|
-
};
|
|
224
|
+
**Usage:** `/search?search=technology&limit=20&templateData[columnsClass]=col-md-4`
|
|
868
225
|
|
|
869
|
-
|
|
870
|
-
searchSets: searchSets
|
|
871
|
-
});
|
|
872
|
-
```
|
|
226
|
+
**Full documentation:** See `.claude/search-guide.md`
|
|
873
227
|
|
|
874
228
|
## Enhanced Templating System
|
|
875
229
|
|
|
876
|
-
|
|
877
|
-
Every template has access to system variables providing context about the current request:
|
|
878
|
-
|
|
879
|
-
```html
|
|
880
|
-
<!-- Current request information -->
|
|
881
|
-
{{currentRequest.baseUrl}} <!-- https://example.com -->
|
|
882
|
-
{{currentRequest.protocol}} <!-- https -->
|
|
883
|
-
{{currentRequest.host}} <!-- example.com -->
|
|
884
|
-
{{currentRequest.path}} <!-- /articles/123 -->
|
|
885
|
-
{{currentRequest.method}} <!-- GET -->
|
|
886
|
-
{{currentRequest.userAgent}} <!-- Browser information -->
|
|
887
|
-
{{currentRequest.isSecure}} <!-- true/false -->
|
|
888
|
-
|
|
889
|
-
<!-- Current route data (if matched) -->
|
|
890
|
-
{{currentRoute.id}} <!-- Route ID -->
|
|
891
|
-
{{currentRoute.path}} <!-- Route path pattern -->
|
|
892
|
-
{{currentRoute.title}} <!-- Route title -->
|
|
893
|
-
{{currentRoute.template}} <!-- Route template -->
|
|
894
|
-
{{currentRoute.layout}} <!-- Route layout -->
|
|
895
|
-
|
|
896
|
-
<!-- Current domain information -->
|
|
897
|
-
{{currentDomain.current}} <!-- Current domain -->
|
|
898
|
-
{{currentDomain.default}} <!-- Default domain -->
|
|
899
|
-
{{currentDomain.resolved}} <!-- Resolved domain -->
|
|
900
|
-
|
|
901
|
-
<!-- System information -->
|
|
902
|
-
{{systemInfo.environment}} <!-- development/production -->
|
|
903
|
-
{{systemInfo.nodeVersion}} <!-- Node.js version -->
|
|
904
|
-
{{systemInfo.timestamp}} <!-- Current timestamp -->
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
### Template Functions
|
|
908
|
-
Templates support dynamic functions for common operations:
|
|
909
|
-
|
|
910
|
-
```html
|
|
911
|
-
<!-- URL generation with current domain -->
|
|
912
|
-
[url(/articles)] <!-- https://example.com/articles -->
|
|
913
|
-
[url(/contact#form)] <!-- https://example.com/contact#form -->
|
|
914
|
-
[url(/css/styles.css)] <!-- https://example.com/css/styles.css -->
|
|
915
|
-
|
|
916
|
-
<!-- Asset URLs with domain -->
|
|
917
|
-
[asset(/assets/images/logo.png)] <!-- https://example.com/images/logo.png -->
|
|
918
|
-
|
|
919
|
-
<!-- Date formatting -->
|
|
920
|
-
[date()] <!-- Current date with default format -->
|
|
921
|
-
[date(now, Y-m-d)] <!-- 2024-01-15 -->
|
|
922
|
-
[date(2024-01-01, d/m/Y)] <!-- 01/01/2024 -->
|
|
923
|
-
|
|
924
|
-
<!-- Internationalization -->
|
|
925
|
-
[translate(welcome.message)] <!-- Translated text -->
|
|
926
|
-
[t(hello.world, Hello World!)] <!-- With fallback -->
|
|
927
|
-
[t(greeting, Hi {name}!, {name: John})] <!-- With interpolation -->
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
### Enhanced Context Passing
|
|
931
|
-
Child content blocks and partials receive context from parent pages:
|
|
932
|
-
|
|
933
|
-
```html
|
|
934
|
-
<!-- In a CMS page or layout -->
|
|
935
|
-
<entity name="cmsBlocks" field="name" value="article-sidebar"/>
|
|
936
|
-
|
|
937
|
-
<!-- Inside the article-sidebar block, you can access: -->
|
|
938
|
-
{{currentEntity.title}} <!-- Parent page title -->
|
|
939
|
-
{{currentEntity.id}} <!-- Parent page ID -->
|
|
940
|
-
{{currentEntity.template}} <!-- Parent page template -->
|
|
941
|
-
<!-- Any other parent page properties -->
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
### Template Functions
|
|
945
|
-
Templates support dynamic content blocks, entity rendering, and collections with advanced query options:
|
|
946
|
-
|
|
947
|
-
**Single Entity Rendering:**
|
|
948
|
-
```html
|
|
949
|
-
<!-- Render by any identifier field (like 'name' for content blocks) -->
|
|
950
|
-
<entity name="cmsBlocks" field="name" value="header-main"/>
|
|
951
|
-
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
|
|
952
|
-
|
|
953
|
-
<!-- Render by ID (default identifier) -->
|
|
954
|
-
<entity name="articles" id="123"/>
|
|
955
|
-
<entity name="cmsPages" id="1"/>
|
|
956
|
-
```
|
|
957
|
-
|
|
958
|
-
**Single Field Collections:**
|
|
959
|
-
```html
|
|
960
|
-
<!-- Extract and concatenate a single field from multiple records -->
|
|
961
|
-
<collection name="cmsBlocks" filters="{status: 'active', category: 'navigation'}" field="content"/>
|
|
962
|
-
<collection name="articles" filters="{featured: true}" field="title"/>
|
|
963
|
-
|
|
964
|
-
<!-- With query options for sorting and limiting -->
|
|
965
|
-
<collection name="articles" filters="{featured: true}" field="title" data="{limit: 5, sortBy: 'created_at', sortDirection: 'desc'}"/>
|
|
966
|
-
<collection name="cmsBlocks" filters="{status: 'active'}" field="content" data="{limit: 3, offset: 10, sortBy: 'priority'}"/>
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
**Loop Collections:**
|
|
970
|
-
```html
|
|
971
|
-
<!-- Loop through records with full template rendering -->
|
|
972
|
-
<collection name="cmsBlocks" filters="{status: 'active'}">
|
|
973
|
-
<div class="block">
|
|
974
|
-
<h3>{{row.title}}</h3>
|
|
975
|
-
<div class="content">{{row.content}}</div>
|
|
976
|
-
</div>
|
|
977
|
-
</collection>
|
|
978
|
-
|
|
979
|
-
<collection name="articles" filters="{category: 'technology'}">
|
|
980
|
-
<div class="article">
|
|
981
|
-
<h4>{{row.title}}</h4>
|
|
982
|
-
<p>{{row.summary}}</p>
|
|
983
|
-
<img src="{{row.featured_image}}" alt="{{row.title}}">
|
|
984
|
-
</div>
|
|
985
|
-
</collection>
|
|
986
|
-
|
|
987
|
-
<!-- With pagination and sorting -->
|
|
988
|
-
<collection name="articles" filters="{featured: true}" data="{limit: 10, offset: 0, sortBy: 'created_at', sortDirection: 'asc'}">
|
|
989
|
-
<div class="article-card">
|
|
990
|
-
<h4>{{row.title}}</h4>
|
|
991
|
-
<p>{{row.summary}}</p>
|
|
992
|
-
</div>
|
|
993
|
-
</collection>
|
|
994
|
-
|
|
995
|
-
<!-- Paginated collections with navigation -->
|
|
996
|
-
<collection name="articles"
|
|
997
|
-
filters="{featured: true}"
|
|
998
|
-
data="{limit: 10, sortBy: 'created_at', sortDirection: 'desc'}"
|
|
999
|
-
pagination="articles-1"
|
|
1000
|
-
container="pagedCollection"
|
|
1001
|
-
prevPages="2"
|
|
1002
|
-
nextPages="2">
|
|
1003
|
-
<div class="article-card">
|
|
1004
|
-
<h4>{{row.title}}</h4>
|
|
1005
|
-
<p>{{row.summary}}</p>
|
|
1006
|
-
<span class="date">{{row.created_at}}</span>
|
|
1007
|
-
</div>
|
|
1008
|
-
</collection>
|
|
1009
|
-
|
|
1010
|
-
<!-- Multiple paginated collections on the same page -->
|
|
1011
|
-
<collection name="news"
|
|
1012
|
-
filters="{category: 'technology'}"
|
|
1013
|
-
data="{limit: 5, sortBy: 'published_at'}"
|
|
1014
|
-
pagination="news-tech"
|
|
1015
|
-
container="customPager">
|
|
1016
|
-
<article>{{row.title}}</article>
|
|
1017
|
-
</collection>
|
|
1018
|
-
|
|
1019
|
-
<collection name="events"
|
|
1020
|
-
filters="{upcoming: true}"
|
|
1021
|
-
data="{limit: 8}"
|
|
1022
|
-
pagination="events-upcoming"
|
|
1023
|
-
prevPages="3"
|
|
1024
|
-
nextPages="1">
|
|
1025
|
-
<div class="event">{{row.title}} - {{row.date}}</div>
|
|
1026
|
-
</collection>
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
**Pagination Attributes:**
|
|
1030
|
-
- `pagination="collection-id"` - Enables pagination with unique identifier
|
|
1031
|
-
- `container="templateName"` - Custom pagination template (defaults to "pagedCollection")
|
|
1032
|
-
- `prevPages="2"` - Number of previous page links to show (default: 2)
|
|
1033
|
-
- `nextPages="2"` - Number of next page links to show (default: 2)
|
|
1034
|
-
|
|
1035
|
-
**Pagination URL Parameters:**
|
|
1036
|
-
Pagination state is managed via URL query parameters:
|
|
1037
|
-
```
|
|
1038
|
-
/articles?articles-1-key={"page":2,"limit":10,"sortBy":"created_at","sortDirection":"desc"}
|
|
1039
|
-
/news?news-tech-key={"page":3,"limit":5,"category":"technology"}
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
**Custom Pagination Template:**
|
|
1043
|
-
Create `templates/partials/pagedCollection.html`:
|
|
1044
|
-
```html
|
|
1045
|
-
<div class="row paginated-contents">
|
|
1046
|
-
<div class="collection-content col-lg-12">
|
|
1047
|
-
{{&collectionContentForCurrentPage}}
|
|
1048
|
-
</div>
|
|
1049
|
-
<div class="pagination col-lg-12">
|
|
1050
|
-
<ul class="pagination-list">
|
|
1051
|
-
{{#prevPageUrl}}
|
|
1052
|
-
<li><a href="{{prevPageUrl}}" class="page-link">{{&prevPageLabel}}</a></li>
|
|
1053
|
-
{{/prevPageUrl}}
|
|
1054
|
-
{{#prevPages}}
|
|
1055
|
-
<li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
|
|
1056
|
-
{{/prevPages}}
|
|
1057
|
-
<li class="current">{{¤tPage}}</li>
|
|
1058
|
-
{{#nextPages}}
|
|
1059
|
-
<li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
|
|
1060
|
-
{{/nextPages}}
|
|
1061
|
-
{{#nextPageUrl}}
|
|
1062
|
-
<li><a href="{{nextPageUrl}}" class="page-link">{{&nextPageLabel}}</a></li>
|
|
1063
|
-
{{/nextPageUrl}}
|
|
1064
|
-
</ul>
|
|
1065
|
-
</div>
|
|
1066
|
-
</div>
|
|
1067
|
-
```
|
|
1068
|
-
|
|
1069
|
-
**Available Pagination Template Variables:**
|
|
1070
|
-
- `{{&collectionContentForCurrentPage}}` - Rendered collection items for current page
|
|
1071
|
-
- `{{currentPage}}` - Current page number
|
|
1072
|
-
- `{{totalPages}}` - Total number of pages
|
|
1073
|
-
- `{{totalRecords}}` - Total number of records
|
|
1074
|
-
- `{{prevPageUrl}}` / `{{nextPageUrl}}` - Previous/next page URLs
|
|
1075
|
-
- `{{&prevPageLabel}}` / `{{&nextPageLabel}}` - Previous/next link labels ("Previous"/"Next")
|
|
1076
|
-
- `{{#prevPages}}` / `{{#nextPages}}` - Arrays of page objects with `pageUrl` and `pageLabel`
|
|
1077
|
-
- `{{hasNextPage}}` / `{{hasPrevPage}}` - Boolean flags for navigation availability
|
|
1078
|
-
|
|
1079
|
-
**Custom Partials with Variables:**
|
|
1080
|
-
|
|
1081
|
-
*New HTML-style partial tags:*
|
|
1082
|
-
```html
|
|
1083
|
-
<!-- Clean HTML-style syntax for complex partials -->
|
|
1084
|
-
<partial name="hero"
|
|
1085
|
-
sectionStyle=" bg-black"
|
|
1086
|
-
bigTextHtml="A free open-source platform to create multiplayer games!"
|
|
1087
|
-
mediumTextHtml="Build with Node.js, MySQL, Colyseus, and Phaser 3"
|
|
1088
|
-
htmlContentWrapper='<div class="d-lg-flex"><a href="/documentation" target="_blank" class="btn-get-started">Get Started!</a><a href="https://demo.reldens.com/" target="_blank" class="btn-watch-video"> Demo </a></div>'
|
|
1089
|
-
imageUrl="/assets/web/reldens-check.png"
|
|
1090
|
-
imageAlt="Reldens - MMORPG Platform" />
|
|
1091
|
-
|
|
1092
|
-
<!-- Self-closing and open/close syntax both supported -->
|
|
1093
|
-
<partial name="productCard"
|
|
1094
|
-
title="Premium Package"
|
|
1095
|
-
price="$99"
|
|
1096
|
-
highlighted="true">
|
|
1097
|
-
</partial>
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
*Traditional Mustache syntax is still supported:*
|
|
1101
|
-
```html
|
|
1102
|
-
<!-- Call a partial with an inline JSON object -->
|
|
1103
|
-
{{>hero -{
|
|
1104
|
-
bigTextHtml: "A free open-source platform to create multiplayer games!",
|
|
1105
|
-
mediumTextHtml: "Build with Node.js, MySQL, Colyseus, and Phaser 3",
|
|
1106
|
-
imageUrl: "https://example.com/hero.jpg",
|
|
1107
|
-
ctaText: "Get Started",
|
|
1108
|
-
ctaLink: "/documentation"
|
|
1109
|
-
}-}}
|
|
1110
|
-
|
|
1111
|
-
<!-- Call a partial within collections using row data -->
|
|
1112
|
-
<collection name="cmsPages" filters="{featured: true}" data="{limit: 3}">
|
|
1113
|
-
{{>cardView -{row}-}}
|
|
1114
|
-
</collection>
|
|
1115
|
-
|
|
1116
|
-
<!-- Call a partial with mixed data -->
|
|
1117
|
-
{{>productCard -{
|
|
1118
|
-
title: "Premium Package",
|
|
1119
|
-
price: "$99",
|
|
1120
|
-
features: ["Advanced Analytics", "Priority Support", "Custom Themes"],
|
|
1121
|
-
highlighted: true
|
|
1122
|
-
}-}}
|
|
1123
|
-
```
|
|
1124
|
-
|
|
1125
|
-
**Example Partial Templates:**
|
|
1126
|
-
|
|
1127
|
-
**partials/hero.mustache:**
|
|
1128
|
-
```html
|
|
1129
|
-
<section class="hero{{#sectionStyle}}{{sectionStyle}}{{/sectionStyle}}">
|
|
1130
|
-
<div class="hero-content">
|
|
1131
|
-
<h1>{{&bigTextHtml}}</h1>
|
|
1132
|
-
<p>{{&mediumTextHtml}}</p>
|
|
1133
|
-
{{#htmlContentWrapper}}
|
|
1134
|
-
{{&htmlContentWrapper}}
|
|
1135
|
-
{{/htmlContentWrapper}}
|
|
1136
|
-
{{#imageUrl}}
|
|
1137
|
-
<img src="{{imageUrl}}" alt="{{imageAlt}}" class="hero-image">
|
|
1138
|
-
{{/imageUrl}}
|
|
1139
|
-
</div>
|
|
1140
|
-
</section>
|
|
1141
|
-
```
|
|
1142
|
-
|
|
1143
|
-
**partials/cardView.mustache:**
|
|
1144
|
-
```html
|
|
1145
|
-
<div class="card">
|
|
1146
|
-
<h3>{{title}}</h3>
|
|
1147
|
-
<p>{{json_data.excerpt}}</p>
|
|
1148
|
-
{{#json_data.featured_image}}
|
|
1149
|
-
<img src="{{json_data.featured_image}}" alt="{{title}}">
|
|
1150
|
-
{{/json_data.featured_image}}
|
|
1151
|
-
{{#json_data.cta_text}}
|
|
1152
|
-
<a href="{{json_data.cta_link}}" class="btn">{{json_data.cta_text}}</a>
|
|
1153
|
-
{{/json_data.cta_text}}
|
|
1154
|
-
</div>
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
### Collection Query Options
|
|
1158
|
-
Collections support advanced query parameters for pagination and sorting:
|
|
1159
|
-
|
|
1160
|
-
- **limit** - Maximum number of records to return
|
|
1161
|
-
- **offset** - Number of records to skip (for pagination)
|
|
1162
|
-
- **sortBy** - Field name to sort by
|
|
1163
|
-
- **sortDirection** - Sort direction ('asc' or 'desc')
|
|
1164
|
-
|
|
1165
|
-
**Examples:**
|
|
1166
|
-
```html
|
|
1167
|
-
<!-- Get the first 5 articles sorted by title -->
|
|
1168
|
-
<collection name="articles" filters="{}" field="title" data="{limit: 5, sortBy: 'title'}"/>
|
|
1169
|
-
|
|
1170
|
-
<!-- Paginated results: skip first 20, get next 10 -->
|
|
1171
|
-
<collection name="articles" filters="{published: true}" data="{limit: 10, offset: 20, sortBy: 'created_at', sortDirection: 'desc'}">
|
|
1172
|
-
<article>{{row.title}}</article>
|
|
1173
|
-
</collection>
|
|
1174
|
-
|
|
1175
|
-
<!-- The latest 3 featured articles by creation date -->
|
|
1176
|
-
<collection name="articles" filters="{featured: true}" data="{limit: 3, sortBy: 'created_at', sortDirection: 'desc'}">
|
|
1177
|
-
<div class="featured-article">{{row.title}}</div>
|
|
1178
|
-
</collection>
|
|
1179
|
-
```
|
|
1180
|
-
|
|
1181
|
-
## Internationalization
|
|
1182
|
-
|
|
1183
|
-
### Translation Files
|
|
1184
|
-
Create translation files in the `translations` directory:
|
|
1185
|
-
|
|
1186
|
-
**translations/en.json:**
|
|
1187
|
-
```json
|
|
1188
|
-
{
|
|
1189
|
-
"navigation": {
|
|
1190
|
-
"home": "Home",
|
|
1191
|
-
"about": "About Us",
|
|
1192
|
-
"contact": "Contact"
|
|
1193
|
-
},
|
|
1194
|
-
"messages": {
|
|
1195
|
-
"welcome": "Welcome to our site!",
|
|
1196
|
-
"greeting": "Hello {name}!"
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
```
|
|
1200
|
-
|
|
1201
|
-
**translations/es.json:**
|
|
1202
|
-
```json
|
|
1203
|
-
{
|
|
1204
|
-
"navigation": {
|
|
1205
|
-
"home": "Inicio",
|
|
1206
|
-
"about": "Acerca de",
|
|
1207
|
-
"contact": "Contacto"
|
|
1208
|
-
},
|
|
1209
|
-
"messages": {
|
|
1210
|
-
"welcome": "¡Bienvenido a nuestro sitio!",
|
|
1211
|
-
"greeting": "¡Hola {name}!"
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
### Using Translations in Templates
|
|
1217
|
-
```html
|
|
1218
|
-
<!-- Simple translation -->
|
|
1219
|
-
[translate(navigation.home)]
|
|
1220
|
-
|
|
1221
|
-
<!-- With fallback -->
|
|
1222
|
-
[t(navigation.home, Home)]
|
|
230
|
+
Powerful template engine with Mustache integration, system variables, and dynamic content rendering.
|
|
1223
231
|
|
|
1224
|
-
|
|
1225
|
-
|
|
232
|
+
**Core Features:**
|
|
233
|
+
- System variables: `{{currentRequest.host}}`, `{{currentRoute.title}}`, `{{systemInfo.environment}}`
|
|
234
|
+
- Template functions: `[url(/path)]`, `[asset(/img.png)]`, `[date(now, Y-m-d)]`, `[translate(key)]`
|
|
235
|
+
- Entity rendering: `<entity name="cmsBlocks" field="name" value="header"/>`
|
|
236
|
+
- Collections with pagination: `<collection name="articles" filters="{}" data="{limit: 10}">`
|
|
237
|
+
- Custom partials: `<partial name="hero" title="Welcome"/>` or `{{>hero -{data}-}}`
|
|
1226
238
|
|
|
1227
|
-
|
|
1228
|
-
```
|
|
1229
|
-
|
|
1230
|
-
### Layout System
|
|
1231
|
-
The CMS uses a two-tier layout system:
|
|
1232
|
-
|
|
1233
|
-
**page.html** - Full HTML wrapper:
|
|
1234
|
-
```html
|
|
1235
|
-
<!DOCTYPE html>
|
|
1236
|
-
<html lang="{{locale}}">
|
|
1237
|
-
<head>
|
|
1238
|
-
<title>{{title}}</title>
|
|
1239
|
-
<meta name="description" content="{{description}}"/>
|
|
1240
|
-
<link href="[url(/css/styles.css)]" rel="stylesheet"/>
|
|
1241
|
-
</head>
|
|
1242
|
-
<body class="{{siteHandle}}">
|
|
1243
|
-
{{&content}}
|
|
1244
|
-
<script src="[url(/js/scripts.js)]"></script>
|
|
1245
|
-
</body>
|
|
1246
|
-
</html>
|
|
1247
|
-
```
|
|
239
|
+
**Full documentation:** See `.claude/templating-system-guide.md`
|
|
1248
240
|
|
|
1249
|
-
|
|
1250
|
-
```html
|
|
1251
|
-
<entity name="cmsBlocks" field="name" value="header-main"/>
|
|
1252
|
-
|
|
1253
|
-
<main id="main" class="main-container">
|
|
1254
|
-
<div class="container">
|
|
1255
|
-
<div class="row">
|
|
1256
|
-
<div class="col-md-3">
|
|
1257
|
-
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
|
|
1258
|
-
</div>
|
|
1259
|
-
<div class="col-md-9">
|
|
1260
|
-
{{&content}}
|
|
1261
|
-
</div>
|
|
1262
|
-
</div>
|
|
1263
|
-
</div>
|
|
1264
|
-
</main>
|
|
1265
|
-
|
|
1266
|
-
<entity name="cmsBlocks" field="name" value="footer-main"/>
|
|
1267
|
-
```
|
|
1268
|
-
|
|
1269
|
-
Pages can use different layouts by setting the `layout` field in `cms_pages`:
|
|
1270
|
-
- `default` - Header, sidebar, main content, footer
|
|
1271
|
-
- `full-width` - Full width without sidebars
|
|
1272
|
-
- `minimal` - Basic layout with minimal styling
|
|
1273
|
-
|
|
1274
|
-
### Content Blocks
|
|
1275
|
-
Create reusable content blocks in the `cms_blocks` table via the admin panel:
|
|
1276
|
-
```sql
|
|
1277
|
-
INSERT INTO cms_blocks (name, title, content) VALUES
|
|
1278
|
-
('contact-info', 'Contact Information', '<p>Email: info@example.com</p>'),
|
|
1279
|
-
('article-sidebar', 'Article Categories',
|
|
1280
|
-
'<div class="categories"><h3>Categories</h3><ul><li><a href="[url(/articles/technology)]">Technology</a></li></ul></div>');
|
|
1281
|
-
```
|
|
1282
|
-
|
|
1283
|
-
### Entity Access Control
|
|
1284
|
-
Control which entities are publicly accessible:
|
|
1285
|
-
```javascript
|
|
1286
|
-
const cms = new Manager({
|
|
1287
|
-
entityAccess: {
|
|
1288
|
-
articles: { public: true, operations: ['read'] },
|
|
1289
|
-
cmsPages: { public: true, operations: ['read'] },
|
|
1290
|
-
users: { public: false }
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
```
|
|
241
|
+
## Internationalization & Multi-Domain
|
|
1294
242
|
|
|
1295
|
-
|
|
243
|
+
**Translation files:** JSON-based i18n with `[translate(key)]` and `[t(key, fallback)]` functions
|
|
244
|
+
**Multi-domain support:** Domain-specific templates, partials, and configurations
|
|
245
|
+
**Layout system:** Two-tier layout (page.html + layouts/) with multiple layout options
|
|
246
|
+
**Content blocks:** Reusable content via cms_blocks table
|
|
247
|
+
**Entity access control:** Configure public/private entities and operations
|
|
1296
248
|
|
|
1297
|
-
|
|
1298
|
-
```
|
|
1299
|
-
templates/
|
|
1300
|
-
├── layouts/
|
|
1301
|
-
│ ├── default.html # Body content layouts
|
|
1302
|
-
│ ├── full-width.html
|
|
1303
|
-
│ └── minimal.html
|
|
1304
|
-
├── domains/
|
|
1305
|
-
│ ├── example.com/
|
|
1306
|
-
│ │ ├── layouts/ # Domain-specific layouts
|
|
1307
|
-
│ │ ├── partials/
|
|
1308
|
-
│ │ │ ├── header.html
|
|
1309
|
-
│ │ │ └── footer.html
|
|
1310
|
-
│ │ ├── cms_forms/ # Domain-specific form templates
|
|
1311
|
-
│ │ │ ├── form.html
|
|
1312
|
-
│ │ │ └── field_text.html
|
|
1313
|
-
│ │ ├── page.html # Domain-specific page wrapper
|
|
1314
|
-
│ │ └── index.html
|
|
1315
|
-
│ └── dev.example.com/
|
|
1316
|
-
│ └── page.html
|
|
1317
|
-
├── partials/
|
|
1318
|
-
│ ├── header.html (default)
|
|
1319
|
-
│ └── footer.html (default)
|
|
1320
|
-
├── cms_forms/ # Default form templates
|
|
1321
|
-
│ ├── form.html
|
|
1322
|
-
│ ├── field_text.html
|
|
1323
|
-
│ └── field_email.html
|
|
1324
|
-
├── translations/
|
|
1325
|
-
│ ├── en.json
|
|
1326
|
-
│ ├── es.json
|
|
1327
|
-
│ └── fr.json
|
|
1328
|
-
├── page.html (base HTML wrapper)
|
|
1329
|
-
└── 404.html
|
|
1330
|
-
```
|
|
249
|
+
**Full documentation:** See `.claude/multi-domain-i18n-guide.md`
|
|
1331
250
|
|
|
1332
251
|
## Advanced Usage
|
|
1333
252
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
const cms = new Manager({
|
|
1340
|
-
// Enable aggressive template reloading in development
|
|
1341
|
-
reloadTime: isDevelopment ? -1 : 0,
|
|
1342
|
-
|
|
1343
|
-
// Other development-friendly settings
|
|
1344
|
-
cache: !isDevelopment,
|
|
1345
|
-
|
|
1346
|
-
entityAccess: {
|
|
1347
|
-
articles: { public: true, operations: ['read'] },
|
|
1348
|
-
cmsPages: { public: true, operations: ['read'] }
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
```
|
|
1352
|
-
|
|
1353
|
-
**Development Workflow with Template Reloading:**
|
|
1354
|
-
1. Set `reloadTime: -1` for instant template updates
|
|
1355
|
-
2. Edit admin templates in `admin/templates/` - changes appear immediately
|
|
1356
|
-
3. Edit frontend templates in `templates/` - changes appear on next page load
|
|
1357
|
-
4. No server restart needed for template changes
|
|
1358
|
-
5. Switch to `reloadTime: 0` in production for optimal performance
|
|
1359
|
-
|
|
1360
|
-
### Event System
|
|
1361
|
-
The CMS provides hooks for customization through event listeners:
|
|
253
|
+
**Template Reloading:** Set `reloadTime: -1` for development, `0` for production
|
|
254
|
+
**Event System:** Hook into CMS events for customization
|
|
255
|
+
**Custom Authentication:** Implement custom auth callbacks
|
|
256
|
+
**File Upload Config:** Configure MIME types and allowed extensions
|
|
1362
257
|
|
|
1363
|
-
|
|
1364
|
-
// Listen for template variable events
|
|
1365
|
-
cms.events.on('reldens.afterVariablesCreated', (eventData) => {
|
|
1366
|
-
// Add custom variables
|
|
1367
|
-
eventData.variables.customData = {
|
|
1368
|
-
timestamp: Date.now(),
|
|
1369
|
-
version: '1.0.0'
|
|
1370
|
-
};
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
|
-
// Listen for content processing events
|
|
1374
|
-
cms.events.on('reldens.beforeContentProcess', (eventData) => {
|
|
1375
|
-
// Modify content before processing
|
|
1376
|
-
eventData.content = eventData.content.replace(/\[custom\]/g, 'Custom Value');
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
|
-
cms.events.on('reldens.afterContentProcess', (eventData) => {
|
|
1380
|
-
// Modify processed content
|
|
1381
|
-
eventData.processedContent += '\n<!-- Processed at ' + new Date() + ' -->';
|
|
1382
|
-
});
|
|
1383
|
-
|
|
1384
|
-
// Listen for template reloading events
|
|
1385
|
-
cms.events.on('reldens.templateReloader.templatesChanged', (eventData) => {
|
|
1386
|
-
console.log('Templates changed:', eventData.changedFiles);
|
|
1387
|
-
});
|
|
1388
|
-
|
|
1389
|
-
// Listen for form events
|
|
1390
|
-
cms.events.on('reldens.dynamicForm.afterSave', (eventData) => {
|
|
1391
|
-
// Send email notifications, trigger workflows, etc.
|
|
1392
|
-
console.log('Form submission received:', eventData.result.id);
|
|
1393
|
-
});
|
|
1394
|
-
```
|
|
1395
|
-
|
|
1396
|
-
### Custom Authentication
|
|
1397
|
-
```javascript
|
|
1398
|
-
const customAuth = async (email, password, roleId) => {
|
|
1399
|
-
const user = await yourAuthService.authenticate(email, password);
|
|
1400
|
-
return user && user.role_id === roleId ? user : false;
|
|
1401
|
-
};
|
|
1402
|
-
|
|
1403
|
-
const cms = new Manager({
|
|
1404
|
-
authenticationMethod: 'custom',
|
|
1405
|
-
authenticationCallback: customAuth
|
|
1406
|
-
});
|
|
1407
|
-
```
|
|
258
|
+
**Full documentation:** See `.claude/advanced-usage-guide.md`
|
|
1408
259
|
|
|
1409
|
-
|
|
1410
|
-
```javascript
|
|
1411
|
-
const uploadConfig = {
|
|
1412
|
-
mimeTypes: {
|
|
1413
|
-
image: ['image/jpeg', 'image/png', 'image/webp'],
|
|
1414
|
-
document: ['application/pdf', 'text/plain']
|
|
1415
|
-
},
|
|
1416
|
-
allowedExtensions: {
|
|
1417
|
-
image: ['.jpg', '.jpeg', '.png', '.webp'],
|
|
1418
|
-
document: ['.pdf', '.txt']
|
|
1419
|
-
}
|
|
1420
|
-
};
|
|
260
|
+
## Database Schema
|
|
1421
261
|
|
|
1422
|
-
|
|
1423
|
-
|
|
262
|
+
**Core tables:** routes, cms_pages, cms_blocks, entities_access, entities_meta
|
|
263
|
+
**Forms tables:** cms_forms, cms_forms_submitted
|
|
264
|
+
**Optional:** users, roles
|
|
1424
265
|
|
|
1425
|
-
|
|
1426
|
-
```javascript
|
|
1427
|
-
cms.events.on('reldens.setupAdminRoutes', ({adminManager}) => {
|
|
1428
|
-
// Add custom admin routes
|
|
1429
|
-
adminManager.adminRouter.get('/custom', (req, res) => {
|
|
1430
|
-
res.send('Custom admin page');
|
|
1431
|
-
});
|
|
1432
|
-
});
|
|
1433
|
-
|
|
1434
|
-
cms.events.on('adminEntityExtraData', ({entitySerializedData, entity}) => {
|
|
1435
|
-
// Add extra data to admin views
|
|
1436
|
-
entitySerializedData.customField = 'Custom Value';
|
|
1437
|
-
});
|
|
1438
|
-
```
|
|
1439
|
-
|
|
1440
|
-
## Default database Schema
|
|
1441
|
-
|
|
1442
|
-
### Core Tables
|
|
1443
|
-
- `routes` - URL routing and SEO metadata
|
|
1444
|
-
- `cms_pages` - Page content with layout assignments
|
|
1445
|
-
- `cms_blocks` - Reusable content blocks
|
|
1446
|
-
- `entities_access` - Entity access control rules
|
|
1447
|
-
- `entities_meta` - Generic metadata storage
|
|
1448
|
-
- `cms_pages_meta` - Page-specific metadata
|
|
1449
|
-
|
|
1450
|
-
### Forms Tables
|
|
1451
|
-
- `cms_forms` - Form configurations with JSON schema
|
|
1452
|
-
- `cms_forms_submitted` - Form submissions with JSON data
|
|
1453
|
-
|
|
1454
|
-
### Installation Options
|
|
1455
|
-
The installer provides checkboxes for:
|
|
1456
|
-
- CMS core tables
|
|
1457
|
-
- User authentication system
|
|
1458
|
-
- Default admin user
|
|
1459
|
-
- Default homepage
|
|
1460
|
-
- Default content blocks
|
|
1461
|
-
- Entity access control rules
|
|
1462
|
-
- Dynamic forms system
|
|
266
|
+
**Full documentation:** See `.claude/database-schema.md`
|
|
1463
267
|
|
|
1464
268
|
## API Reference
|
|
1465
269
|
|
|
1466
|
-
|
|
1467
|
-
- `start()` - Initialize and start the CMS
|
|
1468
|
-
- `isInstalled()` - Check if CMS is installed
|
|
1469
|
-
- `initializeServices()` - Initialize all services
|
|
1470
|
-
- `validateProvidedServer()` - Validate provided server instance
|
|
1471
|
-
- `validateProvidedDataServer()` - Validate provided data server
|
|
1472
|
-
- `validateProvidedAdminManager()` - Validate provided admin manager
|
|
1473
|
-
- `validateProvidedFrontend()` - Validate provided frontend
|
|
1474
|
-
- `buildAppServerConfiguration()` - Build server configuration
|
|
1475
|
-
- `initializeCmsAfterInstall(props)` - Post-installation callback
|
|
1476
|
-
|
|
1477
|
-
### Installer Class
|
|
1478
|
-
- `isInstalled()` - Check installation status
|
|
1479
|
-
- `configureAppServerRoutes(app, appServer, appServerFactory, renderEngine)` - Setup installer routes
|
|
1480
|
-
- `executeInstallProcess(req, res)` - Complete installation process
|
|
1481
|
-
- `runSubprocessInstallation(dbConfig, templateVariables)` - Handle subprocess operations
|
|
1482
|
-
- `checkAndInstallPackages(requiredPackages)` - Check and install dependencies
|
|
1483
|
-
- `generateEntities(server, isOverride, isInstallationMode, isDryPrisma, dbConfig)` - Generate entities
|
|
1484
|
-
- `createEnvFile(templateVariables)` - Create environment configuration
|
|
1485
|
-
- `copyAdminDirectory()` - Copy admin assets and templates
|
|
1486
|
-
|
|
1487
|
-
### Frontend Architecture Classes
|
|
1488
|
-
|
|
1489
|
-
#### Frontend Class (Orchestrator)
|
|
1490
|
-
- `initialize()` - Set up frontend routes and templates
|
|
1491
|
-
- `handleRequest(req, res)` - Main request handler
|
|
1492
|
-
- `renderRoute(route, domain, res, req)` - Route-based rendering
|
|
1493
|
-
- `setupStaticAssets()` - Configure static asset serving
|
|
1494
|
-
|
|
1495
|
-
#### TemplateResolver Class
|
|
1496
|
-
- `findTemplatePath(templateName, domain)` - Template discovery with domain fallback
|
|
1497
|
-
- `findLayoutPath(layoutName, domain)` - Layout path resolution
|
|
1498
|
-
- `findTemplateByPath(path, domain)` - Template lookup by URL path
|
|
1499
|
-
- `resolveDomainToFolder(domain)` - Domain to folder mapping
|
|
1500
|
-
- `resolveDomainToSiteKey(domain)` - Domain to site key mapping
|
|
1501
|
-
|
|
1502
|
-
#### TemplateCache Class
|
|
1503
|
-
- `loadPartials()` - Load and cache template partials
|
|
1504
|
-
- `setupDomainTemplates()` - Initialize domain-specific templates
|
|
1505
|
-
- `getPartialsForDomain(domain)` - Get domain-specific partials with fallback
|
|
1506
|
-
|
|
1507
|
-
#### TemplateReloader Class
|
|
1508
|
-
- `checkAndReloadAdminTemplates()` - Check and reload admin templates when changed
|
|
1509
|
-
- `checkAndReloadFrontendTemplates()` - Check and reload frontend templates when changed
|
|
1510
|
-
- `trackTemplateFiles(templatesPaths)` - Start tracking template files for changes
|
|
1511
|
-
- `shouldReloadAdminTemplates(mappedAdminTemplates)` - Check if admin templates need reloading
|
|
1512
|
-
- `shouldReloadFrontendTemplates(templatesPath, templateExtensions)` - Check if frontend templates need reloading
|
|
1513
|
-
- `handleAdminTemplateReload(adminManager)` - Complete admin template reload process
|
|
1514
|
-
- `handleFrontendTemplateReload(templateCache, templateResolver)` - Complete frontend template reload process
|
|
1515
|
-
|
|
1516
|
-
#### RequestProcessor Class
|
|
1517
|
-
- `findRouteByPath(path, domain)` - Database route lookup
|
|
1518
|
-
- `handleRouteRedirect(route, res)` - Handle route redirects
|
|
1519
|
-
- `getDomainFromRequest(req)` - Extract domain from request
|
|
1520
|
-
- `buildCacheKey(path, req)` - Generate cache keys
|
|
1521
|
-
|
|
1522
|
-
#### ContentRenderer Class
|
|
1523
|
-
- `renderWithTemplateContent(content, data, domain, req, route)` - Main content rendering
|
|
1524
|
-
- `generateRouteContent(route, domain, req)` - Route-based content generation
|
|
1525
|
-
- `generateTemplateContent(templatePath, domain, req, data)` - Template-based content generation
|
|
1526
|
-
- `fetchMetaFields(data)` - Process meta fields for templates
|
|
1527
|
-
|
|
1528
|
-
#### EntityAccessManager Class
|
|
1529
|
-
- `loadEntityAccessRules()` - Load entity access configuration
|
|
1530
|
-
- `isEntityAccessible(entityName)` - Check entity accessibility
|
|
1531
|
-
- `findEntityByPath(path)` - Entity lookup by URL path
|
|
1532
|
-
|
|
1533
|
-
#### ResponseManager Class
|
|
1534
|
-
- `renderWithCacheHandler(contentGenerator, errorHandler, responseHandler, domain, res, path, req)` - Generic cached response handler
|
|
1535
|
-
- `renderNotFound(domain, res, req)` - 404 error handling
|
|
1536
|
-
|
|
1537
|
-
#### SearchRequestHandler Class
|
|
1538
|
-
- `handleSearchRequest(req, res)` - Process search requests with template data support
|
|
1539
|
-
|
|
1540
|
-
### TemplateEngine Class
|
|
1541
|
-
- `render(template, data, partials, domain, req, route, currentEntityData)` - Main template rendering with enhanced context
|
|
1542
|
-
- `processAllTemplateFunctions(template, domain, req, systemVariables)` - Process all template functions
|
|
1543
|
-
- `buildEnhancedRenderData(data, systemVariables, currentEntityData)` - Build template context with system variables
|
|
1544
|
-
|
|
1545
|
-
### SystemVariablesProvider Class
|
|
1546
|
-
- `buildSystemVariables(req, route, domain)` - Create system variables for templates
|
|
1547
|
-
- `buildCurrentRequestData(req, domain)` - Build request context
|
|
1548
|
-
- `buildCurrentRouteData(route)` - Build route context
|
|
1549
|
-
- `buildCurrentDomainData(domain)` - Build domain context
|
|
1550
|
-
|
|
1551
|
-
### Search Classes
|
|
1552
|
-
- `Search.parseSearchParameters(query)` - Parse search query parameters including templateData
|
|
1553
|
-
- `Search.executeSearch(config)` - Execute search with configuration
|
|
1554
|
-
- `SearchRenderer.renderSearchResults(searchResults, config, domain, req)` - Render search results with template data
|
|
1555
|
-
|
|
1556
|
-
### Forms System Classes
|
|
1557
|
-
|
|
1558
|
-
#### DynamicForm Class
|
|
1559
|
-
- `validateFormSubmission(formKey, submittedValues, req)` - Validate form submission
|
|
1560
|
-
- `getFormConfig(formKey)` - Load form configuration from database
|
|
1561
|
-
- `validateHoneypot(submittedValues)` - Check honeypot field for bots
|
|
1562
|
-
- `validateFields(fieldsSchema, submittedValues)` - Schema-based field validation
|
|
1563
|
-
- `prepareSubmittedValues(submittedValues, fieldsSchema)` - Process and normalize values
|
|
1564
|
-
- `saveFormSubmission(formConfig, preparedValues)` - Save to database
|
|
1565
|
-
|
|
1566
|
-
#### DynamicFormRenderer Class
|
|
1567
|
-
- `renderForm(formConfig, fieldsToRender, domain, req, attributes)` - Render complete form
|
|
1568
|
-
- `renderFormFields(fieldsToRender, domain, req)` - Render field set
|
|
1569
|
-
- `renderFormField(field, domain, submittedValues, errors)` - Render individual field
|
|
1570
|
-
- `loadFormTemplate(templateName, domain)` - Load form template with domain fallback
|
|
1571
|
-
- `findFormTemplate(templateName, domain)` - Template discovery for forms
|
|
1572
|
-
|
|
1573
|
-
#### DynamicFormRequestHandler Class
|
|
1574
|
-
- `handleFormSubmission(req, res)` - Process POST form submissions
|
|
1575
|
-
- `handleBadRequest(res, message)` - Handle validation errors
|
|
1576
|
-
- `handleSuccessResponse(req, res, formKey, result)` - Handle successful submissions
|
|
1577
|
-
- `buildErrorRedirectPath(req, error, formKey)` - Build error redirect URLs
|
|
1578
|
-
- `buildSuccessRedirectPath(successRedirect, formKey)` - Build success redirect URLs
|
|
1579
|
-
|
|
1580
|
-
#### FormsTransformer Class
|
|
1581
|
-
- `transform(template, domain, req, systemVariables, enhancedData)` - Process cmsForm tags
|
|
1582
|
-
- `findAllFormTags(template)` - Find cmsForm tags in template
|
|
1583
|
-
- `parseFormAttributes(fullTag)` - Parse tag attributes
|
|
1584
|
-
- `parseFieldsFilter(attributes, formConfig)` - Filter fields based on attributes
|
|
1585
|
-
|
|
1586
|
-
### AdminManager Class
|
|
1587
|
-
- `setupAdmin()` - Initialize admin panel
|
|
1588
|
-
- `generateListRouteContent()` - Entity list pages
|
|
1589
|
-
- `generateEditRouteContent()` - Entity edit forms
|
|
1590
|
-
- `processSaveEntity()` - Handle form submissions
|
|
1591
|
-
|
|
1592
|
-
## File Structure
|
|
270
|
+
Complete class and method reference for all CMS components.
|
|
1593
271
|
|
|
1594
|
-
|
|
1595
|
-
project/
|
|
1596
|
-
├── admin/
|
|
1597
|
-
│ └── templates/ # Admin panel templates
|
|
1598
|
-
├── lib/
|
|
1599
|
-
│ ├── frontend/ # Frontend specialized classes
|
|
1600
|
-
│ │ ├── template-resolver.js
|
|
1601
|
-
│ │ ├── template-cache.js
|
|
1602
|
-
│ │ ├── request-processor.js
|
|
1603
|
-
│ │ ├── entity-access-manager.js
|
|
1604
|
-
│ │ ├── content-renderer.js
|
|
1605
|
-
│ │ └── response-manager.js
|
|
1606
|
-
│ ├── template-engine/ # Template processing classes
|
|
1607
|
-
│ │ └── forms-transformer.js
|
|
1608
|
-
│ ├── frontend.js # Main Frontend orchestrator
|
|
1609
|
-
│ ├── template-reloader.js # Template reloading functionality
|
|
1610
|
-
│ ├── search-request-handler.js
|
|
1611
|
-
│ ├── search.js # Search functionality
|
|
1612
|
-
│ ├── search-renderer.js # Search result rendering
|
|
1613
|
-
│ ├── dynamic-form.js # Forms validation and processing
|
|
1614
|
-
│ ├── dynamic-form-renderer.js # Forms template rendering
|
|
1615
|
-
│ ├── dynamic-form-request-handler.js # Forms request handling
|
|
1616
|
-
│ ├── template-engine.js # Core template processing
|
|
1617
|
-
│ ├── installer.js # Installation with subprocess handling
|
|
1618
|
-
│ ├── manager.js # Main CMS orchestrator
|
|
1619
|
-
│ ├── mysql-installer.js # MySQL-specific installation
|
|
1620
|
-
│ └── prisma-subprocess-worker.js # Subprocess worker
|
|
1621
|
-
├── templates/
|
|
1622
|
-
│ ├── layouts/ # Body content layouts
|
|
1623
|
-
│ ├── domains/ # Domain-specific templates
|
|
1624
|
-
│ │ └── example.com/
|
|
1625
|
-
│ │ └── cms_forms/ # Domain-specific form templates
|
|
1626
|
-
│ ├── partials/ # Shared template partials
|
|
1627
|
-
│ ├── cms_forms/ # Default form templates
|
|
1628
|
-
│ │ ├── form.html # Main form wrapper
|
|
1629
|
-
│ │ ├── field_text.html # Text field template
|
|
1630
|
-
│ │ ├── field_email.html # Email field template
|
|
1631
|
-
│ │ └── field_select.html # Select field template
|
|
1632
|
-
│ ├── page.html # Base HTML wrapper
|
|
1633
|
-
│ └── 404.html # Error page
|
|
1634
|
-
├── translations/
|
|
1635
|
-
│ ├── en.json # English translations
|
|
1636
|
-
│ ├── es.json # Spanish translations
|
|
1637
|
-
│ └── fr.json # French translations
|
|
1638
|
-
├── public/
|
|
1639
|
-
│ ├── css/ # Stylesheets
|
|
1640
|
-
│ ├── js/ # Client scripts
|
|
1641
|
-
│ └── assets/ # Static assets
|
|
1642
|
-
├── entities/ # Generated entity classes
|
|
1643
|
-
├── .env # Environment configuration
|
|
1644
|
-
├── install.lock # Installation lock file
|
|
1645
|
-
└── index.js # Main application file
|
|
1646
|
-
```
|
|
1647
|
-
|
|
1648
|
-
---
|
|
272
|
+
**Full documentation:** See `.claude/api-reference.md`
|
|
1649
273
|
|
|
1650
274
|
## Contributing
|
|
1651
275
|
|