pgkiln 0.44.0 → 0.45.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/db/migrations/102_app_computations.sql +345 -0
- package/db/migrations/103_shortcuts.sql +341 -0
- package/db/migrations/104_report_queries.sql +413 -0
- package/db/migrations/105_component_settings_groups.sql +367 -0
- package/docs/apex-feature-parity.md +10 -10
- package/docs/guide/02-concepts.md +24 -0
- package/docs/guide/03-builder.md +33 -1
- package/docs/guide/04-pages-and-regions.md +10 -1
- package/docs/guide/06-processing.md +22 -0
- package/docs/guide/09-reference.md +13 -0
- package/docs/guide/12-development.md +2 -1
- package/docs/guide/16-files.md +23 -0
- package/docs/guide/18-cli.md +4 -0
- package/package.json +1 -1
- package/public/app.css +17 -0
- package/public/app.js +63 -0
- package/src/appfiles.ts +18 -2
- package/src/builder/advisor.ts +7 -2
- package/src/builder/code-editor.ts +6 -1
- package/src/builder/components.ts +85 -3
- package/src/builder/forms.ts +1 -1
- package/src/builder/shared.ts +39 -6
- package/src/builder/subscriptions.ts +30 -4
- package/src/cli/replace.ts +6 -2
- package/src/metadata.ts +14 -1
- package/src/runtime/component-settings.ts +42 -0
- package/src/runtime/context.ts +4 -0
- package/src/runtime/documents.ts +29 -3
- package/src/runtime/engine.ts +2 -2
- package/src/runtime/grid.ts +22 -4
- package/src/runtime/items.ts +14 -11
- package/src/runtime/logic.ts +12 -7
- package/src/runtime/regions.ts +4 -3
- package/src/runtime/render.ts +1 -0
- package/src/runtime/report.ts +2 -1
- package/src/runtime/routes.ts +4 -2
- package/src/runtime/shortcuts.ts +43 -0
- package/src/security.ts +2 -1
- package/src/subscriptions.ts +67 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 105: component settings and component groups (APEX: Shared Components →
|
|
3
|
+
-- Component Settings, Component Groups)
|
|
4
|
+
--
|
|
5
|
+
-- meta.app.component_settings: an application's defaults for its built-in
|
|
6
|
+
-- component types, used where a component sets nothing itself:
|
|
7
|
+
-- report_rows rows per page of a report (15)
|
|
8
|
+
-- grid_rows rows per page of an interactive grid (25)
|
|
9
|
+
-- popup_lov_rows rows per page of a popup list of values (25)
|
|
10
|
+
-- lov_max_rows rows a list of values reads (5,000)
|
|
11
|
+
-- rating_max stars of a star rating (5)
|
|
12
|
+
-- The builder bounds the values (src/runtime/component-settings.ts); the
|
|
13
|
+
-- runtime bounds them again. Exported with the application (export_app
|
|
14
|
+
-- copies every column of meta.app).
|
|
15
|
+
--
|
|
16
|
+
-- meta.component_group: a named set of an application's components that
|
|
17
|
+
-- other applications subscribe to at once (subscriptions, migration 056):
|
|
18
|
+
-- members are "kind:name" of its lists of values, authorization schemes,
|
|
19
|
+
-- build options, template components and lists. Builder data: the
|
|
20
|
+
-- runtime does not read it.
|
|
21
|
+
-- export_app / import_app are redefined from 104 to carry component_groups.
|
|
22
|
+
-- =====================================================================
|
|
23
|
+
alter table meta.app add column component_settings jsonb not null default '{}'
|
|
24
|
+
check (jsonb_typeof(component_settings) = 'object');
|
|
25
|
+
comment on column meta.app.component_settings is 'Component settings (105): defaults of built-in component types (report_rows, grid_rows, popup_lov_rows, lov_max_rows, rating_max)';
|
|
26
|
+
|
|
27
|
+
create table meta.component_group (
|
|
28
|
+
id serial primary key,
|
|
29
|
+
app_id int not null references meta.app on delete cascade,
|
|
30
|
+
name text not null check (name ~ '^[A-Za-z0-9][A-Za-z0-9 _.-]{0,59}$'),
|
|
31
|
+
description text,
|
|
32
|
+
members text[] not null default '{}' check (cardinality(members) <= 500),
|
|
33
|
+
unique (app_id, name)
|
|
34
|
+
);
|
|
35
|
+
revoke all on meta.component_group from public;
|
|
36
|
+
comment on table meta.component_group is 'Component groups (105): sets of an application''s components ("kind:name") that other applications subscribe to at once';
|
|
37
|
+
|
|
38
|
+
-- (090) like the other tables of an application
|
|
39
|
+
alter table meta.component_group enable row level security;
|
|
40
|
+
create policy workspace on meta.component_group
|
|
41
|
+
using ((select meta.isolation_off()) or app_id in (select meta.visible_apps()))
|
|
42
|
+
with check ((select meta.isolation_off()) or app_id in (select meta.visible_apps()));
|
|
43
|
+
|
|
44
|
+
create or replace function meta.export_app(p_alias text) returns jsonb
|
|
45
|
+
language sql stable set search_path = meta, pg_catalog as $$
|
|
46
|
+
select jsonb_build_object(
|
|
47
|
+
'format', 'pgkiln/2',
|
|
48
|
+
-- (083) availability and build status belong to the installation
|
|
49
|
+
'app', to_jsonb(a) - 'id' - 'created_at' - 'updated_at' - 'availability' - 'availability_message' - 'availability_users' - 'build_status',
|
|
50
|
+
'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), '[]'),
|
|
51
|
+
'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), '[]'),
|
|
52
|
+
'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), '[]'),
|
|
53
|
+
'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), '[]'),
|
|
54
|
+
'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), '[]'),
|
|
55
|
+
'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), '[]'),
|
|
56
|
+
'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), '[]'),
|
|
57
|
+
'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)
|
|
58
|
+
from meta.report_layout x where x.app_id = a.id), '[]'),
|
|
59
|
+
'automations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'next_run_at' - 'last_run_at' - 'last_status' - 'code' order by x.name)
|
|
60
|
+
from meta.automation x where x.app_id = a.id), '[]'),
|
|
61
|
+
-- (044) the actions of the automations, by automation name
|
|
62
|
+
'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")
|
|
63
|
+
from meta.automation_action x where x.app_id = a.id), '[]'),
|
|
64
|
+
'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), '[]'),
|
|
65
|
+
'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), '[]'),
|
|
66
|
+
'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), '[]'),
|
|
67
|
+
'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), '[]'),
|
|
68
|
+
'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), '[]'),
|
|
69
|
+
'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), '[]'),
|
|
70
|
+
-- (030) secrets never leave the installation: they are entered again after an import
|
|
71
|
+
-- (050) nor do the password and refresh token of the OAuth2 flows, or the token state
|
|
72
|
+
'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)
|
|
73
|
+
from meta.web_credential x where x.app_id = a.id), '[]'),
|
|
74
|
+
-- (050) the synchronisation's state (next and last run) belongs to the installation
|
|
75
|
+
'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)
|
|
76
|
+
from meta.rest_source x where x.app_id = a.id), '[]'),
|
|
77
|
+
-- (041) data load definitions
|
|
78
|
+
'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), '[]'),
|
|
79
|
+
-- (042) lists; entries keep their ids so parents can be remapped on import
|
|
80
|
+
'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), '[]'),
|
|
81
|
+
'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), '[]'),
|
|
82
|
+
-- (042) supporting objects: install, upgrade and deinstall scripts (never run on import)
|
|
83
|
+
'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), '[]'),
|
|
84
|
+
-- (069) plug-ins (their files are static files, their templates template components)
|
|
85
|
+
'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), '[]'),
|
|
86
|
+
-- (080) search configurations
|
|
87
|
+
'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), '[]'),
|
|
88
|
+
-- (081) e-mail templates
|
|
89
|
+
'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), '[]'),
|
|
90
|
+
-- (086) application settings (with their values) and dynamic translations
|
|
91
|
+
'app_settings', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_setting x where x.app_id = a.id), '[]'),
|
|
92
|
+
'page_groups', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.page_group x where x.app_id = a.id), '[]'),
|
|
93
|
+
-- (105) component groups
|
|
94
|
+
'component_groups', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.component_group x where x.app_id = a.id), '[]'),
|
|
95
|
+
-- (104) report queries
|
|
96
|
+
'report_queries', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.report_query x where x.app_id = a.id), '[]'),
|
|
97
|
+
-- (103) shortcuts
|
|
98
|
+
'shortcuts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.shortcut x where x.app_id = a.id), '[]'),
|
|
99
|
+
-- (102) application computations
|
|
100
|
+
'app_computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_computation x where x.app_id = a.id), '[]'),
|
|
101
|
+
'dynamic_translations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.language, x.source collate "C") from meta.dynamic_translation x where x.app_id = a.id), '[]'),
|
|
102
|
+
-- (068) static application files (JavaScript, CSS, images, fonts) as base64
|
|
103
|
+
'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)
|
|
104
|
+
from meta.static_file x where x.app_id = a.id), '[]'),
|
|
105
|
+
-- nav entries and regions keep their ids, so parents and references can be remapped on import
|
|
106
|
+
'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), '[]'),
|
|
107
|
+
'pages', coalesce((
|
|
108
|
+
select jsonb_agg(to_jsonb(p) - 'id' - 'app_id' || jsonb_build_object(
|
|
109
|
+
'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), '[]'),
|
|
110
|
+
'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), '[]'),
|
|
111
|
+
'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), '[]'),
|
|
112
|
+
'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), '[]'),
|
|
113
|
+
'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), '[]'),
|
|
114
|
+
'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), '[]'),
|
|
115
|
+
'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), '[]'),
|
|
116
|
+
'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), '[]')
|
|
117
|
+
) order by p.page_no)
|
|
118
|
+
from meta.page p where p.app_id = a.id), '[]'))
|
|
119
|
+
from meta.app a
|
|
120
|
+
where a.alias = p_alias
|
|
121
|
+
$$;
|
|
122
|
+
|
|
123
|
+
-- ---------------------------------------------------------------- import
|
|
124
|
+
-- Same as 081, plus app_settings and dynamic_translations.
|
|
125
|
+
|
|
126
|
+
create or replace function meta.import_app(p_doc jsonb, p_alias text default null) returns int
|
|
127
|
+
language plpgsql set search_path = meta, pg_catalog as $$
|
|
128
|
+
declare
|
|
129
|
+
v_app_id int;
|
|
130
|
+
v_page_id int;
|
|
131
|
+
v_page jsonb;
|
|
132
|
+
v_e jsonb;
|
|
133
|
+
v_rmap jsonb;
|
|
134
|
+
v_nmap jsonb := '{}';
|
|
135
|
+
v_lmap jsonb := '{}';
|
|
136
|
+
v_new_id int;
|
|
137
|
+
begin
|
|
138
|
+
if p_doc->>'format' is distinct from 'pgkiln/2' then
|
|
139
|
+
raise exception 'unsupported export format %', coalesce(p_doc->>'format', '(none)');
|
|
140
|
+
end if;
|
|
141
|
+
|
|
142
|
+
insert into meta.app
|
|
143
|
+
select (jsonb_populate_record(null::meta.app, p_doc->'app' || jsonb_build_object(
|
|
144
|
+
'id', nextval('meta.app_id_seq'),
|
|
145
|
+
'alias', coalesce(p_alias, p_doc->'app'->>'alias'),
|
|
146
|
+
'created_at', now(), 'updated_at', now()))).*
|
|
147
|
+
returning id into v_app_id;
|
|
148
|
+
|
|
149
|
+
insert into meta.authz_scheme
|
|
150
|
+
select (jsonb_populate_record(null::meta.authz_scheme, e || jsonb_build_object('id', nextval('meta.authz_scheme_id_seq'), 'app_id', v_app_id))).*
|
|
151
|
+
from jsonb_array_elements(coalesce(p_doc->'authz_schemes', '[]')) e;
|
|
152
|
+
insert into meta.app_item
|
|
153
|
+
select (jsonb_populate_record(null::meta.app_item, e || jsonb_build_object('id', nextval('meta.app_item_id_seq'), 'app_id', v_app_id))).*
|
|
154
|
+
from jsonb_array_elements(coalesce(p_doc->'app_items', '[]')) e;
|
|
155
|
+
insert into meta.app_process
|
|
156
|
+
select (jsonb_populate_record(null::meta.app_process, e || jsonb_build_object('id', nextval('meta.app_process_id_seq'), 'app_id', v_app_id))).*
|
|
157
|
+
from jsonb_array_elements(coalesce(p_doc->'app_processes', '[]')) e;
|
|
158
|
+
insert into meta.lov
|
|
159
|
+
select (jsonb_populate_record(null::meta.lov, e || jsonb_build_object('id', nextval('meta.lov_id_seq'), 'app_id', v_app_id))).*
|
|
160
|
+
from jsonb_array_elements(coalesce(p_doc->'lovs', '[]')) e;
|
|
161
|
+
insert into meta.app_group_role
|
|
162
|
+
select (jsonb_populate_record(null::meta.app_group_role, e || jsonb_build_object('app_id', v_app_id))).*
|
|
163
|
+
from jsonb_array_elements(coalesce(p_doc->'group_roles', '[]')) e;
|
|
164
|
+
insert into meta.text_message
|
|
165
|
+
select (jsonb_populate_record(null::meta.text_message, e || jsonb_build_object('app_id', v_app_id))).*
|
|
166
|
+
from jsonb_array_elements(coalesce(p_doc->'text_messages', '[]')) e;
|
|
167
|
+
insert into meta.translation
|
|
168
|
+
select (jsonb_populate_record(null::meta.translation, e || jsonb_build_object('app_id', v_app_id))).*
|
|
169
|
+
from jsonb_array_elements(coalesce(p_doc->'translations', '[]')) e;
|
|
170
|
+
insert into meta.report_layout
|
|
171
|
+
select (jsonb_populate_record(null::meta.report_layout, (e - 'logo') || jsonb_build_object(
|
|
172
|
+
'id', nextval('meta.report_layout_id_seq'), 'app_id', v_app_id,
|
|
173
|
+
'logo', null))).*
|
|
174
|
+
from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e;
|
|
175
|
+
update meta.report_layout l
|
|
176
|
+
set logo = decode(e->>'logo', 'base64')
|
|
177
|
+
from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e
|
|
178
|
+
where l.app_id = v_app_id and l.name = e->>'name' and e->>'logo' is not null;
|
|
179
|
+
-- imported automations start switched off: the copy must not run the original's jobs unasked.
|
|
180
|
+
-- (044) a file with actions: the automations' code is ignored; an older file: code becomes the single action
|
|
181
|
+
insert into meta.automation
|
|
182
|
+
select (jsonb_populate_record(null::meta.automation, '{"error_handling": "stop"}'::jsonb
|
|
183
|
+
|| jsonb_strip_nulls(case when p_doc ? 'automation_actions' then e - 'code' else e end)
|
|
184
|
+
|| jsonb_build_object('id', nextval('meta.automation_id_seq'), 'app_id', v_app_id, 'enabled', false))).*
|
|
185
|
+
from jsonb_array_elements(coalesce(p_doc->'automations', '[]')) e;
|
|
186
|
+
insert into meta.automation_action
|
|
187
|
+
select (jsonb_populate_record(null::meta.automation_action, '{"seq": 10}'::jsonb || jsonb_strip_nulls(e)
|
|
188
|
+
|| jsonb_build_object('id', nextval('meta.automation_action_id_seq'), 'app_id', v_app_id))).*
|
|
189
|
+
from jsonb_array_elements(coalesce(p_doc->'automation_actions', '[]')) e;
|
|
190
|
+
insert into meta.document_template
|
|
191
|
+
select (jsonb_populate_record(null::meta.document_template, e || jsonb_build_object('id', nextval('meta.document_template_id_seq'), 'app_id', v_app_id))).*
|
|
192
|
+
from jsonb_array_elements(coalesce(p_doc->'document_templates', '[]')) e;
|
|
193
|
+
insert into meta.task_definition
|
|
194
|
+
select (jsonb_populate_record(null::meta.task_definition, e || jsonb_build_object('id', nextval('meta.task_definition_id_seq'), 'app_id', v_app_id))).*
|
|
195
|
+
from jsonb_array_elements(coalesce(p_doc->'task_definitions', '[]')) e;
|
|
196
|
+
insert into meta.workflow_definition
|
|
197
|
+
select (jsonb_populate_record(null::meta.workflow_definition, e || jsonb_build_object('id', nextval('meta.workflow_definition_id_seq'), 'app_id', v_app_id))).*
|
|
198
|
+
from jsonb_array_elements(coalesce(p_doc->'workflow_definitions', '[]')) e;
|
|
199
|
+
insert into meta.rest_module
|
|
200
|
+
select (jsonb_populate_record(null::meta.rest_module, e || jsonb_build_object('id', nextval('meta.rest_module_id_seq'), 'app_id', v_app_id))).*
|
|
201
|
+
from jsonb_array_elements(coalesce(p_doc->'rest_modules', '[]')) e;
|
|
202
|
+
-- (028) template components: regions and report columns refer to them by static id
|
|
203
|
+
insert into meta.template_component
|
|
204
|
+
select (jsonb_populate_record(null::meta.template_component, e || jsonb_build_object('id', nextval('meta.template_component_id_seq'), 'app_id', v_app_id))).*
|
|
205
|
+
from jsonb_array_elements(coalesce(p_doc->'template_components', '[]')) e;
|
|
206
|
+
-- (029) build options (components name them by name)
|
|
207
|
+
insert into meta.build_option
|
|
208
|
+
select (jsonb_populate_record(null::meta.build_option, e || jsonb_build_object('id', nextval('meta.build_option_id_seq'), 'app_id', v_app_id))).*
|
|
209
|
+
from jsonb_array_elements(coalesce(p_doc->'build_options', '[]')) e;
|
|
210
|
+
-- (030) web credentials arrive without a secret, whatever the document holds
|
|
211
|
+
-- (050) nor a password or refresh token
|
|
212
|
+
insert into meta.web_credential
|
|
213
|
+
select (jsonb_populate_record(null::meta.web_credential, '{"type": "basic", "valid_for": [], "grant_type": "client_credentials"}'::jsonb
|
|
214
|
+
|| jsonb_strip_nulls(e - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at')
|
|
215
|
+
|| jsonb_build_object('id', nextval('meta.web_credential_id_seq'), 'app_id', v_app_id))).*
|
|
216
|
+
from jsonb_array_elements(coalesce(p_doc->'web_credentials', '[]')) e;
|
|
217
|
+
-- (050) write-back operations and synchronisation; an imported synchronisation starts switched off
|
|
218
|
+
insert into meta.rest_source
|
|
219
|
+
select (jsonb_populate_record(null::meta.rest_source, '{"method": "GET", "headers": {}, "params": [], "columns": [], "cache_seconds": 0, "timeout_s": 10, "max_rows": 1000,
|
|
220
|
+
"operations": {}, "key_columns": [], "sync_mode": "merge", "sync_delete": false, "sync_time_zone": "UTC"}'::jsonb
|
|
221
|
+
|| jsonb_strip_nulls(e - 'sync_next_at' - 'sync_last_at' - 'sync_last_status')
|
|
222
|
+
|| jsonb_build_object('id', nextval('meta.rest_source_id_seq'), 'app_id', v_app_id, 'sync_enabled', false))).*
|
|
223
|
+
from jsonb_array_elements(coalesce(p_doc->'rest_sources', '[]')) e;
|
|
224
|
+
|
|
225
|
+
-- (041) data load definitions (the data_load process names them)
|
|
226
|
+
insert into meta.data_load_def
|
|
227
|
+
select (jsonb_populate_record(null::meta.data_load_def, '{"format": "auto", "headers": true, "mode": "append", "skip_errors": false, "columns": []}'::jsonb
|
|
228
|
+
|| jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.data_load_def_id_seq'), 'app_id', v_app_id))).*
|
|
229
|
+
from jsonb_array_elements(coalesce(p_doc->'data_load_definitions', '[]')) e;
|
|
230
|
+
|
|
231
|
+
-- (042) lists and their entries, supporting scripts. Entries are inserted
|
|
232
|
+
-- without a parent first and the parents set afterwards, so the order of
|
|
233
|
+
-- the entries in the document doesn't matter (a moved entry may have a
|
|
234
|
+
-- higher id than its children).
|
|
235
|
+
insert into meta.list
|
|
236
|
+
select (jsonb_populate_record(null::meta.list, '{"type": "static"}'::jsonb || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.list_id_seq'), 'app_id', v_app_id))).*
|
|
237
|
+
from jsonb_array_elements(coalesce(p_doc->'lists', '[]')) e;
|
|
238
|
+
for v_e in select * from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) loop
|
|
239
|
+
insert into meta.list_entry
|
|
240
|
+
select (jsonb_populate_record(null::meta.list_entry, '{"target_items": {}}'::jsonb || jsonb_strip_nulls(v_e) || jsonb_build_object(
|
|
241
|
+
'id', nextval('meta.list_entry_id_seq'), 'app_id', v_app_id, 'parent_id', null))).*
|
|
242
|
+
returning id into v_new_id;
|
|
243
|
+
if v_e->>'id' is not null then
|
|
244
|
+
v_lmap := v_lmap || jsonb_build_object(v_e->>'id', v_new_id);
|
|
245
|
+
end if;
|
|
246
|
+
end loop;
|
|
247
|
+
update meta.list_entry x
|
|
248
|
+
set parent_id = (v_lmap->>(e->>'parent_id'))::int
|
|
249
|
+
from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) e
|
|
250
|
+
where e->>'parent_id' is not null and v_lmap ? (e->>'parent_id') and v_lmap ? (e->>'id')
|
|
251
|
+
and x.id = (v_lmap->>(e->>'id'))::int;
|
|
252
|
+
insert into meta.supporting_script
|
|
253
|
+
select (jsonb_populate_record(null::meta.supporting_script, '{"kind": "install", "seq": 10, "script": ""}'::jsonb || jsonb_strip_nulls(e)
|
|
254
|
+
|| jsonb_build_object('id', nextval('meta.supporting_script_id_seq'), 'app_id', v_app_id))).*
|
|
255
|
+
from jsonb_array_elements(coalesce(p_doc->'supporting_scripts', '[]')) e;
|
|
256
|
+
|
|
257
|
+
-- (069) plug-ins
|
|
258
|
+
insert into meta.plugin
|
|
259
|
+
select (jsonb_populate_record(null::meta.plugin, e || jsonb_build_object('id', nextval('meta.plugin_id_seq'), 'app_id', v_app_id))).*
|
|
260
|
+
from jsonb_array_elements(coalesce(p_doc->'plugins', '[]')) e;
|
|
261
|
+
|
|
262
|
+
-- (080) search configurations (search regions name them)
|
|
263
|
+
insert into meta.search_config
|
|
264
|
+
select (jsonb_populate_record(null::meta.search_config, '{"seq": 10, "source_type": "table", "searchable_columns": [], "max_results": 10}'::jsonb
|
|
265
|
+
|| jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.search_config_id_seq'), 'app_id', v_app_id))).*
|
|
266
|
+
from jsonb_array_elements(coalesce(p_doc->'search_configs', '[]')) e;
|
|
267
|
+
|
|
268
|
+
-- (081) e-mail templates (meta.send_mail_template and the send_email process name them)
|
|
269
|
+
insert into meta.email_template
|
|
270
|
+
select (jsonb_populate_record(null::meta.email_template, e || jsonb_build_object('id', nextval('meta.email_template_id_seq'), 'app_id', v_app_id))).*
|
|
271
|
+
from jsonb_array_elements(coalesce(p_doc->'email_templates', '[]')) e;
|
|
272
|
+
|
|
273
|
+
-- (105) component groups
|
|
274
|
+
insert into meta.component_group
|
|
275
|
+
select (jsonb_populate_record(null::meta.component_group, '{"members": []}'::jsonb || jsonb_strip_nulls(e)
|
|
276
|
+
|| jsonb_build_object('id', nextval('meta.component_group_id_seq'), 'app_id', v_app_id))).*
|
|
277
|
+
from jsonb_array_elements(coalesce(p_doc->'component_groups', '[]')) e;
|
|
278
|
+
|
|
279
|
+
-- (104) report queries
|
|
280
|
+
insert into meta.report_query
|
|
281
|
+
select (jsonb_populate_record(null::meta.report_query, '{"format": "pdf"}'::jsonb || jsonb_strip_nulls(e)
|
|
282
|
+
|| jsonb_build_object('id', nextval('meta.report_query_id_seq'), 'app_id', v_app_id))).*
|
|
283
|
+
from jsonb_array_elements(coalesce(p_doc->'report_queries', '[]')) e;
|
|
284
|
+
|
|
285
|
+
-- (103) shortcuts
|
|
286
|
+
insert into meta.shortcut
|
|
287
|
+
select (jsonb_populate_record(null::meta.shortcut, '{"type": "text"}'::jsonb || jsonb_strip_nulls(e)
|
|
288
|
+
|| jsonb_build_object('id', nextval('meta.shortcut_id_seq'), 'app_id', v_app_id))).*
|
|
289
|
+
from jsonb_array_elements(coalesce(p_doc->'shortcuts', '[]')) e;
|
|
290
|
+
|
|
291
|
+
-- (102) application computations
|
|
292
|
+
insert into meta.app_computation
|
|
293
|
+
select (jsonb_populate_record(null::meta.app_computation, '{"seq": 10, "point": "before_header", "type": "static"}'::jsonb || jsonb_strip_nulls(e)
|
|
294
|
+
|| jsonb_build_object('id', nextval('meta.app_computation_id_seq'), 'app_id', v_app_id))).*
|
|
295
|
+
from jsonb_array_elements(coalesce(p_doc->'app_computations', '[]')) e;
|
|
296
|
+
|
|
297
|
+
-- (089) page groups
|
|
298
|
+
insert into meta.page_group
|
|
299
|
+
select (jsonb_populate_record(null::meta.page_group, e || jsonb_build_object('id', nextval('meta.page_group_id_seq'), 'app_id', v_app_id))).*
|
|
300
|
+
from jsonb_array_elements(coalesce(p_doc->'page_groups', '[]')) e;
|
|
301
|
+
|
|
302
|
+
-- (086) application settings and dynamic translations
|
|
303
|
+
insert into meta.app_setting
|
|
304
|
+
select (jsonb_populate_record(null::meta.app_setting, e || jsonb_build_object('id', nextval('meta.app_setting_id_seq'), 'app_id', v_app_id))).*
|
|
305
|
+
from jsonb_array_elements(coalesce(p_doc->'app_settings', '[]')) e;
|
|
306
|
+
insert into meta.dynamic_translation
|
|
307
|
+
select (jsonb_populate_record(null::meta.dynamic_translation, e || jsonb_build_object('id', nextval('meta.dynamic_translation_id_seq'), 'app_id', v_app_id))).*
|
|
308
|
+
from jsonb_array_elements(coalesce(p_doc->'dynamic_translations', '[]')) e;
|
|
309
|
+
|
|
310
|
+
-- (068) static application files
|
|
311
|
+
insert into meta.static_file (app_id, name, mime, content, updated_at)
|
|
312
|
+
select v_app_id, e->>'name', e->>'mime', decode(coalesce(e->>'content', ''), 'base64'), now()
|
|
313
|
+
from jsonb_array_elements(coalesce(p_doc->'static_files', '[]')) e;
|
|
314
|
+
|
|
315
|
+
for v_e in select * from jsonb_array_elements(coalesce(p_doc->'nav', '[]')) loop
|
|
316
|
+
insert into meta.nav_entry
|
|
317
|
+
select (jsonb_populate_record(null::meta.nav_entry, v_e || jsonb_build_object(
|
|
318
|
+
'id', nextval('meta.nav_entry_id_seq'), 'app_id', v_app_id,
|
|
319
|
+
'parent_id', v_nmap->>(v_e->>'parent_id')))).*
|
|
320
|
+
returning id into v_new_id;
|
|
321
|
+
v_nmap := v_nmap || jsonb_build_object(v_e->>'id', v_new_id);
|
|
322
|
+
end loop;
|
|
323
|
+
|
|
324
|
+
for v_page in select * from jsonb_array_elements(coalesce(p_doc->'pages', '[]')) loop
|
|
325
|
+
insert into meta.page
|
|
326
|
+
select (jsonb_populate_record(null::meta.page, v_page || jsonb_build_object('id', nextval('meta.page_id_seq'), 'app_id', v_app_id))).*
|
|
327
|
+
returning id into v_page_id;
|
|
328
|
+
|
|
329
|
+
v_rmap := '{}';
|
|
330
|
+
for v_e in select * from jsonb_array_elements(coalesce(v_page->'regions', '[]')) loop
|
|
331
|
+
insert into meta.region
|
|
332
|
+
select (jsonb_populate_record(null::meta.region, v_e || jsonb_build_object('id', nextval('meta.region_id_seq'), 'page_id', v_page_id))).*
|
|
333
|
+
returning id into v_new_id;
|
|
334
|
+
v_rmap := v_rmap || jsonb_build_object(v_e->>'id', v_new_id);
|
|
335
|
+
end loop;
|
|
336
|
+
-- facet and map regions point at their report region by id
|
|
337
|
+
update meta.region r
|
|
338
|
+
set config = jsonb_set(r.config, '{report}', to_jsonb((v_rmap->>(r.config->>'report'))::int))
|
|
339
|
+
where r.page_id = v_page_id and r.type in ('facets', 'smart_filters', 'map') and v_rmap ? (r.config->>'report');
|
|
340
|
+
|
|
341
|
+
insert into meta.item
|
|
342
|
+
select (jsonb_populate_record(null::meta.item, e || jsonb_build_object('id', nextval('meta.item_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
|
|
343
|
+
from jsonb_array_elements(coalesce(v_page->'items', '[]')) e;
|
|
344
|
+
insert into meta.button
|
|
345
|
+
select (jsonb_populate_record(null::meta.button, e || jsonb_build_object('id', nextval('meta.button_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
|
|
346
|
+
from jsonb_array_elements(coalesce(v_page->'buttons', '[]')) e;
|
|
347
|
+
insert into meta.dynamic_action
|
|
348
|
+
select (jsonb_populate_record(null::meta.dynamic_action, e || jsonb_build_object('id', nextval('meta.dynamic_action_id_seq'), 'page_id', v_page_id, 'affected_region_id', v_rmap->>(e->>'affected_region_id')))).*
|
|
349
|
+
from jsonb_array_elements(coalesce(v_page->'dynamic_actions', '[]')) e;
|
|
350
|
+
insert into meta.validation
|
|
351
|
+
select (jsonb_populate_record(null::meta.validation, e || jsonb_build_object('id', nextval('meta.validation_id_seq'), 'page_id', v_page_id))).*
|
|
352
|
+
from jsonb_array_elements(coalesce(v_page->'validations', '[]')) e;
|
|
353
|
+
insert into meta.process
|
|
354
|
+
select (jsonb_populate_record(null::meta.process, e || jsonb_build_object('id', nextval('meta.process_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
|
|
355
|
+
from jsonb_array_elements(coalesce(v_page->'processes', '[]')) e;
|
|
356
|
+
insert into meta.computation
|
|
357
|
+
select (jsonb_populate_record(null::meta.computation, e || jsonb_build_object('id', nextval('meta.computation_id_seq'), 'page_id', v_page_id))).*
|
|
358
|
+
from jsonb_array_elements(coalesce(v_page->'computations', '[]')) e;
|
|
359
|
+
insert into meta.branch
|
|
360
|
+
select (jsonb_populate_record(null::meta.branch, e || jsonb_build_object('id', nextval('meta.branch_id_seq'), 'page_id', v_page_id))).*
|
|
361
|
+
from jsonb_array_elements(coalesce(v_page->'branches', '[]')) e;
|
|
362
|
+
end loop;
|
|
363
|
+
|
|
364
|
+
return v_app_id;
|
|
365
|
+
end
|
|
366
|
+
$$;
|
|
367
|
+
|
|
@@ -7,7 +7,7 @@ open item and open an issue or pull request; see [CONTRIBUTING.md](../CONTRIBUTI
|
|
|
7
7
|
Legend: ✅ available · 🟡 partial (see notes) · ❌ not yet · ➖ not planned (a deliberate choice,
|
|
8
8
|
or better served by the PostgreSQL ecosystem; see the notes and [extensions](guide/15-extensions.md))
|
|
9
9
|
|
|
10
|
-
Last reviewed: 2026-10-
|
|
10
|
+
Last reviewed: 2026-10-15 (sprint 83: Component Settings and Component Groups; sprint 82: Report Queries; sprint 81: Application Computations and Shortcuts in Shared Components; sprint 80: the master grid's first row selected when the page opens and after a search; grid cells as text with their format mask until edited, with cell navigation by arrow keys, Enter, F2 and Escape; sprint 79: generated dashboards with sums, donuts and lines, leaving out charts that say nothing; applications open in their own light or dark mode until the user chooses, per application; a user menu with Appearance and Sign in when signed out; the developer toolbar's App and Page texts and debug state; the report search's column scope in the magnifier; sprint 78: generated faceted search reports with one column per foreign key, the faceted search's total row count, Show all and Yes/No values, one search field, friendly headings and number formats in generated grids and master-detail pages, friendly names in the Create Application wizard's page list; sprint 77: the Page Designer's layout and tree by display position, button positions, the tree's context menu with Duplicate and Copy to other page, Show Common / Show All, Go to group and the Template Options dialog; display positions, button positions and side-column page templates at runtime; sprint 76: the interactive report's menus and pagination, date items in the date format; sprint 75: the SQL Workshop's Object Browser and tabs; sprint 74: the builder's information architecture: Shared Components grid, Settings tabs, application home search and tasks; sprint 73: the Create Application wizard on one page with page types and features; sprint 72: the runtime chrome compared with APEX 26.2's: the developer toolbar, the Iris look, the navigation bar's Help menu and Install button; sprint 71: generated applications compared with APEX 26.2's: labels, foreign key names in reports, file columns, format masks, the Home page and the dashboard; sprint 70: treemap, sunburst and timeline charts; sprint 69: dragging tasks on a Gantt chart; sprint 68: the Data Reporter's welcome page, Find my workspace; sprint 67: REST source catalogs from OpenAPI documents, a workspace's export and import (two rows added); sprint 66: a blueprint from an existing application; sprint 65: facet charts; sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
|
|
11
11
|
|
|
12
12
|
## At a glance
|
|
13
13
|
|
|
@@ -62,7 +62,7 @@ two, REST source catalogs and the workspace export, with their features.)
|
|
|
62
62
|
|---|---|---|
|
|
63
63
|
| Classic report | ✅ | `report` with `interactive: false` |
|
|
64
64
|
| Interactive report | ✅ | (sprint 76) An Actions menu of nine entries with dialogs, a column heading menu (sort, hide, control break, the column's values to filter by), a search in one column, hidden columns, pagination with the range, first/last and rows per page. Search, column filters, sort, rows per page, control break, aggregates (with subtotals), highlight, saved private and public reports, computed columns, group by, pivot, chart view, row selection into a page item, CSV, Excel (typed cells) and PDF download, print, reset, reflow on phones. a maximum row count (`max_rows`), natural-language control (*Ask in your own words*, 26.1) and row selection that stays across pages. Not possible: flashback queries (PostgreSQL has none; use an audit or temporal table) |
|
|
65
|
-
| Interactive grid | ✅ | Inline edit, add and delete rows, lists of values, required columns, per-row errors, all-or-nothing save, signed row keys, search and paging; **aggregates** over the whole result (developer and user defined), **frozen columns**, column **reorder/resize/hide** (Actions → Columns, or drag and drop), per-user layouts and **saved grid reports** (private and public), **master-detail** (signed row selection, details refreshed in place), a **row actions menu** (edit, duplicate, delete, links) and **copy/paste** of cell ranges (tab-separated, spreadsheet compatible) ([chapter 4](guide/04-pages-and-regions.md)) |
|
|
65
|
+
| Interactive grid | ✅ | Inline edit, add and delete rows, lists of values, required columns, per-row errors, all-or-nothing save, signed row keys, search and paging; **aggregates** over the whole result (developer and user defined), **frozen columns**, column **reorder/resize/hide** (Actions → Columns, or drag and drop), per-user layouts and **saved grid reports** (private and public), **master-detail** (signed row selection, the first row selected on load and after a search, details refreshed in place), **navigation and edit mode** (cells as text with their format mask until clicked, Enter or F2; arrow keys between cells), a **row actions menu** (edit, duplicate, delete, links) and **copy/paste** of cell ranges (tab-separated, spreadsheet compatible) ([chapter 4](guide/04-pages-and-regions.md)) |
|
|
66
66
|
| Interactive grid views: single row view, chart view, group by | ✅ | (sprint 54) **Single row view**: one row of the search as a form with first/previous/next/last and *Add row*, from the Actions or row actions menu, saved by the grid's own process with the same checks; **group by** and **chart** set up in the Actions menu over every row of the search, as for the interactive report; `views` limits them; Save comes back to the same search, page and view ([chapter 4](guide/04-pages-and-regions.md#grid-interactive-grid)). No pivot view on grids (the interactive report has one), no editing in the single row view of columns the grid doesn't show |
|
|
67
67
|
| Form (automatic row processing) | ✅ | Fetch, insert, update, delete, in a page or a modal dialog. Detects rows deleted meanwhile. (sprint 50) **Lost update detection** (APEX: *Prevent Lost Updates* and *Lock Row*): the page carries a checksum of the row's form columns (`meta.row_version`, independent of time zone and date format, as the app's role); *Save* and *Delete* lock the row and refuse when it changed meanwhile; off per form with `{"lost_update": false}` ([chapter 4](guide/04-pages-and-regions.md#form)) |
|
|
68
68
|
| Charts | ✅ | Bar, column, stacked, line, area, combination (columns + lines), scatter, bubble, donut, pie, gauge, funnel, radar, **Gantt** (start/end, progress, milestones, dependencies, a today line, a time axis from hours to years), **pyramid** (area-proportional segments, or two series back to back) and **polar** (polar area); (sprint 63) **range** (floating columns from low to high), **box plot** (from one value per row or five statistics) and **stock** (candlesticks from open, high, low and close); multi-series, tooltips, data table, (sprint 71) a value axis with gridlines on horizontal bars and top-N with an *Other* bar (`"top": 10`), palette checked for colour-vision deficiency; drill-down links on chart marks (checksummed, only to pages the user may open). Drawn on the server as SVG without inline styles. (sprint 69) **Gantt editing**: drag a task or its end, or the arrow keys, runs the region's SQL as the app's role. (sprint 70) **treemap** (squarified, two levels shown), **sunburst** (up to four rings) from `node_id` / `parent_id` columns, and **timeline** (lanes by a `series` column, points and periods, overlaps in rows). **Missing:** other Oracle JET types (e.g. picto, NBox, Sankey) |
|
|
@@ -110,7 +110,7 @@ two, REST source catalogs and the workspace export, with their features.)
|
|
|
110
110
|
| Application items and processes | ✅ | `after_login`, `before_page` |
|
|
111
111
|
| Validations | ✅ | Not null, SQL expression, regex, required items; `RAISE … USING COLUMN` in PL/pgSQL targets a field |
|
|
112
112
|
| Conditions and authorization on components | ✅ | Pages, regions, items, buttons, processes, dynamic actions, navigation entries; re-checked on submit |
|
|
113
|
-
| Computations | ✅ | Static, item, SQL query, SQL expression and PL/pgSQL function body; before header and after submit; with a condition, authorization and build option ([chapter 6](guide/06-processing.md#computations)) |
|
|
113
|
+
| Computations | ✅ | Static, item, SQL query, SQL expression and PL/pgSQL function body; before header and after submit; with a condition, authorization and build option ([chapter 6](guide/06-processing.md#computations)); **application computations** (sprint 81) for every page: after authentication, before header, after submit ([chapter 6](guide/06-processing.md#application-computations)) |
|
|
114
114
|
| Page processes | ✅ | SQL / PL/pgSQL, form DML, grid DML, data loading (`data_load`), **invoke API** (a REST data source or URL, response values into items), **download** (a file from a query; several rows become one zip; on submit or on load), **execution chains** (child processes with their own conditions, nested, optionally run in the background with a status view), **workflow** (start, terminate, retry), **Generate text with AI** (text or structured output into items) |
|
|
115
115
|
| Branches | ✅ | Before header and after processing, to a page (with items) or a URL inside the app, *When button pressed*, server-side conditions (SQL, exists, item null/equals, request); the button's target page stays the fallback ([chapter 6](guide/06-processing.md#branches)); a PL/pgSQL function returning a path in the app, and a page of another application (with signed items) |
|
|
116
116
|
| Dynamic actions | ✅ | Show, hide, enable, disable, set value (SQL), execute SQL, refresh region or item, alert, submit, set focus, add/remove class, show success/error message and clear errors (26.1); the **Dialog Closed** event (per dialog page, with the dialog's message). **Execute JavaScript**: calls a function a static application file registered with `pgkiln.actions.register` (no inline code: the CSP stays strict) ([chapter 7](guide/07-dynamic-actions.md#execute-javascript)); dynamic action plug-ins |
|
|
@@ -191,7 +191,7 @@ two, REST source catalogs and the workspace export, with their features.)
|
|
|
191
191
|
| Data Workshop (load and unload) | ✅ | SQL Workshop → Load Data: CSV/TSV/XLSX/JSON into a new table (inferred types) or an existing one (append, merge, replace) with a per-row error report; `data_load` process for end users; **XML** (a repeating element, attributes and paths; DTDs refused) and saved **data load definitions** (mapping, transformations, format masks, defaults; used by Load Data and the process, exported with the app). **Unload Data**: a table or view (chosen columns, where, order) or a query to CSV (separator, enclosure, heading), JSON, XLSX or XML (element names), streamed with a cursor in a read-only transaction with a statement timeout ([chapter 16](guide/16-files.md)) |
|
|
192
192
|
| Large downloads without buffering (ORDS streams) | ✅ | CSV and Excel downloads stream from a database cursor (1,000 rows at a time, a streaming Excel writer) up to `DOWNLOAD_MAX_ROWS` (default 1,000,000), so memory stays flat. Report PDFs read their rows from a cursor in batches of 500 (`PDF_MAX_ROWS`, default 5,000, at most 100,000); REST collection handlers stream a chunked JSON array from a cursor |
|
|
193
193
|
| REST data sources, web credentials (26.1: OAuth refresh tokens, password flow) | ✅ | Shared Components → REST data sources: JSON endpoints with path, query, header and body parameters, a row selector, typed columns and a response cache feed reports, cards, charts, calendars, maps, trees, template components and shared lists of values as SQL over `rest`. **Write-back**: insert, update, delete and fetch operations (path and JSON body templates after the source's URL) used by forms and interactive grids. **Synchronisation** into a local table (merge on key columns with optional delete, replace, append) on demand, on a cron schedule or from SQL (`meta.request_rest_sync`), as the app's role, with a run log. Web credentials: basic, API-key header, bearer and OAuth2 client credentials, password and refresh-token grants (refresh tokens stored encrypted and rotated), secrets encrypted and write-only. Outgoing calls only to an allow-list of hosts, with SSRF checks ([chapter 19](guide/19-rest-data-sources.md)). **Missing:** XML/SOAP, the OAuth2 authorization code flow (consent in the browser) |
|
|
194
|
-
| Printing, document generator (PDF) | ✅ | **Document templates**: a query (with JSON columns for lines) fills an HTML template with Mustache-style tags, drawn as PDF with a report layout; buttons and links download them. Report PDF with **report layouts** and a print stylesheet on every page. No Word/Excel templates or DOCX/XLSX output |
|
|
194
|
+
| Printing, document generator (PDF) | ✅ | **Document templates**: a query (with JSON columns for lines) fills an HTML template with Mustache-style tags, drawn as PDF with a report layout; buttons and links download them. (sprint 82) **Report queries**: a named query as PDF, Excel, CSV, JSON, XML or HTML. Report PDF with **report layouts** and a print stylesheet on every page. No Word/Excel templates or DOCX/XLSX output |
|
|
195
195
|
| `APEX_DATA_EXPORT` (a query to CSV, XLSX, PDF, HTML or JSON from PL/SQL) | ✅ | (sprint 53) `meta.data_export(query, format, options)` returns CSV, JSON, XML or HTML as `bytea` with the caller's privileges; the download process's `{"export": …}` sends a query's rows as CSV, JSON, XML, HTML, Excel or PDF |
|
|
196
196
|
| SQL Workshop utilities: schema comparison, generate DDL | ✅ | (sprint 56) SQL Workshop → **DDL**: a schema's objects (types, sequences, tables, functions, views, constraints, indexes, triggers, RLS and policies, comments, grants) from the catalog, in an order that runs, unqualified so it also creates them in another schema. **Compare**: two schemas by their DDL (only in one, different, with a diff) and a change script that makes the target like the source (tables column by column; drops commented out for review). Also `pgkiln ddl`, `pgkiln compare` and the MCP tools `generate_ddl`, `compare_schemas` ([chapter 3](guide/03-builder.md#sql-workshop)). Two schemas of the same database only (APEX: also within one workspace) |
|
|
197
197
|
| Data Reporter: self-service reports for business users (26.1) | ✅ | A **Data Reporter** region ([chapter 4](guide/04-pages-and-regions.md#data_reporter-data-reporter)): the developer offers tables and views with chosen columns, labels and format masks; signed-in users pick columns, filter, group with totals (count, sum, average, minimum, maximum), sort and chart, and save reports privately or shared with the app's users. Runs as the app's role (grants and RLS apply); works without JavaScript and fits phones. (sprint 68) A **welcome page** like 26.1's: a greeting (the developer's own, or pgkiln's), the sources as cards that start a report, the user's reports latest first, shared ones, and a search. One source per report (offer a view to combine tables); no downloads from a reporter yet |
|
|
@@ -312,22 +312,22 @@ Sources: [APEX 26.1 new features](https://docs.oracle.com/en/database/oracle/ape
|
|
|
312
312
|
| APEX | pgkiln |
|
|
313
313
|
|---|---|
|
|
314
314
|
| Application Definition, Application Items, Application Processes, Application Settings, Build Options | The same names (Application Logic) |
|
|
315
|
-
| Application Computations |
|
|
315
|
+
| Application Computations | The same name (Application Logic, sprint 81): after authentication, before header and after submit, before the page's own computations |
|
|
316
316
|
| Authentication Schemes | Settings → Security: one authentication per application (its type and fields); several schemes to switch between are ➖ not planned |
|
|
317
317
|
| Authorization Schemes, Access Control | The same names (Security) |
|
|
318
318
|
| Security Attributes, Session State Protection | Settings → Security and Sign-in; session state protection is per page (Page Designer → *Protection*) |
|
|
319
319
|
| Lists of Values, Lists, Email Templates, Supporting Objects, Subscriptions | The same names (Other Components) |
|
|
320
320
|
| Navigation Menu, Navigation Bar List, Breadcrumbs, Search Configurations | The same names (Navigation and Search); breadcrumbs follow each page's breadcrumb parent |
|
|
321
|
-
| Shortcuts |
|
|
321
|
+
| Shortcuts | The same name (Other Components, sprint 81): text, HTML and PL/pgSQL function body; `"NAME"` in static content, help texts and confirm messages |
|
|
322
322
|
| Themes, Templates, User Interface Attributes, Theme Roller | Themes and Theme Roller, Template Components, Settings → User Interface; page and region templates are a fixed list (➖ no template editor) |
|
|
323
|
-
| Plug-ins, Component Settings |
|
|
324
|
-
| Component Groups |
|
|
323
|
+
| Plug-ins, Component Settings | The same names (User Interface; component settings sprint 83): rows per page of reports, grids and popup lists of values, list of values rows, star rating stars |
|
|
324
|
+
| Component Groups | The same name (Other Components, sprint 83): a library application's lists of values, authorization schemes, build options, template components and lists, subscribed to at once; Page Groups for pages |
|
|
325
325
|
| Progressive Web App | Settings → Progressive Web App |
|
|
326
326
|
| Static Application Files, Static Workspace Files | Static Application Files; workspace files ➖ not planned |
|
|
327
|
-
| Report Layouts, Report Queries |
|
|
327
|
+
| Report Layouts, Report Queries | The same names (Files and Reports, sprint 82): a report query is one query printed as PDF with a report layout, Excel, CSV, JSON, XML or HTML (`?doc=NAME`, document buttons); APEX's several queries for one layout: a document template's JSON columns |
|
|
328
328
|
| REST Data Sources, Web Credentials, REST Enabled SQL, Data Load Definitions | The same names (Data Sources); REST Enabled SQL ➖ not planned (one database) |
|
|
329
329
|
| Workflows, Task Definitions, Automations | The same names |
|
|
330
330
|
| Text Messages, Translate, Dynamic Translations, Globalization Attributes | Translations and Text Messages, Dynamic Translations, Settings → Globalization |
|
|
331
|
-
| Map Backgrounds |
|
|
331
|
+
| Map Backgrounds | ➖ Not planned per application: the installation's map tile server (`MAP_TILE_URL`) is every map's background |
|
|
332
332
|
| AI Attributes, AI Services, AI Agents | AI Services; AI agents ➖ not planned |
|
|
333
333
|
|
|
@@ -28,6 +28,10 @@ meta.app ─┬─ meta.page ─┬─ meta.region (reports, forms, grids
|
|
|
28
28
|
├─ meta.lov (shared lists of values)
|
|
29
29
|
├─ meta.app_item (application items)
|
|
30
30
|
├─ meta.app_process (application processes)
|
|
31
|
+
├─ meta.app_computation (application computations)
|
|
32
|
+
├─ meta.shortcut (shortcuts: "NAME" texts)
|
|
33
|
+
├─ meta.report_query (report queries: ?doc=NAME files)
|
|
34
|
+
├─ meta.component_group (component groups: subscribed at once)
|
|
31
35
|
├─ meta.build_option (build options: include / exclude switches)
|
|
32
36
|
└─ meta.app_user (users of this application)
|
|
33
37
|
|
|
@@ -188,6 +192,26 @@ trailing dot) is replaced by the item's value, **HTML-escaped**:
|
|
|
188
192
|
<p>Welcome back, <b>&AI_ENAME.</b>!</p>
|
|
189
193
|
```
|
|
190
194
|
|
|
195
|
+
### Shortcuts: `"NAME"`
|
|
196
|
+
|
|
197
|
+
**Shared Components → Shortcuts** (APEX: Shortcuts, migration 103) are named texts an application
|
|
198
|
+
reuses. `"NAME"`, the shortcut's upper-case name in double quotes, is replaced by it in a static
|
|
199
|
+
region's content, an item's help text and a button's or menu entry's confirm message, before the
|
|
200
|
+
`&NAME.` substitutions (so a shortcut may contain them). A quoted word that is not the name of one
|
|
201
|
+
of the application's shortcuts stays as it is.
|
|
202
|
+
|
|
203
|
+
| `type` | Replaced by |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `text` | The value, HTML-escaped in a static region |
|
|
206
|
+
| `html` | The value as HTML (in a help text or a confirm message, which are text, its tags show as text) |
|
|
207
|
+
| `function_body` | What the PL/pgSQL function body returns, run as the application's role once per request; an error shows on the page and leaves it empty |
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<p>"SUPPORT_CONTACT"</p> <!-- a shortcut SUPPORT_CONTACT, type html -->
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
A *Delete* button's confirm message `"DELETE_CONFIRM_MSG"` then reads the same on every page.
|
|
214
|
+
|
|
191
215
|
## The application's database role
|
|
192
216
|
|
|
193
217
|
Each application has a `db_role`, the equivalent of APEX's *parsing schema*. Every request runs
|
package/docs/guide/03-builder.md
CHANGED
|
@@ -867,6 +867,11 @@ is the earlier page: every component in one rail, with **Access control** next t
|
|
|
867
867
|
| **Lists of values** | Reusable queries for select lists, referenced as `LOV:NAME` |
|
|
868
868
|
| **Application items** | Session variables not on any page, set only by server-side code |
|
|
869
869
|
| **Application processes** | Code that runs *after login* or *before every page* |
|
|
870
|
+
| **Component groups** | Sets of a library application's components that other applications subscribe to at once ([below](#component-groups)) |
|
|
871
|
+
| **Component settings** | Defaults of built-in component types: rows per page, list of values rows, stars ([below](#component-settings)) |
|
|
872
|
+
| **Report queries** | Named queries printed as PDF, Excel, CSV, JSON, XML or HTML with `?doc=NAME` ([chapter 16](16-files.md#report-queries)) |
|
|
873
|
+
| **Shortcuts** | Named texts: `"NAME"` in static content, help texts and confirm messages ([chapter 2](02-concepts.md#shortcuts-name)) |
|
|
874
|
+
| **Application computations** | Computations of (application) items for every page: at sign-in, before every page, after every submit ([chapter 6](06-processing.md#application-computations)) |
|
|
870
875
|
| **REST modules** | REST endpoints (method, path, SQL) served by pgkiln, with an OpenAPI description ([chapter 13](13-rest-api.md#rest-modules-in-the-builder)) |
|
|
871
876
|
| **Workflows** | Multi-step processes of tasks, SQL, web service calls (invoke API), decisions, waits and parallel branches, with versions and a diagram ([chapter 6](06-processing.md#workflows)) |
|
|
872
877
|
| **Task definitions** | Approvals and action tasks: subject, owners, administrators, due date, the SQL that runs on completion ([chapter 6](06-processing.md#approvals-and-the-task-list)) |
|
|
@@ -907,6 +912,33 @@ log (`subscription`). A subscribed list of values that uses a REST data source n
|
|
|
907
912
|
with the same name in the subscribing application. Subscriptions are links between the
|
|
908
913
|
applications of this installation and are not exported (the type is).
|
|
909
914
|
|
|
915
|
+
#### Component groups
|
|
916
|
+
|
|
917
|
+
**Shared Components → Component Groups** (APEX: Component Groups, migration 105) names a set of a
|
|
918
|
+
theme or library application's components, as `kind:name`: `lov:JOBS, authz_scheme:MANAGER,
|
|
919
|
+
build_option:FEATURE_EXPORT, template_component:status_badge, list:SHORTCUTS`. Saving checks that
|
|
920
|
+
the application has each one (and keeps its name as the application spells it). Another
|
|
921
|
+
application of the same workspace subscribes to the whole group on its Subscriptions page
|
|
922
|
+
(*Subscribe to a component group*): each component is copied and subscribed as above, and a member
|
|
923
|
+
the library no longer has is skipped and named in the message. Groups are exported with the
|
|
924
|
+
application (`shared/component-groups/`).
|
|
925
|
+
|
|
926
|
+
### Component settings
|
|
927
|
+
|
|
928
|
+
**Shared Components → Component Settings** (APEX: Component Settings, migration 105) are the
|
|
929
|
+
application's defaults for its built-in component types, used where a component sets nothing itself
|
|
930
|
+
(`meta.app.component_settings`, exported with the application):
|
|
931
|
+
|
|
932
|
+
| Setting | pgkiln's default | Bounds |
|
|
933
|
+
|---|---|---|
|
|
934
|
+
| Reports: rows per page (`report_rows`) | 15 | 1–1,000 |
|
|
935
|
+
| Interactive grids: rows per page (`grid_rows`) | 25 | 1–200 |
|
|
936
|
+
| Popup lists of values: rows per page (`popup_lov_rows`) | 25 | 1–100 |
|
|
937
|
+
| Lists of values: maximum rows (`lov_max_rows`) | 5,000 | 1–50,000 |
|
|
938
|
+
| Star ratings: stars (`rating_max`) | 5 | 3–10 |
|
|
939
|
+
|
|
940
|
+
A value out of its bounds (an edited export, say) is ignored: the default applies.
|
|
941
|
+
|
|
910
942
|
### Supporting objects
|
|
911
943
|
|
|
912
944
|
APEX's *Supporting Objects*: SQL scripts that create, upgrade or remove what the application needs
|
|
@@ -1325,7 +1357,7 @@ imports as empty. Import refuses other formats.
|
|
|
1325
1357
|
| Section | Contents |
|
|
1326
1358
|
|---|---|
|
|
1327
1359
|
| `app` | the application's settings (name, alias, home page, authentication, theme, languages, …) |
|
|
1328
|
-
| `authz_schemes`, `app_items`, `app_processes`, `lovs` | shared components |
|
|
1360
|
+
| `authz_schemes`, `app_items`, `app_processes`, `app_computations`, `shortcuts`, `report_queries`, `component_groups`, `lovs` | shared components |
|
|
1329
1361
|
| `group_roles` | identity-provider group → role mappings |
|
|
1330
1362
|
| `text_messages`, `translations` | globalization |
|
|
1331
1363
|
| `report_layouts` | report layouts; the logo as base64 |
|
|
@@ -456,7 +456,9 @@ page). Regions with `"master": {"item": …}` are its details: they use the item
|
|
|
456
456
|
select empno, ename, job, sal from hr.emp where deptno = :P27_DEPTNO::int
|
|
457
457
|
```
|
|
458
458
|
|
|
459
|
-
and show *"Select a row above"* until a row is selected.
|
|
459
|
+
and show *"Select a row above"* until a row is selected. As in APEX, the page opens with the
|
|
460
|
+
master's first row selected, and a search or filter that leaves the selected row out selects the
|
|
461
|
+
first row it shows (a region placed above its master still waits for a selection). With JavaScript the details are
|
|
460
462
|
refreshed in place (`GET …/region/:id`, no page reload); without, the link reloads the page.
|
|
461
463
|
The selection is a signed link (bound to the application, page, user, region and value), so a
|
|
462
464
|
user can only select a row the server showed them; the item is not taken from the URL or a
|
|
@@ -480,6 +482,13 @@ on its new rows (and never lets users edit it); saving new rows without a select
|
|
|
480
482
|
The developer's `hidden` columns can't be grouped, charted or shown in the single row view.
|
|
481
483
|
After **Save** the grid comes back as it was: its search, page, sort and view.
|
|
482
484
|
|
|
485
|
+
**Navigation and edit mode** (APEX). With JavaScript an editable cell reads as text, with its
|
|
486
|
+
column's format mask (*2,850.00*) or the list's display value, until it is edited: click it, or
|
|
487
|
+
press Enter or F2 on it, or start typing (which replaces the value). On a cell the arrow keys move
|
|
488
|
+
to the next cell; while editing, Escape goes back to the cell and Enter moves to the cell below.
|
|
489
|
+
The control keeps the raw value; Tab moves through the controls as before. On a phone, where the
|
|
490
|
+
rows are cards, the fields show as fields.
|
|
491
|
+
|
|
483
492
|
**Copy and paste.** Click a cell and Shift+click another to select a range; Ctrl+C copies it
|
|
484
493
|
as tab-separated text (which spreadsheets paste as cells). Ctrl+V pastes such text (from a
|
|
485
494
|
spreadsheet or another grid) from the focused cell on, adding rows when the grid allows inserts;
|