@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
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Password Management Guide
|
|
2
|
+
|
|
3
|
+
**Package**: @reldens/cms
|
|
4
|
+
**Feature**: User Password Management and Encryption
|
|
5
|
+
**Version**: 0.48.0+
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The CMS provides comprehensive password management features to ensure secure handling of user passwords. This guide covers password encryption, updating passwords, and best practices for password security.
|
|
10
|
+
|
|
11
|
+
## Password Encryption
|
|
12
|
+
|
|
13
|
+
### How It Works
|
|
14
|
+
|
|
15
|
+
User passwords are encrypted using PBKDF2 (Password-Based Key Derivation Function 2) with:
|
|
16
|
+
- **Iterations**: 100,000
|
|
17
|
+
- **Key Length**: 64 bytes
|
|
18
|
+
- **Digest**: SHA-512
|
|
19
|
+
- **Salt Length**: 32 bytes (randomly generated per password)
|
|
20
|
+
|
|
21
|
+
The encrypted password is stored in the format: `salt:hash` where both salt and hash are hex-encoded.
|
|
22
|
+
|
|
23
|
+
### Automatic Encryption
|
|
24
|
+
|
|
25
|
+
The CMS includes a `PasswordEncryptionHandler` that automatically encrypts passwords when saving user records through the admin panel.
|
|
26
|
+
|
|
27
|
+
**How It Works**:
|
|
28
|
+
1. Listens to the `reldens.adminBeforeEntitySave` event
|
|
29
|
+
2. Detects when the `users` entity is being saved
|
|
30
|
+
3. Checks if the `password` field is being modified
|
|
31
|
+
4. Verifies the password is not already encrypted
|
|
32
|
+
5. Encrypts the password using `Encryptor.encryptPassword()`
|
|
33
|
+
6. Updates the request body with the encrypted password
|
|
34
|
+
|
|
35
|
+
**Enabled by Default**: The password encryption handler is enabled by default in the Manager configuration.
|
|
36
|
+
|
|
37
|
+
**To Disable** (not recommended):
|
|
38
|
+
```javascript
|
|
39
|
+
const manager = new Manager({
|
|
40
|
+
enablePasswordEncryption: false, // Disables automatic password encryption
|
|
41
|
+
// ... other config
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Manual Encryption
|
|
46
|
+
|
|
47
|
+
To manually encrypt a password in your code:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
51
|
+
|
|
52
|
+
let plainPassword = 'mySecurePassword123';
|
|
53
|
+
let encryptedPassword = Encryptor.encryptPassword(plainPassword);
|
|
54
|
+
// Returns: '64char-salt:128char-hash'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Password Validation
|
|
58
|
+
|
|
59
|
+
During authentication, passwords are validated using:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
63
|
+
|
|
64
|
+
let plainPassword = 'userProvidedPassword';
|
|
65
|
+
let storedPassword = '64char-salt:128char-hash';
|
|
66
|
+
let isValid = Encryptor.validatePassword(plainPassword, storedPassword);
|
|
67
|
+
// Returns: true or false
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Updating User Passwords
|
|
71
|
+
|
|
72
|
+
### Method 1: CLI Command (Recommended)
|
|
73
|
+
|
|
74
|
+
The CMS provides a dedicated CLI command for updating user passwords:
|
|
75
|
+
|
|
76
|
+
#### Basic Usage
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Update by email (will prompt for password)
|
|
80
|
+
npx reldens-cms-update-password --email=admin@example.com
|
|
81
|
+
|
|
82
|
+
# Update by username (will prompt for password)
|
|
83
|
+
npx reldens-cms-update-password --username=admin
|
|
84
|
+
|
|
85
|
+
# Update with password in command (less secure, not recommended)
|
|
86
|
+
npx reldens-cms-update-password --email=admin@example.com --password=newPassword123
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### CLI Options
|
|
90
|
+
|
|
91
|
+
- `--email=[email]` - User email address
|
|
92
|
+
- `--username=[username]` - User username
|
|
93
|
+
- `--password=[password]` - New password (prompted if not provided)
|
|
94
|
+
- `--help` or `-h` - Show help message
|
|
95
|
+
|
|
96
|
+
#### Requirements
|
|
97
|
+
|
|
98
|
+
- Either `--email` or `--username` must be provided
|
|
99
|
+
- CMS must be installed (install.lock file must exist)
|
|
100
|
+
- Entities must be generated for the configured driver
|
|
101
|
+
|
|
102
|
+
#### How It Works
|
|
103
|
+
|
|
104
|
+
The CLI tool is **driver-agnostic** and works with any storage driver:
|
|
105
|
+
1. Reads `RELDENS_STORAGE_DRIVER` from .env (defaults to 'prisma')
|
|
106
|
+
2. Uses `EntitiesLoader` to load entities for the detected driver
|
|
107
|
+
3. If driver is 'prisma', automatically loads Prisma client from `./prisma/client`
|
|
108
|
+
4. Initializes Manager and dataServer with the correct driver
|
|
109
|
+
5. Updates password using the driver's entity repository
|
|
110
|
+
|
|
111
|
+
#### Security Notes
|
|
112
|
+
|
|
113
|
+
- **Interactive Mode** (no --password flag): Most secure, passwords are not stored in command history
|
|
114
|
+
- **Command-Line Mode** (with --password flag): Less secure, password appears in shell history
|
|
115
|
+
|
|
116
|
+
### Method 2: Admin Panel
|
|
117
|
+
|
|
118
|
+
User passwords can be updated through the admin panel when editing a user record.
|
|
119
|
+
|
|
120
|
+
**Password Field Behavior**:
|
|
121
|
+
- **Field Type**: `type="password"` (hidden input, not plain text)
|
|
122
|
+
- **On Edit**: Password field is empty (does not show encrypted hash)
|
|
123
|
+
- **On Create**: Password field is empty and required
|
|
124
|
+
- **On Update**: Password field is optional - only updates if filled
|
|
125
|
+
- **Encryption**: Automatic via `PasswordEncryptionHandler` before save
|
|
126
|
+
|
|
127
|
+
**How It Works**:
|
|
128
|
+
1. When editing a user, the password field is empty (line 605-607 in router-contents.js)
|
|
129
|
+
2. Input type is set to 'password' for security (line 706-708 in router-contents.js)
|
|
130
|
+
3. If password field is left empty, it's skipped during update (line 515-519 in router-contents.js)
|
|
131
|
+
4. If password is entered, it's encrypted by `PasswordEncryptionHandler` before save
|
|
132
|
+
5. Password is stored as encrypted `salt:hash` in database
|
|
133
|
+
|
|
134
|
+
**Configuration**:
|
|
135
|
+
- Password field names configurable via `passwordFieldNames` (default: `['password']`)
|
|
136
|
+
- Can be customized when creating RouterContents instance
|
|
137
|
+
|
|
138
|
+
**Important Notes**:
|
|
139
|
+
- Password encryption is enabled by default via `PasswordEncryptionHandler`
|
|
140
|
+
- If you disabled automatic encryption, passwords will be saved as plain text (not recommended)
|
|
141
|
+
- Always verify encryption is enabled in production environments
|
|
142
|
+
|
|
143
|
+
### Method 3: Direct Database Update
|
|
144
|
+
|
|
145
|
+
For emergency situations or when CLI is not available:
|
|
146
|
+
|
|
147
|
+
1. Generate encrypted password using Node.js:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
151
|
+
let encryptedPassword = Encryptor.encryptPassword('yourNewPassword');
|
|
152
|
+
console.log(encryptedPassword);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
2. Update database directly:
|
|
156
|
+
|
|
157
|
+
```sql
|
|
158
|
+
UPDATE users
|
|
159
|
+
SET password = 'generated-salt:generated-hash'
|
|
160
|
+
WHERE email = 'admin@example.com';
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**⚠️ Warning**: Only use direct database updates as a last resort. Prefer CLI or admin panel methods.
|
|
164
|
+
|
|
165
|
+
### Method 4: Programmatic Update
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const { Manager } = require('@reldens/cms');
|
|
169
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
170
|
+
|
|
171
|
+
async function updateUserPassword(email, newPassword)
|
|
172
|
+
{
|
|
173
|
+
let manager = new Manager({projectRoot: process.cwd()});
|
|
174
|
+
await manager.initializeDataServer();
|
|
175
|
+
let usersEntity = manager.dataServer.getEntity('users');
|
|
176
|
+
let user = await usersEntity.loadOneBy('email', email);
|
|
177
|
+
if(!user){
|
|
178
|
+
console.error('User not found');
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
let encryptedPassword = Encryptor.encryptPassword(newPassword);
|
|
182
|
+
await usersEntity.updateById(user.id, {password: encryptedPassword});
|
|
183
|
+
console.log('Password updated successfully');
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Password Security Best Practices
|
|
189
|
+
|
|
190
|
+
### For Developers
|
|
191
|
+
|
|
192
|
+
1. **Never Store Plain Text Passwords**
|
|
193
|
+
- Always use `Encryptor.encryptPassword()` before saving
|
|
194
|
+
- Verify automatic encryption is enabled in production
|
|
195
|
+
|
|
196
|
+
2. **Verify Password Encryption**
|
|
197
|
+
- Check that stored passwords contain `:` separator
|
|
198
|
+
- Verify salt length is 64 characters (hex)
|
|
199
|
+
- Verify hash length is 128 characters (hex)
|
|
200
|
+
|
|
201
|
+
3. **Use CLI for Password Updates**
|
|
202
|
+
- Prefer interactive mode (no --password flag)
|
|
203
|
+
- Avoid logging passwords in application logs
|
|
204
|
+
|
|
205
|
+
4. **Validate Password Format**
|
|
206
|
+
- Use `PasswordEncryptionHandler.isAlreadyEncrypted()` to check format
|
|
207
|
+
- Don't re-encrypt already encrypted passwords
|
|
208
|
+
|
|
209
|
+
5. **Handle Password Changes**
|
|
210
|
+
- Clear user sessions after password changes
|
|
211
|
+
- Send email notifications for password changes
|
|
212
|
+
- Implement password history to prevent reuse
|
|
213
|
+
|
|
214
|
+
### For Users
|
|
215
|
+
|
|
216
|
+
1. **Strong Password Requirements**
|
|
217
|
+
- Minimum 12 characters
|
|
218
|
+
- Mix of uppercase, lowercase, numbers, and symbols
|
|
219
|
+
- Avoid common words and patterns
|
|
220
|
+
- Use password managers
|
|
221
|
+
|
|
222
|
+
2. **Password Rotation**
|
|
223
|
+
- Change passwords regularly (every 90 days recommended)
|
|
224
|
+
- Change immediately if compromise is suspected
|
|
225
|
+
- Never share passwords
|
|
226
|
+
|
|
227
|
+
3. **Account Security**
|
|
228
|
+
- Use unique passwords for each system
|
|
229
|
+
- Enable two-factor authentication when available
|
|
230
|
+
- Monitor login activity
|
|
231
|
+
|
|
232
|
+
## Password Encryption Handler
|
|
233
|
+
|
|
234
|
+
### Architecture
|
|
235
|
+
|
|
236
|
+
The `PasswordEncryptionHandler` class provides automatic password encryption for user entities.
|
|
237
|
+
|
|
238
|
+
**File**: `lib/password-encryption-handler.js`
|
|
239
|
+
|
|
240
|
+
**Key Methods**:
|
|
241
|
+
- `registerEventListeners()` - Registers event listeners for automatic encryption
|
|
242
|
+
- `handleBeforeEntitySave()` - Processes password encryption before entity save
|
|
243
|
+
- `isAlreadyEncrypted()` - Checks if password is already in encrypted format
|
|
244
|
+
|
|
245
|
+
### Configuration
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
const { Manager } = require('@reldens/cms');
|
|
249
|
+
|
|
250
|
+
let manager = new Manager({
|
|
251
|
+
projectRoot: process.cwd(),
|
|
252
|
+
enablePasswordEncryption: true, // Default: true
|
|
253
|
+
// ... other config
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Customization
|
|
258
|
+
|
|
259
|
+
To customize the password encryption handler:
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
const { PasswordEncryptionHandler } = require('@reldens/cms');
|
|
263
|
+
|
|
264
|
+
let customHandler = new PasswordEncryptionHandler({
|
|
265
|
+
events: eventsManager,
|
|
266
|
+
entityKey: 'users', // Entity to monitor
|
|
267
|
+
passwordField: 'password', // Field name
|
|
268
|
+
enabled: true // Enable/disable
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
customHandler.registerEventListeners();
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Event Integration
|
|
275
|
+
|
|
276
|
+
The password handler hooks into the admin save process:
|
|
277
|
+
|
|
278
|
+
**Event**: `reldens.adminBeforeEntitySave`
|
|
279
|
+
|
|
280
|
+
**Event Data**:
|
|
281
|
+
- `driverResource` - Entity resource being saved
|
|
282
|
+
- `req` - Express request object
|
|
283
|
+
- `res` - Express response object
|
|
284
|
+
- `entityPath` - Entity path
|
|
285
|
+
|
|
286
|
+
**Processing Flow**:
|
|
287
|
+
1. Event is emitted before entity save
|
|
288
|
+
2. Handler checks if entity is 'users'
|
|
289
|
+
3. Handler checks if password field is present
|
|
290
|
+
4. Handler verifies password is not already encrypted
|
|
291
|
+
5. Handler encrypts password and updates `req.body.password`
|
|
292
|
+
6. Entity save continues with encrypted password
|
|
293
|
+
|
|
294
|
+
## Troubleshooting
|
|
295
|
+
|
|
296
|
+
### Common Issues
|
|
297
|
+
|
|
298
|
+
**Issue**: "Encryptor is not a constructor"
|
|
299
|
+
**Solution**: Use `Encryptor.encryptPassword()` directly, not `new Encryptor()`
|
|
300
|
+
|
|
301
|
+
**Issue**: "Invalid password during login"
|
|
302
|
+
**Solution**: Verify password is encrypted in database (should contain `:` and be 192 chars total)
|
|
303
|
+
|
|
304
|
+
**Issue**: "Password not encrypted when saving"
|
|
305
|
+
**Solution**: Verify `enablePasswordEncryption: true` in Manager config
|
|
306
|
+
|
|
307
|
+
**Issue**: "Password gets re-encrypted"
|
|
308
|
+
**Solution**: The handler checks for encryption format, but ensure you're not manually encrypting before save
|
|
309
|
+
|
|
310
|
+
### Debugging
|
|
311
|
+
|
|
312
|
+
Enable debug logging to troubleshoot password issues:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
RELDENS_LOG_LEVEL=9 node .
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Look for these debug messages:
|
|
319
|
+
- "Password encrypted successfully for user entity save"
|
|
320
|
+
- "Password field appears to be already encrypted"
|
|
321
|
+
- "PasswordEncryptionHandler registered for event"
|
|
322
|
+
|
|
323
|
+
### Verification
|
|
324
|
+
|
|
325
|
+
To verify a password is correctly encrypted:
|
|
326
|
+
|
|
327
|
+
```javascript
|
|
328
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
329
|
+
|
|
330
|
+
let storedPassword = 'from-database';
|
|
331
|
+
let parts = storedPassword.split(':');
|
|
332
|
+
|
|
333
|
+
if(2 === parts.length && 64 === parts[0].length && 128 === parts[1].length){
|
|
334
|
+
console.log('Password is correctly encrypted');
|
|
335
|
+
} else {
|
|
336
|
+
console.log('Password format is invalid');
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Migration from Plain Text Passwords
|
|
341
|
+
|
|
342
|
+
If you have existing plain text passwords in your database:
|
|
343
|
+
|
|
344
|
+
1. **Create Migration Script**:
|
|
345
|
+
|
|
346
|
+
```javascript
|
|
347
|
+
const { Manager } = require('@reldens/cms');
|
|
348
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
349
|
+
|
|
350
|
+
async function migratePasswords()
|
|
351
|
+
{
|
|
352
|
+
let manager = new Manager({projectRoot: process.cwd()});
|
|
353
|
+
await manager.initializeDataServer();
|
|
354
|
+
let usersEntity = manager.dataServer.getEntity('users');
|
|
355
|
+
let allUsers = await usersEntity.loadAll();
|
|
356
|
+
for(let user of allUsers){
|
|
357
|
+
let parts = user.password.split(':');
|
|
358
|
+
if(2 === parts.length && 64 === parts[0].length){
|
|
359
|
+
console.log('User '+user.email+' already encrypted, skipping');
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
let encryptedPassword = Encryptor.encryptPassword(user.password);
|
|
363
|
+
await usersEntity.updateById(user.id, {password: encryptedPassword});
|
|
364
|
+
console.log('Migrated password for user: '+user.email);
|
|
365
|
+
}
|
|
366
|
+
console.log('Migration completed');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
migratePasswords().catch(console.error);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
2. **Run Migration**:
|
|
373
|
+
```bash
|
|
374
|
+
node migrate-passwords.js
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
3. **Verify Migration**:
|
|
378
|
+
```sql
|
|
379
|
+
SELECT id, email, LENGTH(password) as pwd_length,
|
|
380
|
+
CASE WHEN password LIKE '%:%' THEN 'Encrypted' ELSE 'Plain' END as status
|
|
381
|
+
FROM users;
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## API Reference
|
|
385
|
+
|
|
386
|
+
### Encryptor Methods
|
|
387
|
+
|
|
388
|
+
**`Encryptor.encryptPassword(password)`**
|
|
389
|
+
- **Parameters**: `password` (string) - Plain text password
|
|
390
|
+
- **Returns**: String in format `salt:hash` or `false` on error
|
|
391
|
+
- **Usage**: Encrypt a password before storing
|
|
392
|
+
|
|
393
|
+
**`Encryptor.validatePassword(password, storedPassword)`**
|
|
394
|
+
- **Parameters**:
|
|
395
|
+
- `password` (string) - Plain text password to verify
|
|
396
|
+
- `storedPassword` (string) - Encrypted password from database
|
|
397
|
+
- **Returns**: Boolean - `true` if password matches, `false` otherwise
|
|
398
|
+
- **Usage**: Validate password during authentication
|
|
399
|
+
|
|
400
|
+
### PasswordEncryptionHandler Methods
|
|
401
|
+
|
|
402
|
+
**`new PasswordEncryptionHandler(props)`**
|
|
403
|
+
- **Parameters**: Object with `events`, `entityKey`, `passwordField`, `enabled`
|
|
404
|
+
- **Returns**: PasswordEncryptionHandler instance
|
|
405
|
+
|
|
406
|
+
**`registerEventListeners()`**
|
|
407
|
+
- **Parameters**: None
|
|
408
|
+
- **Returns**: Boolean - `true` if registered, `false` if disabled
|
|
409
|
+
|
|
410
|
+
**`isAlreadyEncrypted(value)`**
|
|
411
|
+
- **Parameters**: `value` (string) - Password value to check
|
|
412
|
+
- **Returns**: Boolean - `true` if encrypted format, `false` otherwise
|
|
413
|
+
|
|
414
|
+
## Support
|
|
415
|
+
|
|
416
|
+
For issues or questions about password management:
|
|
417
|
+
|
|
418
|
+
- **GitHub Issues**: https://github.com/damian-pastorini/reldens-cms/issues
|
|
419
|
+
- **Discord**: https://discord.gg/HuJMxUY
|
|
420
|
+
- **Email**: info@dwdeveloper.com
|
|
421
|
+
|
|
422
|
+
## Related Documentation
|
|
423
|
+
|
|
424
|
+
- Main CLAUDE.md - Package overview and architecture
|
|
425
|
+
- @reldens/server-utils CLAUDE.md - Encryptor class documentation
|
|
426
|
+
- Security Features section in main documentation
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Search Functionality Guide
|
|
2
|
+
|
|
3
|
+
## Basic Search
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
/search?search=technology
|
|
7
|
+
/search?search=javascript&limit=20
|
|
8
|
+
/search?search=news&renderPartial=newsListView&renderLayout=minimal
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Advanced Search with Template Data
|
|
12
|
+
|
|
13
|
+
Pass custom template variables through URL parameters:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
/search?search=articles&templateData[columnsClass]=col-md-4&templateData[showExcerpt]=true
|
|
17
|
+
/search?search=technology&templateData[columnsClass]=col-lg-6&templateData[cardClass]=shadow-sm&templateData[showAuthor]=false
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Search Template Variables
|
|
21
|
+
|
|
22
|
+
Templates receive dynamic data through URL parameters.
|
|
23
|
+
|
|
24
|
+
**URL Example:**
|
|
25
|
+
```
|
|
26
|
+
/search?search=tech&templateData[columnsClass]=col-md-6&templateData[showDate]=true
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Template (entriesListView.html):**
|
|
30
|
+
```html
|
|
31
|
+
<div class="{{columnsClass}}">
|
|
32
|
+
<div class="card">
|
|
33
|
+
<h3>{{row.title}}</h3>
|
|
34
|
+
<p>{{row.content}}</p>
|
|
35
|
+
{{#showDate}}
|
|
36
|
+
<span class="date">{{row.created_at}}</span>
|
|
37
|
+
{{/showDate}}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Default Values:**
|
|
43
|
+
|
|
44
|
+
- `columnsClass` defaults to `col-lg-6` if not provided or empty
|
|
45
|
+
- Custom variables can be added via `templateData[variableName]=value`
|
|
46
|
+
|
|
47
|
+
## Search Configuration
|
|
48
|
+
|
|
49
|
+
Custom search sets in Manager configuration:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const searchSets = {
|
|
53
|
+
articlesSearch: {
|
|
54
|
+
entities: [{
|
|
55
|
+
name: 'articles',
|
|
56
|
+
fields: ['title', 'content', 'summary'],
|
|
57
|
+
relations: 'authors'
|
|
58
|
+
}],
|
|
59
|
+
pagination: {active: true, limit: 15, sortBy: 'created_at', sortDirection: 'desc'}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const cms = new Manager({
|
|
64
|
+
searchSets: searchSets
|
|
65
|
+
});
|
|
66
|
+
```
|