import policyFile from '../../includes/settings-policy.json'; import { INITIAL_SETTINGS } from '../stores/main/constants'; import type { Settings } from '../stores/main/models'; /** * Which table settings this plugin applies. The policy is the SAME file PHP * enforces on save (includes/settings-policy.json via * includes/settings-policy.php), so what renders and what can be saved never * drift apart. * * Settings that belong to vTables Pro stay in the database untouched (the * server keeps their stored values on every save); here they are replaced * with their defaults for rendering, so a table made with Pro — or with * vTables 0.2.x, where some of them were still free — shows with the free * options until Pro is active again. */ type SectionPolicy = boolean | Record; const SECTIONS = (policyFile as { sections: Record }) .sections; type AnyRecord = Record; function isRecord(value: unknown): value is AnyRecord { return typeof value === 'object' && value !== null && !Array.isArray(value); } function clone(value: T): T { return value === undefined ? value : JSON.parse(JSON.stringify(value)); } /** Whether a settings path (section, optional key) is applied by this plugin. */ export function isSettingAllowed(section: string, key?: string): boolean { const policy = SECTIONS[section]; if (policy === undefined) return true; if (typeof policy === 'boolean') return policy; if (key !== undefined && key in policy && key !== '*') return policy[key]; return policy['*'] ?? true; } /** * Returns a copy of `settings` where every locked path holds its default * (from INITIAL_SETTINGS) — or is dropped when there is no default. */ export function applySettingsPolicy(settings: T): T { const result = clone(settings) as unknown as AnyRecord; const defaults = INITIAL_SETTINGS as unknown as AnyRecord; for (const section of Object.keys(result)) { const policy = SECTIONS[section]; if (policy === undefined || policy === true) continue; if (policy === false) { if (section in defaults) { result[section] = clone(defaults[section]); } else { delete result[section]; } continue; } const current = result[section]; if (!isRecord(current)) continue; const sectionDefaults = isRecord(defaults[section]) ? (defaults[section] as AnyRecord) : {}; for (const key of Object.keys(current)) { if (isSettingAllowed(section, key)) continue; if (key in sectionDefaults) { current[key] = clone(sectionDefaults[key]); } else { delete current[key]; } } } return result as unknown as T; }