@saltcorn/server 1.0.0-beta.17 → 1.0.0-beta.19

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,20 +1,20 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "1.0.0-beta.17",
3
+ "version": "1.0.0-beta.19",
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": "1.0.0-beta.17",
11
- "@saltcorn/builder": "1.0.0-beta.17",
12
- "@saltcorn/data": "1.0.0-beta.17",
13
- "@saltcorn/admin-models": "1.0.0-beta.17",
14
- "@saltcorn/filemanager": "1.0.0-beta.17",
15
- "@saltcorn/markup": "1.0.0-beta.17",
16
- "@saltcorn/plugins-loader": "1.0.0-beta.17",
17
- "@saltcorn/sbadmin2": "1.0.0-beta.17",
10
+ "@saltcorn/base-plugin": "1.0.0-beta.19",
11
+ "@saltcorn/builder": "1.0.0-beta.19",
12
+ "@saltcorn/data": "1.0.0-beta.19",
13
+ "@saltcorn/admin-models": "1.0.0-beta.19",
14
+ "@saltcorn/filemanager": "1.0.0-beta.19",
15
+ "@saltcorn/markup": "1.0.0-beta.19",
16
+ "@saltcorn/plugins-loader": "1.0.0-beta.19",
17
+ "@saltcorn/sbadmin2": "1.0.0-beta.19",
18
18
  "@socket.io/cluster-adapter": "^0.2.1",
19
19
  "@socket.io/sticky": "^1.0.1",
20
20
  "adm-zip": "0.5.10",
@@ -764,6 +764,42 @@ function doMobileTransforms() {
764
764
  }
765
765
  });
766
766
 
767
+ $("[mobile-youtube-video]").each(function () {
768
+ const jThis = $(this);
769
+ const src = jThis.attr("src");
770
+ if (src) {
771
+ const rndid = `m-video-${Math.floor(Math.random() * 16777215).toString(
772
+ 16
773
+ )}`;
774
+ const url = new URL(src);
775
+ const path = url.pathname;
776
+ const imageId = path.split("/").pop();
777
+ const thumbnailContainer = document.createElement("div");
778
+ thumbnailContainer.className = "mobile-thumbnail-container";
779
+ thumbnailContainer.id = rndid;
780
+ const img = document.createElement("img");
781
+ img.src = `https://img.youtube.com/vi/${imageId}/0.jpg`;
782
+ img.style = "width: 100%; max-width: 600px;";
783
+ img.id = rndid;
784
+ img.setAttribute(
785
+ "onclick",
786
+ `openInAppBrowser('${src.replace(
787
+ "com/embed",
788
+ "com/watch"
789
+ )}', '${rndid}')`
790
+ );
791
+ thumbnailContainer.appendChild(img);
792
+ const spinner = document.createElement("div");
793
+ spinner.className = "mobile-thumbnail-spinner-overlay";
794
+ const spinnerInner = document.createElement("div");
795
+ spinnerInner.className = "d-none spinner-border text-light";
796
+ spinnerInner.setAttribute("role", "status");
797
+ spinner.appendChild(spinnerInner);
798
+ thumbnailContainer.appendChild(spinner);
799
+ jThis.replaceWith(thumbnailContainer);
800
+ }
801
+ });
802
+
767
803
  $("button").each(function () {
768
804
  for (const [k, v] of Object.entries({ onclick: replacers.onclick })) {
769
805
  for ({ web, mobile } of v) replaceAttr(this, k, v.web, v.mobile);
@@ -1870,6 +1906,8 @@ function close_saltcorn_modal() {
1870
1906
  }
1871
1907
  }
1872
1908
 
1909
+ let _sc_currently_reloading;
1910
+
1873
1911
  function reload_embedded_view(viewname, new_query_string) {
1874
1912
  const isNode = getIsNode();
1875
1913
  const updater = ($e, res) => {
@@ -1897,15 +1935,19 @@ function reload_embedded_view(viewname, new_query_string) {
1897
1935
  url = url.split("?")[0] + "?" + new_query_string;
1898
1936
  }
1899
1937
  if (isNode) {
1938
+ if (url === _sc_currently_reloading) return;
1939
+ _sc_currently_reloading = url;
1900
1940
  $.ajax(url, {
1901
1941
  headers: {
1902
1942
  pjaxpageload: "true",
1903
1943
  localizedstate: "true", //no admin bar
1904
1944
  },
1905
1945
  success: function (res, textStatus, request) {
1946
+ _sc_currently_reloading = null;
1906
1947
  updater($e, res);
1907
1948
  },
1908
1949
  error: function (res) {
1950
+ _sc_currently_reloading = null;
1909
1951
  if (!checkNetworkError(res))
1910
1952
  notifyAlert({ type: "danger", text: res.responseText });
1911
1953
  },
@@ -590,3 +590,20 @@ button.monospace-copy-btn {
590
590
  .custom-file-label {
591
591
  margin-left: 10px;
592
592
  }
593
+
594
+ .mobile-thumbnail-container {
595
+ position: relative;
596
+ display: inline-block;
597
+ }
598
+
599
+ .mobile-thumbnail-spinner-overlay {
600
+ position: absolute;
601
+ top: 50%;
602
+ left: 50%;
603
+ transform: translate(-50%, -50%);
604
+ z-index: 10;
605
+ }
606
+
607
+ .mobile-thumbnail-container img {
608
+ display: block;
609
+ }
@@ -629,6 +629,7 @@ function checkNetworkError(e) {
629
629
  setTimeout(() => {
630
630
  scNetworkErrorSignaled = false;
631
631
  }, 1000);
632
+ console.error("Network error", e);
632
633
  notifyAlert({
633
634
  type: "danger",
634
635
  text: "Network connection error",
package/routes/admin.js CHANGED
@@ -378,7 +378,9 @@ router.get(
378
378
  : { type: "blank", contents: "" },
379
379
  {
380
380
  type: "card",
381
- title: req.__("Snapshots"),
381
+ title:
382
+ req.__("Snapshots") +
383
+ `<a href="javascript:ajax_modal('/admin/help/Snapshots?')"><i class="fas fa-question-circle ms-1"></i></a>`,
382
384
  titleAjaxIndicator: true,
383
385
  contents: div(
384
386
  p(
@@ -1266,6 +1268,50 @@ const pullCordovaBuilder = (req, res) => {
1266
1268
  });
1267
1269
  };
1268
1270
 
1271
+ const tryInstallSdNotify = (req, res) => {
1272
+ const child = spawn("npm", ["install", "-g", "sd-notify"], {
1273
+ stdio: ["ignore", "pipe", "pipe"],
1274
+ });
1275
+ return new Promise((resolve, reject) => {
1276
+ child.stdout.on("data", (data) => {
1277
+ res.write(data);
1278
+ });
1279
+ child.stderr?.on("data", (data) => {
1280
+ res.write(data);
1281
+ });
1282
+ child.on("exit", function (code, signal) {
1283
+ resolve(code);
1284
+ });
1285
+ child.on("error", (msg) => {
1286
+ const message = msg.message ? msg.message : msg.code;
1287
+ res.write(req.__("Error: ") + message + "\n");
1288
+ resolve(msg.code);
1289
+ });
1290
+ });
1291
+ };
1292
+
1293
+ const pruneDocker = (req, res) => {
1294
+ const child = spawn("docker", ["image", "prune", "-f"], {
1295
+ stdio: ["ignore", "pipe", "pipe"],
1296
+ });
1297
+ return new Promise((resolve, reject) => {
1298
+ child.stdout.on("data", (data) => {
1299
+ res.write(data);
1300
+ });
1301
+ child.stderr?.on("data", (data) => {
1302
+ res.write(data);
1303
+ });
1304
+ child.on("exit", function (code, signal) {
1305
+ resolve(code);
1306
+ });
1307
+ child.on("error", (msg) => {
1308
+ const message = msg.message ? msg.message : msg.code;
1309
+ res.write(req.__("Error: ") + message + "\n");
1310
+ resolve(msg.code);
1311
+ });
1312
+ });
1313
+ };
1314
+
1269
1315
  /*
1270
1316
  * fetch available saltcorn versions and show a dialog to select one
1271
1317
  */
@@ -1449,10 +1495,26 @@ const doInstall = async (req, res, version, deepClean, runPull) => {
1449
1495
  res.write(data);
1450
1496
  });
1451
1497
  child.on("exit", async function (code, signal) {
1452
- if (code === 0 && runPull) {
1453
- res.write(req.__("Pulling the cordova-builder docker image...") + "\n");
1454
- const pullCode = await pullCordovaBuilder(req, res);
1455
- res.write(req.__("Pull done with code %s", pullCode) + "\n");
1498
+ if (code === 0) {
1499
+ if (deepClean) {
1500
+ res.write(req.__("Installing sd-notify") + "\n");
1501
+ const sdNotifyCode = await tryInstallSdNotify(req, res);
1502
+ res.write(
1503
+ req.__("sd-notify install done with code %s", sdNotifyCode) + "\n"
1504
+ );
1505
+ }
1506
+ if (runPull) {
1507
+ res.write(
1508
+ req.__("Pulling the cordova-builder docker image...") + "\n"
1509
+ );
1510
+ const pullCode = await pullCordovaBuilder(req, res);
1511
+ res.write(req.__("Pull done with code %s", pullCode) + "\n");
1512
+ if (pullCode === 0) {
1513
+ res.write(req.__("Pruning docker...") + "\n");
1514
+ const pruneCode = await pruneDocker(req, res);
1515
+ res.write(req.__("Prune done with code %s", pruneCode) + "\n");
1516
+ }
1517
+ }
1456
1518
  }
1457
1519
  res.end(
1458
1520
  version === "latest"
package/routes/fields.js CHANGED
@@ -136,6 +136,9 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
136
136
  sublabel: req.__("Calculated from other fields with a formula"),
137
137
  type: "Bool",
138
138
  disabled: !!id,
139
+ help: {
140
+ topic: "Calculated fields",
141
+ },
139
142
  }),
140
143
  new Field({
141
144
  label: req.__("Required"),
@@ -169,6 +172,9 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
169
172
  type: "Bool",
170
173
  disabled: !!id,
171
174
  showIf: { calculated: true },
175
+ help: {
176
+ topic: "Calculated fields",
177
+ },
172
178
  }),
173
179
  new Field({
174
180
  label: req.__("Protected"),
@@ -176,6 +182,9 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
176
182
  sublabel: req.__("Set role to access"),
177
183
  type: "Bool",
178
184
  showIf: { calculated: false },
185
+ help: {
186
+ topic: "Protected fields",
187
+ },
179
188
  }),
180
189
  {
181
190
  label: req.__("Minimum role to write"),
package/routes/plugins.js CHANGED
@@ -1235,14 +1235,16 @@ router.get(
1235
1235
  let latest =
1236
1236
  update_permitted &&
1237
1237
  (await get_latest_npm_version(plugin_db.location, 1000));
1238
- if (
1239
- latest &&
1240
- !isVersionSupported(latest, await load_plugins.getEngineInfos(plugin_db)) // with cache
1241
- ) {
1242
- // with force fetch
1238
+ let engineInfos = await load_plugins.getEngineInfos(plugin_db); // with cache
1239
+ let forceFetch = true;
1240
+ if (latest && !engineInfos[latest]) {
1241
+ engineInfos = await load_plugins.getEngineInfos(plugin_db, forceFetch);
1242
+ forceFetch = false;
1243
+ }
1244
+ if (latest && !isVersionSupported(latest, engineInfos)) {
1243
1245
  latest = supportedVersion(
1244
1246
  latest,
1245
- await load_plugins.getEngineInfos(plugin_db, true)
1247
+ await load_plugins.getEngineInfos(plugin_db, forceFetch)
1246
1248
  );
1247
1249
  }
1248
1250
  const can_update = update_permitted && latest && mod.version !== latest;
package/routes/tables.js CHANGED
@@ -192,6 +192,9 @@ const tableForm = async (table, req) => {
192
192
  "if(!this.checked && !confirm('Are you sure? This will delete all history')) {this.checked = true; return false}",
193
193
  },
194
194
  type: "Bool",
195
+ help: {
196
+ topic: "Table history",
197
+ },
195
198
  },
196
199
  ...(table.name === "users"
197
200
  ? []
@@ -798,7 +801,7 @@ router.get(
798
801
  key: (r) => attribBadges(r),
799
802
  },
800
803
  { label: req.__("Variable name"), key: (t) => code(t.name) },
801
- ...(table.external || db.isSQLite
804
+ ...(table.external
802
805
  ? []
803
806
  : [
804
807
  {