pgkiln 0.31.0 → 0.36.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/README.md +2 -2
- package/SECURITY.md +10 -0
- package/db/migrations/076_form_lost_update.sql +50 -0
- package/db/migrations/077_warn_unsaved.sql +41 -0
- package/db/migrations/078_url_region.sql +12 -0
- package/db/migrations/079_developer_toolbar.sql +53 -0
- package/db/migrations/080_search.sql +326 -0
- package/db/migrations/081_mail.sql +621 -0
- package/db/migrations/082_app_backups.sql +73 -0
- package/db/migrations/083_availability.sql +156 -0
- package/db/migrations/084_feedback.sql +46 -0
- package/db/migrations/085_page_behaviour.sql +65 -0
- package/db/migrations/086_data_settings.sql +599 -0
- package/db/migrations/087_login_page.sql +175 -0
- package/db/migrations/088_region_templates.sql +18 -0
- package/db/migrations/089_page_groups.sql +330 -0
- package/docs/apex-feature-parity.md +61 -39
- package/docs/guide/01-installation.md +1 -0
- package/docs/guide/03-builder.md +154 -4
- package/docs/guide/04-pages-and-regions.md +157 -3
- package/docs/guide/05-items.md +47 -0
- package/docs/guide/06-processing.md +210 -2
- package/docs/guide/07-dynamic-actions.md +60 -2
- package/docs/guide/08-security.md +56 -4
- package/docs/guide/09-reference.md +21 -0
- package/docs/guide/11-from-apex.md +9 -2
- package/docs/guide/12-development.md +40 -2
- package/docs/guide/14-globalization.md +15 -0
- package/docs/guide/15-extensions.md +1 -1
- package/docs/guide/17-mobile.md +3 -1
- package/docs/guide/18-cli.md +32 -1
- package/docs/guide/20-ai-agents.md +5 -2
- package/examples/hr/hr_50_search.sql +47 -0
- package/examples/hr/hr_51_mail.sql +70 -0
- package/examples/hr/hr_52_feedback.sql +15 -0
- package/examples/hr/hr_53_data_settings.sql +108 -0
- package/examples/hr/hr_54_region_templates.sql +67 -0
- package/examples/hr/hr_55_page_groups.sql +24 -0
- package/package.json +3 -1
- package/public/app.css +122 -3
- package/public/app.js +496 -13
- package/public/builder.css +22 -0
- package/public/builder.js +77 -0
- package/public/code-editor.css +3 -3
- package/public/sw.js +5 -3
- package/src/app.ts +10 -0
- package/src/appfiles.ts +13 -0
- package/src/automations.ts +12 -0
- package/src/builder/advisor.ts +68 -14
- package/src/builder/arrange.ts +2 -2
- package/src/builder/availability.ts +74 -0
- package/src/builder/backups.ts +134 -0
- package/src/builder/components.ts +123 -16
- package/src/builder/ddl-tools.ts +114 -0
- package/src/builder/designer.ts +35 -4
- package/src/builder/feedback.ts +82 -0
- package/src/builder/forms.ts +10 -5
- package/src/builder/home.ts +3 -0
- package/src/builder/locks.ts +15 -5
- package/src/builder/login-page.ts +82 -0
- package/src/builder/mail.ts +425 -0
- package/src/builder/newapp.ts +1 -1
- package/src/builder/region-help.ts +73 -0
- package/src/builder/region-settings.ts +19 -3
- package/src/builder/routes.ts +60 -5
- package/src/builder/search.ts +4 -4
- package/src/builder/shared.ts +3 -2
- package/src/builder/sql.ts +3 -0
- package/src/builder/ui.ts +9 -3
- package/src/cli/apps.ts +46 -5
- package/src/cli/main.ts +66 -5
- package/src/cli/mcp.ts +58 -4
- package/src/cli/replace.ts +40 -4
- package/src/ddl.ts +509 -0
- package/src/geocode.ts +44 -0
- package/src/i18n/ar.ts +56 -0
- package/src/i18n/cs.ts +56 -0
- package/src/i18n/da.ts +56 -0
- package/src/i18n/de.ts +56 -0
- package/src/i18n/el.ts +56 -0
- package/src/i18n/es.ts +56 -0
- package/src/i18n/fi.ts +56 -0
- package/src/i18n/fr.ts +56 -0
- package/src/i18n/he.ts +56 -0
- package/src/i18n/it.ts +56 -0
- package/src/i18n/ja.ts +56 -0
- package/src/i18n/ko.ts +56 -0
- package/src/i18n/nb.ts +56 -0
- package/src/i18n/pl.ts +56 -0
- package/src/i18n/pt.ts +56 -0
- package/src/i18n/ru.ts +56 -0
- package/src/i18n/sv.ts +56 -0
- package/src/i18n/tr.ts +56 -0
- package/src/i18n/uk.ts +56 -0
- package/src/i18n/zh.ts +56 -0
- package/src/i18n.ts +112 -0
- package/src/icons.ts +2 -1
- package/src/layout.ts +3 -2
- package/src/mail/mail.ts +148 -0
- package/src/mail/providers.ts +444 -0
- package/src/metadata.ts +34 -10
- package/src/runtime/ajax.ts +97 -0
- package/src/runtime/authz.ts +1 -1
- package/src/runtime/availability.ts +37 -0
- package/src/runtime/context.ts +46 -6
- package/src/runtime/data-export.ts +94 -0
- package/src/runtime/data-items.ts +194 -0
- package/src/runtime/devbar.ts +184 -0
- package/src/runtime/display-selector.ts +12 -4
- package/src/runtime/engine.ts +26 -1
- package/src/runtime/feedback.ts +128 -0
- package/src/runtime/grid.ts +170 -26
- package/src/runtime/items.ts +8 -0
- package/src/runtime/lists.ts +11 -1
- package/src/runtime/password-reset.ts +171 -0
- package/src/runtime/processes.ts +83 -2
- package/src/runtime/pwa.ts +7 -10
- package/src/runtime/regions.ts +41 -0
- package/src/runtime/render.ts +48 -7
- package/src/runtime/report-views.ts +4 -2
- package/src/runtime/report.ts +36 -22
- package/src/runtime/rest.ts +4 -0
- package/src/runtime/routes.ts +94 -37
- package/src/runtime/search.ts +252 -0
- package/src/runtime/signin.ts +80 -0
- package/src/runtime/url-region.ts +93 -0
- package/src/security.ts +11 -1
- package/src/server.ts +2 -0
- package/src/session.ts +8 -2
- package/src/static-version.ts +34 -0
- package/src/workingcopy.ts +2 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 082: automatic application backups (APEX: Application Backups)
|
|
3
|
+
--
|
|
4
|
+
-- meta.app_backup keeps exports of an application: one before every
|
|
5
|
+
-- import that replaces it (pgkiln import --replace, the MCP import, a
|
|
6
|
+
-- working copy's merge, a restore), one a day when it changed since the
|
|
7
|
+
-- last backup (the scheduler), and the ones a developer makes. The newest
|
|
8
|
+
-- meta.app.backups_keep (default 25, 0 = none) are kept per application.
|
|
9
|
+
-- The builder lists, downloads, restores and deletes them (Application →
|
|
10
|
+
-- Backups); a restore is an import --replace of the backup, so the version
|
|
11
|
+
-- it replaces is backed up too. Installation data: not exported, kept by
|
|
12
|
+
-- an import over the application.
|
|
13
|
+
-- =====================================================================
|
|
14
|
+
|
|
15
|
+
create table meta.app_backup (
|
|
16
|
+
id bigint generated always as identity primary key,
|
|
17
|
+
app_id int not null references meta.app on delete cascade,
|
|
18
|
+
created_at timestamptz not null default now(),
|
|
19
|
+
created_by text,
|
|
20
|
+
reason text not null check (reason in ('import', 'merge', 'restore', 'daily', 'manual')),
|
|
21
|
+
note text check (length(note) <= 500),
|
|
22
|
+
doc jsonb not null,
|
|
23
|
+
-- md5 of the export, so a daily backup of an unchanged application is skipped
|
|
24
|
+
doc_hash text not null,
|
|
25
|
+
size int not null
|
|
26
|
+
);
|
|
27
|
+
create index app_backup_app on meta.app_backup (app_id, created_at desc);
|
|
28
|
+
revoke all on meta.app_backup from public;
|
|
29
|
+
comment on table meta.app_backup is 'Application backups (082): exports kept by this installation, never exported';
|
|
30
|
+
|
|
31
|
+
alter table meta.app add column backups_keep int not null default 25 check (backups_keep between 0 and 500);
|
|
32
|
+
|
|
33
|
+
-- an export made before 082 has no backups_keep: NULL becomes 25
|
|
34
|
+
create function meta.app_backups_keep_default() returns trigger
|
|
35
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
36
|
+
begin
|
|
37
|
+
new.backups_keep := coalesce(new.backups_keep, 25);
|
|
38
|
+
return new;
|
|
39
|
+
end $$;
|
|
40
|
+
create trigger app_backups_keep before insert or update on meta.app
|
|
41
|
+
for each row execute function meta.app_backups_keep_default();
|
|
42
|
+
|
|
43
|
+
-- Back an application up (the server and the command line call it as the owner). A daily
|
|
44
|
+
-- backup of an application that didn't change since its newest backup is skipped (NULL).
|
|
45
|
+
create function meta.backup_app(p_app_id int, p_reason text, p_user text default null, p_note text default null)
|
|
46
|
+
returns bigint
|
|
47
|
+
language plpgsql volatile set search_path = meta, pg_catalog as $$
|
|
48
|
+
declare
|
|
49
|
+
v_alias text;
|
|
50
|
+
v_keep int;
|
|
51
|
+
v_doc jsonb;
|
|
52
|
+
v_hash text;
|
|
53
|
+
v_id bigint;
|
|
54
|
+
begin
|
|
55
|
+
select alias, backups_keep into v_alias, v_keep from meta.app where id = p_app_id;
|
|
56
|
+
if v_alias is null or v_keep = 0 then
|
|
57
|
+
return null;
|
|
58
|
+
end if;
|
|
59
|
+
v_doc := meta.export_app(v_alias);
|
|
60
|
+
v_hash := md5(v_doc::text);
|
|
61
|
+
if p_reason = 'daily' and v_hash = (select doc_hash from meta.app_backup where app_id = p_app_id order by created_at desc, id desc limit 1) then
|
|
62
|
+
return null;
|
|
63
|
+
end if;
|
|
64
|
+
insert into meta.app_backup (app_id, created_by, reason, note, doc, doc_hash, size)
|
|
65
|
+
values (p_app_id, p_user, p_reason, left(p_note, 500), v_doc, v_hash, octet_length(v_doc::text))
|
|
66
|
+
returning id into v_id;
|
|
67
|
+
delete from meta.app_backup
|
|
68
|
+
where app_id = p_app_id
|
|
69
|
+
and id not in (select id from meta.app_backup where app_id = p_app_id order by created_at desc, id desc limit v_keep);
|
|
70
|
+
return v_id;
|
|
71
|
+
end
|
|
72
|
+
$$;
|
|
73
|
+
revoke all on function meta.backup_app(int, text, text, text) from public;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 083: application availability and build status (APEX: Application
|
|
3
|
+
-- Availability and Build Status)
|
|
4
|
+
--
|
|
5
|
+
-- meta.app.availability:
|
|
6
|
+
-- available everyone who may sign in (the default)
|
|
7
|
+
-- developers only developers with a builder session in the same browser
|
|
8
|
+
-- (the developer toolbar's token, migration 079)
|
|
9
|
+
-- restricted only the users in availability_users (and developers)
|
|
10
|
+
-- unavailable nobody: every page shows availability_message (or a default)
|
|
11
|
+
-- Pages, file downloads, REST modules and PostgREST (meta.api_check) follow
|
|
12
|
+
-- it; automations, workflows, background jobs and queued mail keep running.
|
|
13
|
+
--
|
|
14
|
+
-- meta.app.build_status 'run_only': the builder refuses changes to the
|
|
15
|
+
-- application (as if it were locked), except to its availability and build
|
|
16
|
+
-- status. The command line's import --replace (deployment) still works.
|
|
17
|
+
--
|
|
18
|
+
-- Both belong to the installation: export_app leaves them out (redefined from
|
|
19
|
+
-- 081), an import of a new application gets the defaults, and an import over
|
|
20
|
+
-- an application keeps its values (src/cli/replace.ts).
|
|
21
|
+
-- =====================================================================
|
|
22
|
+
|
|
23
|
+
alter table meta.app
|
|
24
|
+
add column availability text not null default 'available' check (availability in ('available', 'developers', 'restricted', 'unavailable')),
|
|
25
|
+
add column availability_message text check (length(availability_message) <= 2000),
|
|
26
|
+
add column availability_users text[] not null default '{}',
|
|
27
|
+
add column build_status text not null default 'run_and_build' check (build_status in ('run_and_build', 'run_only'));
|
|
28
|
+
|
|
29
|
+
-- an export has none of these: NULL becomes the default
|
|
30
|
+
create function meta.app_availability_default() returns trigger
|
|
31
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
32
|
+
begin
|
|
33
|
+
new.availability := coalesce(new.availability, 'available');
|
|
34
|
+
new.availability_users := coalesce(new.availability_users, '{}');
|
|
35
|
+
new.build_status := coalesce(new.build_status, 'run_and_build');
|
|
36
|
+
return new;
|
|
37
|
+
end $$;
|
|
38
|
+
create trigger app_availability before insert or update on meta.app
|
|
39
|
+
for each row execute function meta.app_availability_default();
|
|
40
|
+
|
|
41
|
+
comment on column meta.app.availability is 'available, developers, restricted or unavailable (083)';
|
|
42
|
+
comment on column meta.app.build_status is 'run_and_build, or run_only: the builder refuses changes (083)';
|
|
43
|
+
|
|
44
|
+
-- ---------------------------------------------------------------- export
|
|
45
|
+
-- Same as 081, without the availability and build status.
|
|
46
|
+
create or replace function meta.export_app(p_alias text) returns jsonb
|
|
47
|
+
language sql stable set search_path = meta, pg_catalog as $$
|
|
48
|
+
select jsonb_build_object(
|
|
49
|
+
'format', 'pgkiln/2',
|
|
50
|
+
-- (083) availability and build status belong to the installation
|
|
51
|
+
'app', to_jsonb(a) - 'id' - 'created_at' - 'updated_at' - 'availability' - 'availability_message' - 'availability_users' - 'build_status',
|
|
52
|
+
'authz_schemes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.authz_scheme x where x.app_id = a.id), '[]'),
|
|
53
|
+
'app_items', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_item x where x.app_id = a.id), '[]'),
|
|
54
|
+
'app_processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_process x where x.app_id = a.id), '[]'),
|
|
55
|
+
'lovs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.lov x where x.app_id = a.id), '[]'),
|
|
56
|
+
'group_roles', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.group_name, x.role) from meta.app_group_role x where x.app_id = a.id), '[]'),
|
|
57
|
+
'text_messages', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.name, x.language) from meta.text_message x where x.app_id = a.id), '[]'),
|
|
58
|
+
'translations', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.language, x.source) from meta.translation x where x.app_id = a.id), '[]'),
|
|
59
|
+
'report_layouts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'logo' || jsonb_build_object('logo', encode(x.logo, 'base64')) order by x.name)
|
|
60
|
+
from meta.report_layout x where x.app_id = a.id), '[]'),
|
|
61
|
+
'automations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'next_run_at' - 'last_run_at' - 'last_status' - 'code' order by x.name)
|
|
62
|
+
from meta.automation x where x.app_id = a.id), '[]'),
|
|
63
|
+
-- (044) the actions of the automations, by automation name
|
|
64
|
+
'automation_actions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.automation_name collate "C", x.seq, x.name collate "C")
|
|
65
|
+
from meta.automation_action x where x.app_id = a.id), '[]'),
|
|
66
|
+
'document_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.document_template x where x.app_id = a.id), '[]'),
|
|
67
|
+
'task_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.task_definition x where x.app_id = a.id), '[]'),
|
|
68
|
+
'workflow_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.workflow_definition x where x.app_id = a.id), '[]'),
|
|
69
|
+
'rest_modules', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.rest_module x where x.app_id = a.id), '[]'),
|
|
70
|
+
'template_components', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.static_id) from meta.template_component x where x.app_id = a.id), '[]'),
|
|
71
|
+
'build_options', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.build_option x where x.app_id = a.id), '[]'),
|
|
72
|
+
-- (030) secrets never leave the installation: they are entered again after an import
|
|
73
|
+
-- (050) nor do the password and refresh token of the OAuth2 flows, or the token state
|
|
74
|
+
'web_credentials', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at' order by x.name)
|
|
75
|
+
from meta.web_credential x where x.app_id = a.id), '[]'),
|
|
76
|
+
-- (050) the synchronisation's state (next and last run) belongs to the installation
|
|
77
|
+
'rest_sources', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'sync_next_at' - 'sync_last_at' - 'sync_last_status' order by x.name)
|
|
78
|
+
from meta.rest_source x where x.app_id = a.id), '[]'),
|
|
79
|
+
-- (041) data load definitions
|
|
80
|
+
'data_load_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.data_load_def x where x.app_id = a.id), '[]'),
|
|
81
|
+
-- (042) lists; entries keep their ids so parents can be remapped on import
|
|
82
|
+
'lists', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.list x where x.app_id = a.id), '[]'),
|
|
83
|
+
'list_entries', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.list_name, x.parent_id nulls first, x.seq, x.id) from meta.list_entry x where x.app_id = a.id), '[]'),
|
|
84
|
+
-- (042) supporting objects: install, upgrade and deinstall scripts (never run on import)
|
|
85
|
+
'supporting_scripts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.supporting_script x where x.app_id = a.id), '[]'),
|
|
86
|
+
-- (069) plug-ins (their files are static files, their templates template components)
|
|
87
|
+
'plugins', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.plugin x where x.app_id = a.id), '[]'),
|
|
88
|
+
-- (080) search configurations
|
|
89
|
+
'search_configs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.search_config x where x.app_id = a.id), '[]'),
|
|
90
|
+
-- (081) e-mail templates
|
|
91
|
+
'email_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.email_template x where x.app_id = a.id), '[]'),
|
|
92
|
+
-- (068) static application files (JavaScript, CSS, images, fonts) as base64
|
|
93
|
+
'static_files', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'content' - 'updated_at' || jsonb_build_object('content', translate(encode(x.content, 'base64'), E'\n', '')) order by x.name)
|
|
94
|
+
from meta.static_file x where x.app_id = a.id), '[]'),
|
|
95
|
+
-- nav entries and regions keep their ids, so parents and references can be remapped on import
|
|
96
|
+
'nav', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.parent_id nulls first, x.seq, x.id) from meta.nav_entry x where x.app_id = a.id), '[]'),
|
|
97
|
+
'pages', coalesce((
|
|
98
|
+
select jsonb_agg(to_jsonb(p) - 'id' - 'app_id' || jsonb_build_object(
|
|
99
|
+
'regions', coalesce((select jsonb_agg(to_jsonb(r) - 'page_id' order by r.seq, r.id) from meta.region r where r.page_id = p.id), '[]'),
|
|
100
|
+
'items', coalesce((select jsonb_agg(to_jsonb(i) - 'id' - 'page_id' order by i.seq, i.id) from meta.item i where i.page_id = p.id), '[]'),
|
|
101
|
+
'buttons', coalesce((select jsonb_agg(to_jsonb(b) - 'id' - 'page_id' order by b.seq, b.id) from meta.button b where b.page_id = p.id), '[]'),
|
|
102
|
+
'dynamic_actions', coalesce((select jsonb_agg(to_jsonb(d) - 'id' - 'page_id' order by d.seq, d.id) from meta.dynamic_action d where d.page_id = p.id), '[]'),
|
|
103
|
+
'validations', coalesce((select jsonb_agg(to_jsonb(v) - 'id' - 'page_id' order by v.seq, v.id) from meta.validation v where v.page_id = p.id), '[]'),
|
|
104
|
+
'processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.process x where x.page_id = p.id), '[]'),
|
|
105
|
+
'computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.computation x where x.page_id = p.id), '[]'),
|
|
106
|
+
'branches', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.branch x where x.page_id = p.id), '[]')
|
|
107
|
+
) order by p.page_no)
|
|
108
|
+
from meta.page p where p.app_id = a.id), '[]'))
|
|
109
|
+
from meta.app a
|
|
110
|
+
where a.alias = p_alias
|
|
111
|
+
$$;
|
|
112
|
+
|
|
113
|
+
-- ---------------------------------------------------------------- PostgREST
|
|
114
|
+
-- Same as 011, plus the application's availability.
|
|
115
|
+
create or replace function meta.api_check() returns void
|
|
116
|
+
language plpgsql stable security definer set search_path = meta, pg_catalog as $$
|
|
117
|
+
declare
|
|
118
|
+
v_claims jsonb := meta.jwt_claims();
|
|
119
|
+
v_role text := current_setting('role', true); -- the SET ROLE PostgREST did
|
|
120
|
+
v_app meta.app;
|
|
121
|
+
v_acc meta.account;
|
|
122
|
+
begin
|
|
123
|
+
if v_claims is null or v_role is null or v_role in ('none', 'pgkiln_anon') then
|
|
124
|
+
return;
|
|
125
|
+
end if;
|
|
126
|
+
select * into v_app from meta.app a where a.alias = v_claims->>'app';
|
|
127
|
+
if not found or v_app.api_role is distinct from v_role then
|
|
128
|
+
raise exception 'This token is not valid for an application with API role %.', v_role
|
|
129
|
+
using errcode = 'PT401';
|
|
130
|
+
end if;
|
|
131
|
+
-- (083) the application's availability: unavailable or for developers only answers 503;
|
|
132
|
+
-- restricted only for the users it names (never for an OAuth client)
|
|
133
|
+
if v_app.availability in ('unavailable', 'developers') then
|
|
134
|
+
raise exception '%', coalesce(nullif(v_app.availability_message, ''), 'The application is not available at the moment.') using errcode = 'PT503';
|
|
135
|
+
end if;
|
|
136
|
+
if v_app.availability = 'restricted'
|
|
137
|
+
and (v_claims ? 'client_id' or not lower(meta.app_user()) = any (select lower(u) from unnest(v_app.availability_users) u)) then
|
|
138
|
+
raise exception 'The application is not available to this account at the moment.' using errcode = 'PT403';
|
|
139
|
+
end if;
|
|
140
|
+
if v_claims ? 'client_id' then
|
|
141
|
+
if not exists (select 1 from meta.api_client c
|
|
142
|
+
where c.client_id = v_claims->>'client_id' and c.app_id = v_app.id and c.active) then
|
|
143
|
+
raise exception 'The OAuth client of this token does not exist or was revoked.' using errcode = 'PT401';
|
|
144
|
+
end if;
|
|
145
|
+
return;
|
|
146
|
+
end if;
|
|
147
|
+
select * into v_acc from meta.account ac where lower(ac.username) = lower(meta.app_user());
|
|
148
|
+
if not found or not v_acc.active then
|
|
149
|
+
raise exception 'The account of this token does not exist or is inactive.' using errcode = 'PT403';
|
|
150
|
+
end if;
|
|
151
|
+
if v_app.access_control <> 'any_user'
|
|
152
|
+
and not exists (select 1 from meta.app_access aa where aa.app_id = v_app.id and aa.account_id = v_acc.id) then
|
|
153
|
+
raise exception 'The account of this token has no access to the application.' using errcode = 'PT403';
|
|
154
|
+
end if;
|
|
155
|
+
end
|
|
156
|
+
$$;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 084: feedback (APEX: Feedback, Team Development → Feedback)
|
|
3
|
+
--
|
|
4
|
+
-- With meta.app.feedback on, the application's navigation bar has a
|
|
5
|
+
-- "Feedback" link: users send a comment, an idea or a problem (with an
|
|
6
|
+
-- optional rating) about the page they came from, and see their earlier
|
|
7
|
+
-- feedback with the developers' answers. Developers read, answer and
|
|
8
|
+
-- close it in the builder (Application → Feedback). The setting travels
|
|
9
|
+
-- with the export; the feedback itself is installation data.
|
|
10
|
+
-- =====================================================================
|
|
11
|
+
|
|
12
|
+
alter table meta.app add column feedback boolean not null default false;
|
|
13
|
+
|
|
14
|
+
-- an export made before 084 has no feedback setting: NULL becomes false
|
|
15
|
+
create function meta.app_feedback_default() returns trigger
|
|
16
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
17
|
+
begin
|
|
18
|
+
new.feedback := coalesce(new.feedback, false);
|
|
19
|
+
return new;
|
|
20
|
+
end $$;
|
|
21
|
+
create trigger app_feedback before insert or update on meta.app
|
|
22
|
+
for each row execute function meta.app_feedback_default();
|
|
23
|
+
|
|
24
|
+
create table meta.feedback (
|
|
25
|
+
id bigint generated always as identity primary key,
|
|
26
|
+
app_id int not null references meta.app on delete cascade,
|
|
27
|
+
page_no int,
|
|
28
|
+
username text not null,
|
|
29
|
+
session_id uuid,
|
|
30
|
+
kind text not null check (kind in ('comment', 'idea', 'problem')),
|
|
31
|
+
rating smallint check (rating between 1 and 5),
|
|
32
|
+
body text not null check (length(body) between 1 and 4000),
|
|
33
|
+
user_agent text check (length(user_agent) <= 300),
|
|
34
|
+
status text not null default 'open' check (status in ('open', 'in_progress', 'done', 'declined')),
|
|
35
|
+
response text check (length(response) <= 4000),
|
|
36
|
+
responded_by text,
|
|
37
|
+
responded_at timestamptz,
|
|
38
|
+
created_at timestamptz not null default now()
|
|
39
|
+
);
|
|
40
|
+
create index feedback_app on meta.feedback (app_id, created_at desc);
|
|
41
|
+
revoke all on meta.feedback from public;
|
|
42
|
+
-- the runtime records feedback and shows users their own (src/runtime/feedback.ts); developers answer
|
|
43
|
+
grant select, insert on meta.feedback to pgkiln_runtime;
|
|
44
|
+
|
|
45
|
+
comment on table meta.feedback is 'Feedback of application users and the developers'' answers (084); not exported';
|
|
46
|
+
comment on column meta.app.feedback is 'Users can send feedback from the navigation bar (084)';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 085: page behaviour (sprint 52)
|
|
3
|
+
--
|
|
4
|
+
-- Dynamic actions (APEX: events and actions that were missing):
|
|
5
|
+
-- events after_refresh (a region was refreshed or loaded; trigger: region
|
|
6
|
+
-- ids or static ids), selection_change (rows selected in a report
|
|
7
|
+
-- or grid; trigger: region ids or static ids), focus and blur (Get /
|
|
8
|
+
-- Lose Focus of items), key_release (items), before_submit (the page
|
|
9
|
+
-- is about to be submitted; a declined Confirm stops it), custom
|
|
10
|
+
-- (trigger: event names, raised with pgkiln.trigger(name, value))
|
|
11
|
+
-- actions confirm (a question; No stops the actions after it), close_dialog
|
|
12
|
+
-- (close the modal page and refresh its parent), cancel_dialog,
|
|
13
|
+
-- clear (empty the affected items), open_region / close_region,
|
|
14
|
+
-- download (runs a download process of the page by name), print,
|
|
15
|
+
-- current_position (latitude,longitude into the affected item),
|
|
16
|
+
-- share (the device's share sheet, or the link copied)
|
|
17
|
+
-- The actions of one event and trigger now run one after the other, in
|
|
18
|
+
-- sequence order (APEX: the true actions of a dynamic action).
|
|
19
|
+
--
|
|
20
|
+
-- Ajax callbacks (APEX: On Demand processes, apex.server.process): page and
|
|
21
|
+
-- application processes with point "ajax" run only when JavaScript calls
|
|
22
|
+
-- them by name: POST /a/<alias>/<page>/ajax/<name> (src/runtime/ajax.ts).
|
|
23
|
+
--
|
|
24
|
+
-- meta.app.error_function (APEX: Error Handling Function): a function of the
|
|
25
|
+
-- application, function(jsonb) returns jsonb, that may rewrite every error
|
|
26
|
+
-- message shown to users (src/runtime/context.ts publicError).
|
|
27
|
+
--
|
|
28
|
+
-- meta.app.session_warning_seconds (APEX: Session timeout warning): seconds
|
|
29
|
+
-- before the session ends that the page warns and offers to stay signed in
|
|
30
|
+
-- (0: no warning).
|
|
31
|
+
-- =====================================================================
|
|
32
|
+
|
|
33
|
+
alter table meta.dynamic_action drop constraint dynamic_action_event_check;
|
|
34
|
+
alter table meta.dynamic_action add constraint dynamic_action_event_check check (event in (
|
|
35
|
+
'change', 'click', 'load', 'dialog_closed', 'after_refresh', 'selection_change', 'focus', 'blur', 'key_release', 'before_submit', 'custom'));
|
|
36
|
+
|
|
37
|
+
alter table meta.dynamic_action drop constraint dynamic_action_action_check;
|
|
38
|
+
alter table meta.dynamic_action add constraint dynamic_action_action_check check (action in (
|
|
39
|
+
'show', 'hide', 'enable', 'disable', 'set_value', 'execute_sql', 'refresh_region', 'refresh_item', 'alert', 'submit',
|
|
40
|
+
'set_focus', 'add_class', 'remove_class', 'show_success', 'show_error', 'clear_errors', 'ai_generate', 'execute_javascript', 'plugin',
|
|
41
|
+
'push_subscribe', 'confirm', 'close_dialog', 'cancel_dialog', 'clear', 'open_region', 'close_region', 'download', 'print',
|
|
42
|
+
'current_position', 'share'));
|
|
43
|
+
|
|
44
|
+
alter table meta.process drop constraint process_point_check;
|
|
45
|
+
alter table meta.process add constraint process_point_check check (point in ('submit', 'load', 'ajax'));
|
|
46
|
+
alter table meta.app_process drop constraint app_process_point_check;
|
|
47
|
+
alter table meta.app_process add constraint app_process_point_check check (point in ('after_login', 'before_page', 'ajax'));
|
|
48
|
+
|
|
49
|
+
alter table meta.app
|
|
50
|
+
-- schema.function: called as the application's role with a jsonb error, returns jsonb (or null)
|
|
51
|
+
add column error_function text check (error_function ~ '^[A-Za-z_][A-Za-z0-9_$]{0,62}\.[A-Za-z_][A-Za-z0-9_$]{0,62}$'),
|
|
52
|
+
add column session_warning_seconds int not null default 120 check (session_warning_seconds between 0 and 3600);
|
|
53
|
+
|
|
54
|
+
-- an export made before 085 has no session warning: NULL becomes 120
|
|
55
|
+
create function meta.app_session_warning_default() returns trigger
|
|
56
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
57
|
+
begin
|
|
58
|
+
new.session_warning_seconds := coalesce(new.session_warning_seconds, 120);
|
|
59
|
+
return new;
|
|
60
|
+
end $$;
|
|
61
|
+
create trigger app_session_warning before insert or update on meta.app
|
|
62
|
+
for each row execute function meta.app_session_warning_default();
|
|
63
|
+
|
|
64
|
+
comment on column meta.app.error_function is 'schema.function(jsonb) returns jsonb that rewrites error messages (085)';
|
|
65
|
+
comment on column meta.app.session_warning_seconds is 'Warn this many seconds before the session ends; 0: no warning (085)';
|