@saltcorn/server 0.8.5 → 0.8.6-beta.10

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.5",
3
+ "version": "0.8.6-beta.10",
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.5",
10
- "@saltcorn/builder": "0.8.5",
11
- "@saltcorn/data": "0.8.5",
12
- "@saltcorn/admin-models": "0.8.5",
13
- "@saltcorn/filemanager": "0.8.5",
14
- "@saltcorn/markup": "0.8.5",
15
- "@saltcorn/sbadmin2": "0.8.5",
9
+ "@saltcorn/base-plugin": "0.8.6-beta.10",
10
+ "@saltcorn/builder": "0.8.6-beta.10",
11
+ "@saltcorn/data": "0.8.6-beta.10",
12
+ "@saltcorn/admin-models": "0.8.6-beta.10",
13
+ "@saltcorn/filemanager": "0.8.6-beta.10",
14
+ "@saltcorn/markup": "0.8.6-beta.10",
15
+ "@saltcorn/sbadmin2": "0.8.6-beta.10",
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
 
@@ -399,6 +435,7 @@ function reload_on_init() {
399
435
  localStorage.setItem("reload_on_init", true);
400
436
  }
401
437
  function initialize_page() {
438
+ const isNode = typeof parent?.saltcorn?.data?.state === "undefined";
402
439
  //console.log("init page");
403
440
  $(".blur-on-enter-keypress").bind("keyup", function (e) {
404
441
  if (e.keyCode === 13) e.target.blur();
@@ -444,7 +481,9 @@ function initialize_page() {
444
481
  if ($(this).find(".editicon").length === 0) {
445
482
  var current = $(this).html();
446
483
  $(this).html(
447
- `<span class="current">${current}</span><i class="editicon fas fa-edit ms-1"></i>`
484
+ `<span class="current">${current}</span><i class="editicon ${
485
+ !isNode ? "visible" : ""
486
+ } fas fa-edit ms-1"></i>`
448
487
  );
449
488
  }
450
489
  });
@@ -510,12 +549,20 @@ function initialize_page() {
510
549
  } else
511
550
  $(this).replaceWith(
512
551
  `<form method="post" action="${url}" ${
513
- ajax ? `onsubmit="inline_ajax_submit(event, '${opts}')"` : ""
552
+ ajax
553
+ ? `onsubmit="inline_${
554
+ isNode ? "ajax" : "local"
555
+ }_submit(event, '${opts}')"`
556
+ : ""
514
557
  }>
515
- <input type="hidden" name="_csrf" value="${_sc_globalCsrf}">
516
- <input type="${
517
- type === "Integer" || type === "Float" ? "number" : "text"
518
- }" name="${key}" value="${escapeHtml(current)}">
558
+ ${
559
+ isNode
560
+ ? `<input type="hidden" name="_csrf" value="${_sc_globalCsrf}"></input>`
561
+ : ""
562
+ }
563
+ <input type="${
564
+ type === "Integer" || type === "Float" ? "number" : "text"
565
+ }" name="${key}" value="${escapeHtml(current)}">
519
566
  <button type="submit" class="btn btn-sm btn-primary">OK</button>
520
567
  <button onclick="cancel_inline_edit(event, '${opts}')" type="button" class="btn btn-sm btn-danger"><i class="fas fa-times"></i></button>
521
568
  </form>`
@@ -568,35 +615,7 @@ function initialize_page() {
568
615
  });
569
616
  }, 100);
570
617
  });
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
- });
618
+
600
619
  if ($.fn.historyTabs && $.fn.tab)
601
620
  $('a[data-bs-toggle="tab"].deeplink').historyTabs();
602
621
  init_bs5_dropdowns();
@@ -633,6 +652,7 @@ function initialize_page() {
633
652
  $(initialize_page);
634
653
 
635
654
  function cancel_inline_edit(e, opts1) {
655
+ const isNode = typeof parent?.saltcorn?.data?.state === "undefined";
636
656
  var opts = JSON.parse(decodeURIComponent(opts1 || "") || "{}");
637
657
  var form = $(e.target).closest("form");
638
658
  var json_fk_opt;
@@ -660,17 +680,47 @@ function cancel_inline_edit(e, opts1) {
660
680
  <span class="current">${
661
681
  json_fk_opt || opts.current_label || opts.current
662
682
  }</span>
663
- <i class="editicon fas fa-edit ms-1"></i>
683
+ <i class="editicon ${!isNode ? "visible" : ""} fas fa-edit ms-1"></i>
664
684
  </div>`);
665
685
  initialize_page();
666
686
  }
667
687
 
688
+ function inline_submit_success(e, form, opts) {
689
+ const isNode = typeof parent?.saltcorn?.data?.state === "undefined";
690
+ const formDataArray = form.serializeArray();
691
+ if (opts) {
692
+ let rawVal = formDataArray.find((f) => f.name == opts.key).value;
693
+ let val =
694
+ opts.is_key || (opts.schema && opts.schema.type.startsWith("Key to "))
695
+ ? form.find("select").find("option:selected").text()
696
+ : rawVal;
697
+
698
+ $(e.target).replaceWith(`<div
699
+ data-inline-edit-field="${opts.key}"
700
+ ${opts.ajax ? `data-inline-edit-ajax="true"` : ""}
701
+ ${opts.type ? `data-inline-edit-type="${opts.type}"` : ""}
702
+ ${opts.current ? `data-inline-edit-current="${rawVal}"` : ""}
703
+ ${
704
+ opts.schema
705
+ ? `data-inline-edit-schema="${encodeURIComponent(
706
+ JSON.stringify(opts.schema)
707
+ )}"`
708
+ : ""
709
+ }
710
+ ${opts.current_label ? `data-inline-edit-current-label="${val}"` : ""}
711
+ data-inline-edit-dest-url="${opts.url}">
712
+ <span class="current">${val}</span>
713
+ <i class="editicon ${!isNode ? "visible" : ""} fas fa-edit ms-1"></i>
714
+ </div>`);
715
+ initialize_page();
716
+ } else location.reload();
717
+ }
718
+
668
719
  function inline_ajax_submit(e, opts1) {
669
720
  var opts = JSON.parse(decodeURIComponent(opts1 || "") || "{}");
670
721
  e.preventDefault();
671
722
  var form = $(e.target).closest("form");
672
723
  var form_data = form.serialize();
673
- var formDataArray = form.serializeArray();
674
724
  var url = form.attr("action");
675
725
  $.ajax(url, {
676
726
  type: "POST",
@@ -679,32 +729,7 @@ function inline_ajax_submit(e, opts1) {
679
729
  },
680
730
  data: form_data,
681
731
  success: function (res) {
682
- if (opts) {
683
- let rawVal = formDataArray.find((f) => f.name == opts.key).value;
684
- let val =
685
- opts.is_key || (opts.schema && opts.schema.type.startsWith("Key to "))
686
- ? form.find("select").find("option:selected").text()
687
- : rawVal;
688
-
689
- $(e.target).replaceWith(`<div
690
- data-inline-edit-field="${opts.key}"
691
- ${opts.ajax ? `data-inline-edit-ajax="true"` : ""}
692
- ${opts.type ? `data-inline-edit-type="${opts.type}"` : ""}
693
- ${opts.current ? `data-inline-edit-current="${rawVal}"` : ""}
694
- ${
695
- opts.schema
696
- ? `data-inline-edit-schema="${encodeURIComponent(
697
- JSON.stringify(opts.schema)
698
- )}"`
699
- : ""
700
- }
701
- ${opts.current_label ? `data-inline-edit-current-label="${val}"` : ""}
702
- data-inline-edit-dest-url="${opts.url}">
703
- <span class="current">${val}</span>
704
- <i class="editicon fas fa-edit ms-1"></i>
705
- </div>`);
706
- initialize_page();
707
- } else location.reload();
732
+ inline_submit_success(e, form, opts);
708
733
  },
709
734
  error: function (e) {
710
735
  ajax_done(
@@ -1006,12 +1031,15 @@ function room_older(viewname, room_id, btn) {
1006
1031
  }
1007
1032
 
1008
1033
  function init_room(viewname, room_id) {
1009
- const socket = parent?.config?.server_path
1010
- ? io(parent.config.server_path, {
1011
- query: `jwt=${localStorage.getItem("auth_jwt")}`,
1012
- transports: ["websocket"],
1013
- })
1014
- : io({ transports: ["websocket"] });
1034
+ let socket = null;
1035
+ if (parent?.saltcorn?.data?.state) {
1036
+ const { server_path, jwt } =
1037
+ parent.saltcorn.data.state.getState().mobileConfig;
1038
+ socket = io(server_path, {
1039
+ query: `jwt=${jwt}`,
1040
+ transports: ["websocket"],
1041
+ });
1042
+ } else socket = io({ transports: ["websocket"] });
1015
1043
 
1016
1044
  socket.emit("join_room", [viewname, room_id]);
1017
1045
  socket.on("message", (msg) => {
@@ -402,3 +402,20 @@ table.table-inner-grid td {
402
402
  div.unread-notify {
403
403
  border-left: 4px solid green;
404
404
  }
405
+
406
+ .mobile-data-inline-edit {
407
+ position: relative;
408
+ }
409
+
410
+ .mobile-data-inline-edit::after {
411
+ content: "";
412
+ position: absolute;
413
+ top: -25%;
414
+ left: 0%;
415
+ width: 100%;
416
+ height: 150%;
417
+ }
418
+
419
+ .mt-6 {
420
+ margin-top: 5rem;
421
+ }
@@ -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}`;
@@ -599,11 +599,11 @@ function poll_mobile_build_finished(outDirName, pollCount, orginalBtnHtml) {
599
599
  data: { build_dir: outDirName },
600
600
  success: function (res) {
601
601
  if (!res.finished) {
602
- if (pollCount >= 50) {
602
+ if (pollCount >= 100) {
603
603
  removeSpinner("buildMobileAppBtnId", orginalBtnHtml);
604
604
  notifyAlert({
605
605
  type: "danger",
606
- text: "unable to get the build results",
606
+ text: "Unable to get the build results",
607
607
  });
608
608
  } else {
609
609
  setTimeout(() => {
package/routes/admin.js CHANGED
@@ -990,6 +990,9 @@ router.post(
990
990
  child.stdout.on("data", (data) => {
991
991
  res.write(data);
992
992
  });
993
+ child.stderr.on("data", (data) => {
994
+ res.write(data);
995
+ });
993
996
  child.on("exit", function (code, signal) {
994
997
  res.end(
995
998
  req.__(
@@ -1564,6 +1567,27 @@ router.get(
1564
1567
  placeholder: getState().getConfig("base_url") || "",
1565
1568
  })
1566
1569
  )
1570
+ ),
1571
+ div(
1572
+ // TODO only for some tables?
1573
+ { class: "row pb-2" },
1574
+ div(
1575
+ { class: "col-sm-4" },
1576
+ input({
1577
+ type: "checkbox",
1578
+ id: "offlineModeBoxId",
1579
+ class: "form-check-input me-2",
1580
+ name: "allowOfflineMode",
1581
+ checked: true,
1582
+ }),
1583
+ label(
1584
+ {
1585
+ for: "offlineModeBoxId",
1586
+ class: "form-label",
1587
+ },
1588
+ req.__("Allow offline mode")
1589
+ )
1590
+ )
1567
1591
  )
1568
1592
  ),
1569
1593
  button(
@@ -1664,6 +1688,7 @@ router.post(
1664
1688
  useDocker,
1665
1689
  appFile,
1666
1690
  serverURL,
1691
+ allowOfflineMode,
1667
1692
  } = req.body;
1668
1693
  if (!androidPlatform && !iOSPlatform) {
1669
1694
  return res.json({
@@ -1711,6 +1736,7 @@ router.post(
1711
1736
  }
1712
1737
  if (appFile) spawnParams.push("-a", appFile);
1713
1738
  if (serverURL) spawnParams.push("-s", serverURL);
1739
+ if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
1714
1740
  if (
1715
1741
  db.is_it_multi_tenant() &&
1716
1742
  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.
@@ -19,6 +19,7 @@ const Router = require("express-promise-router");
19
19
  const { error_catcher } = require("./utils.js");
20
20
  //const { mkTable, renderForm, link, post_btn } = require("@saltcorn/markup");
21
21
  const { getState } = require("@saltcorn/data/db/state");
22
+ const { prepare_update_row } = require("@saltcorn/data/web-mobile-commons");
22
23
  const Table = require("@saltcorn/data/models/table");
23
24
  const View = require("@saltcorn/data/models/view");
24
25
  //const Field = require("@saltcorn/data/models/field");
@@ -73,7 +74,7 @@ function accessAllowedRead(req, user, table, allow_ownership) {
73
74
  ? req.user.role_id
74
75
  : user && user.role_id
75
76
  ? user.role_id
76
- : 10;
77
+ : 100;
77
78
 
78
79
  return (
79
80
  role <= table.min_role_read ||
@@ -96,7 +97,7 @@ function accessAllowedWrite(req, user, table) {
96
97
  ? req.user.role_id
97
98
  : user && user.role_id
98
99
  ? user.role_id
99
- : 10;
100
+ : 100;
100
101
 
101
102
  return (
102
103
  role <= table.min_role_write ||
@@ -117,7 +118,7 @@ function accessAllowed(req, user, trigger) {
117
118
  ? req.user.role_id
118
119
  : user && user.role_id
119
120
  ? user.role_id
120
- : 10;
121
+ : 100;
121
122
 
122
123
  return role <= trigger.min_role;
123
124
  }
@@ -152,7 +153,7 @@ router.post(
152
153
  "jwt",
153
154
  { session: false },
154
155
  async function (err, user, info) {
155
- const role = user && user.id ? user.role_id : 10;
156
+ const role = user && user.id ? user.role_id : 100;
156
157
  if (
157
158
  role <= view.min_role ||
158
159
  (await view.authorise_get({ req, ...view })) // TODO set query to state
@@ -185,7 +186,9 @@ router.post(
185
186
  )(req, res, next);
186
187
  })
187
188
  );
188
-
189
+ /**
190
+ *
191
+ */
189
192
  router.get(
190
193
  "/:tableName/distinct/:fieldName",
191
194
  //passport.authenticate("api-bearer", { session: false }),
@@ -206,9 +209,7 @@ router.get(
206
209
  { session: false },
207
210
  async function (err, user, info) {
208
211
  if (accessAllowedRead(req, user, table)) {
209
- const field = (await table.getFields()).find(
210
- (f) => f.name === fieldName
211
- );
212
+ const field = table.getFields().find((f) => f.name === fieldName);
212
213
  if (!field) {
213
214
  res.status(404).json({ error: req.__("Not found") });
214
215
  return;
@@ -268,7 +269,7 @@ router.get(
268
269
  if (versioncount === "on") {
269
270
  const joinOpts = {
270
271
  orderBy: "id",
271
- forUser: req.user || user || { role_id: 10 },
272
+ forUser: req.user || user || { role_id: 100 },
272
273
  forPublic: !(req.user || user),
273
274
  aggregations: {
274
275
  _versions: {
@@ -281,7 +282,7 @@ router.get(
281
282
  };
282
283
  rows = await table.getJoinedRows(joinOpts);
283
284
  } else if (req_query && req_query !== {}) {
284
- const tbl_fields = await table.getFields();
285
+ const tbl_fields = table.getFields();
285
286
  readState(req_query, tbl_fields, req);
286
287
  const qstate = await stateFieldsToWhere({
287
288
  fields: tbl_fields,
@@ -335,7 +336,7 @@ router.post(
335
336
 
336
337
  if (!trigger) {
337
338
  getState().log(3, `API action ${actionname} not found`);
338
- res.status(400).json({ error: req.__("Not found") });
339
+ res.status(404).json({ error: req.__("Not found") });
339
340
  return;
340
341
  }
341
342
  await passport.authenticate(
@@ -350,6 +351,7 @@ router.post(
350
351
  body: req.body,
351
352
  row: req.body,
352
353
  req,
354
+ user: user || req.user,
353
355
  });
354
356
  res.json({ success: true, data: resp });
355
357
  } catch (e) {
@@ -387,8 +389,8 @@ router.post(
387
389
  async function (err, user, info) {
388
390
  if (accessAllowedWrite(req, user, table)) {
389
391
  const { _versions, ...row } = req.body;
390
- const fields = await table.getFields();
391
- readState(row, fields);
392
+ const fields = table.getFields();
393
+ readState(row, fields, req);
392
394
  let errors = [];
393
395
  let hasErrors = false;
394
396
  Object.keys(row).forEach((k) => {
@@ -409,7 +411,8 @@ router.post(
409
411
  if (
410
412
  field.required &&
411
413
  !field.primary_key &&
412
- typeof row[field.name] === "undefined"
414
+ typeof row[field.name] === "undefined" &&
415
+ !field.attributes.default
413
416
  ) {
414
417
  hasErrors = true;
415
418
  errors.push(`${field.name}: required`);
@@ -425,7 +428,7 @@ router.post(
425
428
  }
426
429
  const ins_res = await table.tryInsertRow(
427
430
  row,
428
- req.user || user || { role_id: 10 }
431
+ req.user || user || { role_id: 100 }
429
432
  );
430
433
  if (ins_res.error) {
431
434
  getState().log(2, `API POST ${table.name} error: ${ins_res.error}`);
@@ -458,39 +461,15 @@ router.post(
458
461
  return;
459
462
  }
460
463
  await passport.authenticate(
461
- "api-bearer",
464
+ ["api-bearer", "jwt"],
462
465
  { session: false },
463
466
  async function (err, user, info) {
464
467
  if (accessAllowedWrite(req, user, table)) {
465
468
  const { _versions, ...row } = req.body;
466
- const fields = await table.getFields();
467
- readState(row, fields);
468
- let errors = [];
469
- let hasErrors = false;
470
- for (const k of Object.keys(row)) {
471
- const field = fields.find((f) => f.name === k);
472
- if (!field && k.includes(".")) {
473
- const [fnm, jkey] = k.split(".");
474
- const jfield = fields.find((f) => f.name === fnm);
475
- if (jfield?.type?.name === "JSON") {
476
- if (typeof row[fnm] === "undefined") {
477
- const dbrow = await table.getRow({ [table.pk_name]: id });
478
- row[fnm] = dbrow[fnm] || {};
479
- }
480
- row[fnm][jkey] = row[k];
481
- delete row[k];
482
- }
483
- } else if (!field || field.calculated) {
484
- delete row[k];
485
- } else if (field?.type && field.type.validate) {
486
- const vres = field.type.validate(field.attributes || {})(row[k]);
487
- if (vres.error) {
488
- hasErrors = true;
489
- errors.push(`${k}: ${vres.error}`);
490
- }
491
- }
492
- }
493
- if (hasErrors) {
469
+ const fields = table.getFields();
470
+ readState(row, fields, req);
471
+ const errors = await prepare_update_row(table, row, id);
472
+ if (errors.length > 0) {
494
473
  getState().log(
495
474
  2,
496
475
  `API POST ${table.name} error: ${errors.join(", ")}`
@@ -501,7 +480,7 @@ router.post(
501
480
  const ins_res = await table.tryUpdateRow(
502
481
  row,
503
482
  id,
504
- user || req.user || { role_id: 10 }
483
+ user || req.user || { role_id: 100 }
505
484
  );
506
485
 
507
486
  if (ins_res.error) {
@@ -547,12 +526,12 @@ router.delete(
547
526
  //readState(row, fields);
548
527
  await table.deleteRows(
549
528
  { [pk_name]: row[pk_name] },
550
- user || req.user || { role_id: 10 }
529
+ user || req.user || { role_id: 100 }
551
530
  );
552
531
  } else
553
532
  await table.deleteRows(
554
533
  { id },
555
- user || req.user || { role_id: 10 }
534
+ user || req.user || { role_id: 100 }
556
535
  );
557
536
  res.json({ success: true });
558
537
  } 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");