@saltcorn/server 0.8.6-beta.1 → 0.8.6-beta.11
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/app.js +3 -3
- package/auth/admin.js +4 -0
- package/auth/roleadmin.js +7 -4
- package/auth/routes.js +1 -1
- package/errors.js +1 -1
- package/locales/en.json +16 -2
- package/locales/pl.json +1 -1
- package/locales/ru.json +38 -2
- package/locales/uk.json +1170 -0
- package/package.json +8 -8
- package/public/dayjs.min.js +1 -0
- package/public/saltcorn-common.js +97 -69
- package/public/saltcorn.css +17 -0
- package/public/saltcorn.js +3 -3
- package/routes/admin.js +26 -0
- package/routes/api.js +27 -48
- package/routes/common_lists.js +4 -1
- package/routes/delete.js +3 -3
- package/routes/edit.js +1 -1
- package/routes/fields.js +32 -12
- package/routes/files.js +7 -7
- package/routes/homepage.js +3 -3
- package/routes/index.js +4 -41
- package/routes/menu.js +1 -1
- package/routes/page.js +2 -2
- package/routes/pageedit.js +2 -2
- package/routes/scapi.js +1 -1
- package/routes/search.js +4 -5
- package/routes/sync.js +84 -0
- package/routes/tables.js +5 -5
- package/routes/tenant.js +74 -27
- package/routes/view.js +3 -3
- package/routes/viewedit.js +43 -15
- package/tests/admin.test.js +8 -8
- package/tests/auth.test.js +13 -13
- package/tests/crud.test.js +2 -2
- package/tests/files.test.js +6 -6
- package/tests/kittens.test.js +11 -11
- package/tests/page.test.js +4 -5
- package/tests/sync.test.js +108 -0
- package/tests/table.test.js +6 -6
- package/tests/tenant.test.js +3 -3
- package/tests/view.test.js +128 -1
- package/tests/viewedit.test.js +4 -4
- package/wrapper.js +3 -2
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.8.6-beta.
|
|
3
|
+
"version": "0.8.6-beta.11",
|
|
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.
|
|
10
|
-
"@saltcorn/builder": "0.8.6-beta.
|
|
11
|
-
"@saltcorn/data": "0.8.6-beta.
|
|
12
|
-
"@saltcorn/admin-models": "0.8.6-beta.
|
|
13
|
-
"@saltcorn/filemanager": "0.8.6-beta.
|
|
14
|
-
"@saltcorn/markup": "0.8.6-beta.
|
|
15
|
-
"@saltcorn/sbadmin2": "0.8.6-beta.
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.6-beta.11",
|
|
10
|
+
"@saltcorn/builder": "0.8.6-beta.11",
|
|
11
|
+
"@saltcorn/data": "0.8.6-beta.11",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.6-beta.11",
|
|
13
|
+
"@saltcorn/filemanager": "0.8.6-beta.11",
|
|
14
|
+
"@saltcorn/markup": "0.8.6-beta.11",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.6-beta.11",
|
|
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
|
|
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
|
|
552
|
+
ajax
|
|
553
|
+
? `onsubmit="inline_${
|
|
554
|
+
isNode ? "ajax" : "local"
|
|
555
|
+
}_submit(event, '${opts}')"`
|
|
556
|
+
: ""
|
|
514
557
|
}>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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) => {
|
package/public/saltcorn.css
CHANGED
|
@@ -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
|
+
}
|
package/public/saltcorn.js
CHANGED
|
@@ -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 >=
|
|
602
|
+
if (pollCount >= 100) {
|
|
603
603
|
removeSpinner("buildMobileAppBtnId", orginalBtnHtml);
|
|
604
604
|
notifyAlert({
|
|
605
605
|
type: "danger",
|
|
606
|
-
text: "
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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 :
|
|
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 =
|
|
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:
|
|
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 =
|
|
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(
|
|
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 =
|
|
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:
|
|
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 =
|
|
467
|
-
readState(row, fields);
|
|
468
|
-
|
|
469
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
534
|
+
user || req.user || { role_id: 100 }
|
|
556
535
|
);
|
|
557
536
|
res.json({ success: true });
|
|
558
537
|
} catch (e) {
|
package/routes/common_lists.js
CHANGED
|
@@ -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) =>
|
|
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 :
|
|
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:
|
|
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:
|
|
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(
|