@saltcorn/server 0.8.7 → 0.8.8-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.8.7",
3
+ "version": "0.8.8-beta.0",
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.7",
10
- "@saltcorn/builder": "0.8.7",
11
- "@saltcorn/data": "0.8.7",
12
- "@saltcorn/admin-models": "0.8.7",
13
- "@saltcorn/filemanager": "0.8.7",
14
- "@saltcorn/markup": "0.8.7",
15
- "@saltcorn/sbadmin2": "0.8.7",
9
+ "@saltcorn/base-plugin": "0.8.8-beta.0",
10
+ "@saltcorn/builder": "0.8.8-beta.0",
11
+ "@saltcorn/data": "0.8.8-beta.0",
12
+ "@saltcorn/admin-models": "0.8.8-beta.0",
13
+ "@saltcorn/filemanager": "0.8.8-beta.0",
14
+ "@saltcorn/markup": "0.8.8-beta.0",
15
+ "@saltcorn/sbadmin2": "0.8.8-beta.0",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
@@ -49,7 +49,7 @@ function buildCard(node) {
49
49
  <h5 class="card-title">${type}</h5>
50
50
  <h6 class="card-subtitle text-muted">${label}</h6>
51
51
  </div>
52
- <div class="card-body">
52
+ <div class="card-body pt-1">
53
53
  ${!isVirtual ? buildTagBadges(node) : "<h5>virtual</h5>"}
54
54
  ${buildCardBody(node)}
55
55
  <div>
@@ -198,6 +198,7 @@ function buildTagBadges(node) {
198
198
  id="_${type}_${objectId}_badges_id"
199
199
  class="mb-3"
200
200
  >
201
+ <h6 class="text-muted mb-0">Tags:</h6>
201
202
  ${existingTagBadges(node)}
202
203
  <button
203
204
  class="badge bg-primary"
@@ -486,6 +487,25 @@ function reloadCy(keepViewPos) {
486
487
  });
487
488
  }
488
489
 
490
+ function takePicture() {
491
+ const base64 = window.cy.png({ bg: "white" }).substr(22);
492
+ const decoded = window.atob(base64);
493
+ const bytes = new Uint8Array(decoded.length);
494
+ for (let i = 0; i < decoded.length; i++) {
495
+ bytes[i] = decoded.charCodeAt(i);
496
+ }
497
+ const blob = new Blob([bytes], { type: "image/png" });
498
+ const DOMURL = self.URL || self.webkitURL || self;
499
+ const url = DOMURL.createObjectURL(blob);
500
+ const link = document.createElement("a");
501
+ link.href = url;
502
+ link.download = "app-diagram.png";
503
+ link.click();
504
+ setTimeout(() => {
505
+ URL.revokeObjectURL(url);
506
+ }, 2000);
507
+ }
508
+
489
509
  function toggleEntityFilter(type) {
490
510
  switch (type) {
491
511
  case "views": {
@@ -122,9 +122,11 @@ function apply_showif() {
122
122
  if (currentOptionsSet === qs) return;
123
123
 
124
124
  const activate = (success, qs) => {
125
+ if (e.prop("data-fetch-options-current-set") === qs) return;
125
126
  e.empty();
126
127
  e.prop("data-fetch-options-current-set", qs);
127
- if (!dynwhere.required) e.append($(`<option></option>`));
128
+ const toAppend = [];
129
+ if (!dynwhere.required) toAppend.push(`<option></option>`);
128
130
  let currentDataOption = undefined;
129
131
  const dataOptions = [];
130
132
  success.forEach((r) => {
@@ -141,8 +143,10 @@ function apply_showif() {
141
143
  const html = `<option ${
142
144
  selected ? "selected" : ""
143
145
  } value="${value}">${label}</option>`;
144
- e.append($(html));
146
+ toAppend.push(html);
145
147
  });
148
+ e.html(toAppend.join(""));
149
+
146
150
  //TODO: also sort inserted HTML options
147
151
  dataOptions.sort((a, b) =>
148
152
  (a.text?.toLowerCase?.() || a.text) >
@@ -853,30 +857,41 @@ function emptyAlerts() {
853
857
  }
854
858
 
855
859
  function press_store_button(clicked) {
856
- const width = $(clicked).width();
857
- $(clicked).html('<i class="fas fa-spinner fa-spin"></i>').width(width);
860
+ let btn = clicked;
861
+ if ($(clicked).is("form")) btn = $(clicked).find("button[type=submit]");
862
+
863
+ const width = $(btn).width();
864
+ $(btn).html('<i class="fas fa-spinner fa-spin"></i>').width(width);
858
865
  }
859
866
 
860
867
  function common_done(res, isWeb = true) {
861
- if (res.notify) notifyAlert(res.notify);
862
- if (res.error) notifyAlert({ type: "danger", text: res.error });
863
- if (res.eval_js) eval(res.eval_js);
868
+ const handle = (element, fn) => {
869
+ if (Array.isArray(element)) for (const current of element) fn(current);
870
+ else fn(element);
871
+ };
872
+ if (res.notify) handle(res.notify, notifyAlert);
873
+ if (res.error)
874
+ handle(res.error, (text) => notifyAlert({ type: "danger", text: text }));
875
+ if (res.eval_js) handle(res.eval_js, eval);
876
+
864
877
  if (res.reload_page) {
865
878
  (isWeb ? location : parent.location).reload(); //TODO notify to cookie if reload or goto
866
879
  }
867
880
  if (res.download) {
868
- const dataurl = `data:${
869
- res.download.mimetype || "application/octet-stream"
870
- };base64,${res.download.blob}`;
871
- fetch(dataurl)
872
- .then((res) => res.blob())
873
- .then((blob) => {
874
- const link = document.createElement("a");
875
- link.href = window.URL.createObjectURL(blob);
876
- if (res.download.filename) link.download = res.download.filename;
877
- else link.target = "_blank";
878
- link.click();
879
- });
881
+ handle(res.download, (download) => {
882
+ const dataurl = `data:${
883
+ download.mimetype || "application/octet-stream"
884
+ };base64,${download.blob}`;
885
+ fetch(dataurl)
886
+ .then((res) => res.blob())
887
+ .then((blob) => {
888
+ const link = document.createElement("a");
889
+ link.href = window.URL.createObjectURL(blob);
890
+ if (download.filename) link.download = download.filename;
891
+ else link.target = "_blank";
892
+ link.click();
893
+ });
894
+ });
880
895
  }
881
896
  if (res.goto && !isWeb)
882
897
  // TODO ch
@@ -901,6 +916,12 @@ function common_done(res, isWeb = true) {
901
916
  if (res.popup) {
902
917
  ajax_modal(res.popup);
903
918
  }
919
+ if (res.suppressed) {
920
+ notifyAlert({
921
+ type: "warning",
922
+ text: res.suppressed,
923
+ });
924
+ }
904
925
  }
905
926
 
906
927
  const repeaterCopyValuesToForm = (form, editor, noTriggerChange) => {
@@ -195,8 +195,11 @@ function ajax_done(res) {
195
195
  common_done(res);
196
196
  }
197
197
 
198
- function view_post(viewname, route, data, onDone) {
199
- $.ajax("/view/" + viewname + "/" + route, {
198
+ function view_post(viewname, route, data, onDone, sendState) {
199
+ const query = sendState
200
+ ? `?${new URL(get_current_state_url()).searchParams.toString()}`
201
+ : "";
202
+ $.ajax("/view/" + viewname + "/" + route + query, {
200
203
  dataType: "json",
201
204
  type: "POST",
202
205
  headers: {
@@ -250,12 +253,15 @@ function close_saltcorn_modal() {
250
253
  var myModalEl = document.getElementById("scmodal");
251
254
  if (!myModalEl) return;
252
255
  var modal = bootstrap.Modal.getInstance(myModalEl);
253
- if (modal) modal.dispose();
256
+ if (modal) {
257
+ if (modal.hide) modal.hide();
258
+ if (modal.dispose) modal.dispose();
259
+ }
254
260
  }
255
261
 
256
262
  function ensure_modal_exists_and_closed() {
257
263
  if ($("#scmodal").length === 0) {
258
- $("body").append(`<div id="scmodal", class="modal">
264
+ $("body").append(`<div id="scmodal" class="modal">
259
265
  <div class="modal-dialog">
260
266
  <div class="modal-content">
261
267
  <div class="modal-header">
@@ -264,7 +270,7 @@ function ensure_modal_exists_and_closed() {
264
270
  <span class="sc-ajax-indicator-wrapper">
265
271
  <span class="sc-ajax-indicator ms-2" style="display: none;"><i class="fas fa-save"></i></span>
266
272
  </span>
267
- <a class="sc-modal-linkout ms-2" href="" target="_blank"><i class="fas fa-expand-alt"></i></a>
273
+ <a class="sc-modal-linkout ms-2" onclick="close_saltcorn_modal()" href="" target="_blank"><i class="fas fa-expand-alt"></i></a>
268
274
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
269
275
  </button>
270
276
  </div>
package/routes/admin.js CHANGED
@@ -2019,6 +2019,8 @@ router.post(
2019
2019
  }
2020
2020
  if (form.values.tables) {
2021
2021
  await db.deleteWhere("_sc_table_constraints");
2022
+ await db.deleteWhere("_sc_model_instances");
2023
+ await db.deleteWhere("_sc_models");
2022
2024
 
2023
2025
  const tables = await Table.find();
2024
2026
 
@@ -101,9 +101,9 @@ const tablesList = async (tables, req, { tagId, domId, showList } = {}) => {
101
101
  * @param {object} req
102
102
  * @returns {Form}
103
103
  */
104
- const editViewRoleForm = (view, roles, req) =>
104
+ const editViewRoleForm = (view, roles, req, on_done_redirect_str) =>
105
105
  editRoleForm({
106
- url: `/viewedit/setrole/${view.id}`,
106
+ url: `/viewedit/setrole/${view.id}${on_done_redirect_str || ""}`,
107
107
  current_role: view.min_role,
108
108
  roles,
109
109
  req,
@@ -114,7 +114,7 @@ const editViewRoleForm = (view, roles, req) =>
114
114
  * @param {object} req
115
115
  * @returns {div}
116
116
  */
117
- const view_dropdown = (view, req) =>
117
+ const view_dropdown = (view, req, on_done_redirect_str = "") =>
118
118
  settingsDropdown(`dropdownMenuButton${view.id}`, [
119
119
  a(
120
120
  {
@@ -126,17 +126,19 @@ const view_dropdown = (view, req) =>
126
126
  a(
127
127
  {
128
128
  class: "dropdown-item",
129
- href: `/viewedit/edit/${encodeURIComponent(view.name)}`,
129
+ href: `/viewedit/edit/${encodeURIComponent(
130
+ view.name
131
+ )}${on_done_redirect_str}`,
130
132
  },
131
133
  '<i class="fas fa-edit"></i>&nbsp;' + req.__("Edit")
132
134
  ),
133
135
  post_dropdown_item(
134
- `/viewedit/add-to-menu/${view.id}`,
136
+ `/viewedit/add-to-menu/${view.id}${on_done_redirect_str}`,
135
137
  '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
136
138
  req
137
139
  ),
138
140
  post_dropdown_item(
139
- `/viewedit/clone/${view.id}`,
141
+ `/viewedit/clone/${view.id}${on_done_redirect_str}`,
140
142
  '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
141
143
  req
142
144
  ),
@@ -149,7 +151,7 @@ const view_dropdown = (view, req) =>
149
151
  ),
150
152
  div({ class: "dropdown-divider" }),
151
153
  post_dropdown_item(
152
- `/viewedit/delete/${view.id}`,
154
+ `/viewedit/delete/${view.id}${on_done_redirect_str}`,
153
155
  '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
154
156
  req,
155
157
  true,
@@ -169,9 +171,15 @@ const setTableRefs = async (views) => {
169
171
  return views;
170
172
  };
171
173
 
172
- const viewsList = async (views, req, { tagId, domId, showList } = {}) => {
174
+ const viewsList = async (
175
+ views,
176
+ req,
177
+ { tagId, domId, showList, on_done_redirect, notable } = {}
178
+ ) => {
173
179
  const roles = await User.get_roles();
174
-
180
+ const on_done_redirect_str = on_done_redirect
181
+ ? `?on_done_redirect=${on_done_redirect}`
182
+ : "";
175
183
  return views.length > 0
176
184
  ? mkTable(
177
185
  [
@@ -200,29 +208,36 @@ const viewsList = async (views, req, { tagId, domId, showList } = {}) => {
200
208
  ? `javascript:set_state_field('_sortby', 'viewtemplate')`
201
209
  : undefined,
202
210
  },
203
- {
204
- label: req.__("Table"),
205
- key: (r) => link(`/table/${r.table}`, r.table),
206
- sortlink: !tagId
207
- ? `javascript:set_state_field('_sortby', 'table')`
208
- : undefined,
209
- },
211
+ ...(notable
212
+ ? []
213
+ : [
214
+ {
215
+ label: req.__("Table"),
216
+ key: (r) => link(`/table/${r.table}`, r.table),
217
+ sortlink: !tagId
218
+ ? `javascript:set_state_field('_sortby', 'table')`
219
+ : undefined,
220
+ },
221
+ ]),
210
222
  {
211
223
  label: req.__("Role to access"),
212
- key: (row) => editViewRoleForm(row, roles, req),
224
+ key: (row) =>
225
+ editViewRoleForm(row, roles, req, on_done_redirect_str),
213
226
  },
214
227
  {
215
228
  label: "",
216
229
  key: (r) =>
217
230
  link(
218
- `/viewedit/config/${encodeURIComponent(r.name)}`,
231
+ `/viewedit/config/${encodeURIComponent(
232
+ r.name
233
+ )}${on_done_redirect_str}`,
219
234
  req.__("Configure")
220
235
  ),
221
236
  },
222
237
  !tagId
223
238
  ? {
224
239
  label: "",
225
- key: (r) => view_dropdown(r, req),
240
+ key: (r) => view_dropdown(r, req, on_done_redirect_str),
226
241
  }
227
242
  : {
228
243
  label: req.__("Remove From Tag"),