@saltcorn/server 0.8.6-beta.2 → 0.8.6-beta.4

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.6-beta.2",
3
+ "version": "0.8.6-beta.4",
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.6-beta.2",
10
- "@saltcorn/builder": "0.8.6-beta.2",
11
- "@saltcorn/data": "0.8.6-beta.2",
12
- "@saltcorn/admin-models": "0.8.6-beta.2",
13
- "@saltcorn/filemanager": "0.8.6-beta.2",
14
- "@saltcorn/markup": "0.8.6-beta.2",
15
- "@saltcorn/sbadmin2": "0.8.6-beta.2",
9
+ "@saltcorn/base-plugin": "0.8.6-beta.4",
10
+ "@saltcorn/builder": "0.8.6-beta.4",
11
+ "@saltcorn/data": "0.8.6-beta.4",
12
+ "@saltcorn/admin-models": "0.8.6-beta.4",
13
+ "@saltcorn/filemanager": "0.8.6-beta.4",
14
+ "@saltcorn/markup": "0.8.6-beta.4",
15
+ "@saltcorn/sbadmin2": "0.8.6-beta.4",
16
16
  "@socket.io/cluster-adapter": "^0.2.1",
17
17
  "@socket.io/sticky": "^1.0.1",
18
18
  "adm-zip": "0.5.10",
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",f="month",h="quarter",c="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,f),s=n-i<0,u=e.clone().add(r+(s?-1:1),f);return+(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:f,y:c,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:h}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p=function(t){return t instanceof _},S=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},w=function(t,e){if(p(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},O=v;O.l=S,O.i=p,O.w=function(t,e){return w(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=S(t.locale,null,!0),this.parse(t)}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(O.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return O},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=w(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return w(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<w(t)},m.$g=function(t,e,n){return O.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!O.u(e)||e,h=O.p(t),l=function(t,e){var i=O.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return O.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(h){case c:return r?l(1,0):l(31,11);case f:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=O.p(t),h="set"+(this.$u?"UTC":""),l=(n={},n[a]=h+"Date",n[d]=h+"Date",n[f]=h+"Month",n[c]=h+"FullYear",n[u]=h+"Hours",n[s]=h+"Minutes",n[i]=h+"Seconds",n[r]=h+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===f||o===c){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[O.p(t)]()},m.add=function(r,h){var d,l=this;r=Number(r);var $=O.p(h),y=function(t){var e=w(l);return O.w(e.date(e.date()+Math.round(t*r)),l)};if($===f)return this.set(f,this.$M+r);if($===c)return this.set(c,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return O.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=O.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,f=n.months,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},c=function(t){return O.s(s%12||12,t,"0")},d=n.meridiem||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r},$={YY:String(this.$y).slice(-2),YYYY:this.$y,M:a+1,MM:O.s(a+1,2,"0"),MMM:h(n.monthsShort,a,f,3),MMMM:h(f,a),D:this.$D,DD:O.s(this.$D,2,"0"),d:String(this.$W),dd:h(n.weekdaysMin,this.$W,o,2),ddd:h(n.weekdaysShort,this.$W,o,3),dddd:o[this.$W],H:String(s),HH:O.s(s,2,"0"),h:c(1),hh:c(2),a:d(s,u,!0),A:d(s,u,!1),m:String(u),mm:O.s(u,2,"0"),s:String(this.$s),ss:O.s(this.$s,2,"0"),SSS:O.s(this.$ms,3,"0"),Z:i};return r.replace(y,(function(t,e){return e||$[t]||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=O.p(d),M=w(r),m=(M.utcOffset()-this.utcOffset())*e,v=this-M,g=O.m(this,M);return g=($={},$[c]=g/12,$[f]=g,$[h]=g/3,$[o]=(v-m)/6048e5,$[a]=(v-m)/864e5,$[u]=v/n,$[s]=v/e,$[i]=v/t,$)[y]||v,l?g:O.a(g)},m.daysInMonth=function(){return this.endOf(f).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=S(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return O.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),T=_.prototype;return w.prototype=T,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",f],["$y",c],["$D",d]].forEach((function(t){T[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),w.extend=function(t,e){return t.$i||(t(e,_,w),t.$i=!0),w},w.locale=S,w.isDayjs=p,w.unix=function(t){return w(1e3*t)},w.en=D[g],w.Ls=D,w.p={},w}));
@@ -257,6 +257,42 @@ function apply_showif() {
257
257
  },
258
258
  });
259
259
  });
260
+ const locale =
261
+ navigator.userLanguage ||
262
+ (navigator.languages &&
263
+ navigator.languages.length &&
264
+ navigator.languages[0]) ||
265
+ navigator.language ||
266
+ navigator.browserLanguage ||
267
+ navigator.systemLanguage ||
268
+ "en";
269
+ window.detected_locale = locale;
270
+ const parse = (s) => JSON.parse(decodeURIComponent(s));
271
+ $("time[locale-time-options]").each(function () {
272
+ var el = $(this);
273
+ var date = new Date(el.attr("datetime"));
274
+ const options = parse(el.attr("locale-time-options"));
275
+ el.text(date.toLocaleTimeString(locale, options));
276
+ });
277
+ $("time[locale-options]").each(function () {
278
+ var el = $(this);
279
+ var date = new Date(el.attr("datetime"));
280
+ const options = parse(el.attr("locale-options"));
281
+ el.text(date.toLocaleString(locale, options));
282
+ });
283
+ $("time[locale-date-options]").each(function () {
284
+ var el = $(this);
285
+ var date = new Date(el.attr("datetime"));
286
+ const options = parse(el.attr("locale-date-options"));
287
+ el.text(date.toLocaleDateString(locale, options));
288
+ });
289
+ $("time[locale-date-format]").each(function () {
290
+ var el = $(this);
291
+ var date = el.attr("datetime");
292
+ const format = parse(el.attr("locale-date-format"));
293
+ el.text(dayjs(date).format(format));
294
+ });
295
+
260
296
  _apply_showif_plugins.forEach((p) => p());
261
297
  }
262
298
 
@@ -568,35 +604,7 @@ function initialize_page() {
568
604
  });
569
605
  }, 100);
570
606
  });
571
- const locale =
572
- navigator.userLanguage ||
573
- (navigator.languages &&
574
- navigator.languages.length &&
575
- navigator.languages[0]) ||
576
- navigator.language ||
577
- navigator.browserLanguage ||
578
- navigator.systemLanguage ||
579
- "en";
580
- window.detected_locale = locale;
581
- const parse = (s) => JSON.parse(decodeURIComponent(s));
582
- $("time[locale-time-options]").each(function () {
583
- var el = $(this);
584
- var date = new Date(el.attr("datetime"));
585
- const options = parse(el.attr("locale-time-options"));
586
- el.text(date.toLocaleTimeString(locale, options));
587
- });
588
- $("time[locale-options]").each(function () {
589
- var el = $(this);
590
- var date = new Date(el.attr("datetime"));
591
- const options = parse(el.attr("locale-options"));
592
- el.text(date.toLocaleString(locale, options));
593
- });
594
- $("time[locale-date-options]").each(function () {
595
- var el = $(this);
596
- var date = new Date(el.attr("datetime"));
597
- const options = parse(el.attr("locale-date-options"));
598
- el.text(date.toLocaleDateString(locale, options));
599
- });
607
+
600
608
  if ($.fn.historyTabs && $.fn.tab)
601
609
  $('a[data-bs-toggle="tab"].deeplink').historyTabs();
602
610
  init_bs5_dropdowns();
@@ -79,7 +79,7 @@ function set_state_field(key, value) {
79
79
  function check_state_field(that) {
80
80
  const checked = that.checked;
81
81
  const name = that.name;
82
- const value = that.value;
82
+ const value = encodeURIComponent(that.value);
83
83
  var separator = window.location.href.indexOf("?") !== -1 ? "&" : "?";
84
84
  let dest;
85
85
  if (checked) dest = get_current_state_url() + `${separator}${name}=${value}`;
package/routes/admin.js CHANGED
@@ -1564,6 +1564,27 @@ router.get(
1564
1564
  placeholder: getState().getConfig("base_url") || "",
1565
1565
  })
1566
1566
  )
1567
+ ),
1568
+ div(
1569
+ // TODO only for some tables?
1570
+ { class: "row pb-2" },
1571
+ div(
1572
+ { class: "col-sm-4" },
1573
+ input({
1574
+ type: "checkbox",
1575
+ id: "offlineModeBoxId",
1576
+ class: "form-check-input me-2",
1577
+ name: "allowOfflineMode",
1578
+ checked: true,
1579
+ }),
1580
+ label(
1581
+ {
1582
+ for: "offlineModeBoxId",
1583
+ class: "form-label",
1584
+ },
1585
+ req.__("Allow offline mode")
1586
+ )
1587
+ )
1567
1588
  )
1568
1589
  ),
1569
1590
  button(
@@ -1664,6 +1685,7 @@ router.post(
1664
1685
  useDocker,
1665
1686
  appFile,
1666
1687
  serverURL,
1688
+ allowOfflineMode,
1667
1689
  } = req.body;
1668
1690
  if (!androidPlatform && !iOSPlatform) {
1669
1691
  return res.json({
@@ -1711,6 +1733,7 @@ router.post(
1711
1733
  }
1712
1734
  if (appFile) spawnParams.push("-a", appFile);
1713
1735
  if (serverURL) spawnParams.push("-s", serverURL);
1736
+ if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
1714
1737
  if (
1715
1738
  db.is_it_multi_tenant() &&
1716
1739
  db.getTenantSchema() !== db.connectObj.default_schema
package/routes/api.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Table Data API handler
3
3
  * Allows to manipulate with saltcorn tables data.
4
4
  *
5
- * Attention! Currently you cannot insert / update users table via this api
5
+ * Attention! Currently, you cannot insert / update users table via this api
6
6
  * because users table has specific meaning in SC and
7
7
  * not all required (mandatory) fields of user available via this api.
8
8
  * For now this is platform limitation.
@@ -73,7 +73,7 @@ function accessAllowedRead(req, user, table, allow_ownership) {
73
73
  ? req.user.role_id
74
74
  : user && user.role_id
75
75
  ? user.role_id
76
- : 10;
76
+ : 100;
77
77
 
78
78
  return (
79
79
  role <= table.min_role_read ||
@@ -96,7 +96,7 @@ function accessAllowedWrite(req, user, table) {
96
96
  ? req.user.role_id
97
97
  : user && user.role_id
98
98
  ? user.role_id
99
- : 10;
99
+ : 100;
100
100
 
101
101
  return (
102
102
  role <= table.min_role_write ||
@@ -117,7 +117,7 @@ function accessAllowed(req, user, trigger) {
117
117
  ? req.user.role_id
118
118
  : user && user.role_id
119
119
  ? user.role_id
120
- : 10;
120
+ : 100;
121
121
 
122
122
  return role <= trigger.min_role;
123
123
  }
@@ -152,7 +152,7 @@ router.post(
152
152
  "jwt",
153
153
  { session: false },
154
154
  async function (err, user, info) {
155
- const role = user && user.id ? user.role_id : 10;
155
+ const role = user && user.id ? user.role_id : 100;
156
156
  if (
157
157
  role <= view.min_role ||
158
158
  (await view.authorise_get({ req, ...view })) // TODO set query to state
@@ -185,7 +185,9 @@ router.post(
185
185
  )(req, res, next);
186
186
  })
187
187
  );
188
-
188
+ /**
189
+ *
190
+ */
189
191
  router.get(
190
192
  "/:tableName/distinct/:fieldName",
191
193
  //passport.authenticate("api-bearer", { session: false }),
@@ -206,9 +208,7 @@ router.get(
206
208
  { session: false },
207
209
  async function (err, user, info) {
208
210
  if (accessAllowedRead(req, user, table)) {
209
- const field = (await table.getFields()).find(
210
- (f) => f.name === fieldName
211
- );
211
+ const field = table.getFields().find((f) => f.name === fieldName);
212
212
  if (!field) {
213
213
  res.status(404).json({ error: req.__("Not found") });
214
214
  return;
@@ -268,7 +268,7 @@ router.get(
268
268
  if (versioncount === "on") {
269
269
  const joinOpts = {
270
270
  orderBy: "id",
271
- forUser: req.user || user || { role_id: 10 },
271
+ forUser: req.user || user || { role_id: 100 },
272
272
  forPublic: !(req.user || user),
273
273
  aggregations: {
274
274
  _versions: {
@@ -281,7 +281,7 @@ router.get(
281
281
  };
282
282
  rows = await table.getJoinedRows(joinOpts);
283
283
  } else if (req_query && req_query !== {}) {
284
- const tbl_fields = await table.getFields();
284
+ const tbl_fields = table.getFields();
285
285
  readState(req_query, tbl_fields, req);
286
286
  const qstate = await stateFieldsToWhere({
287
287
  fields: tbl_fields,
@@ -335,7 +335,7 @@ router.post(
335
335
 
336
336
  if (!trigger) {
337
337
  getState().log(3, `API action ${actionname} not found`);
338
- res.status(400).json({ error: req.__("Not found") });
338
+ res.status(404).json({ error: req.__("Not found") });
339
339
  return;
340
340
  }
341
341
  await passport.authenticate(
@@ -350,6 +350,7 @@ router.post(
350
350
  body: req.body,
351
351
  row: req.body,
352
352
  req,
353
+ user: user || req.user,
353
354
  });
354
355
  res.json({ success: true, data: resp });
355
356
  } catch (e) {
@@ -387,8 +388,8 @@ router.post(
387
388
  async function (err, user, info) {
388
389
  if (accessAllowedWrite(req, user, table)) {
389
390
  const { _versions, ...row } = req.body;
390
- const fields = await table.getFields();
391
- readState(row, fields);
391
+ const fields = table.getFields();
392
+ readState(row, fields, req);
392
393
  let errors = [];
393
394
  let hasErrors = false;
394
395
  Object.keys(row).forEach((k) => {
@@ -409,7 +410,8 @@ router.post(
409
410
  if (
410
411
  field.required &&
411
412
  !field.primary_key &&
412
- typeof row[field.name] === "undefined"
413
+ typeof row[field.name] === "undefined" &&
414
+ !field.attributes.default
413
415
  ) {
414
416
  hasErrors = true;
415
417
  errors.push(`${field.name}: required`);
@@ -425,7 +427,7 @@ router.post(
425
427
  }
426
428
  const ins_res = await table.tryInsertRow(
427
429
  row,
428
- req.user || user || { role_id: 10 }
430
+ req.user || user || { role_id: 100 }
429
431
  );
430
432
  if (ins_res.error) {
431
433
  getState().log(2, `API POST ${table.name} error: ${ins_res.error}`);
@@ -463,8 +465,8 @@ router.post(
463
465
  async function (err, user, info) {
464
466
  if (accessAllowedWrite(req, user, table)) {
465
467
  const { _versions, ...row } = req.body;
466
- const fields = await table.getFields();
467
- readState(row, fields);
468
+ const fields = table.getFields();
469
+ readState(row, fields, req);
468
470
  let errors = [];
469
471
  let hasErrors = false;
470
472
  for (const k of Object.keys(row)) {
@@ -501,7 +503,7 @@ router.post(
501
503
  const ins_res = await table.tryUpdateRow(
502
504
  row,
503
505
  id,
504
- user || req.user || { role_id: 10 }
506
+ user || req.user || { role_id: 100 }
505
507
  );
506
508
 
507
509
  if (ins_res.error) {
@@ -547,12 +549,12 @@ router.delete(
547
549
  //readState(row, fields);
548
550
  await table.deleteRows(
549
551
  { [pk_name]: row[pk_name] },
550
- user || req.user || { role_id: 10 }
552
+ user || req.user || { role_id: 100 }
551
553
  );
552
554
  } else
553
555
  await table.deleteRows(
554
556
  { id },
555
- user || req.user || { role_id: 10 }
557
+ user || req.user || { role_id: 100 }
556
558
  );
557
559
  res.json({ success: true });
558
560
  } catch (e) {
@@ -363,7 +363,10 @@ const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
363
363
  { label: req.__("Action"), key: "action" },
364
364
  {
365
365
  label: req.__("Table or Channel"),
366
- key: (r) => r.table_name || r.channel,
366
+ key: (r) =>
367
+ r.table_name
368
+ ? a({ href: `/table/${r.table_name}` }, r.table_name)
369
+ : r.channel,
367
370
  },
368
371
  {
369
372
  label: req.__("When"),
package/routes/delete.js CHANGED
@@ -34,14 +34,14 @@ router.post(
34
34
  const { redirect } = req.query;
35
35
  // todo check that works after where change
36
36
  const table = await Table.findOne({ name: tableName });
37
- const role = req.user && req.user.id ? req.user.role_id : 10;
37
+ const role = req.user && req.user.id ? req.user.role_id : 100;
38
38
  try {
39
39
  if (role <= table.min_role_write)
40
- await table.deleteRows({ id }, req.user || { role_id: 10 });
40
+ await table.deleteRows({ id }, req.user || { role_id: 100 });
41
41
  else if (table.ownership_field_id && req.user) {
42
42
  const row = await table.getRow({ id });
43
43
  if (row && table.is_owner(req.user, row))
44
- await table.deleteRows({ id }, req.user || { role_id: 10 });
44
+ await table.deleteRows({ id }, req.user || { role_id: 100 });
45
45
  else req.flash("error", req.__("Not authorized"));
46
46
  } else
47
47
  req.flash(
package/routes/edit.js CHANGED
@@ -41,7 +41,7 @@ router.post(
41
41
  await table.updateRow(
42
42
  { [field_name]: !row[field_name] },
43
43
  id,
44
- req.user || { role_id: 10 }
44
+ req.user || { role_id: 100 }
45
45
  );
46
46
 
47
47
  if (req.xhr) res.send("OK");
package/routes/fields.js CHANGED
@@ -83,6 +83,13 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
83
83
  if (!s || s === "") return req.__("Missing label");
84
84
  if (!id && existing_names.includes(Field.labelToName(s)))
85
85
  return req.__("Column %s already exists", s);
86
+ if (Field.labelToName(s) === "row")
87
+ return req.__("Not a valid field name");
88
+ try {
89
+ new Function(Field.labelToName(s), "return;");
90
+ } catch {
91
+ return req.__("Not a valid field name");
92
+ }
86
93
  },
87
94
  }),
88
95
  // description
@@ -464,14 +471,11 @@ const fieldFlow = (req) =>
464
471
  },
465
472
  {
466
473
  name: req.__("Default"),
467
- onlyWhen: async (context) => {
468
- if (!context.required || context.id || context.calculated)
469
- return false;
474
+ onlyWhen: async (context) => context.required && !context.calculated,
475
+
476
+ form: async (context) => {
470
477
  const table = await Table.findOne({ id: context.table_id });
471
478
  const nrows = await table.countRows();
472
- return nrows > 0;
473
- },
474
- form: async (context) => {
475
479
  const formfield = new Field({
476
480
  name: "default",
477
481
  label: req.__("Default"),
@@ -484,12 +488,28 @@ const fieldFlow = (req) =>
484
488
  },
485
489
  });
486
490
  await formfield.fill_fkey_options();
487
- return new Form({
488
- blurb: req.__(
489
- "A default value is required when adding required fields to nonempty tables"
490
- ),
491
- fields: [formfield],
491
+ const defaultOptional = nrows === 0 || context.id;
492
+ if (defaultOptional) formfield.showIf = { set_default: true };
493
+
494
+ const form = new Form({
495
+ blurb: defaultOptional
496
+ ? req.__("Set a default value for missing data")
497
+ : req.__(
498
+ "A default value is required when adding required fields to nonempty tables"
499
+ ),
500
+ fields: [
501
+ ...(defaultOptional
502
+ ? [{ name: "set_default", label: "Set Default", type: "Bool" }]
503
+ : []),
504
+ formfield,
505
+ ],
492
506
  });
507
+ if (
508
+ typeof context.default !== "undefined" &&
509
+ context.default !== null
510
+ )
511
+ form.values.set_default = true;
512
+ return form;
493
513
  },
494
514
  },
495
515
  ],
@@ -717,7 +737,7 @@ router.post(
717
737
  error_catcher(async (req, res) => {
718
738
  const { tableName, fieldName, fieldview } = req.params;
719
739
  const table = await Table.findOne({ name: tableName });
720
- const role = req.user && req.user.id ? req.user.role_id : 10;
740
+ const role = req.user && req.user.id ? req.user.role_id : 100;
721
741
 
722
742
  const fields = await table.getFields();
723
743
  let row = { ...req.body };
package/routes/files.js CHANGED
@@ -130,7 +130,7 @@ router.get(
130
130
  router.get(
131
131
  "/download/*",
132
132
  error_catcher(async (req, res) => {
133
- const role = req.user && req.user.id ? req.user.role_id : 10;
133
+ const role = req.user && req.user.id ? req.user.role_id : 100;
134
134
  const user_id = req.user && req.user.id;
135
135
  const serve_path = req.params[0];
136
136
  const file = await File.findOne(serve_path);
@@ -155,7 +155,7 @@ router.post(
155
155
  isAdmin,
156
156
 
157
157
  error_catcher(async (req, res) => {
158
- const role = req.user && req.user.id ? req.user.role_id : 10;
158
+ const role = req.user && req.user.id ? req.user.role_id : 100;
159
159
  const user_id = req.user && req.user.id;
160
160
  const files = req.body.files;
161
161
  const location = req.body.location;
@@ -189,7 +189,7 @@ router.post(
189
189
  router.get(
190
190
  "/serve/*",
191
191
  error_catcher(async (req, res) => {
192
- const role = req.user && req.user.id ? req.user.role_id : 10;
192
+ const role = req.user && req.user.id ? req.user.role_id : 100;
193
193
  const user_id = req.user && req.user.id;
194
194
  const serve_path = req.params[0];
195
195
  //let file;
@@ -201,7 +201,7 @@ router.get(
201
201
  (role <= file.min_role_read || (user_id && user_id === file.user_id))
202
202
  ) {
203
203
  res.type(file.mimetype);
204
- const cacheability = file.min_role_read === 10 ? "public" : "private";
204
+ const cacheability = file.min_role_read === 100 ? "public" : "private";
205
205
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
206
206
  if (file.s3_store) s3storage.serveObject(file, res, false);
207
207
  else res.sendFile(file.location);
@@ -228,7 +228,7 @@ router.get(
228
228
  router.get(
229
229
  "/resize/:width_str/:height_str/*",
230
230
  error_catcher(async (req, res) => {
231
- const role = req.user && req.user.id ? req.user.role_id : 10;
231
+ const role = req.user && req.user.id ? req.user.role_id : 100;
232
232
  const user_id = req.user && req.user.id;
233
233
  const { width_str, height_str } = req.params;
234
234
  const serve_path = req.params[0];
@@ -240,7 +240,7 @@ router.get(
240
240
  (role <= file.min_role_read || (user_id && user_id === file.user_id))
241
241
  ) {
242
242
  res.type(file.mimetype);
243
- const cacheability = file.min_role_read === 10 ? "public" : "private";
243
+ const cacheability = file.min_role_read === 100 ? "public" : "private";
244
244
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
245
245
  //TODO s3
246
246
  if (file.s3_store) s3storage.serveObject(file, res, false);
@@ -386,7 +386,7 @@ router.post(
386
386
  let { folder } = req.body;
387
387
  let jsonResp = {};
388
388
  const min_role_upload = getState().getConfig("min_role_upload", 1);
389
- const role = req.user && req.user.id ? req.user.role_id : 10;
389
+ const role = req.user && req.user.id ? req.user.role_id : 100;
390
390
  let file_for_redirect;
391
391
  if (role > +min_role_upload) {
392
392
  if (!req.xhr) req.flash("warning", req.__("Not authorized"));
@@ -426,7 +426,7 @@ const welcome_page = async (req) => {
426
426
  * @returns {Promise<void>}
427
427
  */
428
428
  const no_views_logged_in = async (req, res) => {
429
- const role = req.user && req.user.id ? req.user.role_id : 10;
429
+ const role = req.user && req.user.id ? req.user.role_id : 100;
430
430
  if (role > 1 || req.user.tenant !== db.getTenantSchema())
431
431
  res.sendWrap(req.__("Hello"), req.__("Welcome to Saltcorn!"));
432
432
  else {
@@ -463,7 +463,7 @@ const no_views_logged_in = async (req, res) => {
463
463
  const get_config_response = async (role_id, res, req) => {
464
464
  const modernCfg = getState().getConfig("home_page_by_role", false);
465
465
  // predefined roles
466
- const legacy_role = { 10: "public", 8: "user", 4: "staff", 1: "admin" }[
466
+ const legacy_role = { 100: "public", 80: "user", 40: "staff", 1: "admin" }[
467
467
  role_id
468
468
  ];
469
469
  let homeCfg = modernCfg && modernCfg[role_id];
@@ -497,7 +497,7 @@ module.exports =
497
497
  */
498
498
  async (req, res) => {
499
499
  const isAuth = req.user && req.user.id;
500
- const role_id = req.user ? req.user.role_id : 10;
500
+ const role_id = req.user ? req.user.role_id : 100;
501
501
  const cfgResp = await get_config_response(role_id, res, req);
502
502
  if (cfgResp) return;
503
503
 
package/routes/index.js CHANGED
@@ -1,44 +1,5 @@
1
1
  /**
2
2
  * Index is Main Router of App
3
- * @category server
4
- * @module routes/index
5
- * @subcategory routes
6
- */
7
-
8
- /**
9
- * All files in the routes module.
10
- * @namespace routes_overview
11
- * @property {module:routes/actions} actions
12
- * @property {module:routes/admin} admin
13
- * @property {module:routes/api} api
14
- * @property {module:routes/config} config
15
- * @property {module:routes/crashlog} crashlog
16
- * @property {module:routes/delete} delete
17
- * @property {module:routes/edit} edit
18
- * @property {module:routes/eventlog} eventlog
19
- * @property {module:routes/events} events
20
- * @property {module:routes/fields} fields
21
- * @property {module:routes/files} files
22
- * @property {module:routes/homepage} homepage
23
- * @property {module:routes/infoarch} infoarch
24
- * @property {module:routes/library} library
25
- * @property {module:routes/list} list
26
- * @property {module:routes/menu} menu
27
- * @property {module:routes/packs} packs
28
- * @property {module:routes/page} page
29
- * @property {module:routes/pageedit} pageedit
30
- * @property {module:routes/plugins} plugins
31
- * @property {module:routes/scapi} scapi
32
- * @property {module:routes/search} search
33
- * @property {module:routes/settings} settings
34
- * @property {module:routes/tables} tables
35
- * @property {module:routes/tenant} tenant
36
- * @property {module:routes/utils} utils
37
- * @property {module:routes/view} view
38
- * @property {module:routes/viewedit} viewedit
39
- *
40
- * @category server
41
- * @subcategory routes
42
3
  */
43
4
 
44
5
  const table = require("./tables");
@@ -71,7 +32,8 @@ const useradmin = require("../auth/admin");
71
32
  const roleadmin = require("../auth/roleadmin");
72
33
  const tags = require("./tags");
73
34
  const tagentries = require("./tag_entries");
74
- const dataDiagram = require("./diagram");
35
+ const diagram = require("./diagram");
36
+ const sync = require("./sync");
75
37
 
76
38
  module.exports =
77
39
  /**
@@ -109,5 +71,6 @@ module.exports =
109
71
  app.use("/roleadmin", roleadmin);
110
72
  app.use("/tag", tags);
111
73
  app.use("/tag-entries", tagentries);
112
- app.use("/diagram", dataDiagram);
74
+ app.use("/diagram", diagram);
75
+ app.use("/sync", sync);
113
76
  };
package/routes/menu.js CHANGED
@@ -454,7 +454,7 @@ router.post(
454
454
  "/runaction/:name",
455
455
  error_catcher(async (req, res) => {
456
456
  const { name } = req.params;
457
- const role = (req.user || {}).role_id || 10;
457
+ const role = (req.user || {}).role_id || 100;
458
458
  const state = getState();
459
459
  const menu_items = state.getConfig("menu_items");
460
460
  let menu_item;
package/routes/page.js CHANGED
@@ -42,7 +42,7 @@ router.get(
42
42
  state.log(3, `Route /page/${pagename} user=${req.user?.id}`);
43
43
  const tic = state.logLevel >= 5 ? new Date() : null;
44
44
 
45
- const role = req.user && req.user.id ? req.user.role_id : 10;
45
+ const role = req.user && req.user.id ? req.user.role_id : 100;
46
46
  const db_page = await Page.findOne({ name: pagename });
47
47
  if (db_page && role <= db_page.min_role) {
48
48
  const contents = await db_page.run(req.query, { res, req });
@@ -104,7 +104,7 @@ router.post(
104
104
  "/:pagename/action/:rndid",
105
105
  error_catcher(async (req, res) => {
106
106
  const { pagename, rndid } = req.params;
107
- const role = req.user && req.user.id ? req.user.role_id : 10;
107
+ const role = req.user && req.user.id ? req.user.role_id : 100;
108
108
  const db_page = await Page.findOne({ name: pagename });
109
109
  if (db_page && role <= db_page.min_role) {
110
110
  let col;
@@ -387,7 +387,7 @@ router.get(
387
387
  isAdmin,
388
388
  error_catcher(async (req, res) => {
389
389
  const { pagename } = req.params;
390
- const page = await Page.findOne({ name: pagename });
390
+ const [page] = await Page.find({ name: pagename });
391
391
  if (!page) {
392
392
  req.flash("error", req.__(`Page %s not found`, pagename));
393
393
  res.redirect(`/pageedit`);
@@ -506,7 +506,7 @@ router.post(
506
506
  const valres = form.validate(req.body);
507
507
  if (valres.success) {
508
508
  const home_page_by_role =
509
- getState().getConfigCopy("home_page_by_role", []) || [];
509
+ getState().getConfigCopy("home_page_by_role", {}) || {};
510
510
  for (const role of roles) {
511
511
  home_page_by_role[role.id] = valres.success[role.role];
512
512
  }
package/routes/scapi.js CHANGED
@@ -49,7 +49,7 @@ function accessAllowedRead(req, user) {
49
49
  ? req.user.role_id
50
50
  : user && user.role_id
51
51
  ? user.role_id
52
- : 10;
52
+ : 100;
53
53
 
54
54
  if (role === 1) return true;
55
55
  return false;