@saltcorn/server 0.9.3-beta.8 → 0.9.3
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/help/index.js +1 -0
- package/locales/en.json +8 -2
- package/package.json +8 -8
- package/public/saltcorn-common.js +12 -7
- package/public/saltcorn.css +4 -0
- package/routes/actions.js +7 -0
- package/routes/fields.js +12 -3
- package/routes/viewedit.js +3 -0
- package/tests/view.test.js +191 -0
package/help/index.js
CHANGED
package/locales/en.json
CHANGED
|
@@ -352,7 +352,6 @@
|
|
|
352
352
|
"Next": "Next",
|
|
353
353
|
"Save": "Save",
|
|
354
354
|
"Calculated fields cannot have File type": "Calculated fields cannot have File type",
|
|
355
|
-
"Calculated fields cannot have Key type": "Calculated fields cannot have Key type",
|
|
356
355
|
"On Field": "On Field",
|
|
357
356
|
"Field in %s table": "Field in %s table",
|
|
358
357
|
"Action on row": "Action on row",
|
|
@@ -1338,5 +1337,12 @@
|
|
|
1338
1337
|
"Your page groups": "Your page groups",
|
|
1339
1338
|
"A group has pages with an eligible formula. When you request a group, then the first page where the formula matches gets served. This way, you can choose a page depending on the screen of the device.": "A group has pages with an eligible formula. When you request a group, then the first page where the formula matches gets served. This way, you can choose a page depending on the screen of the device.",
|
|
1340
1339
|
"Create page group": "Create page group",
|
|
1341
|
-
"Page groups": "Page groups"
|
|
1340
|
+
"Page groups": "Page groups",
|
|
1341
|
+
"Calculated non-stored fields cannot have Key type": "Calculated non-stored fields cannot have Key type",
|
|
1342
|
+
"Row click URL": "Row click URL",
|
|
1343
|
+
"Formula. Navigate to this URL when row is clicked": "Formula. Navigate to this URL when row is clicked",
|
|
1344
|
+
"Time of day": "Time of day",
|
|
1345
|
+
"UTC timezone": "UTC timezone",
|
|
1346
|
+
"Show if formula": "Show if formula",
|
|
1347
|
+
"Show link or embed if true, don't show if false. Based on state variables from URL query string and <code>user</code>. For the full state use <code>row</code>. Example: <code>!!row.createlink</code> to show link if and only if state has <code>createlink</code>.": "Show link or embed if true, don't show if false. Based on state variables from URL query string and <code>user</code>. For the full state use <code>row</code>. Example: <code>!!row.createlink</code> to show link if and only if state has <code>createlink</code>."
|
|
1342
1348
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.3
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "Server app for Saltcorn, open-source no-code platform",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@aws-sdk/client-s3": "^3.451.0",
|
|
10
|
-
"@saltcorn/base-plugin": "0.9.3
|
|
11
|
-
"@saltcorn/builder": "0.9.3
|
|
12
|
-
"@saltcorn/data": "0.9.3
|
|
13
|
-
"@saltcorn/admin-models": "0.9.3
|
|
14
|
-
"@saltcorn/filemanager": "0.9.3
|
|
15
|
-
"@saltcorn/markup": "0.9.3
|
|
16
|
-
"@saltcorn/sbadmin2": "0.9.3
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.3",
|
|
11
|
+
"@saltcorn/builder": "0.9.3",
|
|
12
|
+
"@saltcorn/data": "0.9.3",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.3",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.3",
|
|
15
|
+
"@saltcorn/markup": "0.9.3",
|
|
16
|
+
"@saltcorn/sbadmin2": "0.9.3",
|
|
17
17
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
18
18
|
"@socket.io/sticky": "^1.0.1",
|
|
19
19
|
"adm-zip": "0.5.10",
|
|
@@ -105,13 +105,17 @@ function apply_showif() {
|
|
|
105
105
|
}
|
|
106
106
|
});
|
|
107
107
|
$("[data-dyn-href]").each(function (ix, element) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
try {
|
|
109
|
+
const e = $(element);
|
|
110
|
+
const rec = get_form_record(e);
|
|
111
|
+
const href = new Function(
|
|
112
|
+
`{${Object.keys(rec).filter(valid_js_var_name).join(",")}}`,
|
|
113
|
+
"return " + e.attr("data-dyn-href")
|
|
114
|
+
)(rec);
|
|
115
|
+
e.attr("href", href);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (window._sc_loglevel > 4) console.error(e);
|
|
118
|
+
}
|
|
115
119
|
});
|
|
116
120
|
$("[data-calc-options]").each(function (ix, element) {
|
|
117
121
|
var e = $(element);
|
|
@@ -423,6 +427,7 @@ function get_form_record(e_in, select_labels) {
|
|
|
423
427
|
|
|
424
428
|
e.find("input[name],select[name],textarea[name]").each(function () {
|
|
425
429
|
const $this = $(this);
|
|
430
|
+
if ($this.prop("disabled")) return;
|
|
426
431
|
const name = $this.attr("data-fieldname") || $this.attr("name");
|
|
427
432
|
if (select_labels && $this.prop("tagName").toLowerCase() === "select")
|
|
428
433
|
rec[name] = $this.find("option:selected").text();
|
package/public/saltcorn.css
CHANGED
package/routes/actions.js
CHANGED
|
@@ -192,6 +192,13 @@ const triggerForm = async (req, trigger) => {
|
|
|
192
192
|
"The table for which the trigger condition is checked."
|
|
193
193
|
),
|
|
194
194
|
},
|
|
195
|
+
{
|
|
196
|
+
name: "channel",
|
|
197
|
+
label: req.__("Time of day"),
|
|
198
|
+
input_type: "time_of_day",
|
|
199
|
+
showIf: { when_trigger: "Daily" },
|
|
200
|
+
sublabel: req.__("UTC timezone"),
|
|
201
|
+
},
|
|
195
202
|
{
|
|
196
203
|
name: "channel",
|
|
197
204
|
label: req.__("Channel"),
|
package/routes/fields.js
CHANGED
|
@@ -74,8 +74,8 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
|
74
74
|
validator: (vs) => {
|
|
75
75
|
if (vs.calculated && vs.type === "File")
|
|
76
76
|
return req.__("Calculated fields cannot have File type");
|
|
77
|
-
if (vs.calculated && vs.type.startsWith("Key to"))
|
|
78
|
-
return req.__("Calculated fields cannot have Key type");
|
|
77
|
+
if (vs.calculated && !vs.stored && vs.type.startsWith("Key to"))
|
|
78
|
+
return req.__("Calculated non-stored fields cannot have Key type");
|
|
79
79
|
},
|
|
80
80
|
fields: [
|
|
81
81
|
new Field({
|
|
@@ -310,7 +310,12 @@ const fieldFlow = (req) =>
|
|
|
310
310
|
const nrows = await table.countRows({});
|
|
311
311
|
const existing_fields = table.getFields();
|
|
312
312
|
const existingNames = existing_fields.map((f) => f.name);
|
|
313
|
-
const fkey_opts = [
|
|
313
|
+
const fkey_opts = [
|
|
314
|
+
"File",
|
|
315
|
+
...tables
|
|
316
|
+
.filter((t) => !t.provider_name && !t.external)
|
|
317
|
+
.map((t) => `Key to ${t.name}`),
|
|
318
|
+
];
|
|
314
319
|
const form = await fieldForm(
|
|
315
320
|
req,
|
|
316
321
|
fkey_opts,
|
|
@@ -1196,6 +1201,10 @@ router.post(
|
|
|
1196
1201
|
formFields.forEach((ff) => {
|
|
1197
1202
|
ff.class = ff.class ? `${ff.class} item-menu` : "item-menu";
|
|
1198
1203
|
});
|
|
1204
|
+
if (req.query?.accept == "json") {
|
|
1205
|
+
res.json(formFields);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1199
1208
|
|
|
1200
1209
|
const form = new Form({
|
|
1201
1210
|
formStyle: "vert",
|
package/routes/viewedit.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
error_catcher,
|
|
19
19
|
addOnDoneRedirect,
|
|
20
20
|
is_relative_url,
|
|
21
|
+
setTenant,
|
|
21
22
|
} = require("./utils.js");
|
|
22
23
|
const { setTableRefs, viewsList } = require("./common_lists");
|
|
23
24
|
const Form = require("@saltcorn/data/models/form");
|
|
@@ -631,6 +632,7 @@ router.get(
|
|
|
631
632
|
router.post(
|
|
632
633
|
"/config/:name",
|
|
633
634
|
isAdmin,
|
|
635
|
+
setTenant,
|
|
634
636
|
error_catcher(async (req, res) => {
|
|
635
637
|
const { name } = req.params;
|
|
636
638
|
|
|
@@ -769,6 +771,7 @@ router.post(
|
|
|
769
771
|
router.post(
|
|
770
772
|
"/saveconfig/:viewname",
|
|
771
773
|
isAdmin,
|
|
774
|
+
setTenant,
|
|
772
775
|
error_catcher(async (req, res) => {
|
|
773
776
|
const { viewname } = req.params;
|
|
774
777
|
|
package/tests/view.test.js
CHANGED
|
@@ -677,6 +677,197 @@ describe("many to many relations", () => {
|
|
|
677
677
|
});
|
|
678
678
|
});
|
|
679
679
|
|
|
680
|
+
describe("relation path to query and state", () => {
|
|
681
|
+
it("ChildList one layer", async () => {
|
|
682
|
+
const app = await getApp({ disableCsrf: true });
|
|
683
|
+
const loginCookie = await getAdminLoginCookie();
|
|
684
|
+
// my_department
|
|
685
|
+
await request(app)
|
|
686
|
+
.get(`/view/show_department_with_employee_list?id=1`)
|
|
687
|
+
.set("Cookie", loginCookie)
|
|
688
|
+
// view link
|
|
689
|
+
.expect(toInclude("/view/list_employees?department=1"))
|
|
690
|
+
// embedded list
|
|
691
|
+
.expect(toInclude("my_department"))
|
|
692
|
+
.expect(toNotInclude("department_without_employees"))
|
|
693
|
+
.expect(toInclude("manager"))
|
|
694
|
+
.expect(toInclude("my_employee"))
|
|
695
|
+
.expect(toInclude("/view/create_employee?department=1"));
|
|
696
|
+
|
|
697
|
+
// department_without_employees
|
|
698
|
+
await request(app)
|
|
699
|
+
.get(`/view/show_department_with_employee_list?id=2`)
|
|
700
|
+
.set("Cookie", loginCookie)
|
|
701
|
+
// view link
|
|
702
|
+
.expect(toInclude("/view/list_employees?department=2"))
|
|
703
|
+
// embedded list
|
|
704
|
+
.expect(toNotInclude("my_department"))
|
|
705
|
+
.expect(toNotInclude("department_without_employees"))
|
|
706
|
+
.expect(toNotInclude("manager"))
|
|
707
|
+
.expect(toNotInclude("my_employee"))
|
|
708
|
+
.expect(toNotInclude("/view/create_employee?department=1"));
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it("ChildList two layers", async () => {
|
|
712
|
+
const app = await getApp({ disableCsrf: true });
|
|
713
|
+
const loginCookie = await getAdminLoginCookie();
|
|
714
|
+
await request(app)
|
|
715
|
+
.get(`/view/show_cover_with_artist_on_album?id=1`)
|
|
716
|
+
.set("Cookie", loginCookie)
|
|
717
|
+
// view link
|
|
718
|
+
.expect(
|
|
719
|
+
toInclude(
|
|
720
|
+
"/view/artist_plays_on_album_list?artist_plays_on_album.album.albums.cover=1"
|
|
721
|
+
)
|
|
722
|
+
)
|
|
723
|
+
// embedded list
|
|
724
|
+
.expect(toInclude("artist A"))
|
|
725
|
+
.expect(toInclude("artist B"))
|
|
726
|
+
.expect(toInclude("album A"));
|
|
727
|
+
|
|
728
|
+
await request(app)
|
|
729
|
+
.get(`/view/show_cover_with_artist_on_album?id=2`)
|
|
730
|
+
.set("Cookie", loginCookie)
|
|
731
|
+
// view link
|
|
732
|
+
.expect(
|
|
733
|
+
toInclude(
|
|
734
|
+
"/view/artist_plays_on_album_list?artist_plays_on_album.album.albums.cover=2"
|
|
735
|
+
)
|
|
736
|
+
)
|
|
737
|
+
// embedded list
|
|
738
|
+
.expect(toInclude("artist A"))
|
|
739
|
+
.expect(toNotInclude("artist B"))
|
|
740
|
+
.expect(toInclude("album B"));
|
|
741
|
+
|
|
742
|
+
await request(app)
|
|
743
|
+
.get(`/view/show_cover_with_artist_on_album?id=3`)
|
|
744
|
+
.set("Cookie", loginCookie)
|
|
745
|
+
// view link
|
|
746
|
+
.expect(
|
|
747
|
+
toInclude(
|
|
748
|
+
"/view/artist_plays_on_album_list?artist_plays_on_album.album.albums.cover=3"
|
|
749
|
+
)
|
|
750
|
+
)
|
|
751
|
+
// embedded list
|
|
752
|
+
.expect(toNotInclude("artist A"))
|
|
753
|
+
.expect(toNotInclude("artist B"))
|
|
754
|
+
.expect(toNotInclude("album A"))
|
|
755
|
+
.expect(toNotInclude("album B"));
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it("OneToOneSHow", async () => {
|
|
759
|
+
const app = await getApp({ disableCsrf: true });
|
|
760
|
+
const loginCookie = await getAdminLoginCookie();
|
|
761
|
+
await request(app)
|
|
762
|
+
.get(`/view/show_cover_with_album?id=1`)
|
|
763
|
+
.set("Cookie", loginCookie)
|
|
764
|
+
// view link
|
|
765
|
+
.expect(toInclude("/view/show_album?cover=1"))
|
|
766
|
+
// embedded show
|
|
767
|
+
.expect(toInclude("album A"));
|
|
768
|
+
|
|
769
|
+
await request(app)
|
|
770
|
+
.get(`/view/show_cover_with_album?id=2`)
|
|
771
|
+
.set("Cookie", loginCookie)
|
|
772
|
+
// view link
|
|
773
|
+
.expect(toInclude("/view/show_album?cover=2"))
|
|
774
|
+
// embedded show
|
|
775
|
+
.expect(toInclude("blue cover"))
|
|
776
|
+
.expect(toInclude("album B"));
|
|
777
|
+
|
|
778
|
+
await request(app)
|
|
779
|
+
.get(`/view/show_cover_with_album?id=3`)
|
|
780
|
+
.set("Cookie", loginCookie)
|
|
781
|
+
// view link
|
|
782
|
+
.expect(toInclude("/view/show_album?cover=3"))
|
|
783
|
+
// embedded show
|
|
784
|
+
.expect(toInclude("red cover"))
|
|
785
|
+
.expect(toInclude("No row selected"));
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it("Own", async () => {
|
|
789
|
+
const app = await getApp({ disableCsrf: true });
|
|
790
|
+
const loginCookie = await getAdminLoginCookie();
|
|
791
|
+
await request(app)
|
|
792
|
+
.get(`/view/show_artist_with_edit_artist?id=1`)
|
|
793
|
+
.set("Cookie", loginCookie)
|
|
794
|
+
// view link
|
|
795
|
+
.expect(toInclude("/view/edit_artist?id=1"))
|
|
796
|
+
// embedded edit
|
|
797
|
+
.expect(toInclude(`value="artist A"`));
|
|
798
|
+
|
|
799
|
+
await request(app)
|
|
800
|
+
.get(`/view/show_artist_with_edit_artist?id=2`)
|
|
801
|
+
.set("Cookie", loginCookie)
|
|
802
|
+
// view link
|
|
803
|
+
.expect(toInclude("/view/edit_artist?id=2"))
|
|
804
|
+
// embedded edit
|
|
805
|
+
.expect(toInclude(`value="artist B"`));
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it("Parent", async () => {
|
|
809
|
+
const app = await getApp({ disableCsrf: true });
|
|
810
|
+
const loginCookie = await getAdminLoginCookie();
|
|
811
|
+
await request(app)
|
|
812
|
+
.get(`/view/show_album_with_cover?id=1`)
|
|
813
|
+
.set("Cookie", loginCookie)
|
|
814
|
+
// view link
|
|
815
|
+
.expect(toInclude("/view/show_cover?id=1"))
|
|
816
|
+
// embedded show
|
|
817
|
+
.expect(toInclude("green cover"));
|
|
818
|
+
|
|
819
|
+
await request(app)
|
|
820
|
+
.get(`/view/show_album_with_cover?id=2`)
|
|
821
|
+
.set("Cookie", loginCookie)
|
|
822
|
+
// view link
|
|
823
|
+
.expect(toInclude("/view/show_cover?id=2"))
|
|
824
|
+
// embedded show
|
|
825
|
+
.expect(toInclude("blue cover"));
|
|
826
|
+
|
|
827
|
+
await request(app)
|
|
828
|
+
.get(`/view/show_album_with_cover?id=3`)
|
|
829
|
+
.set("Cookie", loginCookie)
|
|
830
|
+
// view link
|
|
831
|
+
.expect(toNotInclude("/view/show_cover?id=3"))
|
|
832
|
+
// embedded show
|
|
833
|
+
.expect(toInclude("No row selected"));
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it("RelationPath", async () => {
|
|
837
|
+
const app = await getApp({ disableCsrf: true });
|
|
838
|
+
const loginCookie = await getAdminLoginCookie();
|
|
839
|
+
await request(app)
|
|
840
|
+
.get(`/view/track_on_album_with_artists_on_album?id=1`)
|
|
841
|
+
.set("Cookie", loginCookie)
|
|
842
|
+
// view link
|
|
843
|
+
.expect(
|
|
844
|
+
toInclude(
|
|
845
|
+
"/view/artist_plays_on_album_list?.tracks_on_album.album.artist_plays_on_album$album=1"
|
|
846
|
+
)
|
|
847
|
+
)
|
|
848
|
+
// embedded show
|
|
849
|
+
.expect(toInclude("artist A"))
|
|
850
|
+
.expect(toInclude("artist B"))
|
|
851
|
+
.expect(toInclude("album A"))
|
|
852
|
+
.expect(toNotInclude("album B"));
|
|
853
|
+
|
|
854
|
+
await request(app)
|
|
855
|
+
.get(`/view/track_on_album_with_artists_on_album?id=2`)
|
|
856
|
+
.set("Cookie", loginCookie)
|
|
857
|
+
// view link
|
|
858
|
+
.expect(
|
|
859
|
+
toInclude(
|
|
860
|
+
"/view/artist_plays_on_album_list?.tracks_on_album.album.artist_plays_on_album$album=2"
|
|
861
|
+
)
|
|
862
|
+
)
|
|
863
|
+
// embedded show
|
|
864
|
+
.expect(toInclude("artist A"))
|
|
865
|
+
.expect(toNotInclude("artist B"))
|
|
866
|
+
.expect(toNotInclude("album A"))
|
|
867
|
+
.expect(toInclude("album B"));
|
|
868
|
+
});
|
|
869
|
+
});
|
|
870
|
+
|
|
680
871
|
describe("legacy relations with relation path", () => {
|
|
681
872
|
it("Independent feed", async () => {
|
|
682
873
|
const app = await getApp({ disableCsrf: true });
|