@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/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 +37 -29
- package/public/saltcorn.js +1 -1
- package/routes/admin.js +23 -0
- package/routes/api.js +23 -21
- 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 +24 -11
- 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.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.
|
|
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.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
|
-
|
|
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();
|
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}`;
|
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
|
-
:
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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 :
|
|
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 =
|
|
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:
|
|
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 =
|
|
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(
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
557
|
+
user || req.user || { role_id: 100 }
|
|
556
558
|
);
|
|
557
559
|
res.json({ success: true });
|
|
558
560
|
} 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(
|
package/routes/edit.js
CHANGED
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
|
-
|
|
469
|
-
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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 :
|
|
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 :
|
|
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 :
|
|
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 :
|
|
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 ===
|
|
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 :
|
|
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 ===
|
|
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 :
|
|
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"));
|
package/routes/homepage.js
CHANGED
|
@@ -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 :
|
|
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 = {
|
|
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 :
|
|
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
|
|
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",
|
|
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 ||
|
|
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 :
|
|
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 :
|
|
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;
|
package/routes/pageedit.js
CHANGED
|
@@ -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.
|
|
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
|
}
|