@saltcorn/server 0.9.1-beta.9 → 0.9.2-rc.1
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/auth/routes.js +23 -3
- package/help/Extra state formula.tmd +4 -1
- package/load_plugins.js +10 -1
- package/locales/en.json +8 -1
- package/markup/admin.js +13 -4
- package/package.json +8 -8
- package/public/dayjs.min.js +3 -1
- package/public/saltcorn-common.js +47 -20
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +58 -3
- package/routes/actions.js +12 -4
- package/routes/common_lists.js +47 -37
- package/routes/fields.js +4 -1
- package/routes/homepage.js +75 -2
- package/routes/menu.js +11 -2
- package/routes/pageedit.js +5 -1
- package/routes/plugins.js +2 -0
- package/routes/view.js +20 -6
- package/routes/viewedit.js +7 -4
- package/tests/view.test.js +39 -0
package/auth/routes.js
CHANGED
|
@@ -1586,9 +1586,17 @@ router.post(
|
|
|
1586
1586
|
*/
|
|
1587
1587
|
router.post(
|
|
1588
1588
|
"/settings",
|
|
1589
|
+
setTenant,
|
|
1589
1590
|
loggedIn,
|
|
1590
1591
|
error_catcher(async (req, res) => {
|
|
1591
|
-
const user = await User.findOne({ id: req.user
|
|
1592
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
1593
|
+
if (!user) {
|
|
1594
|
+
res.sendWrap(
|
|
1595
|
+
req.__("User settings") || "User settings",
|
|
1596
|
+
"Must be logged in"
|
|
1597
|
+
);
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1592
1600
|
if (req.body.new_password && user.password) {
|
|
1593
1601
|
const pwform = changPwForm(req);
|
|
1594
1602
|
|
|
@@ -1615,12 +1623,24 @@ router.post(
|
|
|
1615
1623
|
if (user_settings_form) {
|
|
1616
1624
|
const view = await View.findOne({ name: user_settings_form });
|
|
1617
1625
|
if (view) {
|
|
1626
|
+
const fakeRes = {
|
|
1627
|
+
status() {},
|
|
1628
|
+
sendWrap() {},
|
|
1629
|
+
json() {},
|
|
1630
|
+
redirect() {},
|
|
1631
|
+
};
|
|
1618
1632
|
await view.runPost({ id: user.id }, req.body, {
|
|
1619
1633
|
req,
|
|
1620
|
-
res,
|
|
1634
|
+
res: fakeRes,
|
|
1621
1635
|
redirect: "/auth/settings",
|
|
1622
1636
|
});
|
|
1623
|
-
|
|
1637
|
+
const u = await User.findForSession({ id: user.id });
|
|
1638
|
+
req.login(u.session_object, function (err) {
|
|
1639
|
+
if (err) req.flash("danger", err);
|
|
1640
|
+
else req.flash("success", req.__("User settings changed"));
|
|
1641
|
+
|
|
1642
|
+
res.redirect("/auth/settings");
|
|
1643
|
+
});
|
|
1624
1644
|
}
|
|
1625
1645
|
} else {
|
|
1626
1646
|
res.redirect("/auth/settings");
|
|
@@ -68,4 +68,7 @@ variable name by a dollar sign (in pages and Show views). For instance if your U
|
|
|
68
68
|
you can access the value of `foo` (here 1) as `$foo`. In Filter views, there is no
|
|
69
69
|
underlying row and the purpose of the view is fundamentally to manipulate the state
|
|
70
70
|
so the state in extra state formulae is accessed without dollar sign; in the
|
|
71
|
-
previous example `foo` instead of `$foo`.
|
|
71
|
+
previous example `foo` instead of `$foo`.
|
|
72
|
+
|
|
73
|
+
Use `session_id` to refer to the current session ID string (unique for the user's session
|
|
74
|
+
whether the are logged in or not).
|
package/load_plugins.js
CHANGED
|
@@ -29,6 +29,15 @@ const staticDependencies = {
|
|
|
29
29
|
"@saltcorn/data/models/fieldrepeat": require("@saltcorn/data/models/fieldrepeat"),
|
|
30
30
|
"@saltcorn/data/models/table": require("@saltcorn/data/models/table"),
|
|
31
31
|
"@saltcorn/data/models/form": require("@saltcorn/data/models/form"),
|
|
32
|
+
"@saltcorn/data/models/discovery": require("@saltcorn/data/models/discovery"),
|
|
33
|
+
"@saltcorn/data/models/config": require("@saltcorn/data/models/config"),
|
|
34
|
+
"@saltcorn/data/models/library": require("@saltcorn/data/models/library"),
|
|
35
|
+
"@saltcorn/data/models/model": require("@saltcorn/data/models/model"),
|
|
36
|
+
"@saltcorn/data/models/model_instance": require("@saltcorn/data/models/model_instance"),
|
|
37
|
+
"@saltcorn/data/models/notification": require("@saltcorn/data/models/notification"),
|
|
38
|
+
"@saltcorn/data/models/role": require("@saltcorn/data/models/role"),
|
|
39
|
+
"@saltcorn/data/models/tag": require("@saltcorn/data/models/tag"),
|
|
40
|
+
"@saltcorn/data/models/tag_entry": require("@saltcorn/data/models/tag_entry"),
|
|
32
41
|
"@saltcorn/data/models/view": require("@saltcorn/data/models/view"),
|
|
33
42
|
"@saltcorn/data/models/page": require("@saltcorn/data/models/page"),
|
|
34
43
|
"@saltcorn/data/models/file": require("@saltcorn/data/models/file"),
|
|
@@ -202,7 +211,7 @@ const loadAndSaveNewPlugin = async (plugin, force, noSignalOrDB, manager) => {
|
|
|
202
211
|
const { version, plugin_module, location } = await requirePlugin(
|
|
203
212
|
plugin,
|
|
204
213
|
force,
|
|
205
|
-
manager
|
|
214
|
+
manager
|
|
206
215
|
);
|
|
207
216
|
|
|
208
217
|
// install dependecies
|
package/locales/en.json
CHANGED
|
@@ -1278,5 +1278,12 @@
|
|
|
1278
1278
|
"HTML file to use as page content": "HTML file to use as page content",
|
|
1279
1279
|
"Offline mode: cannot load file": "Offline mode: cannot load file",
|
|
1280
1280
|
"None - use drag and drop builder": "None - use drag and drop builder",
|
|
1281
|
-
"Do not pick or compare time": "Do not pick or compare time"
|
|
1281
|
+
"Do not pick or compare time": "Do not pick or compare time",
|
|
1282
|
+
"Installed theme": "Installed theme",
|
|
1283
|
+
"Theme for role": "Theme for role",
|
|
1284
|
+
"Set theme for each user role »": "Set theme for each user role »",
|
|
1285
|
+
"Available themes": "Available themes",
|
|
1286
|
+
"Install more themes »": "Install more themes »",
|
|
1287
|
+
"Configure action": "Configure action",
|
|
1288
|
+
"No changes detected, snapshot skipped": "No changes detected, snapshot skipped"
|
|
1282
1289
|
}
|
package/markup/admin.js
CHANGED
|
@@ -77,11 +77,14 @@ const add_edit_bar = ({
|
|
|
77
77
|
req,
|
|
78
78
|
viewtemplate,
|
|
79
79
|
table,
|
|
80
|
+
view,
|
|
80
81
|
cfgUrl,
|
|
81
82
|
}) => {
|
|
83
|
+
if (req && req.headers.localizedstate)
|
|
84
|
+
return { above: [contents], noWrapTop: true };
|
|
82
85
|
if (role > 1 && req && req.xhr) return { above: [contents] }; //make sure not put in card
|
|
83
86
|
if (role > 1) return contents;
|
|
84
|
-
|
|
87
|
+
|
|
85
88
|
let viewSpec = "";
|
|
86
89
|
if (viewtemplate) viewSpec = viewtemplate;
|
|
87
90
|
if (table) {
|
|
@@ -89,20 +92,26 @@ const add_edit_bar = ({
|
|
|
89
92
|
if (tbl)
|
|
90
93
|
viewSpec = `${viewSpec} on <a href="/table/${table}">${tbl.name}</a>`;
|
|
91
94
|
}
|
|
95
|
+
const singleton = view?.viewtemplateObj?.singleton;
|
|
92
96
|
|
|
93
97
|
const bar = div(
|
|
94
98
|
{ class: "alert alert-light d-print-none admin-edit-bar" },
|
|
95
99
|
title,
|
|
96
100
|
what && span({ class: "ms-1 me-2 badge bg-primary" }, what),
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
!singleton &&
|
|
102
|
+
a(
|
|
103
|
+
{ class: "ms-2", href: url },
|
|
104
|
+
"Edit ",
|
|
105
|
+
i({ class: "fas fa-edit" })
|
|
106
|
+
),
|
|
107
|
+
cfgUrl && !singleton
|
|
99
108
|
? a(
|
|
100
109
|
{ class: "ms-1 me-3", href: cfgUrl },
|
|
101
110
|
"Configure ",
|
|
102
111
|
i({ class: "fas fa-cog" })
|
|
103
112
|
)
|
|
104
113
|
: "",
|
|
105
|
-
viewSpec
|
|
114
|
+
!singleton && viewSpec
|
|
106
115
|
);
|
|
107
116
|
|
|
108
117
|
if (contents.above) {
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2-rc.1",
|
|
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
9
|
"@aws-sdk/client-s3": "^3.451.0",
|
|
10
|
-
"@saltcorn/base-plugin": "0.9.
|
|
11
|
-
"@saltcorn/builder": "0.9.
|
|
12
|
-
"@saltcorn/data": "0.9.
|
|
13
|
-
"@saltcorn/admin-models": "0.9.
|
|
14
|
-
"@saltcorn/filemanager": "0.9.
|
|
15
|
-
"@saltcorn/markup": "0.9.
|
|
16
|
-
"@saltcorn/sbadmin2": "0.9.
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.2-rc.1",
|
|
11
|
+
"@saltcorn/builder": "0.9.2-rc.1",
|
|
12
|
+
"@saltcorn/data": "0.9.2-rc.1",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.2-rc.1",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.2-rc.1",
|
|
15
|
+
"@saltcorn/markup": "0.9.2-rc.1",
|
|
16
|
+
"@saltcorn/sbadmin2": "0.9.2-rc.1",
|
|
17
17
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
18
18
|
"@socket.io/sticky": "^1.0.1",
|
|
19
19
|
"adm-zip": "0.5.10",
|
package/public/dayjs.min.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
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}));
|
|
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}));
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).dayjs_plugin_advancedFormat=t()}(this,(function(){"use strict";return function(e,t){var r=t.prototype,n=r.format;r.format=function(e){var t=this,r=this.$locale();if(!this.isValid())return n.bind(this)(e);var s=this.$utils(),a=(e||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g,(function(e){switch(e){case"Q":return Math.ceil((t.$M+1)/3);case"Do":return r.ordinal(t.$D);case"gggg":return t.weekYear();case"GGGG":return t.isoWeekYear();case"wo":return r.ordinal(t.week(),"W");case"w":case"ww":return s.s(t.week(),"w"===e?1:2,"0");case"W":case"WW":return s.s(t.isoWeek(),"W"===e?1:2,"0");case"k":case"kk":return s.s(String(0===t.$H?24:t.$H),"k"===e?1:2,"0");case"X":return Math.floor(t.$d.getTime()/1e3);case"x":return t.$d.getTime();case"z":return"["+t.offsetName()+"]";case"zzz":return"["+t.offsetName("long")+"]";default:return e}}));return n.bind(this)(a)}}}));
|
|
3
|
+
dayjs.extend(window.dayjs_plugin_advancedFormat)
|
|
@@ -34,6 +34,11 @@ function add_repeater(nm) {
|
|
|
34
34
|
$(element).attr("name", newnm).attr("id", newid);
|
|
35
35
|
});
|
|
36
36
|
newe.appendTo($("div.repeats-" + nm));
|
|
37
|
+
newe.find("[data-on-cloned]").each(function (ix, element) {
|
|
38
|
+
(function (str) {
|
|
39
|
+
return eval(str);
|
|
40
|
+
}).call(element, $(element).attr("data-on-cloned"));
|
|
41
|
+
});
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const _apply_showif_plugins = [];
|
|
@@ -160,7 +165,7 @@ function apply_showif() {
|
|
|
160
165
|
e.prop("data-fetch-options-current-set", qs);
|
|
161
166
|
const toAppend = [];
|
|
162
167
|
if (!dynwhere.required)
|
|
163
|
-
toAppend.push({ label: dynwhere.neutral_label || "" });
|
|
168
|
+
toAppend.push({ label: dynwhere.neutral_label || "", value: "" });
|
|
164
169
|
let currentDataOption = undefined;
|
|
165
170
|
const dataOptions = [];
|
|
166
171
|
//console.log(success);
|
|
@@ -196,7 +201,7 @@ function apply_showif() {
|
|
|
196
201
|
.map(
|
|
197
202
|
({ label, value, selected }) =>
|
|
198
203
|
`<option${selected ? ` selected` : ""}${
|
|
199
|
-
value ? ` value="${value}"` : ""
|
|
204
|
+
typeof value !== "undefined" ? ` value="${value}"` : ""
|
|
200
205
|
}>${label || ""}</option>`
|
|
201
206
|
)
|
|
202
207
|
.join("")
|
|
@@ -416,6 +421,8 @@ function get_form_record(e_in, select_labels) {
|
|
|
416
421
|
}
|
|
417
422
|
function showIfFormulaInputs(e, fml) {
|
|
418
423
|
const rec = get_form_record(e);
|
|
424
|
+
if (window._sc_loglevel > 4)
|
|
425
|
+
console.log(`show if fml ${fml} form_record`, rec);
|
|
419
426
|
try {
|
|
420
427
|
return new Function(
|
|
421
428
|
"row",
|
|
@@ -423,7 +430,11 @@ function showIfFormulaInputs(e, fml) {
|
|
|
423
430
|
"return " + fml
|
|
424
431
|
)(rec, rec);
|
|
425
432
|
} catch (e) {
|
|
426
|
-
throw new Error(
|
|
433
|
+
throw new Error(
|
|
434
|
+
`Error in evaluating showIf formula ${fml} with values ${JSON.stringify(
|
|
435
|
+
rec
|
|
436
|
+
)}: ${e.message}`
|
|
437
|
+
);
|
|
427
438
|
}
|
|
428
439
|
}
|
|
429
440
|
|
|
@@ -604,7 +615,7 @@ function initialize_page() {
|
|
|
604
615
|
schema = JSON.parse(decodeURIComponent(schema));
|
|
605
616
|
}
|
|
606
617
|
if (type === "Date") {
|
|
607
|
-
console.log("timeelsems", $(this).find("span.current time"));
|
|
618
|
+
//console.log("timeelsems", $(this).find("span.current time"));
|
|
608
619
|
current =
|
|
609
620
|
$(this).attr("data-inline-edit-current") ||
|
|
610
621
|
$(this).find("span.current time").attr("datetime"); // ||
|
|
@@ -1043,7 +1054,10 @@ function notifyAlert(note, spin) {
|
|
|
1043
1054
|
type = note.type;
|
|
1044
1055
|
}
|
|
1045
1056
|
const { id, html } = buildToast(txt, type, spin);
|
|
1046
|
-
$("#
|
|
1057
|
+
let $modal = $("#scmodal");
|
|
1058
|
+
if ($modal.length && $modal.hasClass("show"))
|
|
1059
|
+
$("#modal-toasts-area").append(html);
|
|
1060
|
+
else $("#toasts-area").append(html);
|
|
1047
1061
|
if (type === "success") {
|
|
1048
1062
|
setTimeout(() => {
|
|
1049
1063
|
$(`#${id}`).removeClass("show");
|
|
@@ -1074,28 +1088,41 @@ function restore_old_button(btnId) {
|
|
|
1074
1088
|
btn.removeData("old-text");
|
|
1075
1089
|
}
|
|
1076
1090
|
|
|
1077
|
-
function common_done(res, viewname, isWeb = true) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1091
|
+
async function common_done(res, viewname, isWeb = true) {
|
|
1092
|
+
if (window._sc_loglevel > 4)
|
|
1093
|
+
console.log("ajax result directives", viewname, res);
|
|
1094
|
+
const handle = async (element, fn) => {
|
|
1095
|
+
if (Array.isArray(element))
|
|
1096
|
+
for (const current of element) await fn(current);
|
|
1097
|
+
else await fn(element);
|
|
1098
|
+
};
|
|
1099
|
+
const eval_it = async (s) => {
|
|
1100
|
+
if (res.row && res.field_names) {
|
|
1101
|
+
const f = new Function(`viewname, row, {${res.field_names}}`, s);
|
|
1102
|
+
const evalres = await f(viewname, res.row, res.row);
|
|
1103
|
+
if (evalres) await common_done(evalres, viewname, isWeb);
|
|
1104
|
+
} else {
|
|
1105
|
+
const f = new Function(`viewname`, s);
|
|
1106
|
+
const evalres = await f(viewname);
|
|
1107
|
+
if (evalres) await common_done(evalres, viewname, isWeb);
|
|
1108
|
+
}
|
|
1081
1109
|
};
|
|
1082
|
-
if (res.notify) handle(res.notify, notifyAlert);
|
|
1110
|
+
if (res.notify) await handle(res.notify, notifyAlert);
|
|
1083
1111
|
if (res.error)
|
|
1084
|
-
handle(res.error, (text) =>
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1112
|
+
await handle(res.error, (text) =>
|
|
1113
|
+
notifyAlert({ type: "danger", text: text })
|
|
1114
|
+
);
|
|
1115
|
+
if (res.notify_success)
|
|
1116
|
+
await handle(res.notify_success, (text) =>
|
|
1117
|
+
notifyAlert({ type: "success", text: text })
|
|
1118
|
+
);
|
|
1119
|
+
if (res.eval_js) await handle(res.eval_js, eval_it);
|
|
1093
1120
|
|
|
1094
1121
|
if (res.reload_page) {
|
|
1095
1122
|
(isWeb ? location : parent).reload(); //TODO notify to cookie if reload or goto
|
|
1096
1123
|
}
|
|
1097
1124
|
if (res.download) {
|
|
1098
|
-
handle(res.download, (download) => {
|
|
1125
|
+
await handle(res.download, (download) => {
|
|
1099
1126
|
const dataurl = `data:${
|
|
1100
1127
|
download.mimetype || "application/octet-stream"
|
|
1101
1128
|
};base64,${download.blob}`;
|
package/public/saltcorn.css
CHANGED
package/public/saltcorn.js
CHANGED
|
@@ -135,6 +135,36 @@ $(function () {
|
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
function reload_embedded_view(viewname) {
|
|
139
|
+
if (window._sc_loglevel > 4)
|
|
140
|
+
console.log(
|
|
141
|
+
"reload_embedded_view",
|
|
142
|
+
viewname,
|
|
143
|
+
"found",
|
|
144
|
+
$(`[data-sc-embed-viewname="${viewname}"]`).length
|
|
145
|
+
);
|
|
146
|
+
$(`[data-sc-embed-viewname="${viewname}"]`).each(function () {
|
|
147
|
+
const $e = $(this);
|
|
148
|
+
const url =
|
|
149
|
+
$e.attr("data-sc-local-state") || $e.attr("data-sc-view-source");
|
|
150
|
+
if (!url) return;
|
|
151
|
+
const headers = {
|
|
152
|
+
pjaxpageload: "true",
|
|
153
|
+
localizedstate: "true", //no admin bar
|
|
154
|
+
};
|
|
155
|
+
$.ajax(url, {
|
|
156
|
+
headers,
|
|
157
|
+
success: function (res, textStatus, request) {
|
|
158
|
+
$e.html(res);
|
|
159
|
+
initialize_page();
|
|
160
|
+
},
|
|
161
|
+
error: function (res) {
|
|
162
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
138
168
|
function pjax_to(href, e) {
|
|
139
169
|
let $modal = $("#scmodal");
|
|
140
170
|
const inModal = $modal.length && $modal.hasClass("show");
|
|
@@ -257,6 +287,7 @@ function globalErrorCatcher(message, source, lineno, colno, error) {
|
|
|
257
287
|
}
|
|
258
288
|
|
|
259
289
|
function close_saltcorn_modal() {
|
|
290
|
+
$("#scmodal").off("hidden.bs.modal");
|
|
260
291
|
var myModalEl = document.getElementById("scmodal");
|
|
261
292
|
if (!myModalEl) return;
|
|
262
293
|
var modal = bootstrap.Modal.getInstance(myModalEl);
|
|
@@ -281,6 +312,13 @@ function ensure_modal_exists_and_closed() {
|
|
|
281
312
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
282
313
|
</button>
|
|
283
314
|
</div>
|
|
315
|
+
<div
|
|
316
|
+
id="modal-toasts-area"
|
|
317
|
+
class="toast-container position-fixed top-0 end-0 p-2 "
|
|
318
|
+
style: "z-index: 7000;"
|
|
319
|
+
aria-live="polite"
|
|
320
|
+
aria-atomic="true">
|
|
321
|
+
</div>
|
|
284
322
|
</div>
|
|
285
323
|
<div class="modal-body">
|
|
286
324
|
<p>Modal body text goes here.</p>
|
|
@@ -292,9 +330,9 @@ function ensure_modal_exists_and_closed() {
|
|
|
292
330
|
// remove reload handler added by edit, for when we have popup link
|
|
293
331
|
// in autosave edit in popup
|
|
294
332
|
$("#scmodal").off("hidden.bs.modal");
|
|
295
|
-
|
|
296
333
|
close_saltcorn_modal();
|
|
297
334
|
}
|
|
335
|
+
$("#modal-toasts-area").empty();
|
|
298
336
|
}
|
|
299
337
|
|
|
300
338
|
function expand_thumbnail(img_id, filename) {
|
|
@@ -308,8 +346,10 @@ function expand_thumbnail(img_id, filename) {
|
|
|
308
346
|
|
|
309
347
|
function ajax_modal(url, opts = {}) {
|
|
310
348
|
ensure_modal_exists_and_closed();
|
|
349
|
+
$("#scmodal").removeClass("no-submit-reload");
|
|
350
|
+
$("#scmodal").attr("data-on-close-reload-view", opts.reload_view || null);
|
|
351
|
+
|
|
311
352
|
if (opts.submitReload === false) $("#scmodal").addClass("no-submit-reload");
|
|
312
|
-
else $("#scmodal").removeClass("no-submit-reload");
|
|
313
353
|
$.ajax(url, {
|
|
314
354
|
headers: {
|
|
315
355
|
SaltcornModalRequest: "true",
|
|
@@ -364,6 +404,11 @@ function submitWithAjax(e) {
|
|
|
364
404
|
notifyAlert({ type: "danger", text: res.responseJSON.error });
|
|
365
405
|
});
|
|
366
406
|
}
|
|
407
|
+
function saveAndContinueAsync(e) {
|
|
408
|
+
return new Promise((resolve, reject) => {
|
|
409
|
+
saveAndContinue(e, (x) => resolve(x));
|
|
410
|
+
});
|
|
411
|
+
}
|
|
367
412
|
|
|
368
413
|
function saveAndContinue(e, k) {
|
|
369
414
|
var form = $(e).closest("form");
|
|
@@ -390,6 +435,9 @@ function saveAndContinue(e, k) {
|
|
|
390
435
|
if (res.notify) {
|
|
391
436
|
notifyAlert(res.notify);
|
|
392
437
|
}
|
|
438
|
+
if (res.notify_success) {
|
|
439
|
+
notifyAlert({ type: "success", text: res.notify });
|
|
440
|
+
}
|
|
393
441
|
if (res.reload_page) {
|
|
394
442
|
location.reload(); //TODO notify to cookie if reload or goto
|
|
395
443
|
}
|
|
@@ -506,8 +554,15 @@ function ajaxSubmitForm(e) {
|
|
|
506
554
|
contentType: false,
|
|
507
555
|
success: function (res) {
|
|
508
556
|
var no_reload = $("#scmodal").hasClass("no-submit-reload");
|
|
557
|
+
const on_close_reload_view = $("#scmodal").attr(
|
|
558
|
+
"data-on-close-reload-view"
|
|
559
|
+
);
|
|
509
560
|
$("#scmodal").modal("hide");
|
|
510
|
-
if (
|
|
561
|
+
if (on_close_reload_view) {
|
|
562
|
+
const viewE = $(`[data-sc-embed-viewname="${on_close_reload_view}"]`);
|
|
563
|
+
if (viewE.length) reload_embedded_view(on_close_reload_view);
|
|
564
|
+
else location.reload();
|
|
565
|
+
} else if (!no_reload) location.reload();
|
|
511
566
|
else common_done(res, form.attr("data-viewname"));
|
|
512
567
|
},
|
|
513
568
|
error: function (request) {
|
package/routes/actions.js
CHANGED
|
@@ -400,7 +400,7 @@ router.get(
|
|
|
400
400
|
{ class: "ms-3" },
|
|
401
401
|
trigger.action,
|
|
402
402
|
table ? ` on ` + a({ href: `/table/${table.name}` }, table.name) : ""
|
|
403
|
-
);
|
|
403
|
+
) + a({href: `/actions/testrun/${id}`, class: "ms-2" }, req.__("Test run")+" »");
|
|
404
404
|
if (!action) {
|
|
405
405
|
req.flash("warning", req.__("Action not found"));
|
|
406
406
|
res.redirect(`/actions/`);
|
|
@@ -478,7 +478,9 @@ router.get(
|
|
|
478
478
|
res.redirect(`/actions/`);
|
|
479
479
|
} else {
|
|
480
480
|
// get configuration fields
|
|
481
|
-
const cfgFields = await getActionConfigFields(action, table
|
|
481
|
+
const cfgFields = await getActionConfigFields(action, table, {
|
|
482
|
+
mode: "trigger",
|
|
483
|
+
});
|
|
482
484
|
// create form
|
|
483
485
|
const form = new Form({
|
|
484
486
|
action: addOnDoneRedirect(`/actions/configure/${id}`, req),
|
|
@@ -523,7 +525,9 @@ router.post(
|
|
|
523
525
|
const table = trigger.table_id
|
|
524
526
|
? Table.findOne({ id: trigger.table_id })
|
|
525
527
|
: null;
|
|
526
|
-
const cfgFields = await getActionConfigFields(action, table
|
|
528
|
+
const cfgFields = await getActionConfigFields(action, table, {
|
|
529
|
+
mode: "trigger",
|
|
530
|
+
});
|
|
527
531
|
const form = new Form({
|
|
528
532
|
action: `/actions/configure/${id}`,
|
|
529
533
|
fields: cfgFields,
|
|
@@ -614,7 +618,7 @@ router.get(
|
|
|
614
618
|
let table, row;
|
|
615
619
|
if (trigger.table_id) {
|
|
616
620
|
table = Table.findOne({ id: trigger.table_id });
|
|
617
|
-
row = await table.getRow({});
|
|
621
|
+
row = await table.getRow({}, {orderBy: "RANDOM()"});
|
|
618
622
|
}
|
|
619
623
|
let runres;
|
|
620
624
|
|
|
@@ -661,6 +665,10 @@ router.get(
|
|
|
661
665
|
{ href: `/actions`, class: "mt-4 btn btn-primary me-1" },
|
|
662
666
|
"« " + req.__("back to actions")
|
|
663
667
|
),
|
|
668
|
+
a(
|
|
669
|
+
{ href: `/actions/configure/${trigger.id}`, class: "mt-4 btn btn-primary me-1" },
|
|
670
|
+
req.__("Configure action")
|
|
671
|
+
),
|
|
664
672
|
a(
|
|
665
673
|
{
|
|
666
674
|
href: `/actions/testrun/${id}`,
|
package/routes/common_lists.js
CHANGED
|
@@ -123,40 +123,46 @@ const view_dropdown = (view, req, on_done_redirect_str = "") =>
|
|
|
123
123
|
},
|
|
124
124
|
'<i class="fas fa-running"></i> ' + req.__("Run")
|
|
125
125
|
),
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
view.id &&
|
|
127
|
+
a(
|
|
128
|
+
{
|
|
129
|
+
class: "dropdown-item",
|
|
130
|
+
href: `/viewedit/edit/${encodeURIComponent(
|
|
131
|
+
view.name
|
|
132
|
+
)}${on_done_redirect_str}`,
|
|
133
|
+
},
|
|
134
|
+
'<i class="fas fa-edit"></i> ' + req.__("Edit")
|
|
135
|
+
),
|
|
135
136
|
post_dropdown_item(
|
|
136
|
-
`/viewedit/add-to-menu/${
|
|
137
|
+
`/viewedit/add-to-menu/${encodeURIComponent(
|
|
138
|
+
view.name
|
|
139
|
+
)}${on_done_redirect_str}`,
|
|
137
140
|
'<i class="fas fa-bars"></i> ' + req.__("Add to menu"),
|
|
138
141
|
req
|
|
139
142
|
),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
view.id &&
|
|
144
|
+
post_dropdown_item(
|
|
145
|
+
`/viewedit/clone/${view.id}${on_done_redirect_str}`,
|
|
146
|
+
'<i class="far fa-copy"></i> ' + req.__("Duplicate"),
|
|
147
|
+
req
|
|
148
|
+
),
|
|
149
|
+
view.id &&
|
|
150
|
+
a(
|
|
151
|
+
{
|
|
152
|
+
class: "dropdown-item",
|
|
153
|
+
href: `javascript:ajax_modal('/admin/snapshot-restore/view/${view.name}')`,
|
|
154
|
+
},
|
|
155
|
+
'<i class="fas fa-undo-alt"></i> ' + req.__("Restore")
|
|
156
|
+
),
|
|
157
|
+
view.id && div({ class: "dropdown-divider" }),
|
|
158
|
+
view.id &&
|
|
159
|
+
post_dropdown_item(
|
|
160
|
+
`/viewedit/delete/${view.id}${on_done_redirect_str}`,
|
|
161
|
+
'<i class="far fa-trash-alt"></i> ' + req.__("Delete"),
|
|
162
|
+
req,
|
|
163
|
+
true,
|
|
164
|
+
view.name
|
|
165
|
+
),
|
|
160
166
|
]);
|
|
161
167
|
|
|
162
168
|
const setTableRefs = async (views) => {
|
|
@@ -222,17 +228,21 @@ const viewsList = async (
|
|
|
222
228
|
{
|
|
223
229
|
label: req.__("Role to access"),
|
|
224
230
|
key: (row) =>
|
|
225
|
-
|
|
231
|
+
row.id
|
|
232
|
+
? editViewRoleForm(row, roles, req, on_done_redirect_str)
|
|
233
|
+
: "admin",
|
|
226
234
|
},
|
|
227
235
|
{
|
|
228
236
|
label: "",
|
|
229
237
|
key: (r) =>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
r.id && r.viewtemplateObj?.configuration_workflow
|
|
239
|
+
? link(
|
|
240
|
+
`/viewedit/config/${encodeURIComponent(
|
|
241
|
+
r.name
|
|
242
|
+
)}${on_done_redirect_str}`,
|
|
243
|
+
req.__("Configure")
|
|
244
|
+
)
|
|
245
|
+
: "",
|
|
236
246
|
},
|
|
237
247
|
!tagId
|
|
238
248
|
? {
|
package/routes/fields.js
CHANGED
|
@@ -1169,7 +1169,10 @@ router.post(
|
|
|
1169
1169
|
}
|
|
1170
1170
|
|
|
1171
1171
|
const field = table.getField(fieldName);
|
|
1172
|
-
|
|
1172
|
+
if (!field) {
|
|
1173
|
+
res.send("");
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1173
1176
|
const fieldViewConfigForms = await calcfldViewConfig([field], false, 0);
|
|
1174
1177
|
const formFields = fieldViewConfigForms[field.name][fv_name];
|
|
1175
1178
|
if (!formFields) {
|
package/routes/homepage.js
CHANGED
|
@@ -12,8 +12,9 @@ const View = require("@saltcorn/data/models/view");
|
|
|
12
12
|
const User = require("@saltcorn/data/models/user");
|
|
13
13
|
const File = require("@saltcorn/data/models/file");
|
|
14
14
|
const Page = require("@saltcorn/data/models/page");
|
|
15
|
+
const Plugin = require("@saltcorn/data/models/plugin");
|
|
15
16
|
const { link, mkTable } = require("@saltcorn/markup");
|
|
16
|
-
const { div, a, p, i } = require("@saltcorn/markup/tags");
|
|
17
|
+
const { div, a, p, i, h5, span } = require("@saltcorn/markup/tags");
|
|
17
18
|
const Table = require("@saltcorn/data/models/table");
|
|
18
19
|
const { get_cached_packs } = require("@saltcorn/admin-models/models/pack");
|
|
19
20
|
// const { restore_backup } = require("../markup/admin");
|
|
@@ -310,10 +311,80 @@ const packTab = (req, packlist) =>
|
|
|
310
311
|
{ noHeader: true }
|
|
311
312
|
),
|
|
312
313
|
a(
|
|
313
|
-
{ href: `/plugins?set=packs`, class: "btn btn-primary" },
|
|
314
|
+
{ href: `/plugins?set=packs`, class: "btn btn-sm btn-primary" },
|
|
314
315
|
req.__("Go to pack store »")
|
|
315
316
|
)
|
|
316
317
|
);
|
|
318
|
+
|
|
319
|
+
const themeCard = (req, roleMap) => {
|
|
320
|
+
const state_layouts = getState().layouts;
|
|
321
|
+
const state_layout_names = Object.keys(state_layouts);
|
|
322
|
+
const layout_by_role = getState().getConfig("layout_by_role");
|
|
323
|
+
const used_layout_by_role = {};
|
|
324
|
+
Object.keys(roleMap).forEach((role_id) => {
|
|
325
|
+
used_layout_by_role[role_id] =
|
|
326
|
+
layout_by_role[role_id] ||
|
|
327
|
+
state_layout_names[state_layout_names.length - 1];
|
|
328
|
+
});
|
|
329
|
+
const themes_available = Plugin.get_cached_plugins().filter(
|
|
330
|
+
(p) => p.has_theme && !state_layout_names.includes(p.name)
|
|
331
|
+
);
|
|
332
|
+
const layouts = Object.entries(getState().layouts)
|
|
333
|
+
.filter(([nm, v]) => nm !== "emergency")
|
|
334
|
+
.map(([name, layout]) => {
|
|
335
|
+
let plugin = getState().plugins[name];
|
|
336
|
+
const for_role = Object.entries(used_layout_by_role)
|
|
337
|
+
.filter(([role, rname]) => rname === name)
|
|
338
|
+
.map(([role, rname]) =>
|
|
339
|
+
span({ class: "badge bg-info" }, roleMap[role])
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
name,
|
|
344
|
+
layout,
|
|
345
|
+
plugin,
|
|
346
|
+
for_role,
|
|
347
|
+
edit_cfg_link: plugin?.configuration_workflow
|
|
348
|
+
? a(
|
|
349
|
+
{
|
|
350
|
+
href: `/plugins/configure/${encodeURIComponent(name)}`,
|
|
351
|
+
},
|
|
352
|
+
i({ class: "fa fa-cog ms-2" })
|
|
353
|
+
)
|
|
354
|
+
: "",
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
const show_installable = themes_available.length > 0 || layouts.length == 1;
|
|
358
|
+
return div(
|
|
359
|
+
{ class: "pb-3 pt-2 pe-4" },
|
|
360
|
+
mkTable(
|
|
361
|
+
[
|
|
362
|
+
{
|
|
363
|
+
label: req.__("Installed theme"),
|
|
364
|
+
key: ({ name, edit_cfg_link }) => `${name}${edit_cfg_link}`,
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
label: req.__("Theme for role"),
|
|
368
|
+
key: ({ for_role }) => for_role.join(" "),
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
layouts
|
|
372
|
+
),
|
|
373
|
+
a({ href: "/roleadmin" }, req.__("Set theme for each user role »")),
|
|
374
|
+
show_installable && h5({ class: "mt-2" }, req.__("Available themes")),
|
|
375
|
+
show_installable &&
|
|
376
|
+
div(
|
|
377
|
+
themes_available
|
|
378
|
+
.map((p) => span({ class: "badge bg-secondary" }, p.name))
|
|
379
|
+
.join(" ")
|
|
380
|
+
),
|
|
381
|
+
show_installable &&
|
|
382
|
+
a(
|
|
383
|
+
{ href: `/plugins?set=themes`, class: "mt-2" },
|
|
384
|
+
req.__("Install more themes »")
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
};
|
|
317
388
|
/**
|
|
318
389
|
* Help Card
|
|
319
390
|
* @param req
|
|
@@ -412,10 +483,12 @@ const welcome_page = async (req) => {
|
|
|
412
483
|
users.length > 4
|
|
413
484
|
? {
|
|
414
485
|
Users: await usersTab(req, users, roleMap),
|
|
486
|
+
Theme: themeCard(req, roleMap),
|
|
415
487
|
Help: helpCard(req),
|
|
416
488
|
}
|
|
417
489
|
: {
|
|
418
490
|
Help: helpCard(req),
|
|
491
|
+
Theme: themeCard(req, roleMap),
|
|
419
492
|
Users: await usersTab(req, users, roleMap),
|
|
420
493
|
},
|
|
421
494
|
},
|
package/routes/menu.js
CHANGED
|
@@ -307,7 +307,16 @@ const menuForm = async (req) => {
|
|
|
307
307
|
name: "location",
|
|
308
308
|
label: req.__("Location"),
|
|
309
309
|
showIf: {
|
|
310
|
-
type: [
|
|
310
|
+
type: [
|
|
311
|
+
"View",
|
|
312
|
+
"Page",
|
|
313
|
+
"Link",
|
|
314
|
+
"Header",
|
|
315
|
+
"Dynamic",
|
|
316
|
+
"Search",
|
|
317
|
+
"Separator",
|
|
318
|
+
"Action",
|
|
319
|
+
],
|
|
311
320
|
},
|
|
312
321
|
sublabel: req.__("Not all themes support all locations"),
|
|
313
322
|
class: "item-menu",
|
|
@@ -317,7 +326,7 @@ const menuForm = async (req) => {
|
|
|
317
326
|
//default: "Standard",
|
|
318
327
|
attributes: {
|
|
319
328
|
inline: true,
|
|
320
|
-
options: "Standard, Mobile Bottom",
|
|
329
|
+
options: "Standard, Mobile Bottom, Secondary Menu",
|
|
321
330
|
},
|
|
322
331
|
},
|
|
323
332
|
],
|
package/routes/pageedit.js
CHANGED
|
@@ -167,7 +167,9 @@ const pageBuilderData = async (req, context) => {
|
|
|
167
167
|
for (const name of actions) {
|
|
168
168
|
const action = stateActions[name];
|
|
169
169
|
if (action && action.configFields) {
|
|
170
|
-
actionConfigForms[name] = await getActionConfigFields(action
|
|
170
|
+
actionConfigForms[name] = await getActionConfigFields(action, null, {
|
|
171
|
+
mode: "page",
|
|
172
|
+
});
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
const library = (await Library.find({})).filter((l) => l.suitableFor("page"));
|
|
@@ -214,9 +216,11 @@ const pageBuilderData = async (req, context) => {
|
|
|
214
216
|
images,
|
|
215
217
|
pages,
|
|
216
218
|
actions,
|
|
219
|
+
builtInActions: ["GoBack"],
|
|
217
220
|
library,
|
|
218
221
|
min_role: context.min_role,
|
|
219
222
|
actionConfigForms,
|
|
223
|
+
allowMultiStepAction: true,
|
|
220
224
|
page_name: context.name,
|
|
221
225
|
page_id: context.id,
|
|
222
226
|
mode: "page",
|
package/routes/plugins.js
CHANGED
|
@@ -1243,6 +1243,8 @@ router.post(
|
|
|
1243
1243
|
}
|
|
1244
1244
|
await load_plugins.loadAndSaveNewPlugin(plugin, forceReInstall);
|
|
1245
1245
|
const plugin_module = getState().plugins[name];
|
|
1246
|
+
await getState().refresh_views();
|
|
1247
|
+
|
|
1246
1248
|
if (plugin_module && plugin_module.configuration_workflow) {
|
|
1247
1249
|
const plugin_db = await Plugin.findOne({ name });
|
|
1248
1250
|
req.flash(
|
package/routes/view.js
CHANGED
|
@@ -10,7 +10,7 @@ const View = require("@saltcorn/data/models/view");
|
|
|
10
10
|
const Table = require("@saltcorn/data/models/table");
|
|
11
11
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
12
12
|
|
|
13
|
-
const { text, style } = require("@saltcorn/markup/tags");
|
|
13
|
+
const { text, style, div } = require("@saltcorn/markup/tags");
|
|
14
14
|
const {
|
|
15
15
|
isAdmin,
|
|
16
16
|
error_catcher,
|
|
@@ -70,11 +70,11 @@ router.get(
|
|
|
70
70
|
}
|
|
71
71
|
const isModal = req.headers?.saltcornmodalrequest;
|
|
72
72
|
|
|
73
|
-
const
|
|
73
|
+
const contents0 = await view.run_possibly_on_page(query, req, res);
|
|
74
74
|
const title =
|
|
75
75
|
isModal && view.attributes?.popup_title
|
|
76
76
|
? view.attributes?.popup_title
|
|
77
|
-
: scan_for_page_title(
|
|
77
|
+
: scan_for_page_title(contents0, view.name);
|
|
78
78
|
if (isModal && view.attributes?.popup_width)
|
|
79
79
|
res.set(
|
|
80
80
|
"SaltcornModalWidth",
|
|
@@ -95,9 +95,21 @@ router.get(
|
|
|
95
95
|
name: viewname,
|
|
96
96
|
render_time: ms,
|
|
97
97
|
});
|
|
98
|
-
if (typeof
|
|
99
|
-
res.redirect(
|
|
100
|
-
else
|
|
98
|
+
if (typeof contents0 === "object" && contents0.goto)
|
|
99
|
+
res.redirect(contents0.goto);
|
|
100
|
+
else {
|
|
101
|
+
const qs = "";
|
|
102
|
+
const contents =
|
|
103
|
+
typeof contents0 === "string" && !req.xhr
|
|
104
|
+
? div(
|
|
105
|
+
{
|
|
106
|
+
class: "d-inline",
|
|
107
|
+
"data-sc-embed-viewname": view.name,
|
|
108
|
+
"data-sc-view-source": req.originalUrl,
|
|
109
|
+
},
|
|
110
|
+
contents0
|
|
111
|
+
)
|
|
112
|
+
: contents0;
|
|
101
113
|
res.sendWrap(
|
|
102
114
|
title,
|
|
103
115
|
add_edit_bar({
|
|
@@ -108,10 +120,12 @@ router.get(
|
|
|
108
120
|
cfgUrl: `/viewedit/config/${encodeURIComponent(view.name)}`,
|
|
109
121
|
contents,
|
|
110
122
|
req,
|
|
123
|
+
view,
|
|
111
124
|
viewtemplate: view.viewtemplate,
|
|
112
125
|
table: view.table_id || view.exttable_name,
|
|
113
126
|
})
|
|
114
127
|
);
|
|
128
|
+
}
|
|
115
129
|
})
|
|
116
130
|
);
|
|
117
131
|
|
package/routes/viewedit.js
CHANGED
|
@@ -118,6 +118,9 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
118
118
|
.filter(([k, v]) => !v.tableless)
|
|
119
119
|
.map(([k, v]) => k);
|
|
120
120
|
const slugOptions = await Table.allSlugOptions();
|
|
121
|
+
const viewpatternOptions = Object.values(getState().viewtemplates)
|
|
122
|
+
.filter((vt) => !vt.singleton)
|
|
123
|
+
.map((vt) => vt.name);
|
|
121
124
|
return new Form({
|
|
122
125
|
action: addOnDoneRedirect("/viewedit/save", req),
|
|
123
126
|
submitLabel: req.__("Configure") + " »",
|
|
@@ -151,7 +154,7 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
151
154
|
topic: "View patterns",
|
|
152
155
|
context: {},
|
|
153
156
|
},
|
|
154
|
-
options:
|
|
157
|
+
options: viewpatternOptions,
|
|
155
158
|
attributes: {
|
|
156
159
|
explainers: mapObjectValues(
|
|
157
160
|
getState().viewtemplates,
|
|
@@ -661,11 +664,11 @@ router.post(
|
|
|
661
664
|
* @function
|
|
662
665
|
*/
|
|
663
666
|
router.post(
|
|
664
|
-
"/add-to-menu/:
|
|
667
|
+
"/add-to-menu/:viewname",
|
|
665
668
|
isAdmin,
|
|
666
669
|
error_catcher(async (req, res) => {
|
|
667
|
-
const {
|
|
668
|
-
const view = await View.findOne({
|
|
670
|
+
const { viewname } = req.params;
|
|
671
|
+
const view = await View.findOne({ name: viewname });
|
|
669
672
|
await add_to_menu({
|
|
670
673
|
label: view.name,
|
|
671
674
|
type: "View",
|
package/tests/view.test.js
CHANGED
|
@@ -643,4 +643,43 @@ describe("many to many relations", () => {
|
|
|
643
643
|
.expect(toInclude("album A"))
|
|
644
644
|
.expect(toNotInclude("album B"));
|
|
645
645
|
});
|
|
646
|
+
|
|
647
|
+
it("fan_club feed with query", async () => {
|
|
648
|
+
const app = await getApp({ disableCsrf: true });
|
|
649
|
+
const loginCookie = await getAdminLoginCookie();
|
|
650
|
+
|
|
651
|
+
const queryObj_1 = {
|
|
652
|
+
relation:
|
|
653
|
+
".pressing_job.album.artist_plays_on_album$album.artist.fan_club$artist",
|
|
654
|
+
srcId: 1,
|
|
655
|
+
};
|
|
656
|
+
await request(app)
|
|
657
|
+
.get(
|
|
658
|
+
`/view/fan_club_feed?_inbound_relation_path_=${encodeURIComponent(
|
|
659
|
+
JSON.stringify(queryObj_1)
|
|
660
|
+
)}`
|
|
661
|
+
)
|
|
662
|
+
.set("Cookie", loginCookie)
|
|
663
|
+
.expect(toInclude("crazy fan club"))
|
|
664
|
+
.expect(toInclude("another club"))
|
|
665
|
+
.expect(toInclude("fan club"))
|
|
666
|
+
.expect(toInclude("fan club official"));
|
|
667
|
+
|
|
668
|
+
const queryObj_2 = {
|
|
669
|
+
relation:
|
|
670
|
+
".pressing_job.album.artist_plays_on_album$album.artist.fan_club$artist",
|
|
671
|
+
srcId: 2,
|
|
672
|
+
};
|
|
673
|
+
await request(app)
|
|
674
|
+
.get(
|
|
675
|
+
`/view/fan_club_feed?_inbound_relation_path_=${encodeURIComponent(
|
|
676
|
+
JSON.stringify(queryObj_2)
|
|
677
|
+
)}`
|
|
678
|
+
)
|
|
679
|
+
.set("Cookie", loginCookie)
|
|
680
|
+
.expect(toInclude("crazy fan club"))
|
|
681
|
+
.expect(toNotInclude("another club"))
|
|
682
|
+
.expect(toInclude("fan club"))
|
|
683
|
+
.expect(toInclude("fan club official"));
|
|
684
|
+
});
|
|
646
685
|
});
|