@saltcorn/server 0.6.4-beta.6 → 0.7.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.
@@ -220,3 +220,7 @@ footer.bs-mobile-nav-footer {
220
220
  .welcome-page-entity-list {
221
221
  min-height: 35vh;
222
222
  }
223
+
224
+ .form-group {
225
+ margin-bottom: 1rem;
226
+ }
@@ -30,16 +30,20 @@ function add_repeater(nm) {
30
30
  function apply_showif() {
31
31
  $("[data-show-if]").each(function (ix, element) {
32
32
  var e = $(element);
33
- var to_show = new Function(
34
- "e",
35
- "return " + decodeURIComponent(e.attr("data-show-if"))
36
- );
37
- if (to_show(e))
38
- e.show()
39
- .find("input, textarea, button, select")
40
- .prop("disabled", e.attr("data-disabled") || false);
41
- else
42
- e.hide().find("input, textarea, button, select").prop("disabled", true);
33
+ try {
34
+ var to_show = new Function(
35
+ "e",
36
+ "return " + decodeURIComponent(e.attr("data-show-if"))
37
+ );
38
+ if (to_show(e))
39
+ e.show()
40
+ .find("input, textarea, button, select")
41
+ .prop("disabled", e.attr("data-disabled") || false);
42
+ else
43
+ e.hide().find("input, textarea, button, select").prop("disabled", true);
44
+ } catch (e) {
45
+ console.error(e);
46
+ }
43
47
  });
44
48
  $("[data-calc-options]").each(function (ix, element) {
45
49
  var e = $(element);
@@ -55,8 +59,19 @@ function apply_showif() {
55
59
  //console.log(val, options, current,data)
56
60
  e.empty();
57
61
  (options || []).forEach((o) => {
58
- if (current === o) e.append($("<option selected>" + o + "</option>"));
59
- else e.append($("<option>" + o + "</option>"));
62
+ if (!(o && o.label && o.value)) {
63
+ if (`${current}` === `${o}`)
64
+ e.append($("<option selected>" + o + "</option>"));
65
+ else e.append($("<option>" + o + "</option>"));
66
+ } else {
67
+ e.append(
68
+ $(
69
+ `<option ${
70
+ `${current}` === `${o.value}` ? "selected" : ""
71
+ } value="${o.value}">${o.label}</option>`
72
+ )
73
+ );
74
+ }
60
75
  });
61
76
  e.change(function (ec) {
62
77
  e.attr("data-selected", ec.target.value);
@@ -76,12 +91,14 @@ function apply_showif() {
76
91
  });
77
92
  });
78
93
  }
79
- function get_form_record(e) {
94
+ function get_form_record(e, select_labels) {
80
95
  const rec = {};
81
96
  e.closest("form")
82
97
  .find("input[name],select[name]")
83
98
  .each(function () {
84
- rec[$(this).attr("name")] = $(this).val();
99
+ if (select_labels && $(this).prop("tagName").toLowerCase() === "select")
100
+ rec[$(this).attr("name")] = $(this).find("option:selected").text();
101
+ else rec[$(this).attr("name")] = $(this).val();
85
102
  });
86
103
  return rec;
87
104
  }
@@ -193,7 +210,7 @@ function initialize_page() {
193
210
  if ($(this).find(".editicon").length === 0) {
194
211
  var current = $(this).html();
195
212
  $(this).html(
196
- `<span class="current">${current}</span><i class="editicon fas fa-edit ml-1"></i>`
213
+ `<span class="current">${current}</span><i class="editicon fas fa-edit ms-1"></i>`
197
214
  );
198
215
  }
199
216
  });
@@ -275,7 +292,7 @@ function initialize_page() {
275
292
  const options = parse(el.attr("locale-date-options"));
276
293
  el.text(date.toLocaleDateString(locale, options));
277
294
  });
278
- $('a[data-toggle="tab"]').historyTabs();
295
+ $('a[data-bs-toggle="tab"].deeplink').historyTabs();
279
296
  }
280
297
 
281
298
  $(initialize_page);
@@ -423,8 +440,7 @@ function notifyAlert(note) {
423
440
  $("#alerts-area")
424
441
  .append(`<div class="alert alert-${type} alert-dismissible fade show" role="alert">
425
442
  ${txt}
426
- <button type="button" class="close" data-dismiss="alert" aria-label="Close">
427
- <span aria-hidden="true">&times;</span>
443
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
428
444
  </button>
429
445
  </div>`);
430
446
  }
@@ -499,8 +515,7 @@ function ajax_modal(url, opts = {}) {
499
515
  <div class="modal-content">
500
516
  <div class="modal-header">
501
517
  <h5 class="modal-title">Modal title</h5>
502
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
503
- <span aria-hidden="true">&times;</span>
518
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
504
519
  </button>
505
520
  </div>
506
521
  <div class="modal-body">
@@ -517,7 +532,7 @@ function ajax_modal(url, opts = {}) {
517
532
  var title = request.getResponseHeader("Page-Title");
518
533
  if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
519
534
  $("#scmodal .modal-body").html(res);
520
- $("#scmodal").modal();
535
+ new bootstrap.Modal($("#scmodal")).show();
521
536
  initialize_page();
522
537
  (opts.onOpen || function () {})(res);
523
538
  $("#scmodal").on("hidden.bs.modal", function (e) {
@@ -624,11 +639,13 @@ function make_unique_field(
624
639
  id,
625
640
  table_id,
626
641
  field_name,
627
- value,
642
+ elem,
628
643
  space,
629
644
  start,
630
- always_append
645
+ always_append,
646
+ char_type
631
647
  ) {
648
+ const value = $(elem).val();
632
649
  if (!value) return;
633
650
  $.ajax(
634
651
  `/api/${table_id}?approximate=true&${encodeURIComponent(
@@ -638,12 +655,25 @@ function make_unique_field(
638
655
  type: "GET",
639
656
  success: function (res) {
640
657
  if (res.success) {
658
+ const gen_char = (i) => {
659
+ switch (char_type) {
660
+ case "Lowercase Letters":
661
+ return String.fromCharCode("a".charCodeAt(0) + i);
662
+ break;
663
+ case "Uppercase Letters":
664
+ return String.fromCharCode("A".charCodeAt(0) + i);
665
+ break;
666
+ default:
667
+ return i;
668
+ break;
669
+ }
670
+ };
641
671
  const vals = res.success
642
672
  .map((o) => o[field_name])
643
673
  .filter((s) => s.startsWith(value));
644
674
  if (vals.includes(value) || always_append) {
645
675
  for (let i = start || 0; i < vals.length + (start || 0) + 2; i++) {
646
- const newname = `${value}${space ? " " : ""}${i}`;
676
+ const newname = `${value}${space ? " " : ""}${gen_char(i)}`;
647
677
  if (!vals.includes(newname)) {
648
678
  $("#" + id).val(newname);
649
679
  return;
@@ -721,6 +751,16 @@ function room_older(viewname, room_id, btn) {
721
751
  );
722
752
  }
723
753
 
754
+ function fill_formula_btn_click(btn) {
755
+ const formula = decodeURIComponent($(btn).attr("data-formula"));
756
+ const rec = get_form_record($(btn), true);
757
+ const val = new Function(
758
+ `{${Object.keys(rec).join(",")}}`,
759
+ "return " + formula
760
+ )(rec);
761
+ $(btn).closest(".input-group").find("input").val(val);
762
+ }
763
+
724
764
  /*
725
765
  https://github.com/jeffdavidgreen/bootstrap-html5-history-tabs/blob/master/bootstrap-history-tabs.js
726
766
  Copyright (c) 2015 Jeff Green
package/routes/actions.js CHANGED
@@ -144,20 +144,18 @@ router.get(
144
144
  contents: table(
145
145
  tbody(
146
146
  tr(
147
- td({ class: "pr-2" }, req.__("Actions available")),
147
+ td({ class: "pe-2" }, req.__("Actions available")),
148
148
  td(
149
149
  actions
150
- .map((a) =>
151
- span({ class: "badge badge-primary" }, a.name)
152
- )
150
+ .map((a) => span({ class: "badge bg-primary" }, a.name))
153
151
  .join("&nbsp;")
154
152
  )
155
153
  ),
156
154
  tr(
157
- td({ class: "pr-2" }, req.__("Event types")),
155
+ td({ class: "pe-2" }, req.__("Event types")),
158
156
  td(
159
157
  Trigger.when_options
160
- .map((a) => span({ class: "badge badge-secondary" }, a))
158
+ .map((a) => span({ class: "badge bg-secondary" }, a))
161
159
  .join("&nbsp;")
162
160
  )
163
161
  )
package/routes/admin.js CHANGED
@@ -46,7 +46,10 @@ const {
46
46
  get_process_init_time,
47
47
  } = require("@saltcorn/data/db/state");
48
48
  const { loadAllPlugins } = require("../load_plugins");
49
- const { create_backup, restore } = require("@saltcorn/admin-models/models/backup");
49
+ const {
50
+ create_backup,
51
+ restore,
52
+ } = require("@saltcorn/admin-models/models/backup");
50
53
  const fs = require("fs");
51
54
  const load_plugins = require("../load_plugins");
52
55
  const {
@@ -299,7 +302,7 @@ router.get(
299
302
  post_btn("/admin/backup", req.__("Backup"), req.csrfToken())
300
303
  )
301
304
  ),
302
- td(p({ class: "ml-4 pt-2" }, req.__("Download a backup")))
305
+ td(p({ class: "ms-4 pt-2" }, req.__("Download a backup")))
303
306
  ),
304
307
  tr(td(div({ class: "my-4" }))),
305
308
  tr(
@@ -310,7 +313,7 @@ router.get(
310
313
  req.__("Restore"),
311
314
  ])
312
315
  ),
313
- td(p({ class: "ml-4" }, req.__("Restore a backup")))
316
+ td(p({ class: "ms-4" }, req.__("Restore a backup")))
314
317
  )
315
318
  )
316
319
  ),
@@ -393,7 +396,7 @@ router.get(
393
396
  )
394
397
  : isRoot && is_latest
395
398
  ? span(
396
- { class: "badge badge-primary ml-2" },
399
+ { class: "badge bg-primary ms-2" },
397
400
  req.__("Latest")
398
401
  )
399
402
  : "")
@@ -155,9 +155,9 @@ router.get(
155
155
  a(
156
156
  {
157
157
  href: "/eventlog/custom/new",
158
- class: "btn btn-primary mt-1 mr-3",
158
+ class: "btn btn-primary mt-1 me-3",
159
159
  },
160
- i({ class: "fas fa-plus-square mr-1" }),
160
+ i({ class: "fas fa-plus-square me-1" }),
161
161
  req.__("Create custom event")
162
162
  ),
163
163
  },
@@ -50,17 +50,17 @@ const tableCard = (tables, req) => ({
50
50
  contents:
51
51
  (tables.length <= 1
52
52
  ? p(
53
- { class: "mt-2 pr-2" },
53
+ { class: "mt-2 pe-2" },
54
54
  i(req.__("Tables organise data by fields and rows."))
55
55
  )
56
56
  : "") + tableTable(tables, req),
57
- bodyClass: "py-0 pr-0",
57
+ bodyClass: "py-0 pe-0",
58
58
  footer: div(
59
59
  a({ href: `/table/new`, class: "btn btn-primary" }, req.__("Create table")),
60
60
  a(
61
61
  {
62
62
  href: `/table/create-from-csv`,
63
- class: "btn btn-secondary ml-2",
63
+ class: "btn btn-secondary ms-2",
64
64
  },
65
65
  req.__("CSV upload")
66
66
  )
@@ -97,11 +97,11 @@ const viewCard = (views, req) => ({
97
97
  type: "card",
98
98
  title: link("/viewedit", req.__("Views")),
99
99
  class: "welcome-page-entity-list",
100
- bodyClass: "py-0 pr-0",
100
+ bodyClass: "py-0 pe-0",
101
101
  contents:
102
102
  (views.length <= 1
103
103
  ? p(
104
- { class: "mt-2 pr-2" },
104
+ { class: "mt-2 pe-2" },
105
105
  i(
106
106
  req.__(
107
107
  "Views display data from tables. A view is a view template applied to a table, with configuration."
@@ -152,7 +152,7 @@ const pageCard = (pages, req) => ({
152
152
  contents:
153
153
  (pages.length <= 1
154
154
  ? p(
155
- { class: "mt-2 pr-2" },
155
+ { class: "mt-2 pe-2" },
156
156
  i(
157
157
  req.__(
158
158
  "Pages are the web pages of your application built with a drag-and-drop builder. They have static content, and by embedding views, dynamic content."
@@ -162,8 +162,8 @@ const pageCard = (pages, req) => ({
162
162
  : "") +
163
163
  (pages.length > 0
164
164
  ? pageTable(pages, req)
165
- : div({ class: "mt-2 pr-2" }, p(req.__("No pages")))),
166
- bodyClass: "py-0 pr-0",
165
+ : div({ class: "mt-2 pe-2" }, p(req.__("No pages")))),
166
+ bodyClass: "py-0 pe-0",
167
167
  footer: div(
168
168
  a(
169
169
  { href: `/pageedit/new`, class: "btn btn-primary" },
@@ -231,7 +231,7 @@ const actionsTab = async (req, triggers) => {
231
231
  { class: "pb-3" },
232
232
  triggers.length <= 1 &&
233
233
  p(
234
- { class: "mt-2 pr-2" },
234
+ { class: "mt-2 pe-2" },
235
235
  i(req.__("Triggers run actions in response to events."))
236
236
  ),
237
237
  triggers.length == 0
@@ -262,7 +262,7 @@ const actionsTab = async (req, triggers) => {
262
262
  };
263
263
  const packTab = (req, packlist) =>
264
264
  div(
265
- { class: "pb-3 pt-2 pr-4" },
265
+ { class: "pb-3 pt-2 pe-4" },
266
266
  p(req.__("Instead of building, get up and running in no time with packs")),
267
267
  p(
268
268
  { class: "font-italic" },
@@ -289,7 +289,7 @@ const packTab = (req, packlist) =>
289
289
 
290
290
  const helpCard = (req) =>
291
291
  div(
292
- { class: "pb-3 pt-2 pr-4" },
292
+ { class: "pb-3 pt-2 pe-4" },
293
293
  p(req.__("Confused?")),
294
294
  p(
295
295
  req.__(
@@ -354,7 +354,7 @@ const welcome_page = async (req) => {
354
354
  {
355
355
  type: "card",
356
356
  //title: req.__("Install pack"),
357
- bodyClass: "py-0 pr-0",
357
+ bodyClass: "py-0 pe-0",
358
358
  class: "welcome-page-entity-list",
359
359
 
360
360
  tabContents:
@@ -373,7 +373,7 @@ const welcome_page = async (req) => {
373
373
  {
374
374
  type: "card",
375
375
  //title: req.__("Learn"),
376
- bodyClass: "py-0 pr-0",
376
+ bodyClass: "py-0 pe-0",
377
377
  class: "welcome-page-entity-list",
378
378
  tabContents:
379
379
  users.length > 4
@@ -124,7 +124,7 @@ router.get(
124
124
  href: "/site-structure/localizer/add-lang",
125
125
  class: "btn btn-primary mt-1",
126
126
  },
127
- i({ class: "fas fa-plus-square mr-1" }),
127
+ i({ class: "fas fa-plus-square me-1" }),
128
128
  req.__("Add language")
129
129
  )
130
130
  ),
package/routes/list.js CHANGED
@@ -264,6 +264,7 @@ router.get(
264
264
  width: 40,
265
265
  hozAlign: "center",
266
266
  headerSort: false,
267
+ clipboard: false,
267
268
  cellClick: "__delete_tabulator_row",
268
269
  });
269
270
  res.sendWrap(
@@ -305,24 +306,25 @@ router.get(
305
306
  { href: `/table/${table.id || table.name}`, text: table.name },
306
307
  { text: req.__("Data") },
307
308
  ],
308
- right:
309
+ right: div(
310
+ { class: "d-flex" },
309
311
  button(
310
312
  {
311
- class: "btn btn-sm btn-primary mr-2",
313
+ class: "btn btn-sm btn-primary me-2",
312
314
  onClick: "add_tabulator_row()",
313
315
  },
314
- i({ class: "fas fa-plus mr-1" }),
316
+ i({ class: "fas fa-plus me-1" }),
315
317
  "Add row"
316
- ) +
318
+ ),
317
319
  div(
318
- { class: "dropdown d-inline" },
320
+ { class: "dropdown" },
319
321
  button(
320
322
  {
321
323
  class: "btn btn-sm btn-outline-secondary dropdown-toggle",
322
324
  "data-boundary": "viewport",
323
325
  type: "button",
324
326
  id: "btnHideCols",
325
- "data-toggle": "dropdown",
327
+ "data-bs-toggle": "dropdown",
326
328
  "aria-haspopup": "true",
327
329
  "aria-expanded": "false",
328
330
  },
@@ -330,7 +332,7 @@ router.get(
330
332
  ),
331
333
  div(
332
334
  {
333
- class: "dropdown-menu",
335
+ class: "dropdown-menu dropdown-menu-end",
334
336
  "aria-labelledby": "btnHideCols",
335
337
  },
336
338
  form(
@@ -349,7 +351,8 @@ router.get(
349
351
  )
350
352
  )
351
353
  )
352
- ),
354
+ )
355
+ ),
353
356
  },
354
357
  {
355
358
  type: "blank",
@@ -374,6 +377,10 @@ router.get(
374
377
  height:"100%",
375
378
  pagination:true,
376
379
  paginationSize:20,
380
+ clipboard:true,
381
+ persistence:true,
382
+ persistenceID:"table_tab_${table.name}",
383
+ movableColumns: true,
377
384
  initialSort:[
378
385
  {column:"id", dir:"asc"},
379
386
  ],
package/routes/menu.js CHANGED
@@ -334,7 +334,7 @@ router.get(
334
334
  script: static_pre + "/iconset-fontawesome5-3-1.min.js",
335
335
  },
336
336
  {
337
- script: static_pre + "/bootstrap-iconpicker.min.js",
337
+ script: static_pre + "/bootstrap-iconpicker.js",
338
338
  },
339
339
  {
340
340
  css: static_pre + "/bootstrap-iconpicker.min.css",
@@ -224,6 +224,7 @@ const pageBuilderData = async (req, context) => {
224
224
  roles,
225
225
  fixed_state_fields,
226
226
  next_button_label: "Done",
227
+ fonts: getState().fonts,
227
228
  };
228
229
  };
229
230
 
package/routes/plugins.js CHANGED
@@ -209,7 +209,7 @@ const cfg_link = (req, row) => {
209
209
  if (plugin.configuration_workflow)
210
210
  return a(
211
211
  {
212
- class: "btn btn-secondary btn-sm d-inline mr-1",
212
+ class: "btn btn-secondary btn-sm d-inline me-1",
213
213
  role: "button",
214
214
  href: `/plugins/configure/${encodeURIComponent(row.name)}`,
215
215
  title: req.__("Configure plugin"),
@@ -240,7 +240,7 @@ const info_link = (req, row) =>
240
240
  * @returns {span}
241
241
  */
242
242
  const badge = (title) =>
243
- span({ class: "badge badge-secondary plugin-store" }, title);
243
+ span({ class: "badge bg-secondary plugin-store" }, title);
244
244
 
245
245
  /**
246
246
  *
@@ -427,7 +427,7 @@ const store_actions_dropdown = (req) =>
427
427
  class: "btn btn-outline-secondary",
428
428
  type: "button",
429
429
  id: "dropdownMenuButton",
430
- "data-toggle": "dropdown",
430
+ "data-bs-toggle": "dropdown",
431
431
  "aria-haspopup": "true",
432
432
  "aria-expanded": "false",
433
433
  },
@@ -435,7 +435,7 @@ const store_actions_dropdown = (req) =>
435
435
  ),
436
436
  div(
437
437
  {
438
- class: "dropdown-menu dropdown-menu-right",
438
+ class: "dropdown-menu dropdown-menu-end",
439
439
  "aria-labelledby": "dropdownMenuButton",
440
440
  },
441
441
  a(
@@ -501,13 +501,10 @@ const plugin_store_html = (items, req) => {
501
501
  {
502
502
  type: "card",
503
503
  contents: div(
504
- { class: "d-flex" },
504
+ { class: "d-flex justify-content-between" },
505
505
  storeNavPills(req),
506
- div(
507
- { class: "ml-auto" },
508
- search_bar("q", req.query.q || "", { stateField: "q" })
509
- ),
510
- div({ class: "ml-auto" }, store_actions_dropdown(req))
506
+ div(search_bar("q", req.query.q || "", { stateField: "q" })),
507
+ div(store_actions_dropdown(req))
511
508
  ),
512
509
  },
513
510
  {
@@ -602,6 +599,7 @@ router.post(
602
599
  refresh_plugin_cfg: plugin.name,
603
600
  tenant: db.getTenantSchema(),
604
601
  });
602
+ await sleep(500); // Allow other workers to reload this plugin
605
603
  res.redirect("/plugins");
606
604
  }
607
605
  })
@@ -736,7 +734,7 @@ router.get(
736
734
  ? a(
737
735
  {
738
736
  href: `/plugins/upgrade-plugin/${plugin_db.name}`,
739
- class: "btn btn-primary btn-sm ml-2",
737
+ class: "btn btn-primary btn-sm ms-2",
740
738
  },
741
739
  req.__("Upgrade")
742
740
  )
@@ -748,7 +746,7 @@ router.get(
748
746
  th(req.__("Plugin dependencies")),
749
747
  td(
750
748
  mod.plugin_module.dependencies.map((d) =>
751
- span({ class: "badge badge-primary mr-1" }, d)
749
+ span({ class: "badge bg-primary me-1" }, d)
752
750
  )
753
751
  )
754
752
  )
@@ -31,7 +31,7 @@ const settingsCard = ({ title, icon, blurb, href }) => ({
31
31
  url: href,
32
32
  contents: {
33
33
  besides: [
34
- i({ class: [icon, "fa-3x fa-fw mr-3"] }),
34
+ i({ class: [icon, "fa-3x fa-fw me-3"] }),
35
35
  a({ href }, h3(title)) + p(blurb),
36
36
  ],
37
37
  widths: false,
package/routes/tables.js CHANGED
@@ -488,8 +488,7 @@ router.get(
488
488
  * @param {string} lbl
489
489
  * @returns {string}
490
490
  */
491
- const badge = (col, lbl) =>
492
- `<span class="badge badge-${col}">${lbl}</span>&nbsp;`;
491
+ const badge = (col, lbl) => `<span class="badge bg-${col}">${lbl}</span>&nbsp;`;
493
492
 
494
493
  /**
495
494
  * @param {object} f
@@ -578,7 +577,7 @@ router.get(
578
577
  : (r.type && r.type.name) ||
579
578
  r.type ||
580
579
  r.typename +
581
- span({ class: "badge badge-danger ml-1" }, "Unknown type"),
580
+ span({ class: "badge bg-danger ms-1" }, "Unknown type"),
582
581
  },
583
582
  {
584
583
  label: "",
@@ -1035,22 +1034,22 @@ router.get(
1035
1034
  const createCard = div(
1036
1035
  h5(req.__("Create table")),
1037
1036
  a(
1038
- { href: `/table/new`, class: "btn btn-primary mt-1 mr-3" },
1039
- i({ class: "fas fa-plus-square mr-1" }),
1037
+ { href: `/table/new`, class: "btn btn-primary mt-1 me-3" },
1038
+ i({ class: "fas fa-plus-square me-1" }),
1040
1039
  req.__("Create table")
1041
1040
  ),
1042
1041
  a(
1043
1042
  {
1044
1043
  href: `/table/create-from-csv`,
1045
- class: "btn btn-secondary mr-3 mt-1",
1044
+ class: "btn btn-secondary me-3 mt-1",
1046
1045
  },
1047
- i({ class: "fas fa-upload mr-1" }),
1046
+ i({ class: "fas fa-upload me-1" }),
1048
1047
  req.__("Create from CSV upload")
1049
1048
  ),
1050
1049
  !db.isSQLite &&
1051
1050
  a(
1052
1051
  { href: `/table/discover`, class: "btn btn-secondary mt-1" },
1053
- i({ class: "fas fa-map-signs mr-1" }),
1052
+ i({ class: "fas fa-map-signs me-1" }),
1054
1053
  req.__("Discover tables")
1055
1054
  )
1056
1055
  );
package/routes/utils.js CHANGED
@@ -100,7 +100,8 @@ const set_custom_http_headers = (res, state) => {
100
100
  * @returns {string}
101
101
  */
102
102
  const get_tenant_from_req = (req) => {
103
- if (req.subdomains && req.subdomains.length > 0) return req.subdomains[0];
103
+ if (req.subdomains && req.subdomains.length > 0)
104
+ return req.subdomains[req.subdomains.length - 1];
104
105
 
105
106
  if (req.subdomains && req.subdomains.length == 0)
106
107
  return db.connectObj.default_schema;
@@ -121,8 +122,10 @@ const setTenant = (req, res, next) => {
121
122
  const other_domain = get_other_domain_tenant(req.hostname);
122
123
  if (other_domain) {
123
124
  const state = getTenant(other_domain);
124
- if (!state) res.status(404).send(req.__("Subdomain not found"));
125
- else {
125
+ if (!state) {
126
+ setLanguage(req, res);
127
+ next();
128
+ } else {
126
129
  db.runWithTenant(other_domain, () => {
127
130
  setLanguage(req, res, state);
128
131
  next();
@@ -130,10 +133,11 @@ const setTenant = (req, res, next) => {
130
133
  }
131
134
  } else {
132
135
  const ten = get_tenant_from_req(req);
133
- //console.log("tenant", ten);
134
136
  const state = getTenant(ten);
135
- if (!state) res.status(404).send(req.__("Subdomain not found"));
136
- else {
137
+ if (!state) {
138
+ setLanguage(req, res);
139
+ next();
140
+ } else {
137
141
  db.runWithTenant(ten, () => {
138
142
  setLanguage(req, res, state);
139
143
  next();
@@ -90,7 +90,7 @@ describe("Plugin Endpoints", () => {
90
90
  .expect(toInclude("testfilecontents"));
91
91
  await request(app)
92
92
  .get(
93
- "/plugins/pubdeps/sbadmin2/startbootstrap-sb-admin-2/4.1.4/css/sb-admin-2.min.css"
93
+ "/plugins/pubdeps/sbadmin2/startbootstrap-sb-admin-2-bs5/4.1.5-beta.0/css/sb-admin-2.min.css"
94
94
  )
95
95
  .expect(toInclude("Start Bootstrap"));
96
96
 
@@ -98,6 +98,11 @@ describe("Plugin Endpoints", () => {
98
98
  .post("/plugins/delete/" + p.name)
99
99
  .set("Cookie", loginCookie)
100
100
  .expect(toRedirect("/plugins"));
101
+ await request(app)
102
+ .get(
103
+ "/plugins/pubdeps/sbadmin2/startbootstrap-sb-admin-2-bs5/4.1.5-beta.0/css/sb-admin-2.min.css"
104
+ )
105
+ .expect(toInclude("Start Bootstrap"));
101
106
  });
102
107
  it("should install named without config", async () => {
103
108
  const loginCookie = await getAdminLoginCookie();