@saltcorn/data 0.9.4-beta.2 → 0.9.4-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-plugin/actions.d.ts +156 -67
- package/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +147 -27
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/fieldviews.d.ts.map +1 -1
- package/dist/base-plugin/fieldviews.js +27 -10
- package/dist/base-plugin/fieldviews.js.map +1 -1
- package/dist/base-plugin/fileviews.d.ts +13 -0
- package/dist/base-plugin/fileviews.js +42 -2
- package/dist/base-plugin/fileviews.js.map +1 -1
- package/dist/base-plugin/index.d.ts +5 -2
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/types.d.ts.map +1 -1
- package/dist/base-plugin/types.js +25 -7
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts +2 -0
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +194 -58
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.js +88 -8
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts +3 -2
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +276 -41
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/show.js +130 -64
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts +4 -3
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js +160 -47
- package/dist/base-plugin/viewtemplates/viewable_fields.js.map +1 -1
- package/dist/db/fixtures.d.ts.map +1 -1
- package/dist/db/fixtures.js +196 -30
- package/dist/db/fixtures.js.map +1 -1
- package/dist/db/state.d.ts +5 -0
- package/dist/db/state.d.ts.map +1 -1
- package/dist/db/state.js +20 -0
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.d.ts +1 -0
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +6 -3
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/202402071125.d.ts +2 -0
- package/dist/migrations/202402071125.d.ts.map +1 -0
- package/dist/migrations/202402071125.js +4 -0
- package/dist/migrations/202402071125.js.map +1 -0
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +33 -0
- package/dist/models/config.js.map +1 -1
- package/dist/models/email.d.ts.map +1 -1
- package/dist/models/email.js +6 -2
- package/dist/models/email.js.map +1 -1
- package/dist/models/expression.d.ts +2 -0
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +9 -0
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +4 -4
- package/dist/models/field.js.map +1 -1
- package/dist/models/file.d.ts +2 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/file.js +9 -0
- package/dist/models/file.js.map +1 -1
- package/dist/models/internal/query.d.ts +6 -0
- package/dist/models/internal/query.d.ts.map +1 -0
- package/dist/models/internal/query.js +77 -0
- package/dist/models/internal/query.js.map +1 -0
- package/dist/models/page.d.ts.map +1 -1
- package/dist/models/page.js +4 -0
- package/dist/models/page.js.map +1 -1
- package/dist/models/page_group.d.ts +1 -0
- package/dist/models/page_group.d.ts.map +1 -1
- package/dist/models/page_group.js +1 -0
- package/dist/models/page_group.js.map +1 -1
- package/dist/models/table.d.ts +18 -4
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +78 -50
- package/dist/models/table.js.map +1 -1
- package/dist/models/trigger.d.ts +3 -3
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +69 -7
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +5 -4
- package/dist/models/user.js.map +1 -1
- package/dist/models/view.d.ts +1 -0
- package/dist/models/view.d.ts.map +1 -1
- package/dist/models/view.js +6 -0
- package/dist/models/view.js.map +1 -1
- package/dist/plugin-helper.d.ts +8 -20
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +108 -76
- package/dist/plugin-helper.js.map +1 -1
- package/dist/tests/actions.test.js +143 -2
- package/dist/tests/actions.test.js.map +1 -1
- package/dist/tests/auth.test.js +101 -1
- package/dist/tests/auth.test.js.map +1 -1
- package/dist/tests/auxtest.test.js +4 -0
- package/dist/tests/auxtest.test.js.map +1 -1
- package/dist/tests/calc.test.js +11 -1
- package/dist/tests/calc.test.js.map +1 -1
- package/dist/tests/edit.test.js +394 -0
- package/dist/tests/edit.test.js.map +1 -1
- package/dist/tests/filter.test.d.ts +2 -0
- package/dist/tests/filter.test.d.ts.map +1 -0
- package/dist/tests/filter.test.js +438 -0
- package/dist/tests/filter.test.js.map +1 -0
- package/dist/tests/list.test.d.ts +2 -0
- package/dist/tests/list.test.d.ts.map +1 -0
- package/dist/tests/list.test.js +735 -0
- package/dist/tests/list.test.js.map +1 -0
- package/dist/tests/show.test.d.ts +2 -0
- package/dist/tests/show.test.d.ts.map +1 -0
- package/dist/tests/show.test.js +325 -0
- package/dist/tests/show.test.js.map +1 -0
- package/dist/tests/table.test.js +184 -56
- package/dist/tests/table.test.js.map +1 -1
- package/dist/tests/table_history.test.js +51 -0
- package/dist/tests/table_history.test.js.map +1 -1
- package/dist/tests/view.test.js +21 -1
- package/dist/tests/view.test.js.map +1 -1
- package/dist/utils.d.ts +4 -17
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -32
- package/dist/utils.js.map +1 -1
- package/package.json +8 -7
|
@@ -19,11 +19,11 @@ const { getState } = require("../../db/state");
|
|
|
19
19
|
const { text, text_attr, script, domReady, div, button, i, pre, } = require("@saltcorn/markup/tags");
|
|
20
20
|
const { renderForm } = require("@saltcorn/markup");
|
|
21
21
|
const FieldRepeat = require("../../models/fieldrepeat");
|
|
22
|
-
const { get_expression_function, expressionChecker, eval_expression, freeVariables, } = require("../../models/expression");
|
|
22
|
+
const { get_expression_function, expressionChecker, eval_expression, freeVariables, freeVariablesInInterpolation, } = require("../../models/expression");
|
|
23
23
|
const { InvalidConfiguration, isNode, isOfflineMode, mergeIntoWhere, dollarizeObject, getSessionId, } = require("../../utils");
|
|
24
24
|
const Library = require("../../models/library");
|
|
25
25
|
const { check_view_columns } = require("../../plugin-testing");
|
|
26
|
-
const { initial_config_all_fields, calcfldViewOptions, calcfldViewConfig, get_parent_views, picked_fields_to_query, stateFieldsToWhere, stateFieldsToQuery, getActionConfigFields, run_action_column, add_free_variables_to_joinfields, readState, stateToQueryString, pathToState,
|
|
26
|
+
const { initial_config_all_fields, calcfldViewOptions, calcfldViewConfig, get_parent_views, picked_fields_to_query, stateFieldsToWhere, stateFieldsToQuery, getActionConfigFields, run_action_column, add_free_variables_to_joinfields, readState, stateToQueryString, pathToState, displayType, } = require("../../plugin-helper");
|
|
27
27
|
const { splitUniques, getForm, fill_presets, parse_view_select, get_view_link_query, objToQueryString, action_url, action_link, view_linker, } = require("./viewable_fields");
|
|
28
28
|
const { traverse, getStringsForI18n, translateLayout, traverseSync, } = require("../../models/layout");
|
|
29
29
|
const { asyncMap, isWeb, removeEmptyStrings } = require("../../utils");
|
|
@@ -31,6 +31,7 @@ const { extractFromLayout } = require("../../diagram/node_extract_utils");
|
|
|
31
31
|
const db = require("../../db");
|
|
32
32
|
const { prepare_update_row } = require("../../web-mobile-commons");
|
|
33
33
|
const _ = require("underscore");
|
|
34
|
+
const { Relation, RelationType } = require("@saltcorn/common-code");
|
|
34
35
|
const builtInActions = [
|
|
35
36
|
"Save",
|
|
36
37
|
"SaveAndContinue",
|
|
@@ -73,6 +74,7 @@ const configuration_workflow = (req) => new Workflow({
|
|
|
73
74
|
const triggerActions = [];
|
|
74
75
|
(await Trigger.find({
|
|
75
76
|
when_trigger: { or: ["API call", "Never"] },
|
|
77
|
+
table_id: null,
|
|
76
78
|
})).forEach((tr) => {
|
|
77
79
|
actions.push(tr.name);
|
|
78
80
|
triggerActions.push(tr.name);
|
|
@@ -141,7 +143,7 @@ const configuration_workflow = (req) => new Workflow({
|
|
|
141
143
|
field_view_options.remember = ["edit"];
|
|
142
144
|
}
|
|
143
145
|
const library = (await Library.find({})).filter((l) => l.suitableFor("edit"));
|
|
144
|
-
const myviewrow =
|
|
146
|
+
const myviewrow = View.findOne({ name: context.viewname });
|
|
145
147
|
const { parent_field_list } = await table.get_parent_relations(true, true);
|
|
146
148
|
const pages = await Page.find();
|
|
147
149
|
const groups = (await PageGroup.find()).map((g) => ({
|
|
@@ -149,7 +151,7 @@ const configuration_workflow = (req) => new Workflow({
|
|
|
149
151
|
}));
|
|
150
152
|
return {
|
|
151
153
|
tableName: table.name,
|
|
152
|
-
fields: fields.map((f) => f.toBuilder),
|
|
154
|
+
fields: fields.map((f) => f.toBuilder || f),
|
|
153
155
|
field_view_options,
|
|
154
156
|
parent_field_list,
|
|
155
157
|
handlesTextStyle,
|
|
@@ -376,12 +378,6 @@ const setDateLocales = (form, locale) => {
|
|
|
376
378
|
}
|
|
377
379
|
});
|
|
378
380
|
};
|
|
379
|
-
/**
|
|
380
|
-
* check if a relation path has a CHildList structure
|
|
381
|
-
*/
|
|
382
|
-
const isChildListPath = (viewSelect, subView) => viewSelect.type === "RelationPath" &&
|
|
383
|
-
relationTypeFromPath(subView, viewSelect.path, viewSelect.sourcetable) ===
|
|
384
|
-
"ChildList";
|
|
385
381
|
/**
|
|
386
382
|
* update viewSelect so that it looks like a normal ChildList
|
|
387
383
|
*/
|
|
@@ -469,7 +465,18 @@ const runMany = async (table_id, viewname, { columns, layout, auto_save, split_p
|
|
|
469
465
|
* @returns {Promise<void>}
|
|
470
466
|
*/
|
|
471
467
|
const transformForm = async ({ form, table, req, row, res, getRowQuery, viewname, optionsQuery, }) => {
|
|
468
|
+
let pseudo_row = {};
|
|
469
|
+
if (!row) {
|
|
470
|
+
table.fields.forEach((f) => {
|
|
471
|
+
pseudo_row[f.name] = undefined;
|
|
472
|
+
});
|
|
473
|
+
}
|
|
472
474
|
await traverse(form.layout, {
|
|
475
|
+
container(segment) {
|
|
476
|
+
if (segment.click_action) {
|
|
477
|
+
segment.url = `javascript:view_post('${viewname}', 'run_action', {click_action: '${segment.click_action}', ...get_form_record({viewname: '${viewname}'}) })`;
|
|
478
|
+
}
|
|
479
|
+
},
|
|
473
480
|
async action(segment) {
|
|
474
481
|
if (segment.action_style === "on_page_load") {
|
|
475
482
|
//TODO check segment.min_role
|
|
@@ -481,11 +488,11 @@ const transformForm = async ({ form, table, req, row, res, getRowQuery, viewname
|
|
|
481
488
|
try {
|
|
482
489
|
const actionResult = await run_action_column({
|
|
483
490
|
col: { ...segment },
|
|
484
|
-
referrer: req
|
|
491
|
+
referrer: req?.get?.("Referrer"),
|
|
485
492
|
req,
|
|
486
493
|
res,
|
|
487
494
|
table,
|
|
488
|
-
row,
|
|
495
|
+
row: row || pseudo_row,
|
|
489
496
|
});
|
|
490
497
|
segment.type = "blank";
|
|
491
498
|
segment.style = {};
|
|
@@ -526,6 +533,30 @@ const transformForm = async ({ form, table, req, row, res, getRowQuery, viewname
|
|
|
526
533
|
const qs = objToQueryString(segment.configuration);
|
|
527
534
|
segment.sourceURL = `/field/show-calculated/${table.name}/${segment.join_field}/${segment.fieldview}?${qs}`;
|
|
528
535
|
},
|
|
536
|
+
tabs(segment) {
|
|
537
|
+
const to_delete = new Set();
|
|
538
|
+
(segment.showif || []).forEach((sif, ix) => {
|
|
539
|
+
if (sif) {
|
|
540
|
+
const showit = eval_expression(sif, row || pseudo_row, req.user);
|
|
541
|
+
if (!showit)
|
|
542
|
+
to_delete.add(ix);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
segment.titles = segment.titles.filter((v, ix) => !to_delete.has(ix));
|
|
546
|
+
segment.contents = segment.contents.filter((v, ix) => !to_delete.has(ix));
|
|
547
|
+
(segment.titles || []).forEach((t, ix) => {
|
|
548
|
+
if (typeof t === "string" && t.includes("{{")) {
|
|
549
|
+
const template = _.template(t, {
|
|
550
|
+
interpolate: /\{\{([^#].+?)\}\}/g,
|
|
551
|
+
});
|
|
552
|
+
segment.titles[ix] = template({
|
|
553
|
+
user: req.user,
|
|
554
|
+
row,
|
|
555
|
+
...(row || {}),
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
},
|
|
529
560
|
view_link(segment) {
|
|
530
561
|
segment.type = "blank";
|
|
531
562
|
const view_select = parse_view_select(segment.view);
|
|
@@ -545,11 +576,17 @@ const transformForm = async ({ form, table, req, row, res, getRowQuery, viewname
|
|
|
545
576
|
const view = View.findOne({ name: view_select.viewname });
|
|
546
577
|
if (!view)
|
|
547
578
|
throw new InvalidConfiguration(`Cannot find embedded view: ${view_select.viewname}`);
|
|
548
|
-
|
|
579
|
+
// check if the relation path matches a ChildList relations
|
|
580
|
+
let childListRelPath = false;
|
|
581
|
+
if (segment.relation && view.table_id) {
|
|
582
|
+
const targetTbl = Table.findOne({ id: view.table_id });
|
|
583
|
+
const relation = new Relation(segment.relation, targetTbl.name, displayType(await view.get_state_fields()));
|
|
584
|
+
childListRelPath = relation.type === RelationType.CHILD_LIST;
|
|
585
|
+
}
|
|
549
586
|
// Edit-in-edit
|
|
550
587
|
if (view.viewtemplate === "Edit" &&
|
|
551
|
-
(view_select.type === "ChildList" ||
|
|
552
|
-
if (
|
|
588
|
+
(view_select.type === "ChildList" || childListRelPath)) {
|
|
589
|
+
if (childListRelPath)
|
|
553
590
|
updateViewSelect(view_select);
|
|
554
591
|
const childTable = Table.findOne({ id: view.table_id });
|
|
555
592
|
const childForm = await getForm(childTable, view.name, view.configuration.columns, view.configuration.layout, row?.id, req, !isWeb(req));
|
|
@@ -596,42 +633,51 @@ const transformForm = async ({ form, table, req, row, res, getRowQuery, viewname
|
|
|
596
633
|
segment.field_repeat = fr;
|
|
597
634
|
return;
|
|
598
635
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
636
|
+
let state = {};
|
|
637
|
+
if (view_select.type === "RelationPath" && view.table_id) {
|
|
638
|
+
const targetTbl = Table.findOne({ id: view.table_id });
|
|
639
|
+
if (targetTbl) {
|
|
640
|
+
const relation = new Relation(segment.relation, targetTbl.name, displayType(await view.get_state_fields()));
|
|
641
|
+
const type = relation.type;
|
|
642
|
+
if (!row && type !== RelationType.INDEPENDENT) {
|
|
643
|
+
segment.type = "blank";
|
|
644
|
+
segment.contents = "";
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
state = pathToState(relation, (k) => row[k]);
|
|
648
|
+
}
|
|
607
649
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
650
|
+
else {
|
|
651
|
+
const isIndependent = view_select.type === "Independent";
|
|
652
|
+
// legacy none check ?
|
|
653
|
+
if (!row && !isIndependent) {
|
|
654
|
+
segment.type = "blank";
|
|
655
|
+
segment.contents = "";
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (!view)
|
|
659
|
+
throw new InvalidConfiguration(`Edit view incorrectly configured: cannot find embedded view ${view_select.viewname}`);
|
|
660
|
+
switch (view_select.type) {
|
|
661
|
+
case "Own":
|
|
662
|
+
state = { id: row.id };
|
|
663
|
+
break;
|
|
664
|
+
case "Independent":
|
|
665
|
+
state = {};
|
|
666
|
+
break;
|
|
667
|
+
case "ChildList":
|
|
668
|
+
case "OneToOneShow":
|
|
669
|
+
state = { [view_select.field_name]: row.id };
|
|
670
|
+
break;
|
|
671
|
+
case "ParentShow":
|
|
672
|
+
state = { id: row[view_select.field_name] };
|
|
673
|
+
break;
|
|
615
674
|
}
|
|
616
|
-
case "Own":
|
|
617
|
-
state = { id: row.id };
|
|
618
|
-
break;
|
|
619
|
-
case "Independent":
|
|
620
|
-
state = {};
|
|
621
|
-
break;
|
|
622
|
-
case "ChildList":
|
|
623
|
-
case "OneToOneShow":
|
|
624
|
-
state = { [view_select.field_name]: row.id };
|
|
625
|
-
break;
|
|
626
|
-
case "ParentShow":
|
|
627
|
-
state = { id: row[view_select.field_name] };
|
|
628
|
-
break;
|
|
629
675
|
}
|
|
630
676
|
const extra_state = segment.extra_state_fml
|
|
631
677
|
? eval_expression(segment.extra_state_fml, {
|
|
632
678
|
...dollarizeObject(req.query),
|
|
633
679
|
session_id: getSessionId(req),
|
|
634
|
-
...(row ||
|
|
680
|
+
...(row || pseudo_row),
|
|
635
681
|
}, req.user)
|
|
636
682
|
: {};
|
|
637
683
|
const qs = stateToQueryString({ ...state, ...extra_state });
|
|
@@ -795,6 +841,8 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
795
841
|
fixed,
|
|
796
842
|
auto_save,
|
|
797
843
|
}, { req, res }, body, { getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery }, remote);
|
|
844
|
+
const view = View.findOne({ name: viewname });
|
|
845
|
+
const pagetitle = { title: viewname, no_menu: view?.attributes?.no_menu };
|
|
798
846
|
if (prepResult) {
|
|
799
847
|
let { form, row, pk, id } = prepResult;
|
|
800
848
|
const cancel = body._cancel;
|
|
@@ -827,7 +875,7 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
827
875
|
}
|
|
828
876
|
else {
|
|
829
877
|
req.flash("error", text_attr(ins_upd_error));
|
|
830
|
-
res.sendWrap(
|
|
878
|
+
res.sendWrap(pagetitle, renderForm(form, req.csrfToken()));
|
|
831
879
|
}
|
|
832
880
|
return;
|
|
833
881
|
}
|
|
@@ -835,8 +883,15 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
835
883
|
for (const field of form.fields.filter((f) => f.isRepeat)) {
|
|
836
884
|
const view_select = parse_view_select(field.metadata.view, field.metadata.relation_path);
|
|
837
885
|
const childView = View.findOne({ name: view_select.viewname });
|
|
838
|
-
if (
|
|
839
|
-
|
|
886
|
+
if (!childView)
|
|
887
|
+
throw new InvalidConfiguration(`Cannot find embedded view: ${view_select.viewname}`);
|
|
888
|
+
if (field.metadata.relation_path &&
|
|
889
|
+
view_select.type === "RelationPath") {
|
|
890
|
+
const targetTbl = Table.findOne({ id: childView.table_id });
|
|
891
|
+
const relation = new Relation(field.metadata.relation_path, targetTbl.name, displayType(await childView.get_state_fields()));
|
|
892
|
+
if (relation.type === RelationType.CHILD_LIST)
|
|
893
|
+
updateViewSelect(view_select);
|
|
894
|
+
}
|
|
840
895
|
const childTable = Table.findOne({ id: field.metadata?.table_id });
|
|
841
896
|
const submitted_row_ids = new Set((form.values[field.name] || []).map((srow) => `${srow[childTable.pk_name]}`));
|
|
842
897
|
const childFields = new Set(childTable.fields.map((f) => f.name));
|
|
@@ -853,7 +908,7 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
853
908
|
const upd_res = await childTable.tryUpdateRow(childRow, childRow[childTable.pk_name], req.user || { role_id: 100 });
|
|
854
909
|
if (upd_res.error) {
|
|
855
910
|
req.flash("error", text_attr(upd_res.error));
|
|
856
|
-
res.sendWrap(
|
|
911
|
+
res.sendWrap(pagetitle, renderForm(form, req.csrfToken()));
|
|
857
912
|
return;
|
|
858
913
|
}
|
|
859
914
|
}
|
|
@@ -861,7 +916,7 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
861
916
|
const ins_res = await childTable.tryInsertRow(childRow, req.user || { role_id: 100 });
|
|
862
917
|
if (ins_res.error) {
|
|
863
918
|
req.flash("error", text_attr(ins_res.error));
|
|
864
|
-
res.sendWrap(
|
|
919
|
+
res.sendWrap(pagetitle, renderForm(form, req.csrfToken()));
|
|
865
920
|
return;
|
|
866
921
|
}
|
|
867
922
|
else if (ins_res.success) {
|
|
@@ -1002,6 +1057,56 @@ const doAuthPost = async ({ body, table_id, req }) => {
|
|
|
1002
1057
|
const authorise_post = async ({ body, table_id, req }, { authorizePostQuery }) => {
|
|
1003
1058
|
return await authorizePostQuery(body, table_id);
|
|
1004
1059
|
};
|
|
1060
|
+
const openDataStream = async (tableId, viewName, id, fieldName, fieldView, user, configuration, targetOpts) => {
|
|
1061
|
+
const table = Table.findOne({ id: tableId });
|
|
1062
|
+
const field = table.getField(fieldName);
|
|
1063
|
+
if (!field)
|
|
1064
|
+
throw new InvalidConfiguration(`Field ${fieldName} not found`);
|
|
1065
|
+
if (field.type === "File") {
|
|
1066
|
+
const cfgCol = configuration.columns.find((col) => col.fieldview === fieldView && col.field_name === fieldName);
|
|
1067
|
+
const fileView = getState().fileviews[fieldView];
|
|
1068
|
+
if (!fileView)
|
|
1069
|
+
throw new InvalidConfiguration(`File view ${fieldView} not found`);
|
|
1070
|
+
return await fileView.openDataStream(tableId, id, fieldName, user, cfgCol.configuration, targetOpts);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
// TODO is owner check
|
|
1074
|
+
const authorizeDataStream = async (view, id, fieldName, user, targetOpts) => {
|
|
1075
|
+
if (!user || user.role_id > view.min_role)
|
|
1076
|
+
return false;
|
|
1077
|
+
else {
|
|
1078
|
+
const table = Table.findOne({ id: view.table_id });
|
|
1079
|
+
if (!table || user.role_id > table.min_role_write)
|
|
1080
|
+
return false;
|
|
1081
|
+
else {
|
|
1082
|
+
const field = table.getField(fieldName);
|
|
1083
|
+
if (field.type === "File") {
|
|
1084
|
+
if (targetOpts?.oldTarget) {
|
|
1085
|
+
// continue old file ?
|
|
1086
|
+
const file = await File.findOne(targetOpts.oldTarget);
|
|
1087
|
+
if (file)
|
|
1088
|
+
return file.min_role_read >= user.role_id;
|
|
1089
|
+
}
|
|
1090
|
+
else if (id) {
|
|
1091
|
+
// continue file of existing row ?
|
|
1092
|
+
const row = await table.getRow({ [table.pk_name]: id });
|
|
1093
|
+
const fileCol = row[fieldName];
|
|
1094
|
+
if (fileCol) {
|
|
1095
|
+
const file = await File.findOne(row[fieldName]);
|
|
1096
|
+
if (file)
|
|
1097
|
+
return file.min_role_read >= user.role_id;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
// stream is new or the file does not exist
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
// only files for now
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1005
1110
|
/**
|
|
1006
1111
|
* @param {number} table_id
|
|
1007
1112
|
* @param {*} viewname
|
|
@@ -1112,7 +1217,8 @@ const prepare = async (viewname, table, fields, { columns, layout, fixed, auto_s
|
|
|
1112
1217
|
if (req.xhr)
|
|
1113
1218
|
res.status(422);
|
|
1114
1219
|
await form.fill_fkey_options(false, optionsQuery, req.user);
|
|
1115
|
-
|
|
1220
|
+
const view = View.findOne({ name: viewname });
|
|
1221
|
+
res.sendWrap({ title: viewname, no_menu: view?.attributes?.no_menu }, renderForm(form, req.csrfToken ? req.csrfToken() : false));
|
|
1116
1222
|
return null;
|
|
1117
1223
|
}
|
|
1118
1224
|
let row;
|
|
@@ -1135,7 +1241,7 @@ const prepare = async (viewname, table, fields, { columns, layout, fixed, auto_s
|
|
|
1135
1241
|
}
|
|
1136
1242
|
const file_fields = form.fields.filter((f) => f.type === "File");
|
|
1137
1243
|
for (const field of file_fields) {
|
|
1138
|
-
if (!field.fieldviewObj?.isEdit)
|
|
1244
|
+
if (!field.fieldviewObj?.isEdit || field.fieldviewObj?.isStream)
|
|
1139
1245
|
continue;
|
|
1140
1246
|
if (field.fieldviewObj?.setsFileId) {
|
|
1141
1247
|
//do nothing
|
|
@@ -1304,6 +1410,8 @@ module.exports = {
|
|
|
1304
1410
|
run,
|
|
1305
1411
|
runMany,
|
|
1306
1412
|
runPost,
|
|
1413
|
+
openDataStream,
|
|
1414
|
+
authorizeDataStream,
|
|
1307
1415
|
get_state_fields,
|
|
1308
1416
|
initial_config,
|
|
1309
1417
|
/** @type {boolean} */
|
|
@@ -1335,7 +1443,7 @@ module.exports = {
|
|
|
1335
1443
|
if (Object.keys(uniques).length > 0) {
|
|
1336
1444
|
// add joinfields from certain locations if they are not fields in columns
|
|
1337
1445
|
const joinFields = {};
|
|
1338
|
-
const picked = picked_fields_to_query([], fields, layout);
|
|
1446
|
+
const picked = picked_fields_to_query([], fields, layout, req);
|
|
1339
1447
|
const colFields = new Set(columns.map((c) => c.join_field ? c.join_field.split(".")[0] : c.field_name));
|
|
1340
1448
|
Object.entries(picked.joinFields).forEach(([nm, jfv]) => {
|
|
1341
1449
|
if (!colFields.has(jfv.ref))
|
|
@@ -1369,7 +1477,7 @@ module.exports = {
|
|
|
1369
1477
|
async editManyQuery(state, { limit, offset, orderBy, orderDesc, where }) {
|
|
1370
1478
|
const table = Table.findOne({ id: table_id });
|
|
1371
1479
|
const fields = table.getFields();
|
|
1372
|
-
const { joinFields, aggregations } = picked_fields_to_query(columns, fields);
|
|
1480
|
+
const { joinFields, aggregations } = picked_fields_to_query(columns, fields, undefined, req);
|
|
1373
1481
|
const qstate = await stateFieldsToWhere({ fields, state, table });
|
|
1374
1482
|
const q = await stateFieldsToQuery({ state, fields });
|
|
1375
1483
|
if (where)
|
|
@@ -1474,7 +1582,7 @@ module.exports = {
|
|
|
1474
1582
|
});
|
|
1475
1583
|
},
|
|
1476
1584
|
async actionQuery() {
|
|
1477
|
-
const { rndid, _csrf, onchange_action, onchange_field, ...body } = req.body;
|
|
1585
|
+
const { rndid, _csrf, onchange_action, onchange_field, click_action, ...body } = req.body;
|
|
1478
1586
|
const table = Table.findOne({ id: table_id });
|
|
1479
1587
|
const dbrow = body.id
|
|
1480
1588
|
? await table.getRow({ id: body.id }, {
|
|
@@ -1484,7 +1592,28 @@ module.exports = {
|
|
|
1484
1592
|
: undefined;
|
|
1485
1593
|
const row = { ...dbrow, ...body };
|
|
1486
1594
|
try {
|
|
1487
|
-
if (
|
|
1595
|
+
if (click_action) {
|
|
1596
|
+
let container;
|
|
1597
|
+
traverseSync(layout, {
|
|
1598
|
+
container(segment) {
|
|
1599
|
+
if (segment.click_action === click_action)
|
|
1600
|
+
container = segment;
|
|
1601
|
+
},
|
|
1602
|
+
});
|
|
1603
|
+
if (!container)
|
|
1604
|
+
return { json: { error: "Action not found" } };
|
|
1605
|
+
const trigger = Trigger.findOne({ name: click_action });
|
|
1606
|
+
const result = await trigger.runWithoutRow({
|
|
1607
|
+
table,
|
|
1608
|
+
Table,
|
|
1609
|
+
req,
|
|
1610
|
+
row,
|
|
1611
|
+
referrer: req?.get?.("Referrer"),
|
|
1612
|
+
user: req.user,
|
|
1613
|
+
});
|
|
1614
|
+
return { json: { success: "ok", ...(result || {}) } };
|
|
1615
|
+
}
|
|
1616
|
+
else if (onchange_action && !rndid) {
|
|
1488
1617
|
const fldCol = columns.find((c) => c.field_name === onchange_field &&
|
|
1489
1618
|
c.onchange_action === onchange_action);
|
|
1490
1619
|
if (!fldCol)
|
|
@@ -1495,6 +1624,7 @@ module.exports = {
|
|
|
1495
1624
|
Table,
|
|
1496
1625
|
req,
|
|
1497
1626
|
row,
|
|
1627
|
+
referrer: req?.get?.("Referrer"),
|
|
1498
1628
|
user: req.user,
|
|
1499
1629
|
});
|
|
1500
1630
|
return { json: { success: "ok", ...(result || {}) } };
|
|
@@ -1507,7 +1637,7 @@ module.exports = {
|
|
|
1507
1637
|
table,
|
|
1508
1638
|
row,
|
|
1509
1639
|
res,
|
|
1510
|
-
referrer: req
|
|
1640
|
+
referrer: req?.get?.("Referrer"),
|
|
1511
1641
|
});
|
|
1512
1642
|
//console.log("result", result);
|
|
1513
1643
|
return { json: { success: "ok", ...(result || {}) } };
|
|
@@ -1571,7 +1701,13 @@ module.exports = {
|
|
|
1571
1701
|
async interpolate_title_string(table_id, title, state) {
|
|
1572
1702
|
const tbl = Table.findOne(table_id);
|
|
1573
1703
|
if (state?.[tbl.pk_name]) {
|
|
1574
|
-
const
|
|
1704
|
+
const freeVars = freeVariablesInInterpolation(title);
|
|
1705
|
+
const joinFields = {};
|
|
1706
|
+
add_free_variables_to_joinfields(freeVars, joinFields, tbl.fields);
|
|
1707
|
+
const row = await tbl.getJoinedRow({
|
|
1708
|
+
where: { [tbl.pk_name]: state[tbl.pk_name] },
|
|
1709
|
+
joinFields,
|
|
1710
|
+
});
|
|
1575
1711
|
const template = _.template(title, {
|
|
1576
1712
|
interpolate: /\{\{([^#].+?)\}\}/g,
|
|
1577
1713
|
});
|
|
@@ -1591,7 +1727,7 @@ module.exports = {
|
|
|
1591
1727
|
const errs = [];
|
|
1592
1728
|
const warnings = [];
|
|
1593
1729
|
if (!destination_type || destination_type === "View") {
|
|
1594
|
-
const vwd =
|
|
1730
|
+
const vwd = View.findOne({
|
|
1595
1731
|
name: (view_when_done || "").split(".")[0],
|
|
1596
1732
|
});
|
|
1597
1733
|
if (!vwd)
|