@saltcorn/server 0.7.4-beta.2 → 0.7.4-beta.3

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
@@ -951,7 +951,6 @@
951
951
  "Modules up-to-date": "Modules up-to-date",
952
952
  "User must have this role or higher to read rows from the table, unless they are the owner": "User must have this role or higher to read rows from the table, unless they are the owner",
953
953
  "User must have this role or higher to edit or create new rows in the table, unless they are the owner": "User must have this role or higher to edit or create new rows in the table, unless they are the owner",
954
- "Tag": "Tag",
955
954
  "Tagname": "Tagname",
956
955
  "Create tag": "Create tag",
957
956
  "Tags": "Tags",
@@ -965,6 +964,24 @@
965
964
  "Trigger": "Trigger",
966
965
  "Add %s to tag": "Add %s to tag",
967
966
  "Tag %s deleted": "Tag %s deleted",
968
- "Tag %s created": "Tag %s created"
969
-
970
- }
967
+ "Tag %s created": "Tag %s created",
968
+ "Application diagram": "Application diagram",
969
+ "Diagram": "Diagram",
970
+ "Entry point": "Entry point",
971
+ "Platform": "Platform",
972
+ "docker": "docker",
973
+ "android": "android",
974
+ "iOS": "iOS",
975
+ "App file": "App file",
976
+ "Server URL": "Server URL",
977
+ "Module %s installed, please complete configuration.": "Module %s installed, please complete configuration.",
978
+ "Module %s removed.": "Module %s removed.",
979
+ "Module %s installed": "Module %s installed",
980
+ "Upgrading modules...": "Upgrading modules...",
981
+ "Backup now": "Backup now",
982
+ "Snapshot now": "Snapshot now",
983
+ "Restore/download automated backups »": "Restore/download automated backups »",
984
+ "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).": "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).",
985
+ "List/download snapshots &raquo;": "List/download snapshots &raquo;",
986
+ "Discover tables that are already in the Database, but not known to Saltcorn": "Discover tables that are already in the Database, but not known to Saltcorn"
987
+ }
package/locales/ru.json CHANGED
@@ -180,7 +180,7 @@
180
180
  "Pack %s uninstalled": "Пакет %s удален",
181
181
  "Uninstall": "Удалить",
182
182
  "Result preview for ": "Предварительный просмотр результатов для ",
183
- "Choose views for <a href=\"/search\">search results</a> for each table.<br/>Set to blank to omit table from global search.": "Выберите для каждой таблицы представление для вывода <a href=\"/search\">результатов поиска</a>.<br/>Оставьте незаполненным, если не хотите, чтобы таблица отображалась в глобальном поиске.",
183
+ "Choose views for <a href=\"/search\">search results</a> for each table.<br/>Set to blank to omit table from global search.": "Выберите для каждой таблицы представление для вывода <a href=\"/search\">результатов поиска</a>.<br/>Оставьте незаполненным, если не хотите, чтобы таблица отображалась в глобальном поиске.",
184
184
  "These tables lack suitable views: ": "Указанные таблицы не имеют представлений с поддержкой поиска: ",
185
185
  "Search configuration": "Настройки поиска",
186
186
  "Table saved with version history enabled": "Таблица сохранена с включенной историей версий",
@@ -303,7 +303,7 @@
303
303
  "Clone": "Клонировать",
304
304
  "View %s cloned as %s": "Представление %s клонировано в %s",
305
305
  "Duplicate": "Дублировать",
306
- "View %s duplicated as %s": "Представление %s задублировано в %s",
306
+ "View %s duplicated as %s": "Представление %s дублировано в %s",
307
307
  "The view name will appear as the title of pop-ups showing this view, and in the URL when it is shown alone.": "Имя представления отображается как заголовок всплывающего окна или в URL, в зависимости от режима отображения.",
308
308
  "Saltcorn version": "версия платформы",
309
309
  "Node.js version": "версия Node.js",
@@ -337,7 +337,7 @@
337
337
  "Subtables": "Подчиненные таблицы",
338
338
  "List View": "Представление Список (List)",
339
339
  "Show View": "Представление Show",
340
- "Which related tables would you like to show in sub-lists below the selected item?": "Which related tables would you like to show in sub-lists below the selected item?",
340
+ "Which related tables would you like to show in sub-lists below the selected item?": "Какие связанные таблицы вы хотите отобразить в подчиненных списках (sub-lists) под выбранной записью?",
341
341
  "Order and layout": "Сортировка и Макет",
342
342
  "Order by": "Сортировка по",
343
343
  "Descending": "В обратном порядке",
@@ -801,11 +801,11 @@
801
801
  "Expiration in days": "Срок хранения (в днях)",
802
802
  "Delete old backup files in this directory after the set number of days": "Старые резервные копии в этой папке будут удалять после истечения срока хранения ",
803
803
  "Backup settings updated": "Настройки резервного копирования обновлены",
804
- "Periodic snapshots enabled": "Регулярные снимки (снэпшоты) активированы",
805
- "Snapshot will be made every hour if there are changes": "Снимки (снэпшоты) будут выполняться каждый час",
804
+ "Periodic snapshots enabled": "Регулярные снимки (снепшоты)",
805
+ "Snapshot will be made every hour if there are changes": "Снимки (снепшоты) будут выполняться каждый час",
806
806
  "Manual backup": "Ручное резервное копирование",
807
807
  "Automated backup": "Автоматическое резервное копирование",
808
- "Snapshots": "Снимки (снэпшоты)",
808
+ "Snapshots": "Снимки (снепшоты)",
809
809
  "Mobile app": "Мобильное приложение",
810
810
  "Module store": "Магазин модулей",
811
811
  "Upgrade installed modules": "Обновить установленные модули",
@@ -829,5 +829,58 @@
829
829
  "App file": "Файл приложения",
830
830
  "Server URL": "URL сервера",
831
831
  "android": "android",
832
- "iOS": "iOS"
832
+ "iOS": "iOS",
833
+ "Tag %s created": "Тег %s создан",
834
+ "%s Tag": "%s Тег",
835
+ "Add tables": "Добавить таблицы",
836
+ "Add views": "Добавить представления",
837
+ "Remove From Tag": "Удалить из тега",
838
+ "Add tages": "Добавить теги",
839
+ "Trigger": "Триггер",
840
+ "Tag": "Тег",
841
+ "Download snapshots": "Скачать снепшоты",
842
+ "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).": "Снепшоты сохраняют структуру приложения, но не сохраняют данные в таблицах. Конкретные представления и страницы могут быть восстановлены по ссылке <a href='/viewedit'>view</a> или <a href='/pageedit'>pages</a> (Кнопка \"Восстановить\" в выпадающем меню представления или страницы).",
843
+ "List/download snapshots &raquo;": "Список снепшотов &raquo;",
844
+ "Add %s to tag": "Добавить %s к тегу",
845
+ "Tagname": "Имя тега",
846
+ "Create tag": "Создать тег",
847
+ "Tags": "Теги",
848
+ "New tag": "Новый тег",
849
+ "Tag name": "Имя тега",
850
+ "Tag %s deleted": "Тег %s удален",
851
+ "Add entries to tag": "Добавить объекты в тег",
852
+ "Please select at least one item": "Пожалуйста добавьте хотя бы один объект",
853
+ "Backup now": "Сделать бекап сейчас",
854
+ "Snapshot now": "Сделать снепшот сейчас",
855
+ "Restore/download automated backups &raquo;": "Восстановить/скачать резервные копии &raquo;",
856
+ "No changes detected, snapshot skipped": "Нет изменений, снепшот пропущен",
857
+ "Pattern": "Паттерн",
858
+ "Add pages": "Добавить страницы",
859
+ "Add triggers": "Добавить триггеры",
860
+ "Edit Module": "Настроить Модуль",
861
+ "Page %s duplicated as %s": "Страница %s дублирована в %s",
862
+ "Auto save": "Автосохранение",
863
+ "Save any changes immediately": "Сохранять все изменения данных в форме немедленно",
864
+ "This is the view to which the user will be sent when the form is submitted. The view you specify here can be ignored depending on the context of the form, for instance if it appears in a pop-up the redirect will not take place.": "Указывается представление, куда будет перенаправлен пользователь после отправки формы. В некоторых случаях перенаправление не будет выполнено, например, если форма использована в режиме всплывающего окна.",
865
+ "Destination view": "Destination view",
866
+ "Destination URL Formula": "Destination URL Formula",
867
+ "Back": "Назад",
868
+ "Backup successful": "Резервная копия успешно создана",
869
+ "Save before going back": "Save before going back",
870
+ "Reload after going back": "Reload after going back",
871
+ "Steps to go back": "Steps to go back",
872
+ "View pattern": "Паттерн представления",
873
+ "The view pattern sets the foundation of how the view relates to the table and the behaviour of the view": "The view pattern sets the foundation of how the view relates to the table and the behaviour of the view",
874
+ "This will delete <strong>EVERYTHING</strong> in the selected categories": "Внимание! Будут удалены <strong>ВСЕ</strong> данные в выбранных категориях",
875
+ "Tag not found": "Тэг не найден",
876
+ "Build Result": "Результат сборки",
877
+ "Download automated backup": "Автоматически созданные резервные копии",
878
+ "Restoring automated backup": "Восстановление из автоматически созданной резервной копии",
879
+ "Download one of the backups above": "Скачать один из бекапов (файлов с резервной копией) выше",
880
+ "Clear this application": "Удалить ВСЕ данные в текущем приложении",
881
+ "(tick all boxes)": "(указав все галочки)",
882
+ "When prompted to create the first user, click the link to restore a backup": "Вместо создания первого пользователя, выберите Восстановление из резервной копии (restore a backup)",
883
+ "Select the downloaded backup file": "Выбрать скаченный бекап (файл с резервной копией)",
884
+ "Snapshot successful": "Снимок (Снепшот) выполнен успешно",
885
+ "Unable to build the app:": "Ошибка создания приложения:"
833
886
  }
package/markup/admin.js CHANGED
@@ -203,6 +203,8 @@ const send_infoarch_page = (args) => {
203
203
  { text: "Multitenancy", href: "/tenant/settings" },
204
204
  ]
205
205
  : []),
206
+ { text: "Tags", href: "/tag" },
207
+ { text: "Diagram", href: "/diagram" },
206
208
  ],
207
209
  ...args,
208
210
  });
@@ -288,10 +290,7 @@ const send_admin_page = (args) => {
288
290
  { text: "Backup", href: "/admin/backup" },
289
291
  { text: "Email", href: "/admin/email" },
290
292
  { text: "System", href: "/admin/system" },
291
- ...(isRoot ? [{ text: "Tag", href: "/tag" }] : []),
292
- ...(isRoot
293
- ? [{ text: "Mobile app", href: "/admin/build-mobile-app" }]
294
- : []),
293
+ { text: "Mobile app", href: "/admin/build-mobile-app" },
295
294
  ],
296
295
  ...args,
297
296
  });
@@ -146,7 +146,7 @@ const boolExamples = (type, fields) => {
146
146
  * @returns {p[]}
147
147
  */
148
148
  const expressionBlurb = (type, stored, allFields, req) => {
149
- const fields = allFields.filter((f) => !f.is_fkey && !f.calculated);
149
+ const fields = allFields.filter((f) => !f.calculated);
150
150
  const funs = getState().functions;
151
151
  const funNames = Object.entries(funs)
152
152
  .filter(([k, v]) => !(!stored && v.isAsync))
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.7.4-beta.2",
3
+ "version": "0.7.4-beta.3",
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.7.4-beta.2",
10
- "@saltcorn/builder": "0.7.4-beta.2",
11
- "@saltcorn/data": "0.7.4-beta.2",
12
- "@saltcorn/admin-models": "0.7.4-beta.2",
13
- "@saltcorn/markup": "0.7.4-beta.2",
14
- "@saltcorn/sbadmin2": "0.7.4-beta.2",
9
+ "@saltcorn/base-plugin": "0.7.4-beta.3",
10
+ "@saltcorn/builder": "0.7.4-beta.3",
11
+ "@saltcorn/data": "0.7.4-beta.3",
12
+ "@saltcorn/admin-models": "0.7.4-beta.3",
13
+ "@saltcorn/markup": "0.7.4-beta.3",
14
+ "@saltcorn/sbadmin2": "0.7.4-beta.3",
15
15
  "@socket.io/cluster-adapter": "^0.1.0",
16
16
  "@socket.io/sticky": "^1.0.1",
17
17
  "aws-sdk": "^2.1037.0",
package/public/blockly.js CHANGED
@@ -403,11 +403,10 @@ function activate_blockly({ events, actions, tables }) {
403
403
  Blockly.JavaScript.ORDER_ATOMIC
404
404
  );
405
405
  // TODO: Assemble JavaScript into code variable.
406
- var code = `await fetchJSON(${value_url}, { method: '${dropdown_method}'${
407
- value_body
408
- ? `, body: ${value_body}, headers: { "Content-Type": "application/json" }`
409
- : ""
410
- } })`;
406
+ var code = `await fetchJSON(${value_url}, { method: '${dropdown_method}'${value_body
407
+ ? `, body: ${value_body}, headers: { "Content-Type": "application/json" }`
408
+ : ""
409
+ } })`;
411
410
  // TODO: Change ORDER_NONE to the correct strength.
412
411
  return [code, Blockly.JavaScript.ORDER_NONE];
413
412
  };
@@ -416,6 +415,7 @@ function activate_blockly({ events, actions, tables }) {
416
415
  init: function () {
417
416
  this.appendDummyInput().appendField("Return");
418
417
  this.appendValueInput("GOTO").setCheck("String").appendField("Go to URL");
418
+ this.appendValueInput("POPUP").setCheck("String").appendField("Popup URL");
419
419
  this.appendDummyInput()
420
420
  .appendField("Reload page")
421
421
  .appendField(new Blockly.FieldCheckbox("FALSE"), "RELOAD");
@@ -434,6 +434,11 @@ function activate_blockly({ events, actions, tables }) {
434
434
  "GOTO",
435
435
  Blockly.JavaScript.ORDER_ATOMIC
436
436
  );
437
+ var value_popup = Blockly.JavaScript.valueToCode(
438
+ block,
439
+ "POPUP",
440
+ Blockly.JavaScript.ORDER_ATOMIC
441
+ );
437
442
  var checkbox_reload = block.getFieldValue("RELOAD") == "TRUE";
438
443
  var value_notify = Blockly.JavaScript.valueToCode(
439
444
  block,
@@ -443,6 +448,7 @@ function activate_blockly({ events, actions, tables }) {
443
448
  // TODO: Assemble JavaScript into code variable.
444
449
  let s = "";
445
450
  if (value_goto) s += `goto: ${value_goto},`;
451
+ if (value_popup) s += `popup: ${value_popup},`;
446
452
  if (value_notify) s += `notify: ${value_notify},`;
447
453
  if (checkbox_reload) s += `reload_page: true,`;
448
454
  var code = `return {${s}};\n`;
@@ -4,7 +4,10 @@ function showHideCol(nm, e) {
4
4
  }
5
5
 
6
6
  function lookupIntToString(cell, formatterParams, onRendered) {
7
- const val = `${cell.getValue()}`;
7
+ const cellVal = cell.getValue()
8
+ const val = typeof cellVal === "object" && cellVal !== null
9
+ ? `${cellVal.id}`
10
+ : `${cellVal}`;
8
11
  const res = formatterParams.values[val];
9
12
  return res;
10
13
  }
@@ -150,6 +153,8 @@ function delete_tabulator_row(e, cell) {
150
153
  if (def && def.formatterParams && def.formatterParams.confirm) {
151
154
  if (!confirm("Are you sure you want to delete this row?")) return;
152
155
  }
156
+ const tableName = def?.formatterParams?.tableName || window.tabulator_table_name
157
+
153
158
  const row = cell.getRow().getData();
154
159
  if (!row.id) {
155
160
  cell.getRow().delete();
@@ -157,7 +162,7 @@ function delete_tabulator_row(e, cell) {
157
162
  }
158
163
  $.ajax({
159
164
  type: "DELETE",
160
- url: `/api/${window.tabulator_table_name}/${row.id}`,
165
+ url: `/api/${tableName}/${row.id}`,
161
166
  data: row, // to process primary keys different from id
162
167
  headers: {
163
168
  "CSRF-Token": _sc_globalCsrf,
@@ -86,8 +86,7 @@ function apply_showif() {
86
86
  } else {
87
87
  e.append(
88
88
  $(
89
- `<option ${
90
- `${current}` === `${o.value}` ? "selected" : ""
89
+ `<option ${`${current}` === `${o.value}` ? "selected" : ""
91
90
  } value="${o.value}">${o.label}</option>`
92
91
  )
93
92
  );
@@ -118,15 +117,13 @@ function apply_showif() {
118
117
  resp.success.forEach((r) => {
119
118
  e.append(
120
119
  $(
121
- `<option ${
122
- `${current}` === `${r[dynwhere.refname]}` ? "selected" : ""
123
- } value="${r[dynwhere.refname]}">${
124
- dynwhere.label_formula
125
- ? new Function(
126
- `{${Object.keys(r).join(",")}}`,
127
- "return " + dynwhere.label_formula
128
- )(r)
129
- : r[dynwhere.summary_field]
120
+ `<option ${`${current}` === `${r[dynwhere.refname]}` ? "selected" : ""
121
+ } value="${r[dynwhere.refname]}">${dynwhere.label_formula
122
+ ? new Function(
123
+ `{${Object.keys(r).join(",")}}`,
124
+ "return " + dynwhere.label_formula
125
+ )(r)
126
+ : r[dynwhere.summary_field]
130
127
  }</option>`
131
128
  )
132
129
  );
@@ -466,16 +463,14 @@ function notifyAlert(note, spin) {
466
463
  }
467
464
 
468
465
  $("#alerts-area")
469
- .append(`<div class="alert alert-${type} alert-dismissible fade show ${
470
- spin ? "d-flex align-items-center" : ""
471
- }" role="alert">
466
+ .append(`<div class="alert alert-${type} alert-dismissible fade show ${spin ? "d-flex align-items-center" : ""
467
+ }" role="alert">
472
468
  ${txt}
473
- ${
474
- spin
475
- ? `<div class="spinner-border ms-auto" role="status" aria-hidden="true"></div>`
476
- : `<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
469
+ ${spin
470
+ ? `<div class="spinner-border ms-auto" role="status" aria-hidden="true"></div>`
471
+ : `<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
477
472
  </button>`
478
- }
473
+ }
479
474
  </div>`);
480
475
  }
481
476
 
@@ -492,9 +487,8 @@ function common_done(res, isWeb = true) {
492
487
  (isWeb ? location : parent.location).reload(); //TODO notify to cookie if reload or goto
493
488
  }
494
489
  if (res.download) {
495
- const dataurl = `data:${
496
- res.download.mimetype || "application/octet-stream"
497
- };base64,${res.download.blob}`;
490
+ const dataurl = `data:${res.download.mimetype || "application/octet-stream"
491
+ };base64,${res.download.blob}`;
498
492
  fetch(dataurl)
499
493
  .then((res) => res.blob())
500
494
  .then((blob) => {
@@ -515,6 +509,9 @@ function common_done(res, isWeb = true) {
515
509
  if (res.target === "_blank") window.open(res.goto, "_blank").focus();
516
510
  else window.location.href = res.goto;
517
511
  }
512
+ if (res.popup) {
513
+ ajax_modal(res.popup)
514
+ }
518
515
  }
519
516
 
520
517
  const repeaterCopyValuesToForm = (form, editor, noTriggerChange) => {
@@ -523,10 +520,11 @@ const repeaterCopyValuesToForm = (form, editor, noTriggerChange) => {
523
520
  const setVal = (k, ix, v) => {
524
521
  const $e = form.find(`input[name="${k}_${ix}"]`);
525
522
  if ($e.length) $e.val(v);
526
- else
527
- form.append(
528
- `<input type="hidden" data-repeater-ix="${ix}" name="${k}_${ix}" value="${v}"></input>`
529
- );
523
+ else {
524
+ const $ne = $(`<input type="hidden" data-repeater-ix="${ix}" name="${k}_${ix}"></input>`);
525
+ $ne.val(v);
526
+ form.append($ne);
527
+ }
530
528
  };
531
529
  vs.forEach((v, ix) => {
532
530
  Object.entries(v).forEach(([k, v]) => {
@@ -649,9 +647,9 @@ function room_older(viewname, room_id, btn) {
649
647
  function init_room(viewname, room_id) {
650
648
  const socket = parent?.config?.server_path
651
649
  ? io(parent.config.server_path, {
652
- query: `jwt=${localStorage.getItem("auth_jwt")}`,
653
- transports: ["websocket"],
654
- })
650
+ query: `jwt=${localStorage.getItem("auth_jwt")}`,
651
+ transports: ["websocket"],
652
+ })
655
653
  : io({ transports: ["websocket"] });
656
654
 
657
655
  socket.emit("join_room", [viewname, room_id]);
@@ -51,12 +51,19 @@ function removeQueryStringParameter(uri1, key) {
51
51
  return uri + hash;
52
52
  }
53
53
 
54
+ function get_current_state_url() {
55
+ let $modal = $("#scmodal");
56
+ if ($modal.length === 0 || !$modal.hasClass("show"))
57
+ return window.location.href;
58
+ else return $modal.prop("data-modal-state");
59
+ }
60
+
54
61
  function select_id(id) {
55
- pjax_to(updateQueryStringParameter(window.location.href, "id", id));
62
+ pjax_to(updateQueryStringParameter(get_current_state_url(), "id", id));
56
63
  }
57
64
 
58
65
  function set_state_field(key, value) {
59
- pjax_to(updateQueryStringParameter(window.location.href, key, value));
66
+ pjax_to(updateQueryStringParameter(get_current_state_url(), key, value));
60
67
  }
61
68
 
62
69
  function check_state_field(that) {
@@ -65,13 +72,13 @@ function check_state_field(that) {
65
72
  const value = that.value;
66
73
  var separator = window.location.href.indexOf("?") !== -1 ? "&" : "?";
67
74
  let dest;
68
- if (checked) dest = window.location.href + `${separator}${name}=${value}`;
69
- else dest = window.location.href.replace(`${name}=${value}`, "");
75
+ if (checked) dest = get_current_state_url() + `${separator}${name}=${value}`;
76
+ else dest = get_current_state_url().replace(`${name}=${value}`, "");
70
77
  pjax_to(dest.replace("&&", "&").replace("?&", "?"));
71
78
  }
72
79
 
73
80
  function set_state_fields(kvs) {
74
- var newhref = window.location.href;
81
+ var newhref = get_current_state_url();
75
82
  Object.entries(kvs).forEach((kv) => {
76
83
  if (kv[1].unset && kv[1].unset === true)
77
84
  newhref = removeQueryStringParameter(newhref, kv[0]);
@@ -80,7 +87,7 @@ function set_state_fields(kvs) {
80
87
  pjax_to(newhref.replace("&&", "&").replace("?&", "?"));
81
88
  }
82
89
  function unset_state_field(key) {
83
- pjax_to(removeQueryStringParameter(window.location.href, key));
90
+ pjax_to(removeQueryStringParameter(get_current_state_url(), key));
84
91
  }
85
92
 
86
93
  let loadPage = true;
@@ -93,7 +100,11 @@ $(function () {
93
100
  });
94
101
 
95
102
  function pjax_to(href) {
96
- if (!$("#page-inner-content").length) window.location.href = href;
103
+ let $modal = $("#scmodal");
104
+ const inModal = $modal.length && $modal.hasClass("show")
105
+ let $dest = inModal ? $("#scmodal .modal-body") : $("#page-inner-content");
106
+
107
+ if (!$dest.length) window.location.href = href;
97
108
  else {
98
109
  loadPage = false;
99
110
  $.ajax(href, {
@@ -101,18 +112,21 @@ function pjax_to(href) {
101
112
  pjaxpageload: "true",
102
113
  },
103
114
  success: function (res, textStatus, request) {
104
- window.history.pushState({ url: href }, "", href);
115
+ if (!inModal) window.history.pushState({ url: href }, "", href);
105
116
  setTimeout(() => {
106
117
  loadPage = true;
107
118
  }, 0);
108
- if (res.includes("<!--SCPT:")) {
119
+ if (!inModal && res.includes("<!--SCPT:")) {
109
120
  const start = res.indexOf("<!--SCPT:");
110
121
  const end = res.indexOf("-->", start);
111
122
  document.title = res.substring(start + 9, end);
112
123
  }
113
- $("#page-inner-content").html(res);
124
+ $dest.html(res);
114
125
  initialize_page();
115
126
  },
127
+ error: function (res) {
128
+ notifyAlert({ type: "danger", text: res.responseText });
129
+ }
116
130
  });
117
131
  }
118
132
  }
@@ -120,8 +134,24 @@ function pjax_to(href) {
120
134
  function href_to(href) {
121
135
  window.location.href = href;
122
136
  }
123
- function clear_state() {
124
- pjax_to(window.location.href.split("?")[0]);
137
+ function clear_state(omit_fields_str) {
138
+ let newUrl = get_current_state_url().split("?")[0]
139
+ const hash = get_current_state_url().split("#")[1]
140
+ 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]
143
+ let params = new URLSearchParams(qs);
144
+ newUrl = newUrl + '?'
145
+ omit_fields.forEach(f => {
146
+ if (params.get(f))
147
+ newUrl = updateQueryStringParameter(newUrl, f, params.get(f));
148
+ })
149
+
150
+ }
151
+ if (hash)
152
+ newUrl += '#' + hash;
153
+
154
+ pjax_to(newUrl);
125
155
  }
126
156
 
127
157
  function ajax_done(res) {
@@ -210,6 +240,7 @@ function ajax_modal(url, opts = {}) {
210
240
  var title = request.getResponseHeader("Page-Title");
211
241
  if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
212
242
  $("#scmodal .modal-body").html(res);
243
+ $("#scmodal").prop("data-modal-state", url);
213
244
  new bootstrap.Modal($("#scmodal")).show();
214
245
  initialize_page();
215
246
  (opts.onOpen || function () { })(res);
@@ -468,3 +499,7 @@ Copyright (c) 2015 Jeff Green
468
499
  });
469
500
  };
470
501
  })(jQuery);
502
+
503
+ // Copyright (c) 2011 Marcus Ekwall, http://writeless.se/
504
+ // https://github.com/mekwall/jquery-throttle
505
+ (function (a) { var b = a.jQuery || a.me || (a.me = {}), i = function (e, f, g, h, c, a) { f || (f = 100); var d = !1, j = !1, i = typeof g === "function", l = function (a, b) { d = setTimeout(function () { d = !1; if (h || c) e.apply(a, b), c && (j = +new Date); i && g.apply(a, b) }, f) }, k = function () { if (!d || a) { if (!d && !h && (!c || +new Date - j > f)) e.apply(this, arguments), c && (j = +new Date); (a || !c) && clearTimeout(d); l(this, arguments) } }; if (b.guid) k.guid = e.guid = e.guid || b.guid++; return k }; b.throttle = i; b.debounce = function (a, b, g, h, c) { return i(a, b, g, h, c, !0) } })(this);
package/routes/admin.js CHANGED
@@ -390,7 +390,7 @@ router.get(
390
390
  renderForm(backupForm, req.csrfToken()),
391
391
  a(
392
392
  { href: "/admin/auto-backup-list" },
393
- "Restore/download automated backups &raquo;"
393
+ req.__("Restore/download automated backups &raquo;")
394
394
  ),
395
395
  script(
396
396
  domReady(
@@ -406,13 +406,13 @@ router.get(
406
406
  contents: div(
407
407
  p(
408
408
  i(
409
- "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns)."
409
+ req.__("Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).")
410
410
  )
411
411
  ),
412
412
  renderForm(aSnapshotForm, req.csrfToken()),
413
413
  a(
414
414
  { href: "/admin/snapshot-list" },
415
- "List/download snapshots &raquo;"
415
+ req.__("List/download snapshots &raquo;")
416
416
  )
417
417
  ),
418
418
  },
@@ -472,16 +472,16 @@ router.get(
472
472
  title: req.__("Restoring automated backup"),
473
473
  contents: div(
474
474
  ol(
475
- li("Download one of the backups above"),
475
+ li(req.__("Download one of the backups above")),
476
476
  li(
477
- a({ href: "/admin/clear-all" }, "Clear this application"),
477
+ a({ href: "/admin/clear-all" }, req.__("Clear this application")),
478
478
  " ",
479
- "(tick all boxes)"
479
+ req.__("(tick all boxes)")
480
480
  ),
481
481
  li(
482
- "When prompted to create the first user, click the link to restore a backup"
482
+ req.__("When prompted to create the first user, click the link to restore a backup")
483
483
  ),
484
- li("Select the downloaded backup file")
484
+ li(req.__("Select the downloaded backup file"))
485
485
  )
486
486
  ),
487
487
  },
@@ -551,7 +551,7 @@ router.get(
551
551
  mkTable(
552
552
  [
553
553
  {
554
- label: "When",
554
+ label: req.__("When"),
555
555
  key: (r) =>
556
556
  `${localeDateTime(r.created)} (${moment(r.created).fromNow()})`,
557
557
  },
@@ -618,7 +618,7 @@ const autoBackupForm = (req) =>
618
618
  noSubmitButton: true,
619
619
  additionalButtons: [
620
620
  {
621
- label: "Backup now",
621
+ label: req.__("Backup now"),
622
622
  id: "btnBackupNow",
623
623
  class: "btn btn-outline-secondary",
624
624
  onclick: "ajax_post('/admin/auto-backup-now')",
@@ -671,7 +671,7 @@ const snapshotForm = (req) =>
671
671
  noSubmitButton: true,
672
672
  additionalButtons: [
673
673
  {
674
- label: "Snapshot now",
674
+ label: req.__("Snapshot now"),
675
675
  id: "btnSnapNow",
676
676
  class: "btn btn-outline-secondary",
677
677
  onclick: "ajax_post('/admin/snapshot-now')",
@@ -808,7 +808,6 @@ router.get(
808
808
  " ",
809
809
  req.__("Configuration check")
810
810
  ),
811
-
812
811
  hr(),
813
812
 
814
813
  a(
@@ -945,7 +944,7 @@ router.post(
945
944
  });
946
945
  child.on("exit", function (code, signal) {
947
946
  res.end(
948
- `Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`
947
+ req.__(`Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`)
949
948
  );
950
949
  setTimeout(() => {
951
950
  if (process.send) process.send("RestartServer");
@@ -1193,7 +1192,7 @@ router.get(
1193
1192
  })
1194
1193
  );
1195
1194
  /**
1196
- * /confiuration-check
1195
+ * /configuration-check
1197
1196
  */
1198
1197
  router.get(
1199
1198
  "/configuration-check",
@@ -1576,7 +1575,7 @@ router.post(
1576
1575
  {
1577
1576
  type: "card",
1578
1577
  title: req.__("Build Result"),
1579
- contents: div("The build was successfully"),
1578
+ contents: div(req.__("The build was successfully")),
1580
1579
  },
1581
1580
  files.length > 0 ? app_files_table(files, req) : "",
1582
1581
  ],
@@ -1588,7 +1587,7 @@ router.post(
1588
1587
  type: "card",
1589
1588
  title: req.__("Build Result"),
1590
1589
  contents: div(
1591
- "Unable to build the app:",
1590
+ req.__("Unable to build the app:"),
1592
1591
  pre(code(childOutputs.join("<br/>")))
1593
1592
  ),
1594
1593
  },
@@ -1604,7 +1603,7 @@ router.post(
1604
1603
  type: "card",
1605
1604
  title: req.__("Build Result"),
1606
1605
  contents: div(
1607
- p("Unable to build the app:"),
1606
+ p(req.__("Unable to build the app:")),
1608
1607
  pre(code(message)),
1609
1608
  pre(code(stack))
1610
1609
  ),
package/routes/api.js CHANGED
@@ -71,8 +71,8 @@ function accessAllowedRead(req, user, table) {
71
71
  req.user && req.user.id
72
72
  ? req.user.role_id
73
73
  : user && user.role_id
74
- ? user.role_id
75
- : 10;
74
+ ? user.role_id
75
+ : 10;
76
76
 
77
77
  return role <= table.min_role_read;
78
78
  }
@@ -89,8 +89,8 @@ function accessAllowedWrite(req, user, table) {
89
89
  req.user && req.user.id
90
90
  ? req.user.role_id
91
91
  : user && user.role_id
92
- ? user.role_id
93
- : 10;
92
+ ? user.role_id
93
+ : 10;
94
94
 
95
95
  return role <= table.min_role_write;
96
96
  }
@@ -106,8 +106,8 @@ function accessAllowed(req, user, trigger) {
106
106
  req.user && req.user.id
107
107
  ? req.user.role_id
108
108
  : user && user.role_id
109
- ? user.role_id
110
- : 10;
109
+ ? user.role_id
110
+ : 10;
111
111
 
112
112
  return role <= trigger.min_role;
113
113
  }
@@ -247,6 +247,7 @@ router.get(
247
247
  fields: tbl_fields,
248
248
  approximate: !!approximate,
249
249
  state: req_query,
250
+ table
250
251
  });
251
252
  rows = await table.getRows(qstate);
252
253
  } else {
@@ -0,0 +1,59 @@
1
+ const Page = require("@saltcorn/data/models/page");
2
+ const {
3
+ buildObjectTrees,
4
+ } = require("@saltcorn/data/diagram/node_extract_utils");
5
+ const { generateCyCode } = require("@saltcorn/data/diagram/cy_generate_utils");
6
+ const { getState } = require("@saltcorn/data/db/state");
7
+ const { div, script, domReady } = require("@saltcorn/markup/tags");
8
+ const { isAdmin, error_catcher } = require("./utils.js");
9
+ const Router = require("express-promise-router");
10
+
11
+ const router = new Router();
12
+ module.exports = router;
13
+
14
+ router.get(
15
+ "/",
16
+ isAdmin,
17
+ error_catcher(async (req, res) => {
18
+ const modernCfg = getState().getConfig("home_page_by_role");
19
+ let pages = null;
20
+ if (modernCfg) {
21
+ pages = Object.values(modernCfg)
22
+ .filter((val) => val)
23
+ .map((val) => Page.findOne({ name: val }));
24
+ } else {
25
+ pages = new Array();
26
+ for (const legacyRole of ["public", "user", "staff", "admin"]) {
27
+ const page = await Page.findOne({ name: `${legacyRole}_home` });
28
+ if (page) pages.push(page);
29
+ }
30
+ }
31
+ const cyCode = generateCyCode(await buildObjectTrees(pages));
32
+ res.sendWrap(
33
+ {
34
+ title: req.__(`Application diagram`),
35
+ headers: [
36
+ {
37
+ script:
38
+ "https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.22.1/cytoscape.min.js",
39
+ style: `
40
+ #cy {
41
+ width: 100%;
42
+ height: 900px;
43
+ display: block;
44
+ }`,
45
+ },
46
+ ],
47
+ },
48
+ {
49
+ above: [
50
+ {
51
+ type: "card",
52
+ title: req.__(`Application diagram`),
53
+ contents: [div({ id: "cy" }), script(domReady(cyCode))],
54
+ },
55
+ ],
56
+ }
57
+ );
58
+ })
59
+ );
package/routes/fields.js CHANGED
@@ -19,12 +19,13 @@ const {
19
19
  expressionValidator,
20
20
  get_async_expression_function,
21
21
  get_expression_function,
22
+ freeVariables,
22
23
  } = require("@saltcorn/data/models/expression");
23
24
  const db = require("@saltcorn/data/db");
24
25
 
25
26
  const { isAdmin, error_catcher } = require("./utils.js");
26
27
  const expressionBlurb = require("../markup/expression_blurb");
27
- const { readState } = require("@saltcorn/data/plugin-helper");
28
+ const { readState, add_free_variables_to_joinfields } = require("@saltcorn/data/plugin-helper");
28
29
  const { wizardCardTitle } = require("../markup/forms.js");
29
30
  const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
30
31
  const { applyAsync } = require("@saltcorn/data/utils");
@@ -621,7 +622,11 @@ router.post(
621
622
  const { formula, tablename, stored } = req.body;
622
623
  const table = await Table.findOne({ name: tablename });
623
624
  const fields = await table.getFields();
624
- const rows = await table.getRows({}, { orderBy: "RANDOM()", limit: 1 });
625
+ const freeVars = freeVariables(formula)
626
+ const joinFields = {}
627
+ if (stored)
628
+ add_free_variables_to_joinfields(freeVars, joinFields, fields)
629
+ const rows = await table.getJoinedRows({ joinFields, orderBy: "RANDOM()", limit: 1 });
625
630
  if (rows.length < 1) return "No rows in table";
626
631
  let result;
627
632
  try {
@@ -661,8 +666,12 @@ router.post(
661
666
  return;
662
667
  }
663
668
  const fields = await table.getFields();
664
- const row = { ...req.body };
665
- readState(row, fields);
669
+ let row = { ...req.body };
670
+ if (!row || Object.keys(row).length === 0) {
671
+ const { id } = req.query
672
+ if (id) row = await table.getRow({ id })
673
+ } else
674
+ readState(row, fields);
666
675
 
667
676
  if (fieldName.includes(".")) {
668
677
  //join field
@@ -694,7 +703,7 @@ router.post(
694
703
  return;
695
704
  }
696
705
  } else {
697
- targetField.type.fieldviews[fieldview];
706
+ fv = targetField.type.fieldviews[fieldview];
698
707
  if (!fv)
699
708
  fv =
700
709
  targetField.type.fieldviews.show ||
@@ -743,7 +752,9 @@ router.post(
743
752
 
744
753
  let result;
745
754
  try {
746
- if (field.stored) {
755
+ if (!field.calculated) {
756
+ result = row[field.name]
757
+ } else if (field.stored) {
747
758
  const f = get_async_expression_function(formula, fields);
748
759
  result = await f(row);
749
760
  } else {
@@ -751,7 +762,9 @@ router.post(
751
762
  result = f(row);
752
763
  }
753
764
  const fv = field.type.fieldviews[fieldview];
754
- res.send(fv.run(result));
765
+ if (!fv)
766
+ res.send(text(result));
767
+ else res.send(fv.run(result));
755
768
  } catch (e) {
756
769
  return res.status(400).send(`Error: ${e.message}`);
757
770
  }
package/routes/index.js CHANGED
@@ -72,6 +72,7 @@ const roleadmin = require("../auth/roleadmin");
72
72
  const scapi = require("./scapi");
73
73
  const tags = require("./tags");
74
74
  const tagentries = require("./tag_entries");
75
+ const dataDiagram = require("./diagram");
75
76
 
76
77
  module.exports =
77
78
  /**
@@ -110,4 +111,5 @@ module.exports =
110
111
  app.use("/scapi", scapi);
111
112
  app.use("/tag", tags);
112
113
  app.use("/tag-entries", tagentries);
114
+ app.use("/diagram", dataDiagram);
113
115
  };
@@ -185,9 +185,8 @@ const pageBuilderData = async (req, context) => {
185
185
  const getRootPageForm = (pages, roles, req) => {
186
186
  const form = new Form({
187
187
  action: "/pageedit/set_root_page",
188
- submitLabel: req.__("Save"),
189
- submitButtonClass: "btn-outline-primary",
190
- onChange: "remove_outline(this)",
188
+ noSubmitButton: true,
189
+ onChange: "saveAndContinue(this)",
191
190
  blurb: req.__(
192
191
  "The root page is the page that is served when the user visits the home location (/). This can be set for each user role."
193
192
  ),
package/routes/plugins.js CHANGED
@@ -455,7 +455,7 @@ const store_actions_dropdown = (req) =>
455
455
  {
456
456
  class: "dropdown-item",
457
457
  href: `/plugins/upgrade`,
458
- onClick: `notifyAlert('Upgrading modules...', true)`,
458
+ onClick: `notifyAlert('${req.__("Upgrading modules...")}', true)`,
459
459
  },
460
460
  '<i class="far fa-arrow-alt-circle-up"></i>&nbsp;' +
461
461
  req.__("Upgrade installed modules")
@@ -551,7 +551,7 @@ router.get(
551
551
  const { name } = req.params;
552
552
  const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
553
553
  if (!plugin) {
554
- req.flash("warning", "Plugin not found");
554
+ req.flash("warning", req.__("Module not found"));
555
555
  res.redirect("/plugins");
556
556
  return;
557
557
  }
@@ -792,7 +792,7 @@ router.get(
792
792
  pkgjson = require(path.join(mod.location, "package.json"));
793
793
 
794
794
  if (!plugin_db) {
795
- req.flash("warning", "Plugin not found");
795
+ req.flash("warning", "Module not found");
796
796
  res.redirect("/plugins");
797
797
  return;
798
798
  }
@@ -817,7 +817,7 @@ router.get(
817
817
  ),
818
818
  mod.plugin_module.dependencies
819
819
  ? tr(
820
- th(req.__("Plugin dependencies")),
820
+ th(req.__("Module dependencies")),
821
821
  td(
822
822
  mod.plugin_module.dependencies.map((d) =>
823
823
  span({ class: "badge bg-primary me-1" }, d)
@@ -933,7 +933,7 @@ router.get(
933
933
 
934
934
  const plugin = await Plugin.findOne({ name });
935
935
  await plugin.upgrade_version((p, f) => load_plugins.loadPlugin(p, f));
936
- req.flash("success", req.__(`Plugin up-to-date`));
936
+ req.flash("success", req.__(`Module up-to-date`));
937
937
 
938
938
  res.redirect(`/plugins/info/${plugin.name}`);
939
939
  })
@@ -954,7 +954,7 @@ router.post(
954
954
  if (schema !== db.connectObj.default_schema) {
955
955
  req.flash(
956
956
  "error",
957
- req.__(`Only store plugins can be installed on tenant instances`)
957
+ req.__(`Only store modules can be installed on tenant instances`)
958
958
  );
959
959
  res.redirect(`/plugins`);
960
960
  } else {
@@ -963,12 +963,12 @@ router.post(
963
963
  plugin,
964
964
  schema === db.connectObj.default_schema || plugin.source === "github"
965
965
  );
966
- req.flash("success", req.__(`Plugin %s installed`, plugin.name));
966
+ req.flash("success", req.__(`Module %s installed`, plugin.name));
967
967
  res.redirect(`/plugins`);
968
968
  } catch (e) {
969
969
  req.flash("error", `${e.message}`);
970
970
  const form = pluginForm(req, plugin);
971
- res.sendWrap(req.__(`Edit Plugin`), renderForm(form, req.csrfToken()));
971
+ res.sendWrap(req.__(`Edit Module`), renderForm(form, req.csrfToken()));
972
972
  }
973
973
  }
974
974
  })
@@ -988,7 +988,7 @@ router.post(
988
988
 
989
989
  const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
990
990
  if (!plugin) {
991
- req.flash("warning", "Plugin not found");
991
+ req.flash("warning", "Module not found");
992
992
  res.redirect("/plugins");
993
993
  return;
994
994
  }
@@ -998,11 +998,11 @@ router.post(
998
998
  getState().getConfig("development_mode", false)
999
999
  ) {
1000
1000
  await plugin.delete();
1001
- req.flash("success", req.__(`Plugin %s removed.`, plugin.name));
1001
+ req.flash("success", req.__(`Module %s removed.`, plugin.name));
1002
1002
  } else {
1003
1003
  req.flash(
1004
1004
  "error",
1005
- req.__(`Cannot remove plugin: views %s depend on it`, depviews.join())
1005
+ req.__(`Cannot remove module: views %s depend on it`, depviews.join())
1006
1006
  );
1007
1007
  }
1008
1008
  res.redirect(`/plugins`);
@@ -1025,7 +1025,7 @@ router.post(
1025
1025
  if (!plugin) {
1026
1026
  req.flash(
1027
1027
  "error",
1028
- req.__(`Plugin %s not found`, text(decodeURIComponent(name)))
1028
+ req.__(`Module %s not found`, text(decodeURIComponent(name)))
1029
1029
  );
1030
1030
  res.redirect(`/plugins`);
1031
1031
  return;
@@ -1034,7 +1034,7 @@ router.post(
1034
1034
  if (!isRoot && plugin.unsafe) {
1035
1035
  req.flash(
1036
1036
  "error",
1037
- req.__("Cannot install unsafe plugins on subdomain tenants")
1037
+ req.__("Cannot install unsafe modules on subdomain tenants")
1038
1038
  );
1039
1039
  res.redirect(`/plugins`);
1040
1040
  return;
@@ -1047,14 +1047,14 @@ router.post(
1047
1047
  req.flash(
1048
1048
  "success",
1049
1049
  req.__(
1050
- `Plugin %s installed, please complete configuration.`,
1050
+ `Module %s installed, please complete configuration.`,
1051
1051
  plugin_db.name
1052
1052
  )
1053
1053
  );
1054
1054
  await sleep(1000); // Allow other workers to load this plugin
1055
1055
  res.redirect(`/plugins/configure/${plugin_db.name}`);
1056
1056
  } else {
1057
- req.flash("success", req.__(`Plugin %s installed`, plugin.name));
1057
+ req.flash("success", req.__(`Module %s installed`, plugin.name));
1058
1058
  res.redirect(`/plugins`);
1059
1059
  }
1060
1060
  })
package/routes/tables.js CHANGED
@@ -1013,7 +1013,6 @@ router.get(
1013
1013
  const getRole = (rid) => roles.find((r) => r.id === rid).role;
1014
1014
  const mainCard = await tablesList(rows, req);
1015
1015
  const createCard = div(
1016
- h5(req.__("Create table")),
1017
1016
  a(
1018
1017
  { href: `/table/new`, class: "btn btn-primary mt-1 me-3" },
1019
1018
  i({ class: "fas fa-plus-square me-1" }),
@@ -1029,8 +1028,13 @@ router.get(
1029
1028
  ),
1030
1029
  !db.isSQLite &&
1031
1030
  a(
1032
- { href: `/table/discover`, class: "btn btn-secondary mt-1" },
1031
+ {
1032
+ href: `/table/discover`,
1033
+ class: "btn btn-secondary mt-1",
1034
+ title: req.__("Discover tables that are already in the Database, but not known to Saltcorn"),
1035
+ },
1033
1036
  i({ class: "fas fa-map-signs me-1" }),
1037
+
1034
1038
  req.__("Discover tables")
1035
1039
  )
1036
1040
  );
@@ -105,7 +105,7 @@ router.get(
105
105
  },
106
106
  {
107
107
  type: "card",
108
- title: `Add entries to tag`,
108
+ title: req.__(`Add entries to tag`),
109
109
  contents: buildForm(
110
110
  entry_type,
111
111
  tag_id,
@@ -143,7 +143,7 @@ router.post(
143
143
  const { entry_type, tag_id } = req.params;
144
144
  const { ids } = req.body;
145
145
  if (!ids) {
146
- req.flash("error", req.__("Please select at least on item"));
146
+ req.flash("error", req.__("Please select at least one item"));
147
147
  return res.redirect(`/tag-entries/add/${entry_type}/${tag_id}`);
148
148
  }
149
149
  const fieldName = idField(entry_type);
package/routes/tags.js CHANGED
@@ -6,7 +6,7 @@ const Form = require("@saltcorn/data/models/form");
6
6
  const User = require("@saltcorn/data/models/user");
7
7
 
8
8
  const { isAdmin, error_catcher, csrfField } = require("./utils");
9
- const { send_admin_page } = require("../markup/admin");
9
+ const { send_infoarch_page } = require("../markup/admin");
10
10
 
11
11
  const {
12
12
  mkTable,
@@ -31,7 +31,7 @@ router.get(
31
31
  isAdmin,
32
32
  error_catcher(async (req, res) => {
33
33
  const rows = await Tag.find();
34
- send_admin_page({
34
+ send_infoarch_page({
35
35
  res,
36
36
  req,
37
37
  active_sub: "Tags",
@@ -199,7 +199,7 @@ router.get(
199
199
  href: `/tag-entries/add/pages/${tag.id}`,
200
200
  class: "btn btn-primary",
201
201
  },
202
- req.__("Add tages")
202
+ req.__("Add pages")
203
203
  ),
204
204
  ],
205
205
  },
@@ -222,7 +222,7 @@ router.get(
222
222
  href: `/tag-entries/add/trigger/${tag.id}`,
223
223
  class: "btn btn-primary",
224
224
  },
225
- req.__("Add trigger")
225
+ req.__("Add triggers")
226
226
  ),
227
227
  ],
228
228
  },
@@ -146,6 +146,15 @@ describe("pageedit", () => {
146
146
  .send("id=1")
147
147
  .expect(toRedirect("/pageedit/"));
148
148
  });
149
+ it("show builder", async () => {
150
+ const app = await getApp({ disableCsrf: true });
151
+ const loginCookie = await getAdminLoginCookie();
152
+ await request(app)
153
+ .get("/pageedit/edit/a_page")
154
+ .set("Cookie", loginCookie)
155
+ .expect(toInclude("<script>builder.renderBuilder"));
156
+
157
+ });
149
158
 
150
159
  it("sets root page", async () => {
151
160
  const app = await getApp({ disableCsrf: true });
@@ -43,6 +43,22 @@ describe("viewedit edit endpoint", () => {
43
43
  .set("Cookie", loginCookie)
44
44
  .expect(toInclude("author"));
45
45
  });
46
+ it("show list editor", async () => {
47
+ const loginCookie = await getAdminLoginCookie();
48
+ const app = await getApp({ disableCsrf: true });
49
+ await request(app)
50
+ .get("/viewedit/config/authorlist")
51
+ .set("Cookie", loginCookie)
52
+ .expect(toInclude("author"));
53
+ });
54
+ it("show builder", async () => {
55
+ const loginCookie = await getAdminLoginCookie();
56
+ const app = await getApp({ disableCsrf: true });
57
+ await request(app)
58
+ .get("/viewedit/config/authorshow")
59
+ .set("Cookie", loginCookie)
60
+ .expect(toInclude("<script>builder.renderBuilder"));
61
+ });
46
62
  });
47
63
 
48
64
  describe("viewedit new List", () => {