@memberjunction/core-entities 6.2.0-edge.1 → 6.2.0-edge.3
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/.tsbuildinfo +1 -1
- package/dist/custom/FormScope/FormContributionClaims.d.ts +20 -0
- package/dist/custom/FormScope/FormContributionClaims.d.ts.map +1 -0
- package/dist/custom/FormScope/FormContributionClaims.js +33 -0
- package/dist/custom/FormScope/FormContributionClaims.js.map +1 -0
- package/dist/custom/FormScope/FormContributionLifecycle.d.ts +43 -0
- package/dist/custom/FormScope/FormContributionLifecycle.d.ts.map +1 -0
- package/dist/custom/FormScope/FormContributionLifecycle.js +47 -0
- package/dist/custom/FormScope/FormContributionLifecycle.js.map +1 -0
- package/dist/custom/FormScope/FormContributionRow.d.ts +40 -0
- package/dist/custom/FormScope/FormContributionRow.d.ts.map +1 -0
- package/dist/custom/FormScope/FormContributionRow.js +84 -0
- package/dist/custom/FormScope/FormContributionRow.js.map +1 -0
- package/dist/custom/FormScope/FormPanelHides.d.ts +14 -0
- package/dist/custom/FormScope/FormPanelHides.d.ts.map +1 -0
- package/dist/custom/FormScope/FormPanelHides.js +32 -0
- package/dist/custom/FormScope/FormPanelHides.js.map +1 -0
- package/dist/custom/FormScope/FormScopeRules.d.ts +220 -0
- package/dist/custom/FormScope/FormScopeRules.d.ts.map +1 -0
- package/dist/custom/FormScope/FormScopeRules.js +295 -0
- package/dist/custom/FormScope/FormScopeRules.js.map +1 -0
- package/dist/custom/MJConversationDetailEntityExtended.d.ts +7 -2
- package/dist/custom/MJConversationDetailEntityExtended.d.ts.map +1 -1
- package/dist/custom/MJConversationDetailEntityExtended.js +27 -17
- package/dist/custom/MJConversationDetailEntityExtended.js.map +1 -1
- package/dist/engines/IdentityClaimEngine.d.ts +15 -2
- package/dist/engines/IdentityClaimEngine.d.ts.map +1 -1
- package/dist/engines/IdentityClaimEngine.js.map +1 -1
- package/dist/engines/QueryEngine.d.ts.map +1 -1
- package/dist/engines/QueryEngine.js +2 -1
- package/dist/engines/QueryEngine.js.map +1 -1
- package/dist/engines/component-metadata.d.ts +3 -1
- package/dist/engines/component-metadata.d.ts.map +1 -1
- package/dist/engines/component-metadata.js +12 -5
- package/dist/engines/component-metadata.js.map +1 -1
- package/dist/engines/interactive-forms.d.ts +128 -9
- package/dist/engines/interactive-forms.d.ts.map +1 -1
- package/dist/engines/interactive-forms.js +268 -14
- package/dist/engines/interactive-forms.js.map +1 -1
- package/dist/engines/knowledgeHubMetadata.d.ts +21 -6
- package/dist/engines/knowledgeHubMetadata.d.ts.map +1 -1
- package/dist/engines/knowledgeHubMetadata.js +32 -15
- package/dist/engines/knowledgeHubMetadata.js.map +1 -1
- package/dist/generated/entities/__mj.d.ts +12547 -3929
- package/dist/generated/entities/__mj.d.ts.map +1 -1
- package/dist/generated/entities/__mj.js +65010 -54217
- package/dist/generated/entities/__mj.js.map +1 -1
- package/dist/generated/remote_operations.d.ts +685 -3
- package/dist/generated/remote_operations.d.ts.map +1 -1
- package/dist/generated/remote_operations.js +198 -0
- package/dist/generated/remote_operations.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FormContributionSpecColumns } from './FormContributionRow.js';
|
|
2
|
+
/**
|
|
3
|
+
* The claim rules of `MJ: Entity Form Contributions`, checked in code before anything is written.
|
|
4
|
+
*
|
|
5
|
+
* The database enforces the same rules with CHECK constraints, but a refused row save comes after
|
|
6
|
+
* its component was written. Lives here, beside the spec-to-row mapper, so the server and the
|
|
7
|
+
* browser can check a row the same way.
|
|
8
|
+
*/
|
|
9
|
+
/** The columns the claim rules read. */
|
|
10
|
+
export type ContributionClaimColumns = Pick<FormContributionSpecColumns, 'Presentation' | 'Inclusion' | 'ChromeGroup' | 'RelatedEntityID' | 'RelatedJoinField' | 'ReplacesSectionKey' | 'ReplacesSectionKeys' | 'ReplacesFieldNames' | 'InSectionKey' | 'SectionPosition'>;
|
|
11
|
+
/**
|
|
12
|
+
* Why the database would refuse this row's claim, or null when it would not.
|
|
13
|
+
*
|
|
14
|
+
* Checks `CK_EntityFormContribution_OneClaim` (a related grid, one section, several sections,
|
|
15
|
+
* fields of one section, or a place inside a section — at most one), `BareNoChrome`,
|
|
16
|
+
* `JoinNeedsRelated` and `SectionPositionNeedsSection`. The message names the spec fields, so an
|
|
17
|
+
* agent can correct its spec.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ContributionClaimRefusal(row: ContributionClaimColumns): string | null;
|
|
20
|
+
//# sourceMappingURL=FormContributionClaims.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionClaims.d.ts","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionClaims.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEzE;;;;;;GAMG;AAEH,wCAAwC;AACxC,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,2BAA2B,EACjE,cAAc,GAAG,WAAW,GAAG,aAAa,GAAG,iBAAiB,GAAG,kBAAkB,GACrF,oBAAoB,GAAG,qBAAqB,GAAG,oBAAoB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAAC;AAWhH;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI,CAerF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Each claim column, with the spec field an author sets to make that claim. */
|
|
2
|
+
const CLAIMS = [
|
|
3
|
+
{ Column: 'RelatedEntityID', SpecField: 'relatedEntity' },
|
|
4
|
+
{ Column: 'ReplacesSectionKey', SpecField: 'replacesSectionKey' },
|
|
5
|
+
{ Column: 'ReplacesSectionKeys', SpecField: 'replacesSectionKeys' },
|
|
6
|
+
{ Column: 'ReplacesFieldNames', SpecField: 'replacesFieldNames' },
|
|
7
|
+
{ Column: 'InSectionKey', SpecField: 'inSectionKey' },
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Why the database would refuse this row's claim, or null when it would not.
|
|
11
|
+
*
|
|
12
|
+
* Checks `CK_EntityFormContribution_OneClaim` (a related grid, one section, several sections,
|
|
13
|
+
* fields of one section, or a place inside a section — at most one), `BareNoChrome`,
|
|
14
|
+
* `JoinNeedsRelated` and `SectionPositionNeedsSection`. The message names the spec fields, so an
|
|
15
|
+
* agent can correct its spec.
|
|
16
|
+
*/
|
|
17
|
+
export function ContributionClaimRefusal(row) {
|
|
18
|
+
const made = CLAIMS.filter((claim) => row[claim.Column] != null).map((claim) => claim.SpecField);
|
|
19
|
+
if (made.length > 1) {
|
|
20
|
+
return `A form contribution makes one claim at most, but this one sets ${made.join(' and ')}. Keep one of them.`;
|
|
21
|
+
}
|
|
22
|
+
if (row.Presentation === 'bare' && (row.Inclusion != null || row.ChromeGroup != null)) {
|
|
23
|
+
return 'A bare panel is never a rail item, so it takes no inclusion or chromeGroup.';
|
|
24
|
+
}
|
|
25
|
+
if (row.RelatedJoinField != null && row.RelatedEntityID == null) {
|
|
26
|
+
return 'relatedJoinField qualifies a related-grid claim, so it needs relatedEntity.';
|
|
27
|
+
}
|
|
28
|
+
if (row.SectionPosition != null && row.ReplacesFieldNames == null && row.InSectionKey == null) {
|
|
29
|
+
return 'sectionPosition places a panel inside a section, so it needs inSectionKey or replacesFieldNames.';
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=FormContributionClaims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionClaims.js","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionClaims.ts"],"names":[],"mappings":"AAeA,gFAAgF;AAChF,MAAM,MAAM,GAAiF;IACzF,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE;IACzD,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,oBAAoB,EAAE;IACjE,EAAE,MAAM,EAAE,qBAAqB,EAAE,SAAS,EAAE,qBAAqB,EAAE;IACnE,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,oBAAoB,EAAE;IACjE,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE;CACxD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAA6B;IAClE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACjG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClB,OAAO,kEAAkE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC;IACrH,CAAC;IACD,IAAI,GAAG,CAAC,YAAY,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE,CAAC;QACpF,OAAO,6EAA6E,CAAC;IACzF,CAAC;IACD,IAAI,GAAG,CAAC,gBAAgB,IAAI,IAAI,IAAI,GAAG,CAAC,eAAe,IAAI,IAAI,EAAE,CAAC;QAC9D,OAAO,6EAA6E,CAAC;IACzF,CAAC;IACD,IAAI,GAAG,CAAC,eAAe,IAAI,IAAI,IAAI,GAAG,CAAC,kBAAkB,IAAI,IAAI,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;QAC5F,OAAO,kGAAkG,CAAC;IAC9G,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { MJComponentEntity, MJEntityFormContributionEntity } from '../../generated/entities/__mj.js';
|
|
2
|
+
/**
|
|
3
|
+
* How a form or panel goes live and is retired, as both write paths apply it: the server actions
|
|
4
|
+
* an agent calls, and the form's Manage drawer a person uses. Lives in a package both depend on,
|
|
5
|
+
* so the two cannot disagree about which row an activation retires.
|
|
6
|
+
*/
|
|
7
|
+
/** A form or panel's lifecycle, as the row stores it. */
|
|
8
|
+
export type FormLifecycleStatus = MJEntityFormContributionEntity['Status'];
|
|
9
|
+
/** The status of the Component a form or panel renders. */
|
|
10
|
+
export type FormComponentStatus = NonNullable<MJComponentEntity['Status']>;
|
|
11
|
+
/**
|
|
12
|
+
* The Component status that mirrors a form or panel's lifecycle: a live version is `Published`,
|
|
13
|
+
* a draft is `Draft`, a retired one is `Deprecated`. So a Component read on its own still says
|
|
14
|
+
* whether it is live. An unknown status reads as retired.
|
|
15
|
+
*/
|
|
16
|
+
export declare function FormLifecycleComponentStatus(status: FormLifecycleStatus): FormComponentStatus;
|
|
17
|
+
/** The columns that place a form or panel row: its entity, audience, status and key. */
|
|
18
|
+
export interface FormScopedRow {
|
|
19
|
+
ID: string;
|
|
20
|
+
EntityID: string;
|
|
21
|
+
Status: string;
|
|
22
|
+
Scope: string;
|
|
23
|
+
UserID: string | null;
|
|
24
|
+
RoleID: string | null;
|
|
25
|
+
/** Panels only. A full custom form has no key. */
|
|
26
|
+
ContributionKey?: string | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether two rows are for the same audience: the same scope, the same owner for a `User` row and
|
|
30
|
+
* the same role for a `Role` row. Every `Global` row is for the same audience.
|
|
31
|
+
*/
|
|
32
|
+
export declare function SameFormAudience(a: Pick<FormScopedRow, 'Scope' | 'UserID' | 'RoleID'>, b: Pick<FormScopedRow, 'Scope' | 'UserID' | 'RoleID'>): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The Active panels that activating `target` must retire first: the same entity, the same
|
|
35
|
+
* audience and the same key, other than the target itself. A panel with no key has none: two
|
|
36
|
+
* keyless panels are two panels.
|
|
37
|
+
*
|
|
38
|
+
* `UQ_EntityFormContribution_Key` allows one Active row per entity, key and audience, so these are
|
|
39
|
+
* demoted before the target is promoted, in the same transaction. Keys are trimmed and compared
|
|
40
|
+
* without regard to case, which mirrors the index's case-insensitive collation.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ActiveContributionSiblings<T extends FormScopedRow>(rows: readonly T[], target: FormScopedRow): T[];
|
|
43
|
+
//# sourceMappingURL=FormContributionLifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionLifecycle.d.ts","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionLifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAEvG;;;;GAIG;AAEH,yDAAyD;AACzD,MAAM,MAAM,mBAAmB,GAAG,8BAA8B,CAAC,QAAQ,CAAC,CAAC;AAE3E,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAO7F;AAED,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC5B,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC,EACrD,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC,GACtD,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,GAAG,CAAC,EAAE,CASlH"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
2
|
+
/**
|
|
3
|
+
* The Component status that mirrors a form or panel's lifecycle: a live version is `Published`,
|
|
4
|
+
* a draft is `Draft`, a retired one is `Deprecated`. So a Component read on its own still says
|
|
5
|
+
* whether it is live. An unknown status reads as retired.
|
|
6
|
+
*/
|
|
7
|
+
export function FormLifecycleComponentStatus(status) {
|
|
8
|
+
switch (status) {
|
|
9
|
+
case 'Active': return 'Published';
|
|
10
|
+
case 'Pending': return 'Draft';
|
|
11
|
+
case 'Inactive': return 'Deprecated';
|
|
12
|
+
default: return 'Deprecated';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether two rows are for the same audience: the same scope, the same owner for a `User` row and
|
|
17
|
+
* the same role for a `Role` row. Every `Global` row is for the same audience.
|
|
18
|
+
*/
|
|
19
|
+
export function SameFormAudience(a, b) {
|
|
20
|
+
if (a.Scope !== b.Scope)
|
|
21
|
+
return false;
|
|
22
|
+
if (a.Scope === 'User')
|
|
23
|
+
return UUIDsEqual(a.UserID ?? '', b.UserID ?? '');
|
|
24
|
+
if (a.Scope === 'Role')
|
|
25
|
+
return UUIDsEqual(a.RoleID ?? '', b.RoleID ?? '');
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The Active panels that activating `target` must retire first: the same entity, the same
|
|
30
|
+
* audience and the same key, other than the target itself. A panel with no key has none: two
|
|
31
|
+
* keyless panels are two panels.
|
|
32
|
+
*
|
|
33
|
+
* `UQ_EntityFormContribution_Key` allows one Active row per entity, key and audience, so these are
|
|
34
|
+
* demoted before the target is promoted, in the same transaction. Keys are trimmed and compared
|
|
35
|
+
* without regard to case, which mirrors the index's case-insensitive collation.
|
|
36
|
+
*/
|
|
37
|
+
export function ActiveContributionSiblings(rows, target) {
|
|
38
|
+
const key = (target.ContributionKey ?? '').trim().toLowerCase();
|
|
39
|
+
if (!key)
|
|
40
|
+
return [];
|
|
41
|
+
return rows.filter((row) => row.Status === 'Active'
|
|
42
|
+
&& !UUIDsEqual(row.ID, target.ID)
|
|
43
|
+
&& UUIDsEqual(row.EntityID, target.EntityID)
|
|
44
|
+
&& (row.ContributionKey ?? '').trim().toLowerCase() === key
|
|
45
|
+
&& SameFormAudience(row, target));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=FormContributionLifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionLifecycle.js","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionLifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAepD;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAA2B;IACpE,QAAQ,MAAM,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC,CAAC,OAAO,WAAW,CAAC;QAClC,KAAK,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC;QAC/B,KAAK,UAAU,CAAC,CAAC,OAAO,YAAY,CAAC;QACrC,OAAO,CAAC,CAAC,OAAO,YAAY,CAAC;IACjC,CAAC;AACL,CAAC;AAcD;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC5B,CAAqD,EACrD,CAAqD;IAErD,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAA0B,IAAkB,EAAE,MAAqB;IACzG,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACvB,GAAG,CAAC,MAAM,KAAK,QAAQ;WACpB,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;WAC9B,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;WACzC,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG;WACxD,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type FormContributionSpec } from '@memberjunction/interactive-component-types/forms';
|
|
2
|
+
import type { MJEntityFormContributionEntity } from '../../generated/entities/__mj.js';
|
|
3
|
+
/**
|
|
4
|
+
* The spec-to-row mapping for `MJ: Entity Form Contributions`.
|
|
5
|
+
*
|
|
6
|
+
* The Create and Modify Form Contribution actions, and the Manage drawer's
|
|
7
|
+
* `FormPanelAdminService.SetPlacement` (`@memberjunction/ng-base-forms`), write rows through
|
|
8
|
+
* {@link ApplyContributionSpecToRow}, so the same spec produces the same row on every path.
|
|
9
|
+
* Lives here because both the server and the browser depend on this package.
|
|
10
|
+
*/
|
|
11
|
+
/** What {@link ApplyContributionSpecToRow} needs beyond the spec. */
|
|
12
|
+
export interface ContributionRowOptions {
|
|
13
|
+
/** The ID of the entity `spec.relatedEntity` names, or null. */
|
|
14
|
+
relatedEntityID: string | null;
|
|
15
|
+
/** The registered name of that entity, which the derived key embeds. */
|
|
16
|
+
relatedEntityName: string | null;
|
|
17
|
+
/** The component name that seeds the key of a panel with no other key. */
|
|
18
|
+
componentName: string | null;
|
|
19
|
+
}
|
|
20
|
+
/** The contribution columns a spec's `formContribution` block decides. */
|
|
21
|
+
export type FormContributionSpecColumns = Pick<MJEntityFormContributionEntity, 'Slot' | 'SortKey' | 'Presentation' | 'Title' | 'Icon' | 'Configuration' | 'Inclusion' | 'ChromeGroup' | 'RelatedEntityID' | 'RelatedJoinField' | 'ReplacesSectionKey' | 'ReplacesSectionKeys' | 'ReplacesFieldNames' | 'InSectionKey' | 'SectionPosition' | 'ContributionKey'>;
|
|
22
|
+
/**
|
|
23
|
+
* Writes every column a contribution spec decides onto a row, and clears each claim column the
|
|
24
|
+
* spec does not set, so a row that changes its claim keeps nothing of the old one.
|
|
25
|
+
*
|
|
26
|
+
* The row it writes satisfies these CHECK constraints: `ReplacesFieldNamesShape` and
|
|
27
|
+
* `ReplacesSectionKeysShape` (an empty name list is stored as null), `JoinNeedsRelated` (a join
|
|
28
|
+
* field only with a related entity), `SectionPositionNeedsSection` (a position only for a panel
|
|
29
|
+
* drawn inside a section) and `BareNoChrome` (no Inclusion or ChromeGroup on a bare panel). It
|
|
30
|
+
* does not check `OneClaim`: a spec that makes two claims produces a row the database refuses.
|
|
31
|
+
*
|
|
32
|
+
* The caller resolves the related entity, because that needs metadata, and writes the columns
|
|
33
|
+
* that are not in the spec (scope, owner, status, precedence, names).
|
|
34
|
+
*/
|
|
35
|
+
export declare function ApplyContributionSpecToRow(row: FormContributionSpecColumns, spec: FormContributionSpec, opts: ContributionRowOptions): void;
|
|
36
|
+
/** The columns a spec decides, on a new object, for checking a spec before any row is written. */
|
|
37
|
+
export declare function ContributionSpecColumns(spec: FormContributionSpec, opts: ContributionRowOptions): FormContributionSpecColumns;
|
|
38
|
+
/** The names in a `ReplacesFieldNames` or `ReplacesSectionKeys` cell. Empty for a cell that is not a JSON array. */
|
|
39
|
+
export declare function ParseClaimedFieldNames(raw: string | null | undefined): string[];
|
|
40
|
+
//# sourceMappingURL=FormContributionRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionRow.d.ts","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionRow.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,oBAAoB,EAC5B,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAEpF;;;;;;;GAOG;AAEH,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACnC,gEAAgE;IAChE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wEAAwE;IACxE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0EAA0E;IAC1E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,0EAA0E;AAC1E,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,8BAA8B,EACvE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,eAAe,GAAG,WAAW,GAAG,aAAa,GACtG,iBAAiB,GAAG,kBAAkB,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,oBAAoB,GAC5G,cAAc,GAAG,iBAAiB,GAAG,iBAAiB,CAAC,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACtC,GAAG,EAAE,2BAA2B,EAChC,IAAI,EAAE,oBAAoB,EAC1B,IAAI,EAAE,sBAAsB,GAC7B,IAAI,CAKN;AAED,kGAAkG;AAClG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,sBAAsB,GAAG,2BAA2B,CAS7H;AAED,oHAAoH;AACpH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAI/E"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SafeJSONParse } from '@memberjunction/global';
|
|
2
|
+
import { DEFAULT_FORM_CONTRIBUTION_SLOT, ResolveContributionWriteKey, } from '@memberjunction/interactive-component-types/forms';
|
|
3
|
+
/**
|
|
4
|
+
* Writes every column a contribution spec decides onto a row, and clears each claim column the
|
|
5
|
+
* spec does not set, so a row that changes its claim keeps nothing of the old one.
|
|
6
|
+
*
|
|
7
|
+
* The row it writes satisfies these CHECK constraints: `ReplacesFieldNamesShape` and
|
|
8
|
+
* `ReplacesSectionKeysShape` (an empty name list is stored as null), `JoinNeedsRelated` (a join
|
|
9
|
+
* field only with a related entity), `SectionPositionNeedsSection` (a position only for a panel
|
|
10
|
+
* drawn inside a section) and `BareNoChrome` (no Inclusion or ChromeGroup on a bare panel). It
|
|
11
|
+
* does not check `OneClaim`: a spec that makes two claims produces a row the database refuses.
|
|
12
|
+
*
|
|
13
|
+
* The caller resolves the related entity, because that needs metadata, and writes the columns
|
|
14
|
+
* that are not in the spec (scope, owner, status, precedence, names).
|
|
15
|
+
*/
|
|
16
|
+
export function ApplyContributionSpecToRow(row, spec, opts) {
|
|
17
|
+
applyPlacement(row, spec);
|
|
18
|
+
applyChrome(row, spec);
|
|
19
|
+
applyClaims(row, spec, opts.relatedEntityID);
|
|
20
|
+
row.ContributionKey = ResolveContributionWriteKey(spec, opts.relatedEntityName, opts.componentName);
|
|
21
|
+
}
|
|
22
|
+
/** The columns a spec decides, on a new object, for checking a spec before any row is written. */
|
|
23
|
+
export function ContributionSpecColumns(spec, opts) {
|
|
24
|
+
const row = {
|
|
25
|
+
Slot: DEFAULT_FORM_CONTRIBUTION_SLOT, SortKey: 0, Presentation: 'panel', Title: null, Icon: null,
|
|
26
|
+
Configuration: null, Inclusion: null, ChromeGroup: null, RelatedEntityID: null, RelatedJoinField: null,
|
|
27
|
+
ReplacesSectionKey: null, ReplacesSectionKeys: null, ReplacesFieldNames: null, InSectionKey: null,
|
|
28
|
+
SectionPosition: null, ContributionKey: null,
|
|
29
|
+
};
|
|
30
|
+
ApplyContributionSpecToRow(row, spec, opts);
|
|
31
|
+
return row;
|
|
32
|
+
}
|
|
33
|
+
/** The names in a `ReplacesFieldNames` or `ReplacesSectionKeys` cell. Empty for a cell that is not a JSON array. */
|
|
34
|
+
export function ParseClaimedFieldNames(raw) {
|
|
35
|
+
if (!raw || raw.trim().length === 0)
|
|
36
|
+
return [];
|
|
37
|
+
const parsed = SafeJSONParse(raw, false);
|
|
38
|
+
return Array.isArray(parsed) ? cleanNames(parsed) : [];
|
|
39
|
+
}
|
|
40
|
+
function applyPlacement(row, spec) {
|
|
41
|
+
row.Slot = spec.slot ?? DEFAULT_FORM_CONTRIBUTION_SLOT;
|
|
42
|
+
row.SortKey = spec.sortKey ?? 0;
|
|
43
|
+
row.Presentation = spec.presentation;
|
|
44
|
+
row.Title = spec.title;
|
|
45
|
+
row.Icon = spec.icon ?? null;
|
|
46
|
+
row.Configuration = spec.configuration && Object.keys(spec.configuration).length > 0
|
|
47
|
+
? JSON.stringify(spec.configuration) : null;
|
|
48
|
+
}
|
|
49
|
+
/** A bare panel is never a rail item, so it carries no rail metadata. */
|
|
50
|
+
function applyChrome(row, spec) {
|
|
51
|
+
const bare = spec.presentation === 'bare';
|
|
52
|
+
row.Inclusion = bare ? null : spec.inclusion ?? null;
|
|
53
|
+
row.ChromeGroup = bare ? null : spec.chromeGroup ?? null;
|
|
54
|
+
}
|
|
55
|
+
function applyClaims(row, spec, relatedEntityID) {
|
|
56
|
+
row.RelatedEntityID = relatedEntityID;
|
|
57
|
+
row.RelatedJoinField = relatedEntityID ? spec.relatedJoinField?.trim() || null : null;
|
|
58
|
+
const fieldNames = cleanNames(spec.replacesFieldNames);
|
|
59
|
+
row.ReplacesFieldNames = jsonArrayOrNull(fieldNames);
|
|
60
|
+
// One section is stored in the single-key column whichever spec field named it.
|
|
61
|
+
const sections = cleanNames([
|
|
62
|
+
...(spec.replacesSectionKey ? [spec.replacesSectionKey] : []),
|
|
63
|
+
...(spec.replacesSectionKeys ?? []),
|
|
64
|
+
]);
|
|
65
|
+
row.ReplacesSectionKey = sections.length === 1 ? sections[0] : null;
|
|
66
|
+
row.ReplacesSectionKeys = sections.length > 1 ? JSON.stringify(sections) : null;
|
|
67
|
+
const inSection = spec.inSectionKey?.trim() || null;
|
|
68
|
+
row.InSectionKey = inSection;
|
|
69
|
+
row.SectionPosition = inSection || fieldNames.length > 0 ? spec.sectionPosition ?? null : null;
|
|
70
|
+
}
|
|
71
|
+
/** Trimmed, non-empty names without repeats, in their first-seen order. */
|
|
72
|
+
function cleanNames(names) {
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const raw of names ?? []) {
|
|
75
|
+
const name = typeof raw === 'string' ? raw.trim() : '';
|
|
76
|
+
if (name.length > 0 && !out.includes(name))
|
|
77
|
+
out.push(name);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
function jsonArrayOrNull(names) {
|
|
82
|
+
return names.length > 0 ? JSON.stringify(names) : null;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=FormContributionRow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContributionRow.js","sourceRoot":"","sources":["../../../src/custom/FormScope/FormContributionRow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a user's hidden panels are stored.
|
|
3
|
+
*
|
|
4
|
+
* A user can hide a panel someone else put on their form and bring it back later. The list is an
|
|
5
|
+
* `MJ: User Settings` value per entity: a JSON array of panel keys. Lives here so the browser,
|
|
6
|
+
* which applies the hides, and the server, which reports the form a user sees, read one key.
|
|
7
|
+
*/
|
|
8
|
+
/** `MJ: User Settings` key prefix for the per-entity list of hidden panels. */
|
|
9
|
+
export declare const FORM_PANEL_HIDE_SETTING_PREFIX = "mj.formPanels.hidden.";
|
|
10
|
+
/** The settings key for one entity. Trimmed and lowercased, so case variants share one row. */
|
|
11
|
+
export declare function FormPanelHideSettingKey(entityName: string): string;
|
|
12
|
+
/** The hidden keys in a stored setting, trimmed and without repeats. A missing or malformed value hides nothing. */
|
|
13
|
+
export declare function ParseHiddenFormPanelKeys(raw: string | null | undefined): string[];
|
|
14
|
+
//# sourceMappingURL=FormPanelHides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormPanelHides.d.ts","sourceRoot":"","sources":["../../../src/custom/FormScope/FormPanelHides.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,8BAA8B,0BAA0B,CAAC;AAEtE,+FAA+F;AAC/F,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,oHAAoH;AACpH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAWjF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SafeJSONParse } from '@memberjunction/global';
|
|
2
|
+
/**
|
|
3
|
+
* Where a user's hidden panels are stored.
|
|
4
|
+
*
|
|
5
|
+
* A user can hide a panel someone else put on their form and bring it back later. The list is an
|
|
6
|
+
* `MJ: User Settings` value per entity: a JSON array of panel keys. Lives here so the browser,
|
|
7
|
+
* which applies the hides, and the server, which reports the form a user sees, read one key.
|
|
8
|
+
*/
|
|
9
|
+
/** `MJ: User Settings` key prefix for the per-entity list of hidden panels. */
|
|
10
|
+
export const FORM_PANEL_HIDE_SETTING_PREFIX = 'mj.formPanels.hidden.';
|
|
11
|
+
/** The settings key for one entity. Trimmed and lowercased, so case variants share one row. */
|
|
12
|
+
export function FormPanelHideSettingKey(entityName) {
|
|
13
|
+
return FORM_PANEL_HIDE_SETTING_PREFIX + (entityName ?? '').trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
/** The hidden keys in a stored setting, trimmed and without repeats. A missing or malformed value hides nothing. */
|
|
16
|
+
export function ParseHiddenFormPanelKeys(raw) {
|
|
17
|
+
if (!raw)
|
|
18
|
+
return [];
|
|
19
|
+
const parsed = SafeJSONParse(raw, false);
|
|
20
|
+
if (!Array.isArray(parsed))
|
|
21
|
+
return [];
|
|
22
|
+
const keys = [];
|
|
23
|
+
for (const item of parsed) {
|
|
24
|
+
if (typeof item !== 'string')
|
|
25
|
+
continue;
|
|
26
|
+
const key = item.trim();
|
|
27
|
+
if (key && !keys.includes(key))
|
|
28
|
+
keys.push(key);
|
|
29
|
+
}
|
|
30
|
+
return keys;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=FormPanelHides.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormPanelHides.js","sourceRoot":"","sources":["../../../src/custom/FormScope/FormPanelHides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;;;GAMG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,8BAA8B,GAAG,uBAAuB,CAAC;AAEtE,+FAA+F;AAC/F,MAAM,UAAU,uBAAuB,CAAC,UAAkB;IACtD,OAAO,8BAA8B,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpF,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,wBAAwB,CAAC,GAA8B;IACnE,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,aAAa,CAAU,GAAG,EAAE,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAS;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { type IMetadataProvider, type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJEntityFormContributionEntity } from '../../generated/entities/__mj.js';
|
|
3
|
+
/**
|
|
4
|
+
* Who may write a full custom form or a panel, and at which scope.
|
|
5
|
+
*
|
|
6
|
+
* A form or panel belongs to one user, to a role, or to everyone. Anyone may manage their own.
|
|
7
|
+
* Changing what OTHER people see — creating, editing, removing or re-aiming a role or everyone
|
|
8
|
+
* item — takes the {@link MANAGE_FORM_DEFAULTS_AUTHORIZATION} grant. Changing the component a
|
|
9
|
+
* form or panel draws changes that form or panel, so {@link ComponentWriteRefusal},
|
|
10
|
+
* {@link FormRowComponentRefusal} and {@link ComponentNameCollisionRefusal} apply the same rule to
|
|
11
|
+
* the `MJ: Components` row, to which component a row points at, and to a component's name.
|
|
12
|
+
*
|
|
13
|
+
* Lives here, in a package both the server and the browser depend on, so the two cannot disagree:
|
|
14
|
+
* the server-side entity subclasses enforce this rule on every write path, and the form's drawer
|
|
15
|
+
* calls the same function to decide which controls to draw.
|
|
16
|
+
*/
|
|
17
|
+
/** The authorization that lets a user publish a form or panel to a role or to everyone. */
|
|
18
|
+
export declare const MANAGE_FORM_DEFAULTS_AUTHORIZATION = "Manage Form Defaults";
|
|
19
|
+
/** Who a form or panel is for. Derived from the entity so it tracks the column's value list. */
|
|
20
|
+
export type FormScope = MJEntityFormContributionEntity['Scope'];
|
|
21
|
+
/** True when `value` is exactly `User`, `Role` or `Global`: no padding, this casing. */
|
|
22
|
+
export declare function IsCanonicalFormScope(value: unknown): value is FormScope;
|
|
23
|
+
export type FormScopeOperation = 'create' | 'update' | 'delete';
|
|
24
|
+
/** One write to a form or panel, as the rule needs to see it. */
|
|
25
|
+
export interface FormScopeWrite {
|
|
26
|
+
Operation: FormScopeOperation;
|
|
27
|
+
/** Scope before the write. Null on create. */
|
|
28
|
+
PriorScope: FormScope | null;
|
|
29
|
+
/** Owner before the write. Null on create, or when the item was not personal. */
|
|
30
|
+
PriorUserID: string | null;
|
|
31
|
+
/** Scope after the write. On delete, the same as the prior scope. */
|
|
32
|
+
NextScope: FormScope;
|
|
33
|
+
/** Owner after the write. */
|
|
34
|
+
NextUserID: string | null;
|
|
35
|
+
CallerID: string;
|
|
36
|
+
CallerHoldsGrant: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Why this write is not allowed, or null when it is.
|
|
40
|
+
*
|
|
41
|
+
* Both sides of the write are checked. A personal side must belong to the caller; a shared side
|
|
42
|
+
* needs the grant. Checking only the new side would let a user demote a shared item to their own
|
|
43
|
+
* and take it over; checking only the old side would let them promote their own to everyone.
|
|
44
|
+
*
|
|
45
|
+
* A scope is read the way the database reads it: a side is personal when its scope is `User`
|
|
46
|
+
* after trimming and case-folding. Any side whose scope is not exactly `User` (`Role`, `Global`,
|
|
47
|
+
* a padded or re-cased value, a blank, an unknown value) counts as shared and needs the grant.
|
|
48
|
+
*/
|
|
49
|
+
export declare function FormScopeWriteRefusal(write: FormScopeWrite): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The `MJ: Components` columns that change what a form or panel using the component draws, or
|
|
52
|
+
* which component a lookup by name finds. An update by a holder of the grant is checked only when
|
|
53
|
+
* it changes one of them; an update by any other caller is always checked. A write with no caller
|
|
54
|
+
* (a trusted server context) is not checked.
|
|
55
|
+
*/
|
|
56
|
+
export declare const GUARDED_COMPONENT_FIELDS: readonly ["Specification", "Status", "Name", "Type", "Namespace"];
|
|
57
|
+
/** One of {@link GUARDED_COMPONENT_FIELDS}. */
|
|
58
|
+
export type GuardedComponentField = typeof GUARDED_COMPONENT_FIELDS[number];
|
|
59
|
+
/** A full custom form or panel row that uses a component, as the component rules read it. */
|
|
60
|
+
export interface FormComponentReference {
|
|
61
|
+
/** The row's ID. Read only to leave a row out of its own check. */
|
|
62
|
+
ID?: string;
|
|
63
|
+
Scope: FormScope;
|
|
64
|
+
UserID: string | null;
|
|
65
|
+
}
|
|
66
|
+
/** A component as the ownership test reads it. */
|
|
67
|
+
export interface OwnedComponentCheck {
|
|
68
|
+
/** Every `MJ: Entity Form Contributions` and `MJ: Entity Form Overrides` row that uses the component. */
|
|
69
|
+
References: readonly FormComponentReference[];
|
|
70
|
+
/** True when the component's `Create` record change names the caller. Read only when no row uses it. */
|
|
71
|
+
CreatedByCaller: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** One write to a `MJ: Components` row, as {@link ComponentWriteRefusal} needs to see it. */
|
|
74
|
+
export interface ComponentWrite extends OwnedComponentCheck {
|
|
75
|
+
Operation: FormScopeOperation;
|
|
76
|
+
/** The columns whose new value differs from the stored one. Read on an update by a holder only. */
|
|
77
|
+
ChangedFields: readonly string[];
|
|
78
|
+
/** Null when there is no caller: a trusted server context. */
|
|
79
|
+
CallerID: string | null;
|
|
80
|
+
CallerHoldsGrant: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether this write by a caller must be checked against the forms and panels that use the
|
|
84
|
+
* component: a delete; any update by a caller without the grant, whatever columns it changes; or
|
|
85
|
+
* an update by a holder that changes a {@link GUARDED_COMPONENT_FIELDS} column. With no caller (a
|
|
86
|
+
* trusted server context) nothing is checked. A create is never checked here;
|
|
87
|
+
* {@link ComponentNameCollisionRefusal} covers its name.
|
|
88
|
+
*/
|
|
89
|
+
export declare function ComponentWriteIsGuarded(write: Pick<ComponentWrite, 'Operation' | 'ChangedFields' | 'CallerID' | 'CallerHoldsGrant'>): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether a component is the caller's own: used by at least one row and only by the caller's own
|
|
92
|
+
* personal rows, or used by no row and created by the caller. A row whose scope is not exactly
|
|
93
|
+
* `User` is not the caller's own, whoever it names.
|
|
94
|
+
*/
|
|
95
|
+
export declare function IsCallersOwnComponent(component: OwnedComponentCheck, callerID: string): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Why a write to a component is not allowed, or null when it is. Only a write that
|
|
98
|
+
* {@link ComponentWriteIsGuarded} selects is checked.
|
|
99
|
+
*
|
|
100
|
+
* Without the grant, any update and a delete are allowed only for a component of the caller's own
|
|
101
|
+
* ({@link IsCallersOwnComponent}): used by at least one row and only by the caller's own personal
|
|
102
|
+
* rows, or used by no row and created by the caller. Forms can load a component by name, so a
|
|
103
|
+
* component with no row may still be what someone else's form draws.
|
|
104
|
+
*
|
|
105
|
+
* With the grant, each row that uses the component is checked with {@link FormScopeWriteRefusal}
|
|
106
|
+
* as if the caller were updating that row in place: a `Role` or `Global` row passes, another
|
|
107
|
+
* user's personal row refuses, and the caller's own personal row passes. A component no row uses
|
|
108
|
+
* may be changed.
|
|
109
|
+
*
|
|
110
|
+
* A refusal for a caller without the grant says whether the grant would help. It says the grant
|
|
111
|
+
* would allow the write when a holder making the same write would pass. It says the grant does
|
|
112
|
+
* not change the answer only when a holder would be refused too: the write is one a holder is
|
|
113
|
+
* checked for, and another user's personal row uses the component.
|
|
114
|
+
*/
|
|
115
|
+
export declare function ComponentWriteRefusal(write: ComponentWrite): string | null;
|
|
116
|
+
/** A form or panel row pointed at a component: what the row rule needs to see. */
|
|
117
|
+
export interface FormRowComponentCheck extends OwnedComponentCheck {
|
|
118
|
+
/** The row being written. Null on create. A reference with this ID is left out. */
|
|
119
|
+
RowID: string | null;
|
|
120
|
+
/** Null when there is no caller: a trusted server context. */
|
|
121
|
+
CallerID: string | null;
|
|
122
|
+
CallerHoldsGrant: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Why a form or panel row may not point at this component, or null when it may. Applies when the
|
|
126
|
+
* row is created or its `ComponentID` changes.
|
|
127
|
+
*
|
|
128
|
+
* Without the grant, the component must be the caller's own ({@link IsCallersOwnComponent}): used
|
|
129
|
+
* only by the caller's own personal rows, or used by none and created by the caller. With the
|
|
130
|
+
* grant, every other row that uses it is checked as {@link ComponentWriteRefusal} checks it: a
|
|
131
|
+
* shared row passes, and another user's personal row refuses everyone, an Owner included. Without
|
|
132
|
+
* this, a personal row pointed at someone else's component would make that component the
|
|
133
|
+
* caller's to change, or lock it against everyone else.
|
|
134
|
+
*/
|
|
135
|
+
export declare function FormRowComponentRefusal(check: FormRowComponentCheck): string | null;
|
|
136
|
+
/** A component's name, as {@link ComponentNameCollisionRefusal} needs to see it. */
|
|
137
|
+
export interface ComponentNameCheck {
|
|
138
|
+
/** True on create, or when an update changes `Name` or `Namespace`. */
|
|
139
|
+
NamesComponent: boolean;
|
|
140
|
+
/** Every other component a lookup by this name finds, as the ownership test reads it. */
|
|
141
|
+
Collisions: readonly OwnedComponentCheck[];
|
|
142
|
+
/** Null when there is no caller: a trusted server context. */
|
|
143
|
+
CallerID: string | null;
|
|
144
|
+
CallerHoldsGrant: boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Why a component may not take this name, or null when it may.
|
|
148
|
+
*
|
|
149
|
+
* A form's spec can load a component by name, and the lookup returns whichever match it finds
|
|
150
|
+
* first, so a second component with the same name could stand in for the first. Without the grant,
|
|
151
|
+
* a created or renamed component may share its name only with components that are the caller's
|
|
152
|
+
* own ({@link IsCallersOwnComponent}). A holder is not restricted, so they can resolve a collision.
|
|
153
|
+
*/
|
|
154
|
+
export declare function ComponentNameCollisionRefusal(check: ComponentNameCheck): string | null;
|
|
155
|
+
/**
|
|
156
|
+
* How narrow a scope's audience is, for breaking a tie between two rows at the same precedence.
|
|
157
|
+
* Higher wins: `User` (3) over `Role` (2) over `Global` (1). A value that is not one of the three
|
|
158
|
+
* ranks 0, below every scope.
|
|
159
|
+
*/
|
|
160
|
+
export declare function ContributionScopeRank(scope: FormScope | null | undefined): number;
|
|
161
|
+
/** The fields the same-key tie-break reads from a contribution. */
|
|
162
|
+
export interface RankedFormContribution {
|
|
163
|
+
/** Last-wins rank; null or undefined reads as 0. */
|
|
164
|
+
Precedence: number | null | undefined;
|
|
165
|
+
/** The row's scope. Not read for a compiled panel. */
|
|
166
|
+
Scope?: FormScope | null;
|
|
167
|
+
/** True for a compiled `BaseFormPanel` registration, which wins every precedence tie against a row. */
|
|
168
|
+
Compiled?: boolean;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Whether `candidate` beats `incumbent` for one contribution key: higher precedence first; on a
|
|
172
|
+
* tie a compiled panel beats any row, and between rows the narrower scope wins
|
|
173
|
+
* ({@link ContributionScopeRank}). On a full tie the incumbent stays, so the result follows the
|
|
174
|
+
* order the caller walks the contributions in.
|
|
175
|
+
*/
|
|
176
|
+
export declare function FormContributionOutranks(candidate: RankedFormContribution, incumbent: RankedFormContribution): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Whether a user may pick a full custom form and see it rendered.
|
|
179
|
+
*
|
|
180
|
+
* A live (`Active`) form always. A set-aside (`Inactive`) form only when it is the user's own
|
|
181
|
+
* `User` row: applying a second form sets the first aside, and the user must be able to swap back.
|
|
182
|
+
* A shared form set to `Inactive` was retracted by whoever manages it, so it is neither offered nor
|
|
183
|
+
* rendered. A `Pending` row is a draft and is never either. The browser's form resolver and the
|
|
184
|
+
* server's composition action both decide with this.
|
|
185
|
+
*
|
|
186
|
+
* It checks `Status` and `Scope` only, not whose row it is or which role it is for. Callers pass
|
|
187
|
+
* rows that already apply to the user: their own `User` rows, their roles' rows and `Global` rows.
|
|
188
|
+
*/
|
|
189
|
+
export declare function IsSelectableFormOverride(row: {
|
|
190
|
+
Status: string | null | undefined;
|
|
191
|
+
Scope: string | null | undefined;
|
|
192
|
+
}): boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Entities whose forms show only the user's own full custom forms and panels, lowercased.
|
|
195
|
+
*
|
|
196
|
+
* A full custom form or a panel runs a React spec the runtime interprets. On an identity,
|
|
197
|
+
* permission or form-metadata surface, one published to a role or to everyone would change what
|
|
198
|
+
* other people see where it matters most, so only `User`-scope items render there.
|
|
199
|
+
*/
|
|
200
|
+
export declare const RESTRICTED_FORM_ENTITIES: ReadonlySet<string>;
|
|
201
|
+
/**
|
|
202
|
+
* Whether a full custom form or panel at this scope may render on this entity's form.
|
|
203
|
+
*
|
|
204
|
+
* Always for `User`. Any other scope only off {@link RESTRICTED_FORM_ENTITIES}; the name is
|
|
205
|
+
* matched trimmed and case-folded. Applied where rows are read, so it holds however a row was
|
|
206
|
+
* written, `mj sync` and direct SQL included.
|
|
207
|
+
*/
|
|
208
|
+
export declare function FormScopeAllowedOnEntity(entityName: string | null | undefined, scope: string | null | undefined): boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Whether this user may publish forms and panels to a role or to everyone.
|
|
211
|
+
*
|
|
212
|
+
* An `Owner`-type user counts as holding it: they are the platform's top authority and must never
|
|
213
|
+
* be locked out of this. It does not let them touch other people's personal items — that is
|
|
214
|
+
* {@link FormScopeWriteRefusal}'s ownership rule, which no grant overrides.
|
|
215
|
+
*
|
|
216
|
+
* False without a user or when the authorization is not defined, so a deployment that has not
|
|
217
|
+
* synced the authorization yet fails closed.
|
|
218
|
+
*/
|
|
219
|
+
export declare function UserCanManageFormDefaults(user: UserInfo | null | undefined, provider: IMetadataProvider | null | undefined): boolean;
|
|
220
|
+
//# sourceMappingURL=FormScopeRules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormScopeRules.d.ts","sourceRoot":"","sources":["../../../src/custom/FormScope/FormScopeRules.ts"],"names":[],"mappings":"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"}
|