@saltcorn/server 0.9.0-beta.0 → 0.9.0-beta.2

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/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.9.0-beta.0",
3
+ "version": "0.9.0-beta.2",
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.9.0-beta.0",
10
- "@saltcorn/builder": "0.9.0-beta.0",
11
- "@saltcorn/data": "0.9.0-beta.0",
12
- "@saltcorn/admin-models": "0.9.0-beta.0",
13
- "@saltcorn/filemanager": "0.9.0-beta.0",
14
- "@saltcorn/markup": "0.9.0-beta.0",
15
- "@saltcorn/sbadmin2": "0.9.0-beta.0",
9
+ "@saltcorn/base-plugin": "0.9.0-beta.2",
10
+ "@saltcorn/builder": "0.9.0-beta.2",
11
+ "@saltcorn/data": "0.9.0-beta.2",
12
+ "@saltcorn/admin-models": "0.9.0-beta.2",
13
+ "@saltcorn/filemanager": "0.9.0-beta.2",
14
+ "@saltcorn/markup": "0.9.0-beta.2",
15
+ "@saltcorn/sbadmin2": "0.9.0-beta.2",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
@@ -45,6 +45,7 @@
45
45
  "node-fetch": "2.6.9",
46
46
  "node-watch": "^0.7.2",
47
47
  "notp": "2.0.3",
48
+ "npm-registry-fetch": "16.0.0",
48
49
  "passport": "^0.6.0",
49
50
  "passport-custom": "^1.1.1",
50
51
  "passport-http-bearer": "^1.0.1",
@@ -55,7 +56,7 @@
55
56
  "qrcode": "1.5.1",
56
57
  "resize-with-sharp-or-jimp": "0.1.6",
57
58
  "socket.io": "4.6.0",
58
- "systeminformation": "^5.11.12",
59
+ "systeminformation": "^5.21.7",
59
60
  "thirty-two": "1.0.2",
60
61
  "tmp-promise": "^3.0.2",
61
62
  "uuid": "^8.2.0",
@@ -446,3 +446,7 @@ Copyright (c) 2017 Taha Paksu
446
446
  padding: 5px 9px;
447
447
  z-index: 1;
448
448
  }
449
+
450
+ div.builder-config-field {
451
+ margin-top: 0.5rem;
452
+ }
@@ -78,6 +78,15 @@ function apply_showif() {
78
78
  console.error(e);
79
79
  }
80
80
  });
81
+ $("[data-dyn-href]").each(function (ix, element) {
82
+ const e = $(element);
83
+ const rec = get_form_record(e);
84
+ const href = new Function(
85
+ `{${Object.keys(rec).join(",")}}`,
86
+ "return " + e.attr("data-dyn-href")
87
+ )(rec);
88
+ e.attr("href", href);
89
+ });
81
90
  $("[data-calc-options]").each(function (ix, element) {
82
91
  var e = $(element);
83
92
  var data = JSON.parse(decodeURIComponent(e.attr("data-calc-options")));
@@ -335,18 +344,21 @@ function splitTargetMatch(elemValue, target, keySpec) {
335
344
  return elemValueShort === target;
336
345
  }
337
346
 
338
- function get_form_record(e, select_labels) {
347
+ function get_form_record(e_in, select_labels) {
339
348
  const rec = {};
340
- e.closest(".form-namespace")
341
- .find("input[name],select[name]")
342
- .each(function () {
343
- const name = $(this).attr("data-fieldname") || $(this).attr("name");
344
- if (select_labels && $(this).prop("tagName").toLowerCase() === "select")
345
- rec[name] = $(this).find("option:selected").text();
346
- else if ($(this).prop("type") === "checkbox")
347
- rec[name] = $(this).prop("checked");
348
- else rec[name] = $(this).val();
349
- });
349
+
350
+ const e = e_in.viewname
351
+ ? $(`form[data-viewname=${e_in.viewname}]`)
352
+ : e_in.closest(".form-namespace");
353
+
354
+ e.find("input[name],select[name],textarea[name]").each(function () {
355
+ const name = $(this).attr("data-fieldname") || $(this).attr("name");
356
+ if (select_labels && $(this).prop("tagName").toLowerCase() === "select")
357
+ rec[name] = $(this).find("option:selected").text();
358
+ else if ($(this).prop("type") === "checkbox")
359
+ rec[name] = $(this).prop("checked");
360
+ else rec[name] = $(this).val();
361
+ });
350
362
  return rec;
351
363
  }
352
364
  function showIfFormulaInputs(e, fml) {
@@ -945,15 +957,26 @@ function emptyAlerts() {
945
957
  $("#toasts-area").html("");
946
958
  }
947
959
 
948
- function press_store_button(clicked) {
960
+ function press_store_button(clicked, keepOld) {
949
961
  let btn = clicked;
950
962
  if ($(clicked).is("form")) btn = $(clicked).find("button[type=submit]");
951
-
963
+ if (keepOld) {
964
+ const oldText = $(btn).html();
965
+ $(btn).data("old-text", oldText);
966
+ }
952
967
  const width = $(btn).width();
953
968
  $(btn).html('<i class="fas fa-spinner fa-spin"></i>').width(width);
954
969
  }
955
970
 
956
- function common_done(res, isWeb = true) {
971
+ function restore_old_button(btnId) {
972
+ const btn = $(`#${btnId}`);
973
+ const oldText = $(btn).data("old-text");
974
+ btn.html(oldText);
975
+ btn.css({ width: "" });
976
+ btn.removeData("old-text");
977
+ }
978
+
979
+ function common_done(res, viewname, isWeb = true) {
957
980
  const handle = (element, fn) => {
958
981
  if (Array.isArray(element)) for (const current of element) fn(current);
959
982
  else fn(element);
@@ -982,6 +1005,18 @@ function common_done(res, isWeb = true) {
982
1005
  });
983
1006
  });
984
1007
  }
1008
+ if (res.set_fields && viewname) {
1009
+ Object.keys(res.set_fields).forEach((k) => {
1010
+ const form = $(`form[data-viewname=${viewname}]`);
1011
+ const input = form.find(
1012
+ `input[name=${k}], textarea[name=${k}], select[name=${k}]`
1013
+ );
1014
+ if (input.attr("type") === "checkbox")
1015
+ input.prop("checked", res.set_fields[k]);
1016
+ else input.val(res.set_fields[k]);
1017
+ input.trigger("set_form_field");
1018
+ });
1019
+ }
985
1020
  if (res.goto && !isWeb)
986
1021
  // TODO ch
987
1022
  notifyAlert({
@@ -472,3 +472,17 @@ div.unread-notify {
472
472
  cursor: pointer;
473
473
  text-decoration: underline;
474
474
  }
475
+
476
+ table.help-md thead {
477
+ border-bottom: 1px solid black;
478
+ }
479
+
480
+ table.help-md {
481
+ margin-bottom: 1em;
482
+ }
483
+
484
+ table.help-md td:nth-child(2),
485
+ table.help-md th:nth-child(2) {
486
+ padding-left: 10px;
487
+ padding-right: 10px;
488
+ }
@@ -198,8 +198,8 @@ function clear_state(omit_fields_str, e) {
198
198
  pjax_to(newUrl, e);
199
199
  }
200
200
 
201
- function ajax_done(res) {
202
- common_done(res);
201
+ function ajax_done(res, viewname) {
202
+ common_done(res, viewname);
203
203
  }
204
204
 
205
205
  function view_post(viewname, route, data, onDone, sendState) {
@@ -220,7 +220,7 @@ function view_post(viewname, route, data, onDone, sendState) {
220
220
  })
221
221
  .done(function (res) {
222
222
  if (onDone) onDone(res);
223
- ajax_done(res);
223
+ ajax_done(res, viewname);
224
224
  })
225
225
  .fail(function (res) {
226
226
  notifyAlert({ type: "danger", text: res.responseText });
@@ -340,7 +340,20 @@ function ajax_modal(url, opts = {}) {
340
340
  $("body").css("overflow", "");
341
341
  });
342
342
  },
343
+ ...(opts.onError
344
+ ? {
345
+ error: opts.onError,
346
+ }
347
+ : {}),
348
+ });
349
+ }
350
+
351
+ function selectVersionError(res, btnId) {
352
+ notifyAlert({
353
+ type: "danger",
354
+ text: res.responseJSON?.error || "unknown error",
343
355
  });
356
+ restore_old_button(btnId);
344
357
  }
345
358
 
346
359
  function saveAndContinue(e, k) {
@@ -491,7 +504,7 @@ function ajaxSubmitForm(e) {
491
504
  var no_reload = $("#scmodal").hasClass("no-submit-reload");
492
505
  $("#scmodal").modal("hide");
493
506
  if (!no_reload) location.reload();
494
- else common_done(res);
507
+ else common_done(res, form.attr("data-viewname"));
495
508
  },
496
509
  error: function (request) {
497
510
  var title = request.getResponseHeader("Page-Title");
package/routes/admin.js CHANGED
@@ -1514,6 +1514,8 @@ router.get(
1514
1514
  const plugins = (await Plugin.find()).filter(
1515
1515
  (plugin) => ["base", "sbadmin2"].indexOf(plugin.name) < 0
1516
1516
  );
1517
+ const builderSettings =
1518
+ getState().getConfig("mobile_builder_settings") || {};
1517
1519
  send_admin_page({
1518
1520
  res,
1519
1521
  req,
@@ -1574,7 +1576,14 @@ router.get(
1574
1576
  onClick: "showEntrySelect('view')",
1575
1577
  },
1576
1578
  div(
1577
- { class: "nav-link active", id: "viewNavLinkID" },
1579
+ {
1580
+ class: `nav-link ${
1581
+ !builderSettings.entryPointType || builderSettings.entryPointType === "view"
1582
+ ? "active"
1583
+ : ""
1584
+ }`,
1585
+ id: "viewNavLinkID",
1586
+ },
1578
1587
  req.__("View")
1579
1588
  )
1580
1589
  ),
@@ -1584,7 +1593,14 @@ router.get(
1584
1593
  onClick: "showEntrySelect('page')",
1585
1594
  },
1586
1595
  div(
1587
- { class: "nav-link", id: "pageNavLinkID" },
1596
+ {
1597
+ class: `nav-link ${
1598
+ builderSettings.entryPointType === "page"
1599
+ ? "active"
1600
+ : ""
1601
+ }`,
1602
+ id: "pageNavLinkID",
1603
+ },
1588
1604
  req.__("Page")
1589
1605
  )
1590
1606
  )
@@ -1592,25 +1608,54 @@ router.get(
1592
1608
  // select entry-view
1593
1609
  select(
1594
1610
  {
1595
- class: "form-select",
1596
- name: "entryPoint",
1611
+ class: `form-select ${
1612
+ builderSettings.entryPointType === "page"
1613
+ ? "d-none"
1614
+ : ""
1615
+ }`,
1616
+ ...(!builderSettings.entryPointType || builderSettings.entryPointType === "view"
1617
+ ? { name: "entryPoint" }
1618
+ : {}),
1597
1619
  id: "viewInputID",
1598
1620
  },
1599
1621
  views
1600
1622
  .map((view) =>
1601
- option({ value: view.name }, view.name)
1623
+ option(
1624
+ {
1625
+ value: view.name,
1626
+ selected:
1627
+ builderSettings.entryPointType === "view" &&
1628
+ builderSettings.entryPoint === view.name,
1629
+ },
1630
+ view.name
1631
+ )
1602
1632
  )
1603
1633
  .join(",")
1604
1634
  ),
1605
1635
  // select entry-page
1606
1636
  select(
1607
1637
  {
1608
- class: "form-select d-none",
1638
+ class: `form-select ${
1639
+ !builderSettings.entryPointType || builderSettings.entryPointType === "view"
1640
+ ? "d-none"
1641
+ : ""
1642
+ }`,
1643
+ ...(builderSettings.entryPointType === "page"
1644
+ ? { name: "entryPoint" }
1645
+ : {}),
1609
1646
  id: "pageInputID",
1610
1647
  },
1611
1648
  pages
1612
1649
  .map((page) =>
1613
- option({ value: page.name }, page.name)
1650
+ option(
1651
+ {
1652
+ value: page.name,
1653
+ selected:
1654
+ builderSettings.entryPointType === "page" &&
1655
+ builderSettings.entryPoint === page.name,
1656
+ },
1657
+ page.name
1658
+ )
1614
1659
  )
1615
1660
  .join("")
1616
1661
  )
@@ -1631,6 +1676,7 @@ router.get(
1631
1676
  name: "androidPlatform",
1632
1677
  id: "androidCheckboxId",
1633
1678
  onClick: "toggle_android_platform()",
1679
+ checked: builderSettings.androidPlatform === "on",
1634
1680
  })
1635
1681
  )
1636
1682
  ),
@@ -1645,6 +1691,7 @@ router.get(
1645
1691
  class: "form-check-input",
1646
1692
  name: "iOSPlatform",
1647
1693
  id: "iOSCheckboxId",
1694
+ checked: builderSettings.iOSPlatform === "on",
1648
1695
  })
1649
1696
  )
1650
1697
  )
@@ -1658,7 +1705,8 @@ router.get(
1658
1705
  class: "form-check-input",
1659
1706
  name: "useDocker",
1660
1707
  id: "dockerCheckboxId",
1661
- hidden: true,
1708
+ hidden: builderSettings.androidPlatform !== "on",
1709
+ checked: builderSettings.useDocker === "on",
1662
1710
  })
1663
1711
  )
1664
1712
  ),
@@ -1680,6 +1728,7 @@ router.get(
1680
1728
  name: "appName",
1681
1729
  id: "appNameInputId",
1682
1730
  placeholder: "SaltcornMobileApp",
1731
+ value: builderSettings.appName || "",
1683
1732
  })
1684
1733
  )
1685
1734
  ),
@@ -1701,6 +1750,7 @@ router.get(
1701
1750
  name: "appVersion",
1702
1751
  id: "appVersionInputId",
1703
1752
  placeholder: "1.0.0",
1753
+ value: builderSettings.appVersion || "",
1704
1754
  })
1705
1755
  )
1706
1756
  ),
@@ -1721,6 +1771,7 @@ router.get(
1721
1771
  class: "form-control",
1722
1772
  name: "serverURL",
1723
1773
  id: "serverURLInputId",
1774
+ value: builderSettings.serverURL || "",
1724
1775
  placeholder: getState().getConfig("base_url") || "",
1725
1776
  })
1726
1777
  )
@@ -1746,7 +1797,14 @@ router.get(
1746
1797
  [
1747
1798
  option({ value: "" }, ""),
1748
1799
  ...images.map((image) =>
1749
- option({ value: image.location }, image.filename)
1800
+ option(
1801
+ {
1802
+ value: image.location,
1803
+ selected:
1804
+ builderSettings.appIcon === image.location,
1805
+ },
1806
+ image.filename
1807
+ )
1750
1808
  ),
1751
1809
  ].join("")
1752
1810
  )
@@ -1772,7 +1830,14 @@ router.get(
1772
1830
  [
1773
1831
  option({ value: "" }, ""),
1774
1832
  ...pages.map((page) =>
1775
- option({ value: page.name }, page.name)
1833
+ option(
1834
+ {
1835
+ value: page.name,
1836
+ selected:
1837
+ builderSettings.splashPage === page.name,
1838
+ },
1839
+ page.name
1840
+ )
1776
1841
  ),
1777
1842
  ].join("")
1778
1843
  )
@@ -1788,8 +1853,7 @@ router.get(
1788
1853
  id: "autoPublLoginId",
1789
1854
  class: "form-check-input me-2",
1790
1855
  name: "autoPublicLogin",
1791
- value: "autoPublicLogin",
1792
- checked: false,
1856
+ checked: builderSettings.autoPublicLogin === "on",
1793
1857
  }),
1794
1858
  label(
1795
1859
  {
@@ -1810,9 +1874,8 @@ router.get(
1810
1874
  id: "offlineModeBoxId",
1811
1875
  class: "form-check-input me-2",
1812
1876
  name: "allowOfflineMode",
1813
- value: "allowOfflineMode",
1814
1877
  onClick: "toggle_tbl_sync()",
1815
- checked: true,
1878
+ checked: builderSettings.allowOfflineMode === "on",
1816
1879
  }),
1817
1880
  label(
1818
1881
  {
@@ -1828,6 +1891,7 @@ router.get(
1828
1891
  {
1829
1892
  id: "tblSyncSelectorId",
1830
1893
  class: "row pb-3",
1894
+ hidden: builderSettings.allowOfflineMode !== "on",
1831
1895
  },
1832
1896
  div(
1833
1897
  label(
@@ -1864,6 +1928,12 @@ router.get(
1864
1928
  id: `${table.name}_unsynched_opt`,
1865
1929
  value: table.name,
1866
1930
  label: table.name,
1931
+ hidden:
1932
+ builderSettings.synchedTables?.indexOf(
1933
+ table.name
1934
+ ) >= 0
1935
+ ? true
1936
+ : false,
1867
1937
  })
1868
1938
  )
1869
1939
  )
@@ -1908,7 +1978,12 @@ router.get(
1908
1978
  id: `${table.name}_synched_opt`,
1909
1979
  value: table.name,
1910
1980
  label: table.name,
1911
- hidden: "true",
1981
+ hidden:
1982
+ builderSettings.synchedTables?.indexOf(
1983
+ table.name
1984
+ ) >= 0
1985
+ ? false
1986
+ : true,
1912
1987
  })
1913
1988
  )
1914
1989
  )
@@ -1954,7 +2029,12 @@ router.get(
1954
2029
  id: `${plugin.name}_excluded_opt`,
1955
2030
  value: plugin.name,
1956
2031
  label: plugin.name,
1957
- hidden: "true",
2032
+ hidden:
2033
+ builderSettings.excludedPlugins?.indexOf(
2034
+ plugin.name
2035
+ ) >= 0
2036
+ ? false
2037
+ : true,
1958
2038
  })
1959
2039
  )
1960
2040
  )
@@ -1999,7 +2079,12 @@ router.get(
1999
2079
  id: `${plugin.name}_included_opt`,
2000
2080
  value: plugin.name,
2001
2081
  label: plugin.name,
2002
- // hidden: "true",
2082
+ hidden:
2083
+ builderSettings.excludedPlugins?.indexOf(
2084
+ plugin.name
2085
+ ) >= 0
2086
+ ? true
2087
+ : false,
2003
2088
  })
2004
2089
  )
2005
2090
  )
@@ -2116,6 +2201,8 @@ router.post(
2116
2201
  synchedTables,
2117
2202
  includedPlugins,
2118
2203
  } = req.body;
2204
+ if (!includedPlugins) includedPlugins = [];
2205
+ if (!synchedTables) synchedTables = [];
2119
2206
  if (!androidPlatform && !iOSPlatform) {
2120
2207
  return res.json({
2121
2208
  error: req.__("Please select at least one platform (android or iOS)."),
@@ -2167,9 +2254,9 @@ router.post(
2167
2254
  if (splashPage) spawnParams.push("--splashPage", splashPage);
2168
2255
  if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
2169
2256
  if (autoPublicLogin) spawnParams.push("--autoPublicLogin");
2170
- if (synchedTables?.length > 0)
2257
+ if (synchedTables.length > 0)
2171
2258
  spawnParams.push("--synchedTables", ...synchedTables.map((tbl) => tbl));
2172
- if (includedPlugins?.length > 0)
2259
+ if (includedPlugins.length > 0)
2173
2260
  spawnParams.push(
2174
2261
  "--includedPlugins",
2175
2262
  ...includedPlugins.map((pluginName) => pluginName)
@@ -2180,6 +2267,30 @@ router.post(
2180
2267
  ) {
2181
2268
  spawnParams.push("--tenantAppName", db.getTenantSchema());
2182
2269
  }
2270
+ const excludedPlugins = (await Plugin.find())
2271
+ .filter(
2272
+ (plugin) =>
2273
+ ["base", "sbadmin2"].indexOf(plugin.name) < 0 &&
2274
+ includedPlugins.indexOf(plugin.name) < 0
2275
+ )
2276
+ .map((plugin) => plugin.name);
2277
+ await getState().setConfig("mobile_builder_settings", {
2278
+ entryPoint,
2279
+ entryPointType,
2280
+ androidPlatform,
2281
+ iOSPlatform,
2282
+ useDocker,
2283
+ appName,
2284
+ appVersion,
2285
+ appIcon,
2286
+ serverURL,
2287
+ splashPage,
2288
+ autoPublicLogin,
2289
+ allowOfflineMode,
2290
+ synchedTables: synchedTables,
2291
+ includedPlugins: includedPlugins,
2292
+ excludedPlugins,
2293
+ });
2183
2294
  // end http call, return the out directory name
2184
2295
  // the gui polls for results
2185
2296
  res.json({ build_dir_name: outDirName });
package/routes/menu.js CHANGED
@@ -44,7 +44,7 @@ const menuForm = async (req) => {
44
44
  const views = await View.find({}, { orderBy: "name", nocase: true });
45
45
  const pages = await Page.find({}, { orderBy: "name", nocase: true });
46
46
  const roles = await User.get_roles();
47
- const tables = await Table.find({});
47
+ const tables = await Table.find_with_external({});
48
48
  const dynTableOptions = tables.map((t) => t.name);
49
49
  const dynOrderFieldOptions = {},
50
50
  dynSectionFieldOptions = {};