@saltcorn/server 0.7.4 → 0.8.0-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.
Files changed (50) hide show
  1. package/app.js +18 -11
  2. package/auth/admin.js +370 -120
  3. package/auth/roleadmin.js +5 -23
  4. package/auth/routes.js +40 -15
  5. package/locales/de.json +1049 -273
  6. package/locales/en.json +58 -3
  7. package/locales/es.json +134 -134
  8. package/locales/it.json +6 -1
  9. package/locales/ru.json +44 -7
  10. package/markup/admin.js +46 -42
  11. package/markup/forms.js +4 -3
  12. package/package.json +8 -7
  13. package/public/blockly.js +19 -31
  14. package/public/diagram_utils.js +530 -0
  15. package/public/gridedit.js +4 -1
  16. package/public/jquery-menu-editor.min.js +112 -112
  17. package/public/saltcorn-common.js +31 -8
  18. package/public/saltcorn.css +11 -0
  19. package/public/saltcorn.js +211 -70
  20. package/restart_watcher.js +1 -0
  21. package/routes/actions.js +6 -14
  22. package/routes/admin.js +229 -79
  23. package/routes/api.js +19 -2
  24. package/routes/common_lists.js +137 -134
  25. package/routes/delete.js +6 -5
  26. package/routes/diagram.js +43 -117
  27. package/routes/edit.js +5 -10
  28. package/routes/fields.js +63 -29
  29. package/routes/files.js +137 -101
  30. package/routes/homepage.js +2 -2
  31. package/routes/infoarch.js +2 -2
  32. package/routes/list.js +12 -13
  33. package/routes/page.js +16 -3
  34. package/routes/pageedit.js +13 -8
  35. package/routes/scapi.js +1 -1
  36. package/routes/search.js +1 -1
  37. package/routes/tables.js +9 -14
  38. package/routes/tag_entries.js +31 -10
  39. package/routes/tags.js +10 -10
  40. package/routes/tenant.js +114 -50
  41. package/routes/utils.js +12 -0
  42. package/routes/view.js +3 -4
  43. package/routes/viewedit.js +57 -55
  44. package/serve.js +5 -0
  45. package/tests/admin.test.js +6 -2
  46. package/tests/auth.test.js +20 -0
  47. package/tests/fields.test.js +1 -0
  48. package/tests/files.test.js +11 -20
  49. package/tests/tenant.test.js +12 -2
  50. package/tests/viewedit.test.js +15 -1
@@ -1,8 +1,18 @@
1
- function sortby(k, desc) {
2
- set_state_fields({ _sortby: k, _sortdesc: desc ? "on" : { unset: true } });
1
+ function sortby(k, desc, viewIdentifier) {
2
+ set_state_fields({
3
+ [viewIdentifier ? `_${viewIdentifier}_sortby` : "_sortby"]: k,
4
+ [viewIdentifier ? `_${viewIdentifier}_sortdesc` : "_sortdesc"]: desc
5
+ ? "on"
6
+ : { unset: true },
7
+ });
3
8
  }
4
- function gopage(n, pagesize, extra = {}) {
5
- set_state_fields({ ...extra, _page: n, _pagesize: pagesize });
9
+ function gopage(n, pagesize, viewIdentifier, extra = {}) {
10
+ const cfg = {
11
+ ...extra,
12
+ [viewIdentifier ? `_${viewIdentifier}_page` : "_page"]: n,
13
+ [viewIdentifier ? `_${viewIdentifier}_pagesize` : "_pagesize"]: pagesize,
14
+ };
15
+ set_state_fields(cfg);
6
16
  }
7
17
 
8
18
  if (localStorage.getItem("reload_on_init")) {
@@ -77,8 +87,21 @@ function check_state_field(that) {
77
87
  pjax_to(dest.replace("&&", "&").replace("?&", "?"));
78
88
  }
79
89
 
90
+ function invalidate_pagings(href) {
91
+ let newhref = href;
92
+ const queryObj = Object.fromEntries(new URL(newhref).searchParams.entries());
93
+ const toRemove = Object.keys(queryObj).filter((val) => is_paging_param(val));
94
+ for (const k of toRemove) {
95
+ newhref = removeQueryStringParameter(newhref, k);
96
+ }
97
+ return newhref;
98
+ }
99
+
80
100
  function set_state_fields(kvs) {
81
- var newhref = get_current_state_url();
101
+ let newhref = get_current_state_url();
102
+ if (Object.keys(kvs).some((k) => !is_paging_param(k))) {
103
+ newhref = invalidate_pagings(newhref);
104
+ }
82
105
  Object.entries(kvs).forEach((kv) => {
83
106
  if (kv[1].unset && kv[1].unset === true)
84
107
  newhref = removeQueryStringParameter(newhref, kv[0]);
@@ -94,14 +117,15 @@ let loadPage = true;
94
117
  $(function () {
95
118
  $(window).bind("popstate", function (event) {
96
119
  const ensure_no_final_hash = (s) => (s.endsWith("#") ? s.slice(0, -1) : s);
97
- if (loadPage)
98
- window.location.assign(ensure_no_final_hash(window.location.href));
120
+ const newUrl = ensure_no_final_hash(window.location.href);
121
+ if (loadPage && newUrl !== window.location.href)
122
+ window.location.assign(newUrl);
99
123
  });
100
124
  });
101
125
 
102
126
  function pjax_to(href) {
103
127
  let $modal = $("#scmodal");
104
- const inModal = $modal.length && $modal.hasClass("show")
128
+ const inModal = $modal.length && $modal.hasClass("show");
105
129
  let $dest = inModal ? $("#scmodal .modal-body") : $("#page-inner-content");
106
130
 
107
131
  if (!$dest.length) window.location.href = href;
@@ -126,7 +150,7 @@ function pjax_to(href) {
126
150
  },
127
151
  error: function (res) {
128
152
  notifyAlert({ type: "danger", text: res.responseText });
129
- }
153
+ },
130
154
  });
131
155
  }
132
156
  }
@@ -135,21 +159,19 @@ function href_to(href) {
135
159
  window.location.href = href;
136
160
  }
137
161
  function clear_state(omit_fields_str) {
138
- let newUrl = get_current_state_url().split("?")[0]
139
- const hash = get_current_state_url().split("#")[1]
162
+ let newUrl = get_current_state_url().split("?")[0];
163
+ const hash = get_current_state_url().split("#")[1];
140
164
  if (omit_fields_str) {
141
- const omit_fields = omit_fields_str.split(',').map(s => s.trim())
142
- let qs = (get_current_state_url().split("?")[1] || "").split("#")[0]
165
+ const omit_fields = omit_fields_str.split(",").map((s) => s.trim());
166
+ let qs = (get_current_state_url().split("?")[1] || "").split("#")[0];
143
167
  let params = new URLSearchParams(qs);
144
- newUrl = newUrl + '?'
145
- omit_fields.forEach(f => {
168
+ newUrl = newUrl + "?";
169
+ omit_fields.forEach((f) => {
146
170
  if (params.get(f))
147
171
  newUrl = updateQueryStringParameter(newUrl, f, params.get(f));
148
- })
149
-
172
+ });
150
173
  }
151
- if (hash)
152
- newUrl += '#' + hash;
174
+ if (hash) newUrl += "#" + hash;
153
175
 
154
176
  pjax_to(newUrl);
155
177
  }
@@ -170,12 +192,14 @@ function view_post(viewname, route, data, onDone) {
170
192
  ? "application/x-www-form-urlencoded"
171
193
  : "application/json",
172
194
  data: typeof data === "string" ? data : JSON.stringify(data),
173
- }).done(function (res) {
174
- if (onDone) onDone(res);
175
- ajax_done(res);
176
- }).fail(function (res) {
177
- notifyAlert({ type: "danger", text: res.responseText });
178
- });
195
+ })
196
+ .done(function (res) {
197
+ if (onDone) onDone(res);
198
+ ajax_done(res);
199
+ })
200
+ .fail(function (res) {
201
+ notifyAlert({ type: "danger", text: res.responseText });
202
+ });
179
203
  }
180
204
  var logged_errors = [];
181
205
  function globalErrorCatcher(message, source, lineno, colno, error) {
@@ -254,6 +278,8 @@ function ajax_modal(url, opts = {}) {
254
278
 
255
279
  function saveAndContinue(e, k) {
256
280
  var form = $(e).closest("form");
281
+ const valres = form[0].reportValidity()
282
+ if (!valres) return;
257
283
  submitWithEmptyAction(form[0]);
258
284
  var url = form.attr("action");
259
285
  var form_data = form.serialize();
@@ -300,12 +326,47 @@ function applyViewConfig(e, url, k) {
300
326
  error: function (request) { },
301
327
  success: function (res) {
302
328
  k && k(res);
329
+ !k && updateViewPreview();
303
330
  },
304
331
  });
305
332
 
306
333
  return false;
307
334
  }
308
335
 
336
+ function updateViewPreview() {
337
+ const $preview = $("#viewcfg-preview[data-preview-url]");
338
+ if ($preview.length > 0) {
339
+ const url = $preview.attr("data-preview-url");
340
+ $preview.css({ opacity: 0.5 });
341
+ $.ajax(url, {
342
+ type: "POST",
343
+ headers: {
344
+ "CSRF-Token": _sc_globalCsrf,
345
+ },
346
+
347
+ error: function (request) { },
348
+ success: function (res) {
349
+ $preview.css({ opacity: 1.0 });
350
+
351
+ //disable elements in preview
352
+ $preview.html(res);
353
+ $preview.find("a").attr("href", "#");
354
+ $preview
355
+ .find("[onclick], button, a, input, select")
356
+ .attr("onclick", "return false");
357
+
358
+ $preview.find("textarea").attr("disabled", true);
359
+ $preview.find("input").attr("readonly", true);
360
+
361
+ //disable functions preview migght try to call
362
+ set_state_field = () => { }
363
+ set_state_fields = () => { }
364
+
365
+ },
366
+ });
367
+ }
368
+ }
369
+
309
370
  function ajaxSubmitForm(e) {
310
371
  var form = $(e).closest("form");
311
372
  var url = form.attr("action");
@@ -421,6 +482,17 @@ function test_formula(tablename, stored) {
421
482
  });
422
483
  }
423
484
 
485
+ function create_new_folder(folder) {
486
+ const name = window.prompt("Name of the new folder");
487
+ if (name)
488
+ ajax_post(`/files/new-folder`, {
489
+ data: { name, folder },
490
+ success: (data) => {
491
+ location.reload();
492
+ },
493
+ });
494
+ }
495
+
424
496
  async function fill_formula_btn_click(btn, k) {
425
497
  const formula = decodeURIComponent($(btn).attr("data-formula"));
426
498
  const free_vars = JSON.parse(
@@ -453,58 +525,127 @@ async function fill_formula_btn_click(btn, k) {
453
525
  $(btn).closest(".input-group").find("input").val(val);
454
526
  if (k) k();
455
527
  } catch (e) {
456
- notifyAlert({ type: "danger", text: `Error evaluating fill formula: ${e.message}` })
457
- console.error(e)
528
+ notifyAlert({
529
+ type: "danger",
530
+ text: `Error evaluating fill formula: ${e.message}`,
531
+ });
532
+ console.error(e);
458
533
  }
459
534
  }
460
535
 
461
- /*
462
- https://github.com/jeffdavidgreen/bootstrap-html5-history-tabs/blob/master/bootstrap-history-tabs.js
463
- Copyright (c) 2015 Jeff Green
464
- */
465
-
466
- +(function ($) {
467
- "use strict";
468
- $.fn.historyTabs = function () {
469
- var that = this;
470
- window.addEventListener("popstate", function (event) {
471
- if (event.state) {
472
- $(that)
473
- .filter('[href="' + event.state.url + '"]')
474
- .tab("show");
475
- }
476
- });
477
- return this.each(function (index, element) {
478
- $(element).on("show.bs.tab", function () {
479
- var stateObject = { url: $(this).attr("href") };
480
-
481
- if (window.location.hash && stateObject.url !== window.location.hash) {
482
- window.history.pushState(
483
- stateObject,
484
- document.title,
485
- window.location.pathname +
486
- window.location.search +
487
- $(this).attr("href")
488
- );
536
+ function removeSpinner(elementId, orginalHtml) {
537
+ $(`#${elementId}`).html(orginalHtml);
538
+ }
539
+
540
+ function poll_mobile_build_finished(outDirName, pollCount, orginalBtnHtml) {
541
+ $.ajax("/admin/build-mobile-app/finished", {
542
+ type: "GET",
543
+ data: { build_dir: outDirName },
544
+ success: function (res) {
545
+ if (!res.finished) {
546
+ if (pollCount >= 50) {
547
+ removeSpinner("buildMobileAppBtnId", orginalBtnHtml);
548
+ notifyAlert({
549
+ type: "danger",
550
+ text: "unable to get the build results",
551
+ });
489
552
  } else {
490
- window.history.replaceState(
491
- stateObject,
492
- document.title,
493
- window.location.pathname +
494
- window.location.search +
495
- $(this).attr("href")
496
- );
553
+ setTimeout(() => {
554
+ poll_mobile_build_finished(outDirName, ++pollCount, orginalBtnHtml);
555
+ }, 5000);
497
556
  }
498
- });
499
- if (!window.location.hash && $(element).is(".active")) {
500
- // Shows the first element if there are no query parameters.
501
- $(element).tab("show");
502
- } else if ($(this).attr("href") === window.location.hash) {
503
- $(element).tab("show");
557
+ } else {
558
+ href_to(
559
+ `build-mobile-app/result?build_dir_name=${encodeURIComponent(
560
+ outDirName
561
+ )}`
562
+ );
563
+ }
564
+ },
565
+ });
566
+ }
567
+
568
+ function build_mobile_app(button) {
569
+ const form = $(button).closest("form");
570
+ const params = {};
571
+ form.serializeArray().forEach((item) => {
572
+ params[item.name] = item.value;
573
+ });
574
+ ajax_post("/admin/build-mobile-app", {
575
+ data: params,
576
+ success: (data) => {
577
+ if (data.build_dir_name) {
578
+ handleMessages();
579
+ const orginalBtnHtml = $("#buildMobileAppBtnId").html();
580
+ press_store_button(button);
581
+ poll_mobile_build_finished(data.build_dir_name, 0, orginalBtnHtml);
504
582
  }
583
+ },
584
+ });
585
+ }
586
+
587
+ (() => {
588
+ const e = document.querySelector("[data-sidebar-toggler]");
589
+ let closed = localStorage.getItem("sidebarClosed") === "true";
590
+ if (e) {
591
+ if (closed) {
592
+ e.dispatchEvent(new Event("click"));
593
+ }
594
+ e.addEventListener("click", () => {
595
+ closed = !closed;
596
+ localStorage.setItem("sidebarClosed", `${closed}`);
505
597
  });
506
- };
507
- })(jQuery);
598
+ }
599
+ })()
600
+
601
+
602
+ /*
603
+ https://github.com/jeffdavidgreen/bootstrap-html5-history-tabs/blob/master/bootstrap-history-tabs.js
604
+ Copyright (c) 2015 Jeff Green
605
+ */
606
+
607
+ + (function ($) {
608
+ "use strict";
609
+ $.fn.historyTabs = function () {
610
+ var that = this;
611
+ window.addEventListener("popstate", function (event) {
612
+ if (event.state) {
613
+ $(that)
614
+ .filter('[href="' + event.state.url + '"]')
615
+ .tab("show");
616
+ }
617
+ });
618
+ return this.each(function (index, element) {
619
+ $(element).on("show.bs.tab", function () {
620
+ var stateObject = { url: $(this).attr("href") };
621
+
622
+ if (window.location.hash && stateObject.url !== window.location.hash) {
623
+ window.history.pushState(
624
+ stateObject,
625
+ document.title,
626
+ window.location.pathname +
627
+ window.location.search +
628
+ $(this).attr("href")
629
+ );
630
+ } else {
631
+ window.history.replaceState(
632
+ stateObject,
633
+ document.title,
634
+ window.location.pathname +
635
+ window.location.search +
636
+ $(this).attr("href")
637
+ );
638
+ }
639
+ });
640
+ if (!window.location.hash && $(element).is(".active")) {
641
+ // Shows the first element if there are no query parameters.
642
+ $(element).tab("show");
643
+ } else if ($(this).attr("href") === window.location.hash) {
644
+ $(element).tab("show");
645
+ }
646
+ });
647
+ };
648
+ })(jQuery);
508
649
 
509
650
  // Copyright (c) 2011 Marcus Ekwall, http://writeless.se/
510
651
  // https://github.com/mekwall/jquery-throttle
@@ -23,6 +23,7 @@ const relevantPackages = [
23
23
  "saltcorn-sbadmin2",
24
24
  "server",
25
25
  "sqlite",
26
+ "filemanager"
26
27
  ];
27
28
 
28
29
  /**
package/routes/actions.js CHANGED
@@ -8,7 +8,6 @@ const Router = require("express-promise-router");
8
8
  const {
9
9
  isAdmin,
10
10
  error_catcher,
11
- get_base_url,
12
11
  addOnDoneRedirect,
13
12
  } = require("./utils.js");
14
13
  const { getState } = require("@saltcorn/data/db/state");
@@ -25,17 +24,9 @@ const { getTriggerList } = require("./common_lists");
25
24
  const router = new Router();
26
25
  module.exports = router;
27
26
  const {
28
- mkTable,
29
27
  renderForm,
30
28
  link,
31
- // post_btn,
32
- // settingsDropdown,
33
- // post_dropdown_item,
34
- post_delete_btn,
35
- localeDateTime,
36
- // localeDateTime,
37
29
  } = require("@saltcorn/markup");
38
- const actions = require("@saltcorn/data/base-plugin/actions");
39
30
  const Form = require("@saltcorn/data/models/form");
40
31
  const {
41
32
  div,
@@ -52,14 +43,11 @@ const {
52
43
  h6,
53
44
  pre,
54
45
  text,
55
- hr,
56
46
  } = require("@saltcorn/markup/tags");
57
47
  const Table = require("@saltcorn/data/models/table");
58
48
  const { getActionConfigFields } = require("@saltcorn/data/plugin-helper");
59
49
  const { send_events_page } = require("../markup/admin.js");
60
- const EventLog = require("@saltcorn/data/models/eventlog");
61
50
  const User = require("@saltcorn/data/models/user");
62
- const form = require("@saltcorn/markup/form");
63
51
  const {
64
52
  blocklyImportScripts,
65
53
  blocklyToolbox,
@@ -92,7 +80,6 @@ router.get(
92
80
  error_catcher(async (req, res) => {
93
81
  const triggers = await Trigger.findAllWithTableName();
94
82
  const actions = await getActions();
95
- const base_url = get_base_url(req);
96
83
  send_events_page({
97
84
  res,
98
85
  req,
@@ -388,6 +375,11 @@ router.get(
388
375
  error_catcher(async (req, res) => {
389
376
  const { id } = req.params;
390
377
  const trigger = await Trigger.findOne({ id });
378
+ if (!trigger) {
379
+ req.flash("warning", req.__("Action not found"));
380
+ res.redirect(`/actions/`);
381
+ return
382
+ }
391
383
  const action = getState().actions[trigger.action];
392
384
  if (!action) {
393
385
  req.flash("warning", req.__("Action not found"));
@@ -583,7 +575,7 @@ router.get(
583
575
  };
584
576
  let table, row;
585
577
  if (trigger.table_id) {
586
- table = await Table.findOne(trigger.table_id);
578
+ table = await Table.findOne( { id: trigger.table_id } );
587
579
  row = await table.getRow({});
588
580
  }
589
581
  try {