mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.2 KiB
157 lines
4.2 KiB
/*
|
|
* Squidex Headless CMS
|
|
*
|
|
* @license
|
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
|
|
*/
|
|
|
|
/* eslint-disable @typescript-eslint/no-implied-eval */
|
|
/* eslint-disable no-useless-return */
|
|
|
|
import { Types } from '@app/framework';
|
|
import { FieldRule, SchemaDto } from './../services/schemas.service';
|
|
|
|
export type RuleContext = { data: any; user?: any };
|
|
export type RuleForm = { fieldPath: string };
|
|
|
|
export interface CompiledRules {
|
|
get rules(): ReadonlyArray<CompiledRule>;
|
|
}
|
|
|
|
export interface RulesProvider {
|
|
compileRules(schema: SchemaDto): ReadonlyArray<CompiledRule>;
|
|
|
|
getRules(form: RuleForm): CompiledRules;
|
|
}
|
|
|
|
export class CompiledRule {
|
|
private readonly function: Function;
|
|
|
|
public get field() {
|
|
return this.rule.field;
|
|
}
|
|
|
|
public get action() {
|
|
return this.rule.action;
|
|
}
|
|
|
|
constructor(
|
|
private readonly rule: FieldRule,
|
|
private readonly useItemData: boolean,
|
|
) {
|
|
try {
|
|
this.function = new Function(`return function(user, ctx, data, itemData) { return ${rule.condition} }`)();
|
|
} catch {
|
|
this.function = () => false;
|
|
}
|
|
}
|
|
|
|
public eval(context: RuleContext, itemData: any) {
|
|
try {
|
|
const data = this.useItemData ? itemData || context.data : context.data;
|
|
|
|
return this.function(context.user, context, data, itemData);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
const EMPTY_RULES: CompiledRule[] = [];
|
|
const EMPTY_RULES_STATIC = { rules: EMPTY_RULES };
|
|
|
|
class ComponentRules implements ComponentRules {
|
|
private previouSchema: SchemaDto;
|
|
private compiledRules: ReadonlyArray<CompiledRule> = [];
|
|
|
|
public get rules() {
|
|
const schema = this.schema();
|
|
|
|
if (schema !== this.previouSchema) {
|
|
if (schema) {
|
|
this.compiledRules = Types.fastMerge(this.parent.getRules(this.form).rules, this.getRelativeRules(this.form, schema));
|
|
} else {
|
|
this.compiledRules = EMPTY_RULES;
|
|
}
|
|
}
|
|
|
|
return this.compiledRules;
|
|
}
|
|
|
|
constructor(
|
|
private readonly form: RuleForm,
|
|
private readonly parentPath: string,
|
|
private readonly parent: RulesProvider,
|
|
private readonly schema: () => SchemaDto | undefined,
|
|
) {
|
|
}
|
|
|
|
private getRelativeRules(form: RuleForm, schema: SchemaDto) {
|
|
const rules = this.parent.compileRules(schema);
|
|
|
|
if (rules.length === 0) {
|
|
return EMPTY_RULES;
|
|
}
|
|
|
|
const pathField = form.fieldPath.substr(this.parentPath.length + 1);
|
|
const pathSimplified = pathField.replace('.iv.', '.');
|
|
|
|
return rules.filter(x => x.field === pathField || x.field === pathSimplified);
|
|
}
|
|
}
|
|
|
|
export class ComponentRulesProvider implements RulesProvider {
|
|
constructor(
|
|
private readonly parentPath: string,
|
|
private readonly parent: RulesProvider,
|
|
private readonly schema: () => SchemaDto | undefined,
|
|
) {
|
|
}
|
|
|
|
public compileRules(schema: SchemaDto) {
|
|
return this.parent.compileRules(schema);
|
|
}
|
|
|
|
public getRules(form: RuleForm) {
|
|
return new ComponentRules(form, this.parentPath, this.parent, this.schema);
|
|
}
|
|
}
|
|
|
|
export class RootRulesProvider implements RulesProvider {
|
|
private readonly rulesCache: { [id: string]: ReadonlyArray<CompiledRule> } = {};
|
|
private readonly rules: ReadonlyArray<CompiledRule>;
|
|
|
|
constructor(schema: SchemaDto) {
|
|
this.rules = this.compileRules(schema);
|
|
}
|
|
|
|
public compileRules(schema: SchemaDto) {
|
|
if (!schema) {
|
|
return EMPTY_RULES;
|
|
}
|
|
|
|
let result = this.rulesCache[schema.id];
|
|
|
|
if (!result) {
|
|
result = schema.fieldRules.map(x => new CompiledRule(x, true));
|
|
|
|
this.rulesCache[schema.id] = result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public getRules(form: RuleForm) {
|
|
const allRules = this.rules;
|
|
|
|
if (allRules.length === 0) {
|
|
return EMPTY_RULES_STATIC;
|
|
}
|
|
|
|
const pathField = form.fieldPath;
|
|
const pathSimplified = pathField.replace('.iv.', '.');
|
|
|
|
const rules = allRules.filter(x => x.field === pathField || x.field === pathSimplified);
|
|
|
|
return { rules };
|
|
}
|
|
}
|
|
|