@saltcorn/server 0.8.6-beta.17 → 0.8.6-beta.18

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/locales/en.json CHANGED
@@ -1167,5 +1167,10 @@
1167
1167
  "Migrations": "Migrations",
1168
1168
  "Tag Entries": "Tag Entries",
1169
1169
  "Not a valid field name": "Not a valid field name",
1170
- "Set a default value for missing data": "Set a default value for missing data"
1170
+ "Set a default value for missing data": "Set a default value for missing data",
1171
+ "Table triggers: ": "Table triggers: ",
1172
+ "App name": "App name",
1173
+ "App icon": "App icon",
1174
+ "Splash Page": "Splash Page",
1175
+ "App version": "App version"
1171
1176
  }
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.8.6-beta.17",
3
+ "version": "0.8.6-beta.18",
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
- "@saltcorn/base-plugin": "0.8.6-beta.17",
10
- "@saltcorn/builder": "0.8.6-beta.17",
11
- "@saltcorn/data": "0.8.6-beta.17",
12
- "@saltcorn/admin-models": "0.8.6-beta.17",
13
- "@saltcorn/filemanager": "0.8.6-beta.17",
14
- "@saltcorn/markup": "0.8.6-beta.17",
15
- "@saltcorn/sbadmin2": "0.8.6-beta.17",
9
+ "@saltcorn/base-plugin": "0.8.6-beta.18",
10
+ "@saltcorn/builder": "0.8.6-beta.18",
11
+ "@saltcorn/data": "0.8.6-beta.18",
12
+ "@saltcorn/admin-models": "0.8.6-beta.18",
13
+ "@saltcorn/filemanager": "0.8.6-beta.18",
14
+ "@saltcorn/markup": "0.8.6-beta.18",
15
+ "@saltcorn/sbadmin2": "0.8.6-beta.18",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
19
- "aws-sdk": "^2.1037.0",
19
+ "aws-sdk": "^2.1386.0",
20
20
  "connect-flash": "^0.1.1",
21
21
  "connect-pg-simple": "^6.1.0",
22
22
  "content-disposition": "^0.5.3",
@@ -0,0 +1,70 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/5/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../../addon/mode/simple"), require("../../addon/mode/multiplex"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror", "../../addon/mode/simple", "../../addon/mode/multiplex"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ CodeMirror.defineSimpleMode("handlebars-tags", {
15
+ start: [
16
+ { regex: /\{\{\{/, push: "handlebars_raw", token: "tag" },
17
+ { regex: /\{\{!--/, push: "dash_comment", token: "comment" },
18
+ { regex: /\{\{!/, push: "comment", token: "comment" },
19
+ { regex: /\{\{/, push: "handlebars", token: "tag" }
20
+ ],
21
+ handlebars_raw: [
22
+ { regex: /\}\}\}/, pop: true, token: "tag" },
23
+ ],
24
+ handlebars: [
25
+ { regex: /\}\}/, pop: true, token: "tag" },
26
+
27
+ // Double and single quotes
28
+ { regex: /"(?:[^\\"]|\\.)*"?/, token: "string" },
29
+ { regex: /'(?:[^\\']|\\.)*'?/, token: "string" },
30
+
31
+ // Handlebars keywords
32
+ { regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" },
33
+ { regex: /(?:else|this)\b/, token: "keyword" },
34
+
35
+ // Numeral
36
+ { regex: /\d+/i, token: "number" },
37
+
38
+ // Atoms like = and .
39
+ { regex: /=|~|@|true|false/, token: "atom" },
40
+
41
+ // Paths
42
+ { regex: /(?:\.\.\/)*(?:[A-Za-z_][\w\.]*)+/, token: "variable-2" }
43
+ ],
44
+ dash_comment: [
45
+ { regex: /--\}\}/, pop: true, token: "comment" },
46
+
47
+ // Commented code
48
+ { regex: /./, token: "comment"}
49
+ ],
50
+ comment: [
51
+ { regex: /\}\}/, pop: true, token: "comment" },
52
+ { regex: /./, token: "comment" }
53
+ ],
54
+ meta: {
55
+ blockCommentStart: "{{--",
56
+ blockCommentEnd: "--}}"
57
+ }
58
+ });
59
+
60
+ CodeMirror.defineMode("handlebars", function(config, parserConfig) {
61
+ var handlebars = CodeMirror.getMode(config, "handlebars-tags");
62
+ if (!parserConfig || !parserConfig.base) return handlebars;
63
+ return CodeMirror.multiplexingMode(
64
+ CodeMirror.getMode(config, parserConfig.base),
65
+ {open: "{{", close: /\}\}\}?/, mode: handlebars, parseDelimiters: true}
66
+ );
67
+ });
68
+
69
+ CodeMirror.defineMIME("text/x-handlebars-template", "handlebars");
70
+ });
@@ -0,0 +1,153 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/5/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ var defaultTags = {
15
+ script: [
16
+ ["lang", /(javascript|babel)/i, "javascript"],
17
+ ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
18
+ ["type", /./, "text/plain"],
19
+ [null, null, "javascript"]
20
+ ],
21
+ style: [
22
+ ["lang", /^css$/i, "css"],
23
+ ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
24
+ ["type", /./, "text/plain"],
25
+ [null, null, "css"]
26
+ ]
27
+ };
28
+
29
+ function maybeBackup(stream, pat, style) {
30
+ var cur = stream.current(), close = cur.search(pat);
31
+ if (close > -1) {
32
+ stream.backUp(cur.length - close);
33
+ } else if (cur.match(/<\/?$/)) {
34
+ stream.backUp(cur.length);
35
+ if (!stream.match(pat, false)) stream.match(cur);
36
+ }
37
+ return style;
38
+ }
39
+
40
+ var attrRegexpCache = {};
41
+ function getAttrRegexp(attr) {
42
+ var regexp = attrRegexpCache[attr];
43
+ if (regexp) return regexp;
44
+ return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
45
+ }
46
+
47
+ function getAttrValue(text, attr) {
48
+ var match = text.match(getAttrRegexp(attr))
49
+ return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
50
+ }
51
+
52
+ function getTagRegexp(tagName, anchored) {
53
+ return new RegExp((anchored ? "^" : "") + "<\/\\s*" + tagName + "\\s*>", "i");
54
+ }
55
+
56
+ function addTags(from, to) {
57
+ for (var tag in from) {
58
+ var dest = to[tag] || (to[tag] = []);
59
+ var source = from[tag];
60
+ for (var i = source.length - 1; i >= 0; i--)
61
+ dest.unshift(source[i])
62
+ }
63
+ }
64
+
65
+ function findMatchingMode(tagInfo, tagText) {
66
+ for (var i = 0; i < tagInfo.length; i++) {
67
+ var spec = tagInfo[i];
68
+ if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
69
+ }
70
+ }
71
+
72
+ CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
73
+ var htmlMode = CodeMirror.getMode(config, {
74
+ name: "xml",
75
+ htmlMode: true,
76
+ multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
77
+ multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag,
78
+ allowMissingTagName: parserConfig.allowMissingTagName,
79
+ });
80
+
81
+ var tags = {};
82
+ var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
83
+ addTags(defaultTags, tags);
84
+ if (configTags) addTags(configTags, tags);
85
+ if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
86
+ tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
87
+
88
+ function html(stream, state) {
89
+ var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
90
+ if (tag && !/[<>\s\/]/.test(stream.current()) &&
91
+ (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
92
+ tags.hasOwnProperty(tagName)) {
93
+ state.inTag = tagName + " "
94
+ } else if (state.inTag && tag && />$/.test(stream.current())) {
95
+ var inTag = /^([\S]+) (.*)/.exec(state.inTag)
96
+ state.inTag = null
97
+ var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
98
+ var mode = CodeMirror.getMode(config, modeSpec)
99
+ var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
100
+ state.token = function (stream, state) {
101
+ if (stream.match(endTagA, false)) {
102
+ state.token = html;
103
+ state.localState = state.localMode = null;
104
+ return null;
105
+ }
106
+ return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
107
+ };
108
+ state.localMode = mode;
109
+ state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
110
+ } else if (state.inTag) {
111
+ state.inTag += stream.current()
112
+ if (stream.eol()) state.inTag += " "
113
+ }
114
+ return style;
115
+ };
116
+
117
+ return {
118
+ startState: function () {
119
+ var state = CodeMirror.startState(htmlMode);
120
+ return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
121
+ },
122
+
123
+ copyState: function (state) {
124
+ var local;
125
+ if (state.localState) {
126
+ local = CodeMirror.copyState(state.localMode, state.localState);
127
+ }
128
+ return {token: state.token, inTag: state.inTag,
129
+ localMode: state.localMode, localState: local,
130
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
131
+ },
132
+
133
+ token: function (stream, state) {
134
+ return state.token(stream, state);
135
+ },
136
+
137
+ indent: function (state, textAfter, line) {
138
+ if (!state.localMode || /^\s*<\//.test(textAfter))
139
+ return htmlMode.indent(state.htmlState, textAfter, line);
140
+ else if (state.localMode.indent)
141
+ return state.localMode.indent(state.localState, textAfter, line);
142
+ else
143
+ return CodeMirror.Pass;
144
+ },
145
+
146
+ innerMode: function (state) {
147
+ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
148
+ }
149
+ };
150
+ }, "xml", "javascript", "css");
151
+
152
+ CodeMirror.defineMIME("text/html", "htmlmixed");
153
+ });
package/routes/admin.js CHANGED
@@ -986,7 +986,7 @@ router.post(
986
986
  "npm",
987
987
  ["install", "-g", "@saltcorn/cli@latest", "--unsafe"],
988
988
  {
989
- stdio: ["ignore", "pipe", process.stderr],
989
+ stdio: ["ignore", "pipe", "pipe"],
990
990
  }
991
991
  );
992
992
  child.stdout.on("data", (data) => {
@@ -1387,6 +1387,9 @@ router.get(
1387
1387
  error_catcher(async (req, res) => {
1388
1388
  const views = await View.find();
1389
1389
  const pages = await Page.find();
1390
+ const images = (await File.find({ mime_super: "image" })).filter((image) =>
1391
+ image.filename?.endsWith(".png")
1392
+ );
1390
1393
 
1391
1394
  send_admin_page({
1392
1395
  res,
@@ -1464,7 +1467,7 @@ router.get(
1464
1467
  // select entry-view
1465
1468
  select(
1466
1469
  {
1467
- class: "form-control",
1470
+ class: "form-select",
1468
1471
  name: "entryPoint",
1469
1472
  id: "viewInputID",
1470
1473
  },
@@ -1477,14 +1480,14 @@ router.get(
1477
1480
  // select entry-page
1478
1481
  select(
1479
1482
  {
1480
- class: "form-control d-none",
1483
+ class: "form-select d-none",
1481
1484
  id: "pageInputID",
1482
1485
  },
1483
1486
  pages
1484
1487
  .map((page) =>
1485
1488
  option({ value: page.name }, page.name)
1486
1489
  )
1487
- .join(",")
1490
+ .join("")
1488
1491
  )
1489
1492
  ),
1490
1493
  div(
@@ -1530,28 +1533,51 @@ router.get(
1530
1533
  })
1531
1534
  )
1532
1535
  ),
1536
+ // app name
1533
1537
  div(
1534
1538
  { class: "row pb-2" },
1535
1539
  div(
1536
1540
  { class: "col-sm-8" },
1537
1541
  label(
1538
1542
  {
1539
- for: "appNameInputId",
1543
+ for: "appFileInputId",
1540
1544
  class: "form-label fw-bold",
1541
1545
  },
1542
- req.__("App file")
1546
+ req.__("App name")
1543
1547
  ),
1544
1548
  input({
1545
1549
  type: "text",
1546
1550
  class: "form-control",
1547
- name: "appFile",
1548
- id: "appFileInputId",
1549
- placeholder: "app-debug",
1551
+ name: "appName",
1552
+ id: "appNameInputId",
1553
+ placeholder: "SaltcornMobileApp",
1550
1554
  })
1551
1555
  )
1552
1556
  ),
1557
+ // app version
1553
1558
  div(
1554
- { class: "row pb-3" },
1559
+ { class: "row pb-2" },
1560
+ div(
1561
+ { class: "col-sm-8" },
1562
+ label(
1563
+ {
1564
+ for: "appVersionInputId",
1565
+ class: "form-label fw-bold",
1566
+ },
1567
+ req.__("App version")
1568
+ ),
1569
+ input({
1570
+ type: "text",
1571
+ class: "form-control",
1572
+ name: "appVersion",
1573
+ id: "appVersionInputId",
1574
+ placeholder: "1.0.0",
1575
+ })
1576
+ )
1577
+ ),
1578
+ // server url
1579
+ div(
1580
+ { class: "row pb-2" },
1555
1581
  div(
1556
1582
  { class: "col-sm-8" },
1557
1583
  label(
@@ -1570,6 +1596,60 @@ router.get(
1570
1596
  })
1571
1597
  )
1572
1598
  ),
1599
+ // app icon
1600
+ div(
1601
+ { class: "row pb-2" },
1602
+ div(
1603
+ { class: "col-sm-8" },
1604
+ label(
1605
+ {
1606
+ for: "appIconInputId",
1607
+ class: "form-label fw-bold",
1608
+ },
1609
+ req.__("App icon")
1610
+ ),
1611
+ select(
1612
+ {
1613
+ class: "form-select",
1614
+ name: "appIcon",
1615
+ id: "appIconInputId",
1616
+ },
1617
+ [
1618
+ option({ value: "" }, ""),
1619
+ ...images.map((image) =>
1620
+ option({ value: image.location }, image.filename)
1621
+ ),
1622
+ ].join("")
1623
+ )
1624
+ )
1625
+ ),
1626
+ div(
1627
+ { class: "row pb-3" },
1628
+ div(
1629
+ { class: "col-sm-8" },
1630
+ label(
1631
+ {
1632
+ for: "splashPageInputId",
1633
+ class: "form-label fw-bold",
1634
+ },
1635
+ req.__("Splash Page")
1636
+ ),
1637
+ select(
1638
+ {
1639
+ class: "form-select",
1640
+ name: "splashPage",
1641
+ id: "splashPageInputId",
1642
+ },
1643
+ [
1644
+ option({ value: "" }, ""),
1645
+ ...pages.map((page) =>
1646
+ option({ value: page.name }, page.name)
1647
+ ),
1648
+ ].join("")
1649
+ )
1650
+ )
1651
+ ),
1652
+
1573
1653
  div(
1574
1654
  // TODO only for some tables?
1575
1655
  { class: "row pb-2" },
@@ -1688,8 +1768,11 @@ router.post(
1688
1768
  androidPlatform,
1689
1769
  iOSPlatform,
1690
1770
  useDocker,
1691
- appFile,
1771
+ appName,
1772
+ appVersion,
1773
+ appIcon,
1692
1774
  serverURL,
1775
+ splashPage,
1693
1776
  allowOfflineMode,
1694
1777
  } = req.body;
1695
1778
  if (!androidPlatform && !iOSPlatform) {
@@ -1736,8 +1819,11 @@ router.post(
1736
1819
  spawnParams.push("--buildForEmulator");
1737
1820
  }
1738
1821
  }
1739
- if (appFile) spawnParams.push("-a", appFile);
1822
+ if (appName) spawnParams.push("--appName", appName);
1823
+ if (appVersion) spawnParams.push("--appVersion", appVersion);
1824
+ if (appIcon) spawnParams.push("--appIcon", appIcon);
1740
1825
  if (serverURL) spawnParams.push("-s", serverURL);
1826
+ if (splashPage) spawnParams.push("--splashPage", splashPage);
1741
1827
  if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
1742
1828
  if (
1743
1829
  db.is_it_multi_tenant() &&
package/routes/tables.js CHANGED
@@ -11,6 +11,7 @@ const Table = require("@saltcorn/data/models/table");
11
11
  const File = require("@saltcorn/data/models/file");
12
12
  const View = require("@saltcorn/data/models/view");
13
13
  const User = require("@saltcorn/data/models/user");
14
+ const Trigger = require("@saltcorn/data/models/trigger");
14
15
  const {
15
16
  mkTable,
16
17
  renderForm,
@@ -579,6 +580,7 @@ router.get(
579
580
  const inbound_refs = [
580
581
  ...new Set(child_relations.map(({ table }) => table.name)),
581
582
  ];
583
+ const triggers = table.id ? Trigger.find({ table_id: table.id }) : [];
582
584
  let fieldCard;
583
585
  if (fields.length === 0) {
584
586
  fieldCard = [
@@ -647,6 +649,13 @@ router.get(
647
649
  inbound_refs.map((tnm) => link(`/table/${tnm}`, tnm)).join(", ") +
648
650
  "<br>"
649
651
  : "",
652
+ triggers.length
653
+ ? req.__("Table triggers: ") +
654
+ triggers
655
+ .map((t) => link(`/actions/configure/${t.id}`, t.name))
656
+ .join(", ") +
657
+ "<br>"
658
+ : "",
650
659
  !table.external &&
651
660
  a(
652
661
  {
package/s3storage.js CHANGED
@@ -1,3 +1,4 @@
1
+ require("aws-sdk/lib/maintenance_mode_message").suppress = true;
1
2
  const aws = require("aws-sdk");
2
3
  const multer = require("multer");
3
4
  const multerS3 = require("multer-s3");