@saltcorn/server 0.8.5 → 0.8.6-beta.1

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
@@ -1150,5 +1150,8 @@
1150
1150
  "Tenant Base URL": "Tenant Base URL",
1151
1151
  "Base hostname for newly created tenants. If unset, defaults to hostname": "Base hostname for newly created tenants. If unset, defaults to hostname",
1152
1152
  "Redirect unathorized": "Redirect unathorized",
1153
- "If tenant creation is not authorized, redirect to this URL": "If tenant creation is not authorized, redirect to this URL"
1153
+ "If tenant creation is not authorized, redirect to this URL": "If tenant creation is not authorized, redirect to this URL",
1154
+ "Import table %s": "Import table %s",
1155
+ "Import CSV": "Import CSV",
1156
+ "Show configuration object": "Show configuration object"
1154
1157
  }
package/locales/pl.json CHANGED
@@ -1122,5 +1122,36 @@
1122
1122
  "Mem usage": "Zużycie pamięci",
1123
1123
  "The field that will be shown to the user when choosing a value": "Pole jakie będzie pokazane użytkownikowi przy wyborze wartości",
1124
1124
  "String value must match regular expression": "Wartość ciągu musi odpowiadać wyrażeniu regularnemu",
1125
- "Modules up-to-date. Please restart server": "Moduły zaktualizowane. Proszę zresetować serwer"
1125
+ "Modules up-to-date. Please restart server": "Moduły zaktualizowane. Proszę zresetować serwer",
1126
+ "Install git plugins": "Zainstaluj wtyczki git",
1127
+ "Set available npm modules": "Ustaw dostępne moduły npm",
1128
+ "Only store modules can be installed on tenant instances": "Tylko moduły ze sklepu mogą być zainstalowane na instancjach tenantów",
1129
+ "Unsafe modules": "Moduly niezabezpieczone",
1130
+ "Notifications": "Powiadomienia",
1131
+ "No notifications": "Bez powiadomień",
1132
+ "In user menu": "W menu użytkownika",
1133
+ "Show notifications in the user menu": "Pokaż powiadomienia w menu użytkownika",
1134
+ "Notification settings": "Ustawienia powiadomień",
1135
+ "PWA": "PWA",
1136
+ "Progressive Web Application": "Progresywna Aplikacja Webowa",
1137
+ "Display": "Wyświetlanie",
1138
+ "Progressive Web Application enabled": "Progresywna Aplikacja Webowa włączona",
1139
+ "Saltcorn test email": "E-mail testowy Saltcorn",
1140
+ "Hello from Saltcorn": "Cześć od Saltcorn",
1141
+ "Set colors": "Ustaw kolory",
1142
+ "Theme color": "Kolor motywu",
1143
+ "Background color": "Kolor tła",
1144
+ "Table provider": "Dostawca tabeli",
1145
+ "Database table": "Tabela bazy danych",
1146
+ "Configure provider": "Skonfiguruj dostawcę",
1147
+ "In scope:": "W ramach:",
1148
+ "SSL expiry": "Wygaśnięcie SSL",
1149
+ "A page with this name already exists": "Strona z tą nazwą już istnieje",
1150
+ "Tenant Base URL": "Bazowy URL tenanta",
1151
+ "Base hostname for newly created tenants. If unset, defaults to hostname": "Bazowa nazwa hosta dla nowo utworzonych tenantów. Jeżeli nie ustawione, domyślnie do nazwy hosta",
1152
+ "Redirect unathorized": "Przekierowanie nieautoryzowane",
1153
+ "If tenant creation is not authorized, redirect to this URL": "Jeżeli utworzenie tenanta nie jest autoryzowane, przekieruj do tego URL",
1154
+ "Import table %s": "Importuj tabelę %s",
1155
+ "Import CSV": "Importuj CSV",
1156
+ "Show configuration object": "Pokaż obiekt konfiguracji"
1126
1157
  }
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.8.5",
3
+ "version": "0.8.6-beta.1",
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.5",
10
- "@saltcorn/builder": "0.8.5",
11
- "@saltcorn/data": "0.8.5",
12
- "@saltcorn/admin-models": "0.8.5",
13
- "@saltcorn/filemanager": "0.8.5",
14
- "@saltcorn/markup": "0.8.5",
15
- "@saltcorn/sbadmin2": "0.8.5",
9
+ "@saltcorn/base-plugin": "0.8.6-beta.1",
10
+ "@saltcorn/builder": "0.8.6-beta.1",
11
+ "@saltcorn/data": "0.8.6-beta.1",
12
+ "@saltcorn/admin-models": "0.8.6-beta.1",
13
+ "@saltcorn/filemanager": "0.8.6-beta.1",
14
+ "@saltcorn/markup": "0.8.6-beta.1",
15
+ "@saltcorn/sbadmin2": "0.8.6-beta.1",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
package/routes/files.js CHANGED
@@ -14,7 +14,12 @@ const resizer = require("resize-with-sharp-or-jimp");
14
14
  const db = require("@saltcorn/data/db");
15
15
 
16
16
  const { renderForm } = require("@saltcorn/markup");
17
- const { isAdmin, error_catcher, setTenant } = require("./utils.js");
17
+ const {
18
+ isAdmin,
19
+ error_catcher,
20
+ setTenant,
21
+ is_relative_url,
22
+ } = require("./utils.js");
18
23
  const { h1, div, text } = require("@saltcorn/markup/tags");
19
24
  const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
20
25
  const { strictParseInt } = require("@saltcorn/data/plugin-helper");
@@ -441,6 +446,7 @@ router.post(
441
446
  isAdmin,
442
447
  error_catcher(async (req, res) => {
443
448
  const serve_path = req.params[0];
449
+ const { redirect } = req.query;
444
450
  const f = await File.findOne(serve_path);
445
451
  if (!f) {
446
452
  req.flash("error", "File not found");
@@ -460,7 +466,12 @@ router.post(
460
466
  }
461
467
  req.flash("error", result.error);
462
468
  }
463
- res.redirect(`/files?dir=${encodeURIComponent(f.current_folder)}`);
469
+ if (!req.xhr)
470
+ res.redirect(
471
+ (is_relative_url(redirect) && redirect) ||
472
+ `/files?dir=${encodeURIComponent(f.current_folder)}`
473
+ );
474
+ else res.json({ success: true });
464
475
  })
465
476
  );
466
477
 
package/routes/tables.js CHANGED
@@ -17,6 +17,7 @@ const {
17
17
  link,
18
18
  settingsDropdown,
19
19
  post_delete_btn,
20
+ post_btn,
20
21
  post_dropdown_item,
21
22
  } = require("@saltcorn/markup");
22
23
  const {
@@ -52,7 +53,7 @@ const { getState } = require("@saltcorn/data/db/state");
52
53
  const { cardHeaderTabs } = require("@saltcorn/markup/layout_utils");
53
54
  const { tablesList } = require("./common_lists");
54
55
  const { InvalidConfiguration } = require("@saltcorn/data/utils");
55
-
56
+ const path = require("path");
56
57
  /**
57
58
  * @type {object}
58
59
  * @const
@@ -1463,6 +1464,89 @@ router.post(
1463
1464
  })
1464
1465
  );
1465
1466
 
1467
+ const previewCSV = async ({ newPath, table, req, res, full }) => {
1468
+ let parse_res;
1469
+ try {
1470
+ parse_res = await table.import_csv_file(newPath, {
1471
+ recalc_stored: true,
1472
+ no_table_write: true,
1473
+ });
1474
+ } catch (e) {
1475
+ parse_res = { error: e.message };
1476
+ }
1477
+ if (parse_res.error) {
1478
+ if (parse_res.error) req.flash("error", parse_res.error);
1479
+ await fs.unlink(newPath);
1480
+ res.redirect(`/table/${table.id}`);
1481
+ } else {
1482
+ const rows = parse_res.rows || [];
1483
+ res.sendWrap(req.__(`Import table %s`, table.name), {
1484
+ above: [
1485
+ {
1486
+ type: "breadcrumbs",
1487
+ crumbs: [
1488
+ { text: req.__("Tables"), href: "/table" },
1489
+ { href: `/table/${table.id}`, text: table.name },
1490
+ {
1491
+ text: req.__("Import CSV"),
1492
+ },
1493
+ ],
1494
+ },
1495
+ {
1496
+ type: "card",
1497
+ title: req.__(`Import CSV`),
1498
+ contents: div(
1499
+ {
1500
+ "data-csv-filename": path.basename(newPath),
1501
+ },
1502
+ p(parse_res.success),
1503
+ post_btn(
1504
+ `/files/delete/${path.basename(newPath)}?redirect=/table/${
1505
+ table.id
1506
+ }}`,
1507
+ "Cancel",
1508
+ req.csrfToken(),
1509
+ {
1510
+ btnClass: "btn-danger",
1511
+ formClass: "d-inline me-2",
1512
+ icon: "fa fa-times",
1513
+ }
1514
+ ),
1515
+ post_btn(
1516
+ `/table/finish_upload_to_table/${table.name}/${path.basename(
1517
+ newPath
1518
+ )}`,
1519
+ "Proceed",
1520
+ req.csrfToken(),
1521
+ { icon: "fa fa-check", formClass: "d-inline" }
1522
+ )
1523
+ ),
1524
+ },
1525
+ {
1526
+ type: "card",
1527
+ title: req.__(`Preview`),
1528
+ contents: div(
1529
+ mkTable(
1530
+ table.fields.map((f) => ({ label: f.name, key: f.name })),
1531
+ full ? rows : rows.slice(0, 10)
1532
+ ),
1533
+ !full &&
1534
+ rows.length > 10 &&
1535
+ a(
1536
+ {
1537
+ href: `/table/preview_full_csv_file/${
1538
+ table.name
1539
+ }/${path.basename(newPath)}`,
1540
+ },
1541
+ `See all ${rows.length} rows`
1542
+ )
1543
+ ),
1544
+ },
1545
+ ],
1546
+ });
1547
+ }
1548
+ };
1549
+
1466
1550
  /**
1467
1551
  * Import Table Data from CSV POST handler
1468
1552
  * @name post/upload_to_table/:name,
@@ -1486,15 +1570,39 @@ router.post(
1486
1570
  const newPath = File.get_new_path();
1487
1571
  await req.files.file.mv(newPath);
1488
1572
  //console.log(req.files.file.data)
1573
+ await previewCSV({ newPath, table, res, req });
1574
+ })
1575
+ );
1576
+
1577
+ router.get(
1578
+ "/preview_full_csv_file/:name/:filename",
1579
+ isAdmin,
1580
+ error_catcher(async (req, res) => {
1581
+ const { name, filename } = req.params;
1582
+ const table = await Table.findOne({ name });
1583
+ const f = await File.findOne(filename);
1584
+ await previewCSV({ newPath: f.location, table, res, req, full: true });
1585
+ })
1586
+ );
1587
+
1588
+ router.post(
1589
+ "/finish_upload_to_table/:name/:filename",
1590
+ isAdmin,
1591
+ error_catcher(async (req, res) => {
1592
+ const { name, filename } = req.params;
1593
+ const table = await Table.findOne({ name });
1594
+ const f = await File.findOne(filename);
1595
+
1489
1596
  try {
1490
- const parse_res = await table.import_csv_file(newPath, true);
1597
+ const parse_res = await table.import_csv_file(f.location, {
1598
+ recalc_stored: true,
1599
+ });
1491
1600
  if (parse_res.error) req.flash("error", parse_res.error);
1492
1601
  else req.flash("success", parse_res.success);
1493
1602
  } catch (e) {
1494
1603
  req.flash("error", e.message);
1495
1604
  }
1496
-
1497
- await fs.unlink(newPath);
1605
+ await fs.unlink(f.location);
1498
1606
  res.redirect(`/table/${table.id}`);
1499
1607
  })
1500
1608
  );
@@ -15,6 +15,7 @@ const {
15
15
  script,
16
16
  text,
17
17
  domReady,
18
+ code,
18
19
  pre,
19
20
  } = require("@saltcorn/markup/tags");
20
21
 
@@ -325,6 +326,20 @@ router.get(
325
326
  ),
326
327
  contents: renderForm(form, req.csrfToken()),
327
328
  },
329
+ {
330
+ type: "card",
331
+
332
+ title: req.__("View configuration"),
333
+ contents: {
334
+ type: "tabs",
335
+ contents: [
336
+ pre(code(JSON.stringify(viewrow.configuration, null, 2))),
337
+ ],
338
+ tabsStyle: "Accordion",
339
+ startClosed: true,
340
+ titles: [req.__("Show configuration object")],
341
+ },
342
+ },
328
343
  ],
329
344
  });
330
345
  })
@@ -156,13 +156,25 @@ Pencil, 0.5,2, t`;
156
156
  it("should upload csv to existing table", async () => {
157
157
  const csv = `author,Pages
158
158
  Joe Celko, 856
159
- Gordon Kane, 217`;
159
+ Gordon Kane, 218`;
160
160
  const loginCookie = await getAdminLoginCookie();
161
161
  const app = await getApp({ disableCsrf: true });
162
+ let filename;
162
163
  await request(app)
163
164
  .post("/table/upload_to_table/books")
164
165
  .set("Cookie", loginCookie)
165
166
  .attach("file", Buffer.from(csv, "utf-8"))
167
+ .expect(toInclude(">Preview<"))
168
+ .expect(toInclude("Proceed"))
169
+ .expect((res) => {
170
+ filename = res.text.match(
171
+ /data-csv-filename\=\"([A-Za-z0-9 _\-]*)\"/
172
+ )[1];
173
+ });
174
+
175
+ await request(app)
176
+ .post(`/table/finish_upload_to_table/books/${filename}`)
177
+ .set("Cookie", loginCookie)
166
178
  .expect(toRedirect("/table/2"));
167
179
  await request(app)
168
180
  .get(`/table/2`)