@dyrected/core 2.19.0 → 2.20.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/dist/{app-CXF4MdJc.d.cts → app-v23fNf58.d.cts} +171 -28
- package/dist/{app-CXF4MdJc.d.ts → app-v23fNf58.d.ts} +171 -28
- package/dist/index.cjs +511 -21
- package/dist/index.d.cts +73 -5
- package/dist/index.d.ts +73 -5
- package/dist/index.js +491 -21
- package/dist/server.cjs +1167 -136
- package/dist/server.d.cts +79 -29
- package/dist/server.d.ts +79 -29
- package/dist/server.js +1158 -136
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1799,8 +1799,11 @@ __export(index_exports, {
|
|
|
1799
1799
|
DuplicateKeyError: () => DuplicateKeyError,
|
|
1800
1800
|
DyrectedAIError: () => DyrectedAIError,
|
|
1801
1801
|
DyrectedError: () => DyrectedError,
|
|
1802
|
+
EMAIL_TEMPLATES_COLLECTION_CONFIG: () => EMAIL_TEMPLATES_COLLECTION_CONFIG,
|
|
1803
|
+
EMAIL_TEMPLATES_COLLECTION_SLUG: () => EMAIL_TEMPLATES_COLLECTION_SLUG,
|
|
1802
1804
|
FieldConditionBuilder: () => FieldConditionBuilder,
|
|
1803
1805
|
ForbiddenError: () => ForbiddenError,
|
|
1806
|
+
INTERNAL_SYSTEM_COLLECTIONS: () => INTERNAL_SYSTEM_COLLECTIONS,
|
|
1804
1807
|
LIFECYCLE_EVENTS_COLLECTION: () => LIFECYCLE_EVENTS_COLLECTION,
|
|
1805
1808
|
LIFECYCLE_EVENT_NAMES: () => LIFECYCLE_EVENT_NAMES,
|
|
1806
1809
|
MatchBuilder: () => MatchBuilder,
|
|
@@ -1818,6 +1821,7 @@ __export(index_exports, {
|
|
|
1818
1821
|
WORKFLOW_HISTORY_COLLECTION: () => WORKFLOW_HISTORY_COLLECTION,
|
|
1819
1822
|
abs: () => abs,
|
|
1820
1823
|
addDays: () => addDays,
|
|
1824
|
+
alertBox: () => alertBox,
|
|
1821
1825
|
asCollection: () => asCollection,
|
|
1822
1826
|
assertValidAdminConditionsInConfig: () => assertValidAdminConditionsInConfig,
|
|
1823
1827
|
assertValidDeclarativeAccessInConfig: () => assertValidDeclarativeAccessInConfig,
|
|
@@ -1847,6 +1851,7 @@ __export(index_exports, {
|
|
|
1847
1851
|
createTaskRunner: () => createTaskRunner,
|
|
1848
1852
|
createTrashPurgeTask: () => createTrashPurgeTask,
|
|
1849
1853
|
createWorkflowDocument: () => createWorkflowDocument,
|
|
1854
|
+
ctaButton: () => ctaButton,
|
|
1850
1855
|
defaultValue: () => defaultValue,
|
|
1851
1856
|
defineAction: () => defineAction,
|
|
1852
1857
|
defineArrayField: () => defineArrayField,
|
|
@@ -1893,6 +1898,7 @@ __export(index_exports, {
|
|
|
1893
1898
|
defineUrlField: () => defineUrlField,
|
|
1894
1899
|
defineView: () => defineView,
|
|
1895
1900
|
defineWorkspace: () => defineWorkspace,
|
|
1901
|
+
detailBox: () => detailBox,
|
|
1896
1902
|
diffDays: () => diffDays,
|
|
1897
1903
|
dispatchLifecycleEvent: () => dispatchLifecycleEvent,
|
|
1898
1904
|
dispatchPendingLifecycleEvents: () => dispatchPendingLifecycleEvents,
|
|
@@ -1908,10 +1914,14 @@ __export(index_exports, {
|
|
|
1908
1914
|
displayTab: () => displayTab,
|
|
1909
1915
|
displayTabs: () => displayTabs,
|
|
1910
1916
|
displayText: () => displayText,
|
|
1917
|
+
divider: () => divider,
|
|
1918
|
+
emailTokens: () => emailTokens,
|
|
1911
1919
|
endsWith: () => endsWith,
|
|
1920
|
+
escapeHtml: () => escapeHtml,
|
|
1912
1921
|
evaluateDetailComputed: () => evaluateDetailComputed,
|
|
1913
1922
|
evaluateJexl: () => evaluateJexl,
|
|
1914
1923
|
evaluateJexlSync: () => evaluateJexlSync,
|
|
1924
|
+
eventList: () => eventList,
|
|
1915
1925
|
executeFieldAfterRead: () => executeFieldAfterRead,
|
|
1916
1926
|
executeFieldBeforeChange: () => executeFieldBeforeChange,
|
|
1917
1927
|
expr: () => expr,
|
|
@@ -1928,8 +1938,10 @@ __export(index_exports, {
|
|
|
1928
1938
|
getAdminAuthCollection: () => getAdminAuthCollection,
|
|
1929
1939
|
getAdminRoleForCollection: () => getAdminRoleForCollection,
|
|
1930
1940
|
getAllowedSitesForUser: () => getAllowedSitesForUser,
|
|
1941
|
+
getDefaultEmailTemplate: () => getDefaultEmailTemplate,
|
|
1931
1942
|
getPublicAdminAuthConfig: () => getPublicAdminAuthConfig,
|
|
1932
1943
|
getTrashEntryId: () => getTrashEntryId,
|
|
1944
|
+
heading: () => heading,
|
|
1933
1945
|
includes: () => includes,
|
|
1934
1946
|
initializeWorkflowDocument: () => initializeWorkflowDocument,
|
|
1935
1947
|
isAICollection: () => isAICollection,
|
|
@@ -1942,6 +1954,7 @@ __export(index_exports, {
|
|
|
1942
1954
|
isUserAdmin: () => isUserAdmin,
|
|
1943
1955
|
join: () => join,
|
|
1944
1956
|
last: () => last,
|
|
1957
|
+
layout: () => layout,
|
|
1945
1958
|
length: () => length,
|
|
1946
1959
|
lower: () => lower,
|
|
1947
1960
|
maskEmail: () => maskEmail,
|
|
@@ -1957,6 +1970,7 @@ __export(index_exports, {
|
|
|
1957
1970
|
normalizeGroupKey: () => normalizeGroupKey,
|
|
1958
1971
|
now: () => now,
|
|
1959
1972
|
numericFieldNames: () => numericFieldNames,
|
|
1973
|
+
paragraph: () => paragraph,
|
|
1960
1974
|
parseCron: () => parseCron,
|
|
1961
1975
|
parseMongoWhere: () => parseMongoWhere,
|
|
1962
1976
|
parseSort: () => parseSort,
|
|
@@ -1973,21 +1987,27 @@ __export(index_exports, {
|
|
|
1973
1987
|
resolveAdminAuthCollection: () => resolveAdminAuthCollection,
|
|
1974
1988
|
resolveAllCollectionViews: () => resolveAllCollectionViews,
|
|
1975
1989
|
resolveAuthorizedSiteId: () => resolveAuthorizedSiteId,
|
|
1990
|
+
resolveBlocks: () => resolveBlocks,
|
|
1976
1991
|
resolveDocumentTitle: () => resolveDocumentTitle,
|
|
1977
1992
|
resolveIndexName: () => resolveIndexName,
|
|
1978
1993
|
resolvePrefix: () => resolvePrefix,
|
|
1979
1994
|
resolveTrashConfig: () => resolveTrashConfig,
|
|
1980
1995
|
restoreDocument: () => restoreDocument,
|
|
1981
1996
|
round: () => round,
|
|
1997
|
+
row: () => row,
|
|
1982
1998
|
runCollectionHooks: () => runCollectionHooks,
|
|
1999
|
+
safeLinkDetailBox: () => safeLinkDetailBox,
|
|
1983
2000
|
sanitizeDocForAI: () => sanitizeDocForAI,
|
|
1984
2001
|
saveWorkflowDraft: () => saveWorkflowDraft,
|
|
2002
|
+
sectionLabel: () => sectionLabel,
|
|
1985
2003
|
simplePublishingWorkflow: () => simplePublishingWorkflow,
|
|
1986
2004
|
slugify: () => slugify,
|
|
1987
2005
|
softDeleteDocument: () => softDeleteDocument,
|
|
2006
|
+
spacer: () => spacer,
|
|
1988
2007
|
sqrt: () => sqrt,
|
|
1989
2008
|
startsWith: () => startsWith,
|
|
1990
2009
|
sum: () => sum,
|
|
2010
|
+
table: () => table,
|
|
1991
2011
|
today: () => today,
|
|
1992
2012
|
transitionWorkflow: () => transitionWorkflow,
|
|
1993
2013
|
trim: () => trim,
|
|
@@ -3243,6 +3263,14 @@ async function executeFieldAfterRead(fields, doc, user, db) {
|
|
|
3243
3263
|
|
|
3244
3264
|
// src/trash.ts
|
|
3245
3265
|
var TRASH_COLLECTION = "__trash";
|
|
3266
|
+
var INTERNAL_SYSTEM_COLLECTIONS = /* @__PURE__ */ new Set([
|
|
3267
|
+
"__audit",
|
|
3268
|
+
"__sessions",
|
|
3269
|
+
"__task_locks",
|
|
3270
|
+
"__trash",
|
|
3271
|
+
"__workflow_history",
|
|
3272
|
+
"__lifecycle_events"
|
|
3273
|
+
]);
|
|
3246
3274
|
var TRASH_COLLECTION_CONFIG = {
|
|
3247
3275
|
slug: TRASH_COLLECTION,
|
|
3248
3276
|
labels: { singular: "Trash entry", plural: "Trash entries" },
|
|
@@ -3334,7 +3362,7 @@ function assertValidTrashInConfig(config, source = "config") {
|
|
|
3334
3362
|
if (col.slug === "trash") {
|
|
3335
3363
|
throw new Error(`Collection slug "trash" is reserved by Dyrected for the trash system.`);
|
|
3336
3364
|
}
|
|
3337
|
-
const isSystem = col.slug
|
|
3365
|
+
const isSystem = INTERNAL_SYSTEM_COLLECTIONS.has(col.slug);
|
|
3338
3366
|
if (isSystem && col.trash) {
|
|
3339
3367
|
throw new Error(`System collection "${col.slug}" cannot enable trash.`);
|
|
3340
3368
|
}
|
|
@@ -3713,7 +3741,7 @@ function createTaskRunner(config) {
|
|
|
3713
3741
|
throw error;
|
|
3714
3742
|
}
|
|
3715
3743
|
}
|
|
3716
|
-
async function execute(task,
|
|
3744
|
+
async function execute(task, row2, scheduledFor) {
|
|
3717
3745
|
const started = Date.now();
|
|
3718
3746
|
const lockMs = (task.lockTimeout ?? DEFAULT_LOCK_TIMEOUT_SECONDS) * 1e3;
|
|
3719
3747
|
const token = `${instanceId}:${started}`;
|
|
@@ -3721,7 +3749,7 @@ function createTaskRunner(config) {
|
|
|
3721
3749
|
id: { equals: task.name },
|
|
3722
3750
|
lockedUntil: { less_than: started }
|
|
3723
3751
|
};
|
|
3724
|
-
if (scheduledFor) where.lastScheduledFor = { equals:
|
|
3752
|
+
if (scheduledFor) where.lastScheduledFor = { equals: row2.lastScheduledFor };
|
|
3725
3753
|
const claimed = await db.update({
|
|
3726
3754
|
collection: TASK_LOCKS_COLLECTION,
|
|
3727
3755
|
where,
|
|
@@ -3782,21 +3810,21 @@ function createTaskRunner(config) {
|
|
|
3782
3810
|
async runTask(name) {
|
|
3783
3811
|
const task = tasks.get(name);
|
|
3784
3812
|
if (!task) throw new Error(`Unknown task "${name}".`);
|
|
3785
|
-
const
|
|
3786
|
-
return track(execute(task,
|
|
3813
|
+
const row2 = await ensureLockRow(name, /* @__PURE__ */ new Date());
|
|
3814
|
+
return track(execute(task, row2, void 0));
|
|
3787
3815
|
},
|
|
3788
3816
|
async runDue(now2 = /* @__PURE__ */ new Date()) {
|
|
3789
3817
|
const results = [];
|
|
3790
3818
|
for (const task of tasks.values()) {
|
|
3791
3819
|
if (!task.schedule) continue;
|
|
3792
|
-
const
|
|
3793
|
-
const reference = new Date(String(
|
|
3820
|
+
const row2 = await ensureLockRow(task.name, now2);
|
|
3821
|
+
const reference = new Date(String(row2.lastScheduledFor));
|
|
3794
3822
|
const due = Number.isNaN(reference.getTime()) ? true : nextCronRun(task.schedule, reference).getTime() <= now2.getTime();
|
|
3795
3823
|
if (!due) {
|
|
3796
3824
|
results.push({ name: task.name, status: "skipped", reason: "not-due", durationMs: 0 });
|
|
3797
3825
|
continue;
|
|
3798
3826
|
}
|
|
3799
|
-
results.push(await track(execute(task,
|
|
3827
|
+
results.push(await track(execute(task, row2, floorToMinute(now2))));
|
|
3800
3828
|
}
|
|
3801
3829
|
return results;
|
|
3802
3830
|
},
|
|
@@ -4311,13 +4339,34 @@ function getAdminRoleForCollection(collection) {
|
|
|
4311
4339
|
}
|
|
4312
4340
|
return "admin";
|
|
4313
4341
|
}
|
|
4342
|
+
var STANDARD_ADMIN_ROLES = /* @__PURE__ */ new Set([
|
|
4343
|
+
"admin",
|
|
4344
|
+
"super_admin",
|
|
4345
|
+
"superadmin",
|
|
4346
|
+
"super admin",
|
|
4347
|
+
"super-admin",
|
|
4348
|
+
"owner"
|
|
4349
|
+
]);
|
|
4314
4350
|
function isUserAdmin(user, collection) {
|
|
4315
4351
|
if (!user) return false;
|
|
4316
|
-
const
|
|
4317
|
-
if (
|
|
4352
|
+
const rawRoles = Array.isArray(user.roles) ? user.roles : typeof user.roles === "string" ? [user.roles] : Array.isArray(user.role) ? user.role : typeof user.role === "string" ? [user.role] : [];
|
|
4353
|
+
if (rawRoles.length === 0) return false;
|
|
4354
|
+
const roles = rawRoles.filter((r) => typeof r === "string");
|
|
4318
4355
|
const configuredRole = getAdminRoleForCollection(collection);
|
|
4319
|
-
|
|
4320
|
-
|
|
4356
|
+
for (const r of roles) {
|
|
4357
|
+
if (r === configuredRole || r.toLowerCase() === configuredRole.toLowerCase()) {
|
|
4358
|
+
return true;
|
|
4359
|
+
}
|
|
4360
|
+
const normalized = r.trim().toLowerCase();
|
|
4361
|
+
if (STANDARD_ADMIN_ROLES.has(normalized)) {
|
|
4362
|
+
return true;
|
|
4363
|
+
}
|
|
4364
|
+
const stripped = normalized.replace(/[\s_-]+/g, "");
|
|
4365
|
+
if (stripped === "admin" || stripped === "superadmin" || stripped === "owner") {
|
|
4366
|
+
return true;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
return false;
|
|
4321
4370
|
}
|
|
4322
4371
|
|
|
4323
4372
|
// src/utils/block-references.ts
|
|
@@ -4540,6 +4589,245 @@ function generateDocumentId(collectionOrSlug) {
|
|
|
4540
4589
|
return generateId({ prefix, type });
|
|
4541
4590
|
}
|
|
4542
4591
|
|
|
4592
|
+
// src/services/email-template.ts
|
|
4593
|
+
var emailTokens = {
|
|
4594
|
+
colors: {
|
|
4595
|
+
canvas: "#f6f7f2",
|
|
4596
|
+
surface: "#ffffff",
|
|
4597
|
+
text: "#171717",
|
|
4598
|
+
muted: "#62665b",
|
|
4599
|
+
subtle: "#8a8f82",
|
|
4600
|
+
border: "#dde0d7",
|
|
4601
|
+
accent: "#b6ff2e",
|
|
4602
|
+
code: "#f1f3ec",
|
|
4603
|
+
codeBorder: "#e2e5dc",
|
|
4604
|
+
dangerSurface: "#fff2f0",
|
|
4605
|
+
dangerBorder: "#ffc9c2",
|
|
4606
|
+
dangerText: "#9f251b"
|
|
4607
|
+
},
|
|
4608
|
+
font: "Arial, 'Helvetica Neue', Helvetica, sans-serif",
|
|
4609
|
+
mono: "'Courier New', Courier, monospace",
|
|
4610
|
+
radius: { card: "12px", control: "6px" },
|
|
4611
|
+
width: "600px"
|
|
4612
|
+
};
|
|
4613
|
+
function escapeHtml(value) {
|
|
4614
|
+
return value.replace(
|
|
4615
|
+
/[&<>'"]/g,
|
|
4616
|
+
(character) => ({
|
|
4617
|
+
"&": "&",
|
|
4618
|
+
"<": "<",
|
|
4619
|
+
">": ">",
|
|
4620
|
+
"'": "'",
|
|
4621
|
+
'"': """
|
|
4622
|
+
})[character]
|
|
4623
|
+
);
|
|
4624
|
+
}
|
|
4625
|
+
function safeHttpUrl(value) {
|
|
4626
|
+
if (value.startsWith("{{") && value.endsWith("}}")) {
|
|
4627
|
+
return value;
|
|
4628
|
+
}
|
|
4629
|
+
try {
|
|
4630
|
+
const url = new URL(value);
|
|
4631
|
+
return url.protocol === "https:" || url.protocol === "http:" ? escapeHtml(url.toString()) : void 0;
|
|
4632
|
+
} catch {
|
|
4633
|
+
return void 0;
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
function heading(content) {
|
|
4637
|
+
return `<h1 style="margin:0;font-family:${emailTokens.font};font-size:24px;line-height:1.25;font-weight:700;color:${emailTokens.colors.text}">${escapeHtml(content)}</h1>`;
|
|
4638
|
+
}
|
|
4639
|
+
function paragraph(content, margin = "0 0 16px", size = "15px") {
|
|
4640
|
+
return `<p style="margin:${margin};font-family:${emailTokens.font};font-size:${size};line-height:1.6;color:${emailTokens.colors.muted}">${escapeHtml(content)}</p>`;
|
|
4641
|
+
}
|
|
4642
|
+
function sectionLabel(content) {
|
|
4643
|
+
return `<p style="margin:0 0 8px;font-family:${emailTokens.font};font-size:11px;line-height:1.4;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:${emailTokens.colors.subtle}">${escapeHtml(content)}</p>`;
|
|
4644
|
+
}
|
|
4645
|
+
function divider() {
|
|
4646
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td style="height:1px;background:${emailTokens.colors.border};font-size:0;line-height:0"> </td></tr></table>`;
|
|
4647
|
+
}
|
|
4648
|
+
function spacer(height = 16) {
|
|
4649
|
+
return table(row(" ", `height:${height}px;font-size:0;line-height:0`));
|
|
4650
|
+
}
|
|
4651
|
+
function row(content, cellStyle = "") {
|
|
4652
|
+
return `<tr><td style="${cellStyle}">${content}</td></tr>`;
|
|
4653
|
+
}
|
|
4654
|
+
function table(content, style = "width:100%") {
|
|
4655
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="${style}">${content}</table>`;
|
|
4656
|
+
}
|
|
4657
|
+
function detailBox(content, monospace = false) {
|
|
4658
|
+
const font = monospace ? emailTokens.mono : emailTokens.font;
|
|
4659
|
+
return table(
|
|
4660
|
+
row(
|
|
4661
|
+
escapeHtml(content),
|
|
4662
|
+
`padding:14px 16px;font-family:${font};font-size:13px;line-height:1.5;font-weight:${monospace ? "400" : "700"};color:${emailTokens.colors.text};word-break:break-all`
|
|
4663
|
+
),
|
|
4664
|
+
`width:100%;background:${emailTokens.colors.code};border:1px solid ${emailTokens.colors.codeBorder};border-radius:${emailTokens.radius.control}`
|
|
4665
|
+
);
|
|
4666
|
+
}
|
|
4667
|
+
function eventList(events) {
|
|
4668
|
+
return table(
|
|
4669
|
+
events.map(
|
|
4670
|
+
({ label, value }) => row(
|
|
4671
|
+
`${sectionLabel(label)}${paragraph(value, "0")}`,
|
|
4672
|
+
`padding:12px 0;border-bottom:1px solid ${emailTokens.colors.border}`
|
|
4673
|
+
)
|
|
4674
|
+
).join("")
|
|
4675
|
+
);
|
|
4676
|
+
}
|
|
4677
|
+
function ctaButton(label, href) {
|
|
4678
|
+
const safeHref = safeHttpUrl(href);
|
|
4679
|
+
if (!safeHref) return "";
|
|
4680
|
+
return table(
|
|
4681
|
+
row(
|
|
4682
|
+
`<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`,
|
|
4683
|
+
"padding:8px 0 24px"
|
|
4684
|
+
),
|
|
4685
|
+
"width:auto"
|
|
4686
|
+
);
|
|
4687
|
+
}
|
|
4688
|
+
function safeLinkDetailBox(href) {
|
|
4689
|
+
const safeHref = safeHttpUrl(href);
|
|
4690
|
+
if (!safeHref) return "";
|
|
4691
|
+
return detailBox(safeHref, true);
|
|
4692
|
+
}
|
|
4693
|
+
function alertBox(content) {
|
|
4694
|
+
return table(
|
|
4695
|
+
row(
|
|
4696
|
+
escapeHtml(content),
|
|
4697
|
+
`padding:13px 16px;font-family:${emailTokens.font};font-size:13px;line-height:1.5;color:${emailTokens.colors.dangerText}`
|
|
4698
|
+
),
|
|
4699
|
+
`width:100%;background:${emailTokens.colors.dangerSurface};border:1px solid ${emailTokens.colors.dangerBorder};border-radius:${emailTokens.radius.control}`
|
|
4700
|
+
);
|
|
4701
|
+
}
|
|
4702
|
+
function resolveBlocks(input) {
|
|
4703
|
+
if (Array.isArray(input)) {
|
|
4704
|
+
const parts = [];
|
|
4705
|
+
for (const item of input) {
|
|
4706
|
+
if (Array.isArray(item)) {
|
|
4707
|
+
for (const sub of item) {
|
|
4708
|
+
if (typeof sub === "string" && sub.length > 0) parts.push(sub);
|
|
4709
|
+
}
|
|
4710
|
+
} else if (typeof item === "string" && item.length > 0) {
|
|
4711
|
+
parts.push(item);
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
return parts.join("\n");
|
|
4715
|
+
}
|
|
4716
|
+
return typeof input === "string" ? input : "";
|
|
4717
|
+
}
|
|
4718
|
+
function layout({ preheader, title, content, footer, siteName }) {
|
|
4719
|
+
const resolvedContent = resolveBlocks(content);
|
|
4720
|
+
const resolvedFooter = resolveBlocks(footer);
|
|
4721
|
+
return `<!doctype html>
|
|
4722
|
+
<html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
|
4723
|
+
<head>
|
|
4724
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
4725
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4726
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
4727
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
4728
|
+
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
|
|
4729
|
+
<meta name="color-scheme" content="light">
|
|
4730
|
+
<meta name="supported-color-schemes" content="light">
|
|
4731
|
+
<title>${escapeHtml(title)}</title>
|
|
4732
|
+
</head>
|
|
4733
|
+
<body style="margin:0;padding:0;background-color:${emailTokens.colors.canvas};word-spacing:normal;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%">
|
|
4734
|
+
<div style="display:none;max-height:0;max-width:0;overflow:hidden;opacity:0;mso-hide:all;color:transparent;font-size:1px;line-height:1px">
|
|
4735
|
+
${escapeHtml(preheader)}​͏‌   ͏‌
|
|
4736
|
+
</div>
|
|
4737
|
+
<center lang="en" style="width:100%;background-color:${emailTokens.colors.canvas}">
|
|
4738
|
+
${table(
|
|
4739
|
+
row(
|
|
4740
|
+
table(
|
|
4741
|
+
row(" ", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel(siteName ?? "Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(resolvedContent, "padding:0 32px 32px") + row(
|
|
4742
|
+
`${divider()}${paragraph(resolvedFooter, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "10px 0px 0px 0px", "12px")}`,
|
|
4743
|
+
"padding:0 32px 28px"
|
|
4744
|
+
),
|
|
4745
|
+
`width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`
|
|
4746
|
+
),
|
|
4747
|
+
"padding:32px 12px"
|
|
4748
|
+
),
|
|
4749
|
+
`width:100%;background:${emailTokens.colors.canvas}`
|
|
4750
|
+
)}
|
|
4751
|
+
</center>
|
|
4752
|
+
</body>
|
|
4753
|
+
</html>`;
|
|
4754
|
+
}
|
|
4755
|
+
function getDefaultEmailTemplate(purpose, options) {
|
|
4756
|
+
const siteName = options?.siteName ?? "Dyrected";
|
|
4757
|
+
switch (purpose) {
|
|
4758
|
+
case "invite":
|
|
4759
|
+
return {
|
|
4760
|
+
subject: `You've been invited to ${siteName}`,
|
|
4761
|
+
rawHtml: layout({
|
|
4762
|
+
siteName,
|
|
4763
|
+
preheader: `You've been invited to join ${siteName}.`,
|
|
4764
|
+
title: "You've been invited",
|
|
4765
|
+
content: [
|
|
4766
|
+
paragraph("Use the invitation link below to create your account. The link expires in 7 days."),
|
|
4767
|
+
ctaButton("Accept invitation", "{{url}}"),
|
|
4768
|
+
sectionLabel("Invitation link"),
|
|
4769
|
+
safeLinkDetailBox("{{url}}"),
|
|
4770
|
+
paragraph("If the button does not work, copy and paste the link above into your browser.", "12px 0 0")
|
|
4771
|
+
],
|
|
4772
|
+
footer: "If you weren't expecting this invitation, you can safely ignore this email."
|
|
4773
|
+
})
|
|
4774
|
+
};
|
|
4775
|
+
case "resetPassword":
|
|
4776
|
+
return {
|
|
4777
|
+
subject: "Reset your password",
|
|
4778
|
+
rawHtml: layout({
|
|
4779
|
+
siteName,
|
|
4780
|
+
preheader: `Reset your ${siteName} password.`,
|
|
4781
|
+
title: "Reset your password",
|
|
4782
|
+
content: [
|
|
4783
|
+
paragraph("We received a request to reset your password. The reset link expires in 1 hour."),
|
|
4784
|
+
ctaButton("Reset password", "{{url}}"),
|
|
4785
|
+
sectionLabel("Reset link"),
|
|
4786
|
+
safeLinkDetailBox("{{url}}"),
|
|
4787
|
+
paragraph("If the button does not work, copy and paste the link above into your browser.", "12px 0 0")
|
|
4788
|
+
],
|
|
4789
|
+
footer: "If you didn't request a password reset, you can safely ignore this email."
|
|
4790
|
+
})
|
|
4791
|
+
};
|
|
4792
|
+
case "welcome":
|
|
4793
|
+
return {
|
|
4794
|
+
subject: `Welcome to ${siteName} \u2014 your account is ready`,
|
|
4795
|
+
rawHtml: layout({
|
|
4796
|
+
siteName,
|
|
4797
|
+
preheader: `Your ${siteName} account is ready.`,
|
|
4798
|
+
title: "Welcome \u2014 your account is ready",
|
|
4799
|
+
content: [paragraph("Your account has been created. You can now log in with:"), detailBox("{{email}}")],
|
|
4800
|
+
footer: "If you didn't create this account, you can safely ignore this email."
|
|
4801
|
+
})
|
|
4802
|
+
};
|
|
4803
|
+
case "passwordChanged":
|
|
4804
|
+
return {
|
|
4805
|
+
subject: "Your password has been changed",
|
|
4806
|
+
rawHtml: layout({
|
|
4807
|
+
siteName,
|
|
4808
|
+
preheader: `Your ${siteName} password was changed.`,
|
|
4809
|
+
title: "Password changed",
|
|
4810
|
+
content: [
|
|
4811
|
+
paragraph("The password for your account has been changed."),
|
|
4812
|
+
alertBox("If you did not make this change, please contact support or reset your password immediately.")
|
|
4813
|
+
],
|
|
4814
|
+
footer: "Security notice: this confirms a recent credential change."
|
|
4815
|
+
})
|
|
4816
|
+
};
|
|
4817
|
+
default:
|
|
4818
|
+
return {
|
|
4819
|
+
subject: `Notification from ${siteName}`,
|
|
4820
|
+
rawHtml: layout({
|
|
4821
|
+
siteName,
|
|
4822
|
+
preheader: `Account notification from ${siteName}.`,
|
|
4823
|
+
title: "Account Notification",
|
|
4824
|
+
content: [paragraph(`This is an account notification from ${siteName}.`)],
|
|
4825
|
+
footer: "If you have any questions, please reach out to our team."
|
|
4826
|
+
})
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4543
4831
|
// src/utils/config.ts
|
|
4544
4832
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
4545
4833
|
var SYSTEM_FIELDS = [
|
|
@@ -4681,6 +4969,114 @@ var AUTH_SESSIONS_COLLECTION_CONFIG = {
|
|
|
4681
4969
|
},
|
|
4682
4970
|
admin: { hidden: true }
|
|
4683
4971
|
};
|
|
4972
|
+
var EMAIL_TEMPLATES_COLLECTION_SLUG = "__email_templates";
|
|
4973
|
+
var EMAIL_TEMPLATES_COLLECTION_CONFIG = {
|
|
4974
|
+
slug: EMAIL_TEMPLATES_COLLECTION_SLUG,
|
|
4975
|
+
labels: { singular: "Email Template", plural: "Email Templates" },
|
|
4976
|
+
detail: false,
|
|
4977
|
+
fields: [
|
|
4978
|
+
{
|
|
4979
|
+
name: "collectionSlug",
|
|
4980
|
+
type: "select",
|
|
4981
|
+
label: "Applies To",
|
|
4982
|
+
required: true,
|
|
4983
|
+
defaultValue: "*",
|
|
4984
|
+
options: [
|
|
4985
|
+
{ label: "Global (All Users & Collections)", value: "*" }
|
|
4986
|
+
],
|
|
4987
|
+
admin: {
|
|
4988
|
+
description: "Choose which user collection this template applies to, or 'Global' for all users."
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4991
|
+
{
|
|
4992
|
+
name: "purpose",
|
|
4993
|
+
type: "select",
|
|
4994
|
+
label: "Purpose",
|
|
4995
|
+
required: true,
|
|
4996
|
+
options: [
|
|
4997
|
+
{ label: "Invite", value: "invite" },
|
|
4998
|
+
{ label: "Password Reset", value: "resetPassword" },
|
|
4999
|
+
{ label: "Welcome", value: "welcome" },
|
|
5000
|
+
{ label: "Password Changed", value: "passwordChanged" }
|
|
5001
|
+
],
|
|
5002
|
+
admin: { width: "50%" }
|
|
5003
|
+
},
|
|
5004
|
+
{
|
|
5005
|
+
name: "format",
|
|
5006
|
+
type: "select",
|
|
5007
|
+
label: "Format",
|
|
5008
|
+
required: true,
|
|
5009
|
+
defaultValue: "html",
|
|
5010
|
+
options: [
|
|
5011
|
+
{ label: "HTML", value: "html" },
|
|
5012
|
+
{ label: "External Template", value: "external_template" }
|
|
5013
|
+
],
|
|
5014
|
+
admin: { width: "50%" }
|
|
5015
|
+
},
|
|
5016
|
+
{
|
|
5017
|
+
name: "subject",
|
|
5018
|
+
type: "text",
|
|
5019
|
+
label: "Subject",
|
|
5020
|
+
admin: {
|
|
5021
|
+
description: "Subject line (optional when using external provider template)"
|
|
5022
|
+
}
|
|
5023
|
+
},
|
|
5024
|
+
{
|
|
5025
|
+
name: "rawHtml",
|
|
5026
|
+
type: "textarea",
|
|
5027
|
+
label: "HTML Content",
|
|
5028
|
+
admin: {
|
|
5029
|
+
description: "Raw HTML email content with {{variable}} tags"
|
|
5030
|
+
}
|
|
5031
|
+
},
|
|
5032
|
+
{
|
|
5033
|
+
name: "externalTemplateId",
|
|
5034
|
+
type: "text",
|
|
5035
|
+
label: "External Template ID",
|
|
5036
|
+
admin: {
|
|
5037
|
+
description: "Template ID or alias from Seamailer, Postmark, SendGrid, etc."
|
|
5038
|
+
}
|
|
5039
|
+
},
|
|
5040
|
+
{
|
|
5041
|
+
name: "active",
|
|
5042
|
+
type: "boolean",
|
|
5043
|
+
label: "Active",
|
|
5044
|
+
defaultValue: true,
|
|
5045
|
+
admin: {
|
|
5046
|
+
description: "When active, this template overrides code config. When disabled, falls back to code."
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
],
|
|
5050
|
+
admin: {
|
|
5051
|
+
group: "Settings",
|
|
5052
|
+
icon: "Mail",
|
|
5053
|
+
groupBy: "collectionSlug",
|
|
5054
|
+
defaultColumns: ["purpose", "collectionSlug", "format", "active"]
|
|
5055
|
+
},
|
|
5056
|
+
hooks: {
|
|
5057
|
+
beforeChange: [
|
|
5058
|
+
async ({ data, req }) => {
|
|
5059
|
+
if (data && data.format === "html") {
|
|
5060
|
+
const siteName = req?.config?.admin?.branding?.logoText || "Dyrected";
|
|
5061
|
+
const defaults = getDefaultEmailTemplate(data.purpose || "invite", { siteName });
|
|
5062
|
+
if (!data.subject) {
|
|
5063
|
+
data.subject = defaults.subject;
|
|
5064
|
+
}
|
|
5065
|
+
if (!data.rawHtml) {
|
|
5066
|
+
data.rawHtml = defaults.rawHtml;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
return data;
|
|
5070
|
+
}
|
|
5071
|
+
]
|
|
5072
|
+
},
|
|
5073
|
+
access: {
|
|
5074
|
+
read: ({ user }) => isUserAdmin(user),
|
|
5075
|
+
create: ({ user }) => isUserAdmin(user),
|
|
5076
|
+
update: ({ user }) => isUserAdmin(user),
|
|
5077
|
+
delete: ({ user }) => isUserAdmin(user)
|
|
5078
|
+
}
|
|
5079
|
+
};
|
|
4684
5080
|
function normalizeConfig(config) {
|
|
4685
5081
|
const schemaAwareConfig = normalizeSchemaFragment(config);
|
|
4686
5082
|
assertValidDeclarativeHooksInConfig(schemaAwareConfig, "config");
|
|
@@ -4695,10 +5091,13 @@ function normalizeConfig(config) {
|
|
|
4695
5091
|
const needsTasks = (config.tasks?.length ?? 0) > 0;
|
|
4696
5092
|
const needsAuthSessions = collections.some((col) => !!col.auth);
|
|
4697
5093
|
const needsTrash = schemaAwareConfig.trash?.enabled === true || collections.some((col) => resolveTrashConfig(col, schemaAwareConfig).enabled);
|
|
4698
|
-
const
|
|
5094
|
+
const adminAuthCollection = getAdminAuthCollection({
|
|
4699
5095
|
collections,
|
|
4700
5096
|
adminAuth: schemaAwareConfig.adminAuth
|
|
4701
|
-
})
|
|
5097
|
+
});
|
|
5098
|
+
const adminAuthCollectionSlug = adminAuthCollection?.slug;
|
|
5099
|
+
const adminOnly = ({ user }) => isUserAdmin(user, adminAuthCollection);
|
|
5100
|
+
const adminOtherThanSelf = ({ user, id }) => isUserAdmin(user, adminAuthCollection) && user?.id !== id;
|
|
4702
5101
|
const normalizedCollections = collections.map((col) => {
|
|
4703
5102
|
let fields = col.fields || [];
|
|
4704
5103
|
const existingFieldNames = new Set(fields.map((f) => f.name));
|
|
@@ -4747,7 +5146,7 @@ function normalizeConfig(config) {
|
|
|
4747
5146
|
{ value: "viewer", label: "Viewer" }
|
|
4748
5147
|
],
|
|
4749
5148
|
access: {
|
|
4750
|
-
update:
|
|
5149
|
+
update: adminOnly
|
|
4751
5150
|
}
|
|
4752
5151
|
}
|
|
4753
5152
|
];
|
|
@@ -4765,7 +5164,7 @@ function normalizeConfig(config) {
|
|
|
4765
5164
|
{ value: "pending", label: "Pending" }
|
|
4766
5165
|
],
|
|
4767
5166
|
access: {
|
|
4768
|
-
update:
|
|
5167
|
+
update: adminOtherThanSelf
|
|
4769
5168
|
},
|
|
4770
5169
|
admin: {
|
|
4771
5170
|
condition: `!(data.roles && "admin" in data.roles) && data.role != "admin"`
|
|
@@ -4834,7 +5233,7 @@ function normalizeConfig(config) {
|
|
|
4834
5233
|
access: {
|
|
4835
5234
|
...field.access || {},
|
|
4836
5235
|
// Must be an admin; cannot edit own roles (no self-elevation).
|
|
4837
|
-
update:
|
|
5236
|
+
update: adminOtherThanSelf
|
|
4838
5237
|
}
|
|
4839
5238
|
};
|
|
4840
5239
|
}
|
|
@@ -4843,7 +5242,7 @@ function normalizeConfig(config) {
|
|
|
4843
5242
|
...field,
|
|
4844
5243
|
access: {
|
|
4845
5244
|
...field.access || {},
|
|
4846
|
-
update:
|
|
5245
|
+
update: adminOtherThanSelf
|
|
4847
5246
|
}
|
|
4848
5247
|
};
|
|
4849
5248
|
}
|
|
@@ -4935,6 +5334,52 @@ function normalizeConfig(config) {
|
|
|
4935
5334
|
if (needsTrash && !normalizedCollections.some((col) => col.slug === TRASH_COLLECTION)) {
|
|
4936
5335
|
systemCollections.push(TRASH_COLLECTION_CONFIG);
|
|
4937
5336
|
}
|
|
5337
|
+
const needsEmailTemplates = schemaAwareConfig.email?.adminEditable === true;
|
|
5338
|
+
if (needsEmailTemplates && !normalizedCollections.some((col) => col.slug === EMAIL_TEMPLATES_COLLECTION_SLUG)) {
|
|
5339
|
+
const adminCol = getAdminAuthCollection(schemaAwareConfig);
|
|
5340
|
+
const customAccess = schemaAwareConfig.email?.access;
|
|
5341
|
+
const resolveOpAccess = (op) => {
|
|
5342
|
+
if (typeof customAccess === "function") return customAccess;
|
|
5343
|
+
if (customAccess && typeof customAccess === "object" && customAccess[op]) return customAccess[op];
|
|
5344
|
+
return ({ user }) => isUserAdmin(user, adminCol);
|
|
5345
|
+
};
|
|
5346
|
+
const customPurposes = schemaAwareConfig.email?.purposes ?? [];
|
|
5347
|
+
const purposeOptions = [
|
|
5348
|
+
{ label: "Invite", value: "invite" },
|
|
5349
|
+
{ label: "Password Reset", value: "resetPassword" },
|
|
5350
|
+
{ label: "Welcome", value: "welcome" },
|
|
5351
|
+
{ label: "Password Changed", value: "passwordChanged" },
|
|
5352
|
+
...customPurposes
|
|
5353
|
+
];
|
|
5354
|
+
const targetableCollections = normalizedCollections.filter(
|
|
5355
|
+
(c) => !c.slug.startsWith("__")
|
|
5356
|
+
);
|
|
5357
|
+
const audienceOptions = [
|
|
5358
|
+
{ label: "Global (All Collections)", value: "*" },
|
|
5359
|
+
...targetableCollections.map((c) => ({
|
|
5360
|
+
label: `${c.labels?.singular || c.labels?.plural || c.slug} (${c.slug})`,
|
|
5361
|
+
value: c.slug
|
|
5362
|
+
}))
|
|
5363
|
+
];
|
|
5364
|
+
systemCollections.push({
|
|
5365
|
+
...EMAIL_TEMPLATES_COLLECTION_CONFIG,
|
|
5366
|
+
fields: EMAIL_TEMPLATES_COLLECTION_CONFIG.fields.map((f) => {
|
|
5367
|
+
if (f.name === "collectionSlug") {
|
|
5368
|
+
return { ...f, options: audienceOptions };
|
|
5369
|
+
}
|
|
5370
|
+
if (f.name === "purpose") {
|
|
5371
|
+
return { ...f, options: purposeOptions };
|
|
5372
|
+
}
|
|
5373
|
+
return f;
|
|
5374
|
+
}),
|
|
5375
|
+
access: {
|
|
5376
|
+
read: resolveOpAccess("read"),
|
|
5377
|
+
create: resolveOpAccess("create"),
|
|
5378
|
+
update: resolveOpAccess("update"),
|
|
5379
|
+
delete: resolveOpAccess("delete")
|
|
5380
|
+
}
|
|
5381
|
+
});
|
|
5382
|
+
}
|
|
4938
5383
|
return {
|
|
4939
5384
|
...schemaAwareConfig,
|
|
4940
5385
|
collections: [...normalizedCollections, ...systemCollections],
|
|
@@ -6262,6 +6707,31 @@ function generateOpenApi(config) {
|
|
|
6262
6707
|
spec.paths[`${path}/accept-invite`] = {
|
|
6263
6708
|
post: publicAuthPost("Accept an invitation")
|
|
6264
6709
|
};
|
|
6710
|
+
spec.paths[`${path}/tokens/verify`] = {
|
|
6711
|
+
get: {
|
|
6712
|
+
tags: [collectionTag],
|
|
6713
|
+
summary: `Verify an invite or password reset token`,
|
|
6714
|
+
parameters: [
|
|
6715
|
+
{
|
|
6716
|
+
name: "token",
|
|
6717
|
+
in: "query",
|
|
6718
|
+
required: true,
|
|
6719
|
+
schema: { type: "string" }
|
|
6720
|
+
},
|
|
6721
|
+
{
|
|
6722
|
+
name: "purpose",
|
|
6723
|
+
in: "query",
|
|
6724
|
+
required: false,
|
|
6725
|
+
schema: { type: "string", enum: ["invite", "reset"] }
|
|
6726
|
+
}
|
|
6727
|
+
],
|
|
6728
|
+
responses: {
|
|
6729
|
+
200: {
|
|
6730
|
+
description: "Token verification result"
|
|
6731
|
+
}
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
};
|
|
6265
6735
|
spec.paths[`${path}/{id}/change-password`] = {
|
|
6266
6736
|
post: {
|
|
6267
6737
|
tags: [collectionTag],
|
|
@@ -7459,8 +7929,8 @@ function sanitizeDocForAI(options) {
|
|
|
7459
7929
|
// src/utils/index-name.ts
|
|
7460
7930
|
var import_node_crypto2 = require("crypto");
|
|
7461
7931
|
var MAX_INDEX_NAME_LENGTH = 60;
|
|
7462
|
-
function resolveIndexName(prefix,
|
|
7463
|
-
const full = `${prefix}_${
|
|
7932
|
+
function resolveIndexName(prefix, table2, fields) {
|
|
7933
|
+
const full = `${prefix}_${table2}_${fields.join("_")}`;
|
|
7464
7934
|
if (full.length <= MAX_INDEX_NAME_LENGTH) return full;
|
|
7465
7935
|
const hash = (0, import_node_crypto2.createHash)("sha1").update(full).digest("hex").slice(0, 10);
|
|
7466
7936
|
const tail = (fields[fields.length - 1] ?? "").slice(0, 30);
|
|
@@ -7932,7 +8402,7 @@ function compileNavigation(config) {
|
|
|
7932
8402
|
const mentionedTrash = explicitNav.some((i) => i.trash || i.slug === "trash");
|
|
7933
8403
|
if (!mentionedTrash) {
|
|
7934
8404
|
const hasTrash = collections.some(
|
|
7935
|
-
(col) => !col.slug
|
|
8405
|
+
(col) => !INTERNAL_SYSTEM_COLLECTIONS.has(col.slug) && resolveTrashConfig(col, config).enabled
|
|
7936
8406
|
);
|
|
7937
8407
|
if (hasTrash) {
|
|
7938
8408
|
registerItem(void 0, {
|
|
@@ -8263,8 +8733,11 @@ function defineTab(args) {
|
|
|
8263
8733
|
DuplicateKeyError,
|
|
8264
8734
|
DyrectedAIError,
|
|
8265
8735
|
DyrectedError,
|
|
8736
|
+
EMAIL_TEMPLATES_COLLECTION_CONFIG,
|
|
8737
|
+
EMAIL_TEMPLATES_COLLECTION_SLUG,
|
|
8266
8738
|
FieldConditionBuilder,
|
|
8267
8739
|
ForbiddenError,
|
|
8740
|
+
INTERNAL_SYSTEM_COLLECTIONS,
|
|
8268
8741
|
LIFECYCLE_EVENTS_COLLECTION,
|
|
8269
8742
|
LIFECYCLE_EVENT_NAMES,
|
|
8270
8743
|
MatchBuilder,
|
|
@@ -8282,6 +8755,7 @@ function defineTab(args) {
|
|
|
8282
8755
|
WORKFLOW_HISTORY_COLLECTION,
|
|
8283
8756
|
abs,
|
|
8284
8757
|
addDays,
|
|
8758
|
+
alertBox,
|
|
8285
8759
|
asCollection,
|
|
8286
8760
|
assertValidAdminConditionsInConfig,
|
|
8287
8761
|
assertValidDeclarativeAccessInConfig,
|
|
@@ -8311,6 +8785,7 @@ function defineTab(args) {
|
|
|
8311
8785
|
createTaskRunner,
|
|
8312
8786
|
createTrashPurgeTask,
|
|
8313
8787
|
createWorkflowDocument,
|
|
8788
|
+
ctaButton,
|
|
8314
8789
|
defaultValue,
|
|
8315
8790
|
defineAction,
|
|
8316
8791
|
defineArrayField,
|
|
@@ -8357,6 +8832,7 @@ function defineTab(args) {
|
|
|
8357
8832
|
defineUrlField,
|
|
8358
8833
|
defineView,
|
|
8359
8834
|
defineWorkspace,
|
|
8835
|
+
detailBox,
|
|
8360
8836
|
diffDays,
|
|
8361
8837
|
dispatchLifecycleEvent,
|
|
8362
8838
|
dispatchPendingLifecycleEvents,
|
|
@@ -8372,10 +8848,14 @@ function defineTab(args) {
|
|
|
8372
8848
|
displayTab,
|
|
8373
8849
|
displayTabs,
|
|
8374
8850
|
displayText,
|
|
8851
|
+
divider,
|
|
8852
|
+
emailTokens,
|
|
8375
8853
|
endsWith,
|
|
8854
|
+
escapeHtml,
|
|
8376
8855
|
evaluateDetailComputed,
|
|
8377
8856
|
evaluateJexl,
|
|
8378
8857
|
evaluateJexlSync,
|
|
8858
|
+
eventList,
|
|
8379
8859
|
executeFieldAfterRead,
|
|
8380
8860
|
executeFieldBeforeChange,
|
|
8381
8861
|
expr,
|
|
@@ -8392,8 +8872,10 @@ function defineTab(args) {
|
|
|
8392
8872
|
getAdminAuthCollection,
|
|
8393
8873
|
getAdminRoleForCollection,
|
|
8394
8874
|
getAllowedSitesForUser,
|
|
8875
|
+
getDefaultEmailTemplate,
|
|
8395
8876
|
getPublicAdminAuthConfig,
|
|
8396
8877
|
getTrashEntryId,
|
|
8878
|
+
heading,
|
|
8397
8879
|
includes,
|
|
8398
8880
|
initializeWorkflowDocument,
|
|
8399
8881
|
isAICollection,
|
|
@@ -8406,6 +8888,7 @@ function defineTab(args) {
|
|
|
8406
8888
|
isUserAdmin,
|
|
8407
8889
|
join,
|
|
8408
8890
|
last,
|
|
8891
|
+
layout,
|
|
8409
8892
|
length,
|
|
8410
8893
|
lower,
|
|
8411
8894
|
maskEmail,
|
|
@@ -8421,6 +8904,7 @@ function defineTab(args) {
|
|
|
8421
8904
|
normalizeGroupKey,
|
|
8422
8905
|
now,
|
|
8423
8906
|
numericFieldNames,
|
|
8907
|
+
paragraph,
|
|
8424
8908
|
parseCron,
|
|
8425
8909
|
parseMongoWhere,
|
|
8426
8910
|
parseSort,
|
|
@@ -8437,21 +8921,27 @@ function defineTab(args) {
|
|
|
8437
8921
|
resolveAdminAuthCollection,
|
|
8438
8922
|
resolveAllCollectionViews,
|
|
8439
8923
|
resolveAuthorizedSiteId,
|
|
8924
|
+
resolveBlocks,
|
|
8440
8925
|
resolveDocumentTitle,
|
|
8441
8926
|
resolveIndexName,
|
|
8442
8927
|
resolvePrefix,
|
|
8443
8928
|
resolveTrashConfig,
|
|
8444
8929
|
restoreDocument,
|
|
8445
8930
|
round,
|
|
8931
|
+
row,
|
|
8446
8932
|
runCollectionHooks,
|
|
8933
|
+
safeLinkDetailBox,
|
|
8447
8934
|
sanitizeDocForAI,
|
|
8448
8935
|
saveWorkflowDraft,
|
|
8936
|
+
sectionLabel,
|
|
8449
8937
|
simplePublishingWorkflow,
|
|
8450
8938
|
slugify,
|
|
8451
8939
|
softDeleteDocument,
|
|
8940
|
+
spacer,
|
|
8452
8941
|
sqrt,
|
|
8453
8942
|
startsWith,
|
|
8454
8943
|
sum,
|
|
8944
|
+
table,
|
|
8455
8945
|
today,
|
|
8456
8946
|
transitionWorkflow,
|
|
8457
8947
|
trim,
|