@saltcorn/data 0.8.8-beta.3 → 0.8.8-beta.5
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.map +1 -1
- package/dist/base-plugin/actions.js +9 -3
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/fieldviews.d.ts +30 -16
- package/dist/base-plugin/fieldviews.d.ts.map +1 -1
- package/dist/base-plugin/fieldviews.js +171 -1
- package/dist/base-plugin/fieldviews.js.map +1 -1
- package/dist/base-plugin/index.d.ts +98 -62
- 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 +6 -0
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts +39 -2
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +320 -147
- 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 +19 -10
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +20 -13
- 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 +14 -3
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js +7 -5
- 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 +76 -0
- package/dist/db/fixtures.js.map +1 -1
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +2 -2
- package/dist/models/config.js.map +1 -1
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +10 -8
- 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 +6 -2
- package/dist/models/field.js.map +1 -1
- package/dist/models/form.d.ts +2 -0
- package/dist/models/form.d.ts.map +1 -1
- package/dist/models/form.js +1 -0
- package/dist/models/form.js.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/table.d.ts +1 -0
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +43 -30
- package/dist/models/table.js.map +1 -1
- package/dist/models/table_constraints.d.ts.map +1 -1
- package/dist/models/table_constraints.js +2 -1
- package/dist/models/table_constraints.js.map +1 -1
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +23 -11
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts +2 -0
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +10 -2
- package/dist/models/user.js.map +1 -1
- package/dist/models/view.d.ts.map +1 -1
- package/dist/models/view.js +25 -8
- package/dist/models/view.js.map +1 -1
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +7 -3
- package/dist/plugin-helper.js.map +1 -1
- package/dist/tests/auth.test.js +13 -0
- package/dist/tests/auth.test.js.map +1 -1
- package/dist/tests/auxtest.test.js +2 -1
- package/dist/tests/auxtest.test.js.map +1 -1
- package/dist/tests/calc.test.js +16 -1
- package/dist/tests/calc.test.js.map +1 -1
- package/dist/tests/table.test.js +11 -1
- package/dist/tests/table.test.js.map +1 -1
- package/dist/tests/user.test.js +5 -0
- package/dist/tests/user.test.js.map +1 -1
- package/dist/tests/view.test.js +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +11 -1
- package/dist/utils.js.map +1 -1
- package/package.json +7 -7
|
@@ -22,15 +22,17 @@ const { get_expression_function, expressionChecker, eval_expression, freeVariabl
|
|
|
22
22
|
const { InvalidConfiguration, isNode, mergeIntoWhere } = require("../../utils");
|
|
23
23
|
const Library = require("../../models/library");
|
|
24
24
|
const { check_view_columns } = require("../../plugin-testing");
|
|
25
|
-
const { initial_config_all_fields, calcfldViewOptions, calcfldViewConfig, get_parent_views, get_link_view_opts, picked_fields_to_query, stateFieldsToWhere, stateFieldsToQuery,
|
|
25
|
+
const { initial_config_all_fields, calcfldViewOptions, calcfldViewConfig, get_parent_views, get_link_view_opts, picked_fields_to_query, stateFieldsToWhere, stateFieldsToQuery, run_action_column, add_free_variables_to_joinfields, readState, } = require("../../plugin-helper");
|
|
26
26
|
const { splitUniques, getForm, fill_presets, parse_view_select, get_view_link_query, objToQueryString, action_url, action_link, view_linker, } = require("./viewable_fields");
|
|
27
27
|
const { traverse, getStringsForI18n, translateLayout, traverseSync, } = require("../../models/layout");
|
|
28
|
-
const { asyncMap, isWeb } = require("../../utils");
|
|
28
|
+
const { asyncMap, isWeb, removeEmptyStrings } = require("../../utils");
|
|
29
29
|
const { extractFromLayout } = require("../../diagram/node_extract_utils");
|
|
30
30
|
const db = require("../../db");
|
|
31
|
+
const { prepare_update_row } = require("../../web-mobile-commons");
|
|
31
32
|
const builtInActions = [
|
|
32
33
|
"Save",
|
|
33
34
|
"SaveAndContinue",
|
|
35
|
+
"UpdateMatchingRows",
|
|
34
36
|
"Reset",
|
|
35
37
|
"GoBack",
|
|
36
38
|
"Delete",
|
|
@@ -678,89 +680,18 @@ const render = async ({ table, fields, viewname, columns, layout, row, req, stat
|
|
|
678
680
|
* @param {string} optsTwo.redirect
|
|
679
681
|
* @returns {Promise<void>}
|
|
680
682
|
*/
|
|
681
|
-
const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_done, formula_destinations, auto_save, destination_type, dest_url_formula, page_when_done, }, state, body, { res, req, redirect }, { tryInsertQuery, tryUpdateQuery, getRowQuery, saveFileQuery, optionsQuery }, remote) => {
|
|
683
|
+
const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_done, formula_destinations, auto_save, destination_type, dest_url_formula, page_when_done, }, state, body, { res, req, redirect }, { tryInsertQuery, tryUpdateQuery, getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery, }, remote) => {
|
|
682
684
|
const table = Table.findOne({ id: table_id });
|
|
683
685
|
const fields = table.getFields();
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
});
|
|
694
|
-
setDateLocales(form, req.getLocale());
|
|
695
|
-
await transformForm({
|
|
696
|
-
form,
|
|
697
|
-
table,
|
|
698
|
-
req,
|
|
699
|
-
row: body[table.pk_name]
|
|
700
|
-
? { [table.pk_name]: body[table.pk_name] }
|
|
701
|
-
: undefined,
|
|
702
|
-
getRowQuery,
|
|
703
|
-
viewname,
|
|
704
|
-
optionsQuery,
|
|
705
|
-
});
|
|
706
|
-
const cancel = body._cancel;
|
|
707
|
-
await form.asyncValidate(body);
|
|
708
|
-
if (form.hasErrors && !cancel) {
|
|
709
|
-
if (req.xhr)
|
|
710
|
-
res.status(422);
|
|
711
|
-
await form.fill_fkey_options(false, undefined, req.user);
|
|
712
|
-
res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
let row;
|
|
716
|
-
const pk = fields.find((f) => f.primary_key);
|
|
717
|
-
let id = pk.type.read(body[pk.name]);
|
|
718
|
-
if (typeof id === "undefined") {
|
|
719
|
-
const use_fixed = await fill_presets(table, req, fixed);
|
|
720
|
-
row = { ...use_fixed, ...form.values };
|
|
721
|
-
}
|
|
722
|
-
else if (cancel) {
|
|
723
|
-
//get row
|
|
724
|
-
row = await table.getRow({ id });
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
row = { ...form.values };
|
|
728
|
-
}
|
|
729
|
-
for (const field of form.fields.filter((f) => f.isRepeat)) {
|
|
730
|
-
delete row[field.name];
|
|
731
|
-
}
|
|
732
|
-
const file_fields = form.fields.filter((f) => f.type === "File");
|
|
733
|
-
for (const field of file_fields) {
|
|
734
|
-
if (field.fieldviewObj?.setsFileId) {
|
|
735
|
-
//do nothing
|
|
736
|
-
}
|
|
737
|
-
else if (field.fieldviewObj?.setsDataURL) {
|
|
738
|
-
if (body[field.name]) {
|
|
739
|
-
if (body[field.name].startsWith("data:")) {
|
|
740
|
-
const path_to_serve = await saveFileQuery(body[field.name], field.id, field.fieldview, row);
|
|
741
|
-
row[field.name] = path_to_serve;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
else if (req.files && req.files[field.name]) {
|
|
746
|
-
if (!isNode() && !remote) {
|
|
747
|
-
req.flash("error", "The mobile-app supports no local files, please use a remote table.");
|
|
748
|
-
res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (isNode()) {
|
|
752
|
-
const file = await File.from_req_files(req.files[field.name], req.user ? req.user.id : null, (field.attributes && +field.attributes.min_role_read) || 1, field?.attributes?.folder);
|
|
753
|
-
row[field.name] = file.path_to_serve;
|
|
754
|
-
}
|
|
755
|
-
else {
|
|
756
|
-
const serverResp = await File.upload(req.files[field.name]);
|
|
757
|
-
row[field.name] = serverResp.location;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
delete row[field.name];
|
|
762
|
-
}
|
|
763
|
-
}
|
|
686
|
+
const prepResult = await prepare(viewname, table, fields, {
|
|
687
|
+
columns,
|
|
688
|
+
layout,
|
|
689
|
+
fixed,
|
|
690
|
+
auto_save,
|
|
691
|
+
}, { req, res }, body, { getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery }, remote);
|
|
692
|
+
if (prepResult) {
|
|
693
|
+
let { form, row, pk, id } = prepResult;
|
|
694
|
+
const cancel = body._cancel;
|
|
764
695
|
const originalID = id;
|
|
765
696
|
let trigger_return;
|
|
766
697
|
let ins_upd_error;
|
|
@@ -859,64 +790,14 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
|
|
|
859
790
|
res.json({ view_when_done, ...trigger_return });
|
|
860
791
|
return;
|
|
861
792
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
}
|
|
871
|
-
else if (destination_type === "Page" && page_when_done) {
|
|
872
|
-
res.redirect(`/page/${page_when_done}`);
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
else if (destination_type === "URL formula" && dest_url_formula) {
|
|
876
|
-
const url = eval_expression(dest_url_formula, row);
|
|
877
|
-
res.redirect(url);
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
else if (destination_type !== "View")
|
|
881
|
-
for (const { view, expression } of formula_destinations || []) {
|
|
882
|
-
if (expression) {
|
|
883
|
-
const f = get_expression_function(expression, fields);
|
|
884
|
-
if (f(row)) {
|
|
885
|
-
use_view_when_done = view;
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
if (!use_view_when_done) {
|
|
891
|
-
res.redirect(`/`);
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
894
|
-
const [viewname_when_done, relation] = use_view_when_done.split(".");
|
|
895
|
-
const nxview = await View.findOne({ name: viewname_when_done });
|
|
896
|
-
if (!nxview) {
|
|
897
|
-
req.flash("warning", `View "${use_view_when_done}" not found - change "View when done" in "${viewname}" view`);
|
|
898
|
-
res.redirect(`/`);
|
|
899
|
-
}
|
|
900
|
-
else {
|
|
901
|
-
const state_fields = await nxview.get_state_fields();
|
|
902
|
-
let target = `/view/${text(viewname_when_done)}`;
|
|
903
|
-
let query = "";
|
|
904
|
-
if ((nxview.table_id === table_id || relation) &&
|
|
905
|
-
state_fields.some((sf) => sf.name === pk.name) &&
|
|
906
|
-
viewname_when_done !== viewname) {
|
|
907
|
-
const get_query = get_view_link_query(fields, nxview);
|
|
908
|
-
query = relation
|
|
909
|
-
? `?${pk.name}=${text(row[relation])}`
|
|
910
|
-
: get_query(row);
|
|
911
|
-
}
|
|
912
|
-
const redirectPath = `${target}${query}`;
|
|
913
|
-
if (!isWeb(req)) {
|
|
914
|
-
res.json({ redirect: `get${redirectPath}` });
|
|
915
|
-
}
|
|
916
|
-
else {
|
|
917
|
-
res.redirect(redirectPath);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
793
|
+
await whenDone(viewname, table_id, fields, pk, {
|
|
794
|
+
view_when_done,
|
|
795
|
+
formula_destinations,
|
|
796
|
+
destination_type,
|
|
797
|
+
dest_url_formula,
|
|
798
|
+
page_when_done,
|
|
799
|
+
redirect,
|
|
800
|
+
}, req, res, body, row);
|
|
920
801
|
}
|
|
921
802
|
};
|
|
922
803
|
const doAuthPost = async ({ body, table_id, req }) => {
|
|
@@ -1015,6 +896,253 @@ const run_action = async (table_id, viewname, { columns, layout }, body, { req,
|
|
|
1015
896
|
}
|
|
1016
897
|
return result;
|
|
1017
898
|
};
|
|
899
|
+
const update_matching_rows = async (table_id, viewname, { columns, layout, fixed, view_when_done, formula_destinations, auto_save, destination_type, dest_url_formula, page_when_done, }, body, { req, res, redirect }, { updateMatchingQuery, getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery, }) => {
|
|
900
|
+
const table = Table.findOne({ id: table_id });
|
|
901
|
+
const fields = table.getFields();
|
|
902
|
+
const prepResult = await prepare(viewname, table, fields, {
|
|
903
|
+
columns,
|
|
904
|
+
layout,
|
|
905
|
+
fixed,
|
|
906
|
+
auto_save,
|
|
907
|
+
}, { req, res }, body, { getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery });
|
|
908
|
+
if (prepResult) {
|
|
909
|
+
let { form, row, pk } = prepResult;
|
|
910
|
+
const state = req?.query
|
|
911
|
+
? readState(removeEmptyStrings(req.query), fields, req)
|
|
912
|
+
: {};
|
|
913
|
+
const where = stateFieldsToWhere({ fields, state, table });
|
|
914
|
+
const repeatFields = form.fields.filter((f) => f.isRepeat);
|
|
915
|
+
const childRows = {};
|
|
916
|
+
for (const field of repeatFields)
|
|
917
|
+
childRows[field.name] = form.values[field.name];
|
|
918
|
+
const { id, ...rest } = row;
|
|
919
|
+
const uptResults = await updateMatchingQuery(where, rest, repeatFields, childRows);
|
|
920
|
+
if (uptResults.error || uptResults.rowError || uptResults.inEditError) {
|
|
921
|
+
res.status(422);
|
|
922
|
+
req.flash("error", text_attr(uptResults.error || uptResults.rowError || uptResults.inEditError));
|
|
923
|
+
res.sendWrap(viewname, renderForm(form, req.csrfToken()));
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const { success, danger, goto } = combineResults(uptResults);
|
|
927
|
+
if (success.length > 0) {
|
|
928
|
+
req.flash("success", success);
|
|
929
|
+
}
|
|
930
|
+
if (danger.length > 0) {
|
|
931
|
+
req.flash("danger", danger);
|
|
932
|
+
}
|
|
933
|
+
else if (goto) {
|
|
934
|
+
res.redirect(goto);
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
await whenDone(viewname, table_id, fields, pk, {
|
|
938
|
+
view_when_done,
|
|
939
|
+
formula_destinations,
|
|
940
|
+
destination_type,
|
|
941
|
+
dest_url_formula,
|
|
942
|
+
page_when_done,
|
|
943
|
+
redirect,
|
|
944
|
+
}, req, res, body, row);
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
/**
|
|
948
|
+
* preparations for the form and the data row
|
|
949
|
+
* @param {*} viewname
|
|
950
|
+
* @param {*} table table of the view
|
|
951
|
+
* @param {*} fields all fields in table
|
|
952
|
+
* @param {*} param3 columns, layout, fixed, auto_save
|
|
953
|
+
* @param {*} param4 req, res
|
|
954
|
+
* @param {*} body request body
|
|
955
|
+
* @param {*} param6 getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery
|
|
956
|
+
* @param {*} remote
|
|
957
|
+
* @returns null on error, { form, row, pk, id } on success
|
|
958
|
+
*/
|
|
959
|
+
const prepare = async (viewname, table, fields, { columns, layout, fixed, auto_save }, { req, res }, body, { getRowQuery, saveFileQuery, optionsQuery, getRowByIdQuery }, remote) => {
|
|
960
|
+
const form = await getForm(table, viewname, columns, layout, body.id, req);
|
|
961
|
+
if (auto_save)
|
|
962
|
+
form.onChange = `saveAndContinue(this, ${!isWeb(req) ? `'${form.action}'` : undefined})`;
|
|
963
|
+
Object.entries(body).forEach(([k, v]) => {
|
|
964
|
+
const form_field = form.fields.find((f) => f.name === k);
|
|
965
|
+
const tbl_field = fields.find((f) => f.name === k);
|
|
966
|
+
if (tbl_field && !form_field && !fixed?.[`_block_${k}`]) {
|
|
967
|
+
form.fields.push(new Field({ name: k, input_type: "hidden" }));
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
setDateLocales(form, req.getLocale());
|
|
971
|
+
await transformForm({
|
|
972
|
+
form,
|
|
973
|
+
table,
|
|
974
|
+
req,
|
|
975
|
+
row: body[table.pk_name]
|
|
976
|
+
? { [table.pk_name]: body[table.pk_name] }
|
|
977
|
+
: undefined,
|
|
978
|
+
getRowQuery,
|
|
979
|
+
viewname,
|
|
980
|
+
optionsQuery,
|
|
981
|
+
});
|
|
982
|
+
const cancel = body._cancel;
|
|
983
|
+
await form.asyncValidate(body);
|
|
984
|
+
if (form.hasErrors && !cancel) {
|
|
985
|
+
if (req.xhr)
|
|
986
|
+
res.status(422);
|
|
987
|
+
await form.fill_fkey_options(false, optionsQuery, req.user);
|
|
988
|
+
res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
let row;
|
|
992
|
+
const pk = fields.find((f) => f.primary_key);
|
|
993
|
+
let id = pk.type.read(body[pk.name]);
|
|
994
|
+
if (typeof id === "undefined") {
|
|
995
|
+
const use_fixed = await fill_presets(table, req, fixed);
|
|
996
|
+
row = { ...use_fixed, ...form.values };
|
|
997
|
+
}
|
|
998
|
+
else if (cancel) {
|
|
999
|
+
row = getRowByIdQuery
|
|
1000
|
+
? await getRowByIdQuery(id)
|
|
1001
|
+
: await table.getRow({ id });
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
row = { ...form.values };
|
|
1005
|
+
}
|
|
1006
|
+
for (const field of form.fields.filter((f) => f.isRepeat)) {
|
|
1007
|
+
delete row[field.name];
|
|
1008
|
+
}
|
|
1009
|
+
const file_fields = form.fields.filter((f) => f.type === "File");
|
|
1010
|
+
for (const field of file_fields) {
|
|
1011
|
+
if (field.fieldviewObj?.setsFileId) {
|
|
1012
|
+
//do nothing
|
|
1013
|
+
}
|
|
1014
|
+
else if (field.fieldviewObj?.setsDataURL) {
|
|
1015
|
+
if (body[field.name]) {
|
|
1016
|
+
if (body[field.name].startsWith("data:")) {
|
|
1017
|
+
const path_to_serve = await saveFileQuery(body[field.name], field.id, field.fieldview, row);
|
|
1018
|
+
row[field.name] = path_to_serve;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else if (req.files && req.files[field.name]) {
|
|
1023
|
+
if (!isNode() && !remote) {
|
|
1024
|
+
req.flash("error", "The mobile-app supports no local files, please use a remote table.");
|
|
1025
|
+
res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
if (isNode()) {
|
|
1029
|
+
const file = await File.from_req_files(req.files[field.name], req.user ? req.user.id : null, (field.attributes && +field.attributes.min_role_read) || 1, field?.attributes?.folder);
|
|
1030
|
+
row[field.name] = file.path_to_serve;
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
const serverResp = await File.upload(req.files[field.name]);
|
|
1034
|
+
row[field.name] = serverResp.location;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
delete row[field.name];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
return { form, row, pk, id };
|
|
1042
|
+
};
|
|
1043
|
+
/**
|
|
1044
|
+
* take care of final redirect
|
|
1045
|
+
* @param {*} viewname
|
|
1046
|
+
* @param {*} table_id id of the table of the view
|
|
1047
|
+
* @param {*} fields all fields in table
|
|
1048
|
+
* @param {*} pk private key field
|
|
1049
|
+
* @param {*} param4 view_when_done, formula_destinations, destination_type, dest_url_formula, page_when_done, redirect
|
|
1050
|
+
* @param {*} req
|
|
1051
|
+
* @param {*} res
|
|
1052
|
+
* @param {*} body reuqest body
|
|
1053
|
+
* @param {*} row row of the form
|
|
1054
|
+
* @returns
|
|
1055
|
+
*/
|
|
1056
|
+
const whenDone = async (viewname, table_id, fields, pk, { view_when_done, formula_destinations, destination_type, dest_url_formula, page_when_done, redirect, }, req, res, body, row) => {
|
|
1057
|
+
if (redirect) {
|
|
1058
|
+
res.redirect(redirect);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
let use_view_when_done = view_when_done;
|
|
1062
|
+
if (destination_type === "Back to referer" && body._referer) {
|
|
1063
|
+
res.redirect(body._referer);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
else if (destination_type === "Page" && page_when_done) {
|
|
1067
|
+
res.redirect(`/page/${page_when_done}`);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
else if (destination_type === "URL formula" && dest_url_formula) {
|
|
1071
|
+
const url = eval_expression(dest_url_formula, row);
|
|
1072
|
+
res.redirect(url);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
else if (destination_type !== "View")
|
|
1076
|
+
for (const { view, expression } of formula_destinations || []) {
|
|
1077
|
+
if (expression) {
|
|
1078
|
+
const f = get_expression_function(expression, fields);
|
|
1079
|
+
if (f(row)) {
|
|
1080
|
+
use_view_when_done = view;
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
if (!use_view_when_done) {
|
|
1086
|
+
res.redirect(`/`);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const [viewname_when_done, relation] = use_view_when_done.split(".");
|
|
1090
|
+
const nxview = View.findOne({ name: viewname_when_done });
|
|
1091
|
+
if (!nxview) {
|
|
1092
|
+
req.flash("warning", `View "${use_view_when_done}" not found - change "View when done" in "${viewname}" view`);
|
|
1093
|
+
res.redirect(`/`);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
const state_fields = await nxview.get_state_fields();
|
|
1097
|
+
let target = `/view/${text(viewname_when_done)}`;
|
|
1098
|
+
let query = "";
|
|
1099
|
+
if ((nxview.table_id === table_id || relation) &&
|
|
1100
|
+
state_fields.some((sf) => sf.name === pk.name) &&
|
|
1101
|
+
viewname_when_done !== viewname) {
|
|
1102
|
+
const get_query = get_view_link_query(fields, nxview);
|
|
1103
|
+
query = relation ? `?${pk.name}=${text(row[relation])}` : get_query(row);
|
|
1104
|
+
}
|
|
1105
|
+
const redirectPath = `${target}${query}`;
|
|
1106
|
+
if (!isWeb(req)) {
|
|
1107
|
+
res.json({ redirect: `get${redirectPath}` });
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
res.redirect(redirectPath);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
/**
|
|
1115
|
+
* @param {*} results results from updateMatchingQuery
|
|
1116
|
+
* @returns success, danger, goto
|
|
1117
|
+
*/
|
|
1118
|
+
const combineResults = (results) => {
|
|
1119
|
+
const combined = { success: [], danger: [] };
|
|
1120
|
+
for (const uptResult of results) {
|
|
1121
|
+
const trigger_return = uptResult.trigger_return || {};
|
|
1122
|
+
if (trigger_return.notify && trigger_return.details)
|
|
1123
|
+
combined.success.push(div({ class: "d-inline" }, trigger_return.notify, button({
|
|
1124
|
+
class: "btn btn-sm btn-outline-secondary btn-xs",
|
|
1125
|
+
type: "button",
|
|
1126
|
+
"data-bs-toggle": "collapse",
|
|
1127
|
+
"data-bs-target": "#notifyDetails",
|
|
1128
|
+
"aria-expanded": "false",
|
|
1129
|
+
"aria-controls": "notifyDetails",
|
|
1130
|
+
}, i({ class: "fas fa-plus" })), div({ class: "collapse", id: "notifyDetails" }, pre(trigger_return.details))));
|
|
1131
|
+
else if (trigger_return.notify)
|
|
1132
|
+
combined.success.push(trigger_return.notify);
|
|
1133
|
+
if (trigger_return.error)
|
|
1134
|
+
combined.danger.push(trigger_return.error);
|
|
1135
|
+
if (trigger_return.goto && !combined.goto)
|
|
1136
|
+
combined.trigger_return.goto;
|
|
1137
|
+
}
|
|
1138
|
+
return combined;
|
|
1139
|
+
};
|
|
1140
|
+
const tryUpdateImpl = async (row, id, table, user) => {
|
|
1141
|
+
const result = {};
|
|
1142
|
+
const upd_res = await table.tryUpdateRow(row, id, user || { role_id: 100 }, result);
|
|
1143
|
+
upd_res.trigger_return = result;
|
|
1144
|
+
return upd_res;
|
|
1145
|
+
};
|
|
1018
1146
|
module.exports = {
|
|
1019
1147
|
/** @type {string} */
|
|
1020
1148
|
name: "Edit",
|
|
@@ -1111,11 +1239,8 @@ module.exports = {
|
|
|
1111
1239
|
return ins_res;
|
|
1112
1240
|
},
|
|
1113
1241
|
async tryUpdateQuery(row, id) {
|
|
1114
|
-
const table = Table.findOne(
|
|
1115
|
-
|
|
1116
|
-
const upd_res = await table.tryUpdateRow(row, id, req.user || { role_id: 100 }, result);
|
|
1117
|
-
upd_res.trigger_return = result;
|
|
1118
|
-
return upd_res;
|
|
1242
|
+
const table = Table.findOne(table_id);
|
|
1243
|
+
return await tryUpdateImpl(row, id, table, req.user);
|
|
1119
1244
|
},
|
|
1120
1245
|
async saveFileQuery(fieldVal, fieldId, fieldView, row) {
|
|
1121
1246
|
const field = await Field.findOne({ id: fieldId });
|
|
@@ -1178,6 +1303,10 @@ module.exports = {
|
|
|
1178
1303
|
forUser: req.user,
|
|
1179
1304
|
});
|
|
1180
1305
|
},
|
|
1306
|
+
async getRowByIdQuery(id) {
|
|
1307
|
+
const table = Table.findOne({ id: table_id });
|
|
1308
|
+
return await table.getRow({ id });
|
|
1309
|
+
},
|
|
1181
1310
|
async actionQuery() {
|
|
1182
1311
|
const body = req.body;
|
|
1183
1312
|
const col = columns.find((c) => c.type === "Action" && c.rndid === body.rndid && body.rndid);
|
|
@@ -1207,8 +1336,52 @@ module.exports = {
|
|
|
1207
1336
|
const rows = await db.select(reftable_name, type === "File" ? attributes.select_file_where : where);
|
|
1208
1337
|
return rows;
|
|
1209
1338
|
},
|
|
1339
|
+
async updateMatchingQuery(where, updateVals, repeatFields, childRows) {
|
|
1340
|
+
const table = Table.findOne(table_id);
|
|
1341
|
+
const rows = await table.getRows(where);
|
|
1342
|
+
const results = [];
|
|
1343
|
+
let inTransaction = false;
|
|
1344
|
+
try {
|
|
1345
|
+
if (rows.length === 0)
|
|
1346
|
+
return results;
|
|
1347
|
+
await db.begin();
|
|
1348
|
+
inTransaction = true;
|
|
1349
|
+
for (const row of rows) {
|
|
1350
|
+
const uptRes = await tryUpdateImpl(updateVals, row.id, table, req.user);
|
|
1351
|
+
if (uptRes.error) {
|
|
1352
|
+
inTransaction = false;
|
|
1353
|
+
await db.rollback();
|
|
1354
|
+
return { rowError: uptRes.error };
|
|
1355
|
+
}
|
|
1356
|
+
results.push(uptRes);
|
|
1357
|
+
for (const field of repeatFields) {
|
|
1358
|
+
const childTable = Table.findOne({ id: field.metadata?.table_id });
|
|
1359
|
+
await childTable.deleteRows({ [field.metadata?.relation]: row.id });
|
|
1360
|
+
for (const childRow of childRows[field.name]) {
|
|
1361
|
+
childRow[field.metadata?.relation] = row.id;
|
|
1362
|
+
const insRow = { ...childRow };
|
|
1363
|
+
delete insRow[childTable.pk_name];
|
|
1364
|
+
const insRes = await childTable.tryInsertRow(insRow, req.user || { role_id: 100 });
|
|
1365
|
+
if (insRes.error) {
|
|
1366
|
+
inTransaction = false;
|
|
1367
|
+
await db.rollback();
|
|
1368
|
+
return { inEditError: insRes.error };
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (inTransaction)
|
|
1374
|
+
await db.commit();
|
|
1375
|
+
}
|
|
1376
|
+
catch (error) {
|
|
1377
|
+
if (inTransaction)
|
|
1378
|
+
await db.rollback();
|
|
1379
|
+
return { error: error.message };
|
|
1380
|
+
}
|
|
1381
|
+
return results;
|
|
1382
|
+
},
|
|
1210
1383
|
}),
|
|
1211
|
-
routes: { run_action },
|
|
1384
|
+
routes: { run_action, update_matching_rows },
|
|
1212
1385
|
configCheck: async (view) => {
|
|
1213
1386
|
const { name, configuration: { view_when_done, destination_type, dest_url_formula, formula_destinations, page_when_done, }, } = view;
|
|
1214
1387
|
const errs = [];
|