Skip to content

Commit fff8c54

Browse files
authored
Merge pull request #431 from devforth/next
Next
2 parents 9bbef33 + 5826de5 commit fff8c54

File tree

22 files changed

+251
-97
lines changed

22 files changed

+251
-97
lines changed

adminforth/dataConnectors/postgres.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
400400
}
401401
const primaryKey = this.getPrimaryKey(resource);
402402
const q = `INSERT INTO "${tableName}" (${columns.join(', ')}) VALUES (${placeholders}) RETURNING "${primaryKey}"`;
403+
// console.log('\n🔵 [PG INSERT]:', q);
404+
// console.log('📦 [VALUES]:', JSON.stringify(values, null, 2));
403405
if (process.env.HEAVY_DEBUG_QUERY) {
404406
console.log('🪲📜 PG Q:', q, 'values:', values);
405407
}

adminforth/dataConnectors/sqlite.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
143143
} else {
144144
return value;
145145
}
146+
}
147+
else if (field.isArray?.enabled) {
148+
if (value === null || value === undefined) {
149+
return null;
150+
}
151+
return JSON.stringify(value);
146152
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
147153
return value === null ? null : (value ? 1 : 0);
148154
} else if (field.type == AdminForthDataTypes.JSON) {
@@ -322,7 +328,14 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
322328
const columns = Object.keys(record);
323329
const placeholders = columns.map(() => '?').join(', ');
324330
const values = columns.map((colName) => record[colName]);
325-
const q = this.client.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`);
331+
// const q = this.client.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`);
332+
const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
333+
//console.log('\n🟢 [SQLITE INSERT]:', sql);
334+
//console.log('📦 [VALUES]:', JSON.stringify(values, null, 2));
335+
const q = this.client.prepare(sql);
336+
if (process.env.HEAVY_DEBUG_QUERY) {
337+
console.log('🪲📜 SQL Q:', q, 'values:', values);
338+
}
326339
const ret = await q.run(values);
327340
return ret.lastInsertRowid;
328341
}

adminforth/documentation/docs/tutorial/07-Plugins/04-RichEditor.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,7 @@ export default {
227227
filePath: ({ originalFilename, originalExtension, contentType }) =>
228228
`description_images/${new Date().getFullYear()}/${uuid()}/${originalFilename}.${originalExtension}`,
229229

230-
preview: {
231-
// Used to display preview (if it is image) in list and show views instead of just path
232-
// previewUrl: ({s3Path}) => `https://tmpbucket-adminforth.s3.eu-central-1.amazonaws.com/${s3Path}`,
233-
// show image preview instead of path in list view
234-
// showInList: false,
235-
},
230+
236231
}),
237232
],
238233
} as AdminForthResourceInput;

adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ Leave all settings unchanged (ACL Disabled, Block all public access - checked)
3131
],
3232
"AllowedMethods": [
3333
"HEAD",
34-
"PUT"
34+
"PUT",
35+
"GET"
3536
],
3637
"AllowedOrigins": [
3738
"http://localhost:3500"

adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,43 @@ Links to adapters:
350350
[Google](https://github.com/devforth/adminforth-google-oauth-adapter)
351351
[GitHub](https://github.com/devforth/adminforth-github-oauth-adapter)
352352
[Facebook](https://github.com/devforth/adminforth-facebook-oauth-adapter)
353-
[Keycloak](https://github.com/devforth/adminforth-keycloak-oauth-adapter)
353+
[Keycloak](https://github.com/devforth/adminforth-keycloak-oauth-adapter)
354+
355+
356+
## Fill user full name
357+
358+
If you have a fullName field in your users resource, you can add it to the plugin setup:
359+
360+
```ts
361+
362+
plugins: [
363+
...
364+
365+
new OAuthPlugin({
366+
367+
...
368+
369+
userFullNameField: 'fullName'
370+
371+
...
372+
373+
}),
374+
]
375+
376+
```
377+
378+
This field will be automatically filled with the name that the provider returns, if this field was empty.
379+
380+
> ☝️Not all providers return full name or even if they do, there is no guarantee that they will be correct
381+
382+
> Google Adapter: returns fullName, but if there is no last name - it will return only first name
383+
384+
>Facebook: returns fullName
385+
386+
>Github: returns name or fullName (depends of what user added in name field)
387+
388+
>Keycloak: returns fullName
389+
390+
>Microsoft: returns fullName
391+
392+
>Twitch: return only users display name

adminforth/modules/configValidator.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,18 @@ export default class ConfigValidator implements IConfigValidator {
11591159
} else {
11601160
newConfig.auth.beforeLoginConfirmation = blc;
11611161
}
1162+
1163+
// normalize adminUserAuthorize hooks
1164+
const aua = this.inputConfig.auth.adminUserAuthorize;
1165+
if (!Array.isArray(aua)) {
1166+
if (aua) {
1167+
newConfig.auth.adminUserAuthorize = [aua];
1168+
} else {
1169+
newConfig.auth.adminUserAuthorize = [];
1170+
}
1171+
} else {
1172+
newConfig.auth.adminUserAuthorize = aua;
1173+
}
11621174
}
11631175

11641176
// check for duplicate resourceIds and show which ones are duplicated

adminforth/modules/restApi.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import {
1515
Filters,
1616
} from "../types/Back.js";
1717

18-
import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML } from './utils.js';
18+
import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML, checkShowIf } from './utils.js';
1919

2020
import AdminForthAuth from "../auth.js";
21-
import { ActionCheckSource, AdminForthConfigMenuItem, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourceCommon, AdminForthResourcePages,
21+
import { ActionCheckSource, AdminForthConfigMenuItem, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourceColumnInputCommon, AdminForthResourceCommon, AdminForthResourcePages,
2222
AdminUser, AllowedActionsEnum, AllowedActionsResolved,
2323
AnnouncementBadgeResponse,
2424
GetBaseConfigResponse,
@@ -323,10 +323,13 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
323323

324324
const userPk = dbUser[userResource.columns.find((col) => col.primaryKey).name];
325325

326+
const userAvatarUrl = await this.adminforth.config.auth.avatarUrl?.(adminUser);
327+
326328
const userData = {
327329
[this.adminforth.config.auth.usernameField]: username,
328330
[this.adminforth.config.auth.userFullNameField]: userFullName,
329331
pk: userPk,
332+
userAvatarUrl: userAvatarUrl || null,
330333
};
331334
const checkIsMenuItemVisible = (menuItem) => {
332335
if (typeof menuItem.visible === 'function') {
@@ -1177,7 +1180,10 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
11771180
record[column.name] === undefined &&
11781181
column.showIn.create
11791182
) {
1180-
return { error: `Column '${column.name}' is required`, ok: false };
1183+
const isColumnShownWithShowIf = await checkShowIf(column as AdminForthResourceColumnInputCommon, record) ;
1184+
if (isColumnShownWithShowIf === true) {
1185+
return { error: `Column '${column.name}' is required`, ok: false };
1186+
}
11811187
}
11821188
}
11831189

@@ -1200,8 +1206,11 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
12001206
for (const column of resource.columns) {
12011207
if ((column.required as { create?: boolean })?.create) {
12021208
const shown = await isShown(column, 'create', ctxCreate);
1203-
if (shown && record[column.name] === undefined) {
1204-
return { error: `Column '${column.name}' is required`, ok: false };
1209+
const isColumnShownWithShowIf = await checkShowIf(column as AdminForthResourceColumnInputCommon , record) ;
1210+
if (isColumnShownWithShowIf === true) {
1211+
if (shown && record[column.name] === undefined) {
1212+
return { error: `Column '${column.name}' is required`, ok: false };
1213+
}
12051214
}
12061215
}
12071216
}

adminforth/modules/utils.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'url';
33
import fs from 'fs';
44
import Fuse from 'fuse.js';
55
import crypto from 'crypto';
6-
import AdminForth, { AdminForthConfig } from '../index.js';
6+
import AdminForth, { AdminForthConfig, AdminForthResourceColumnInputCommon, Predicate } from '../index.js';
77
import { RateLimiterMemory, RateLimiterAbstract } from "rate-limiter-flexible";
88
// @ts-ignore-next-line
99

@@ -478,4 +478,60 @@ export function slugifyString(str: string): string {
478478
.toLowerCase()
479479
.replace(/\s+/g, '-')
480480
.replace(/[^a-z0-9-_]/g, '-');
481+
}
482+
483+
export function checkShowIf(c: AdminForthResourceColumnInputCommon, record: Record<string, any>) {
484+
if (!c.showIf) return true;
485+
486+
const evaluatePredicate = (predicate: Predicate): boolean => {
487+
const results: boolean[] = [];
488+
489+
if ("$and" in predicate) {
490+
results.push(predicate.$and.every(evaluatePredicate));
491+
}
492+
493+
if ("$or" in predicate) {
494+
results.push(predicate.$or.some(evaluatePredicate));
495+
}
496+
497+
const fieldEntries = Object.entries(predicate).filter(([key]) => !key.startsWith('$'));
498+
if (fieldEntries.length > 0) {
499+
const fieldResult = fieldEntries.every(([field, condition]) => {
500+
const recordValue = record[field];
501+
502+
if (condition === undefined) {
503+
return true;
504+
}
505+
if (typeof condition !== "object" || condition === null) {
506+
return recordValue === condition;
507+
}
508+
509+
if ("$eq" in condition) return recordValue === condition.$eq;
510+
if ("$not" in condition) return recordValue !== condition.$not;
511+
if ("$gt" in condition) return recordValue > condition.$gt;
512+
if ("$gte" in condition) return recordValue >= condition.$gte;
513+
if ("$lt" in condition) return recordValue < condition.$lt;
514+
if ("$lte" in condition) return recordValue <= condition.$lte;
515+
if ("$in" in condition) return (Array.isArray(condition.$in) && condition.$in.includes(recordValue));
516+
if ("$nin" in condition) return (Array.isArray(condition.$nin) && !condition.$nin.includes(recordValue));
517+
if ("$includes" in condition)
518+
return (
519+
Array.isArray(recordValue) &&
520+
recordValue.includes(condition.$includes)
521+
);
522+
if ("$nincludes" in condition)
523+
return (
524+
Array.isArray(recordValue) &&
525+
!recordValue.includes(condition.$nicludes)
526+
);
527+
528+
return true;
529+
});
530+
results.push(fieldResult);
531+
}
532+
533+
return results.every(result => result);
534+
};
535+
536+
return evaluatePredicate(c.showIf);
481537
}

adminforth/spa/package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

adminforth/spa/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"dependencies": {
1616
"@iconify-prerendered/vue-flag": "^0.28.1748584105",
1717
"@iconify-prerendered/vue-flowbite": "^0.28.1754899090",
18+
"@iconify-prerendered/vue-humbleicons": "^0.28.1754108846",
1819
"@unhead/vue": "^1.9.12",
1920
"@vueuse/core": "^10.10.0",
2021
"apexcharts": "^4.7.0",

0 commit comments

Comments
 (0)