@saltcorn/server 0.9.0-beta.0 → 0.9.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +58 -6
- package/auth/routes.js +16 -20
- package/errors.js +15 -4
- package/help/Actions.tmd +9 -0
- package/help/Extra state formula.tmd +62 -0
- package/help/Field views.tmd +22 -0
- package/help/JavaScript action code.tmd +161 -0
- package/help/Table formula constraint.tmd +6 -4
- package/help/View patterns.tmd +35 -0
- package/help/Where formula.tmd +30 -0
- package/help/index.js +19 -10
- package/load_plugins.js +4 -2
- package/locales/da.json +709 -709
- package/locales/de.json +1049 -1049
- package/locales/en.json +18 -2
- package/locales/pl.json +1155 -1155
- package/locales/ru.json +1101 -1101
- package/locales/si.json +1196 -1196
- package/locales/uk.json +1168 -1168
- package/locales/zh.json +886 -886
- package/package.json +10 -9
- package/public/saltcorn-builder.css +4 -0
- package/public/saltcorn-common.js +85 -17
- package/public/saltcorn.css +14 -0
- package/public/saltcorn.js +33 -7
- package/routes/actions.js +5 -3
- package/routes/admin.js +146 -20
- package/routes/fields.js +15 -3
- package/routes/menu.js +1 -1
- package/routes/packs.js +134 -9
- package/routes/plugins.js +186 -36
- package/routes/sync.js +4 -1
- package/routes/tables.js +4 -3
- package/routes/viewedit.js +21 -1
- package/tests/admin.test.js +2 -2
- package/tests/sync.test.js +140 -6
package/routes/admin.js
CHANGED
|
@@ -285,6 +285,9 @@ router.get(
|
|
|
285
285
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
286
286
|
"auto_backup_expire_days"
|
|
287
287
|
);
|
|
288
|
+
backupForm.values.backup_with_event_log = getState().getConfig(
|
|
289
|
+
"backup_with_event_log"
|
|
290
|
+
);
|
|
288
291
|
//
|
|
289
292
|
const aSnapshotForm = snapshotForm(req);
|
|
290
293
|
aSnapshotForm.values.snapshots_enabled =
|
|
@@ -721,6 +724,15 @@ const autoBackupForm = (req) =>
|
|
|
721
724
|
auto_backup_destination: "Local directory",
|
|
722
725
|
},
|
|
723
726
|
},
|
|
727
|
+
{
|
|
728
|
+
type: "Bool",
|
|
729
|
+
label: req.__("Include Event Logs"),
|
|
730
|
+
sublabel: req.__("Backup with event logs"),
|
|
731
|
+
name: "backup_with_event_log",
|
|
732
|
+
showIf: {
|
|
733
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
734
|
+
},
|
|
735
|
+
},
|
|
724
736
|
],
|
|
725
737
|
});
|
|
726
738
|
|
|
@@ -1514,6 +1526,8 @@ router.get(
|
|
|
1514
1526
|
const plugins = (await Plugin.find()).filter(
|
|
1515
1527
|
(plugin) => ["base", "sbadmin2"].indexOf(plugin.name) < 0
|
|
1516
1528
|
);
|
|
1529
|
+
const builderSettings =
|
|
1530
|
+
getState().getConfig("mobile_builder_settings") || {};
|
|
1517
1531
|
send_admin_page({
|
|
1518
1532
|
res,
|
|
1519
1533
|
req,
|
|
@@ -1543,7 +1557,7 @@ router.get(
|
|
|
1543
1557
|
input({
|
|
1544
1558
|
type: "hidden",
|
|
1545
1559
|
name: "entryPointType",
|
|
1546
|
-
value: "view",
|
|
1560
|
+
value: builderSettings.entryPointType || "view",
|
|
1547
1561
|
id: "entryPointTypeID",
|
|
1548
1562
|
}),
|
|
1549
1563
|
div(
|
|
@@ -1574,7 +1588,15 @@ router.get(
|
|
|
1574
1588
|
onClick: "showEntrySelect('view')",
|
|
1575
1589
|
},
|
|
1576
1590
|
div(
|
|
1577
|
-
{
|
|
1591
|
+
{
|
|
1592
|
+
class: `nav-link ${
|
|
1593
|
+
!builderSettings.entryPointType ||
|
|
1594
|
+
builderSettings.entryPointType === "view"
|
|
1595
|
+
? "active"
|
|
1596
|
+
: ""
|
|
1597
|
+
}`,
|
|
1598
|
+
id: "viewNavLinkID",
|
|
1599
|
+
},
|
|
1578
1600
|
req.__("View")
|
|
1579
1601
|
)
|
|
1580
1602
|
),
|
|
@@ -1584,7 +1606,14 @@ router.get(
|
|
|
1584
1606
|
onClick: "showEntrySelect('page')",
|
|
1585
1607
|
},
|
|
1586
1608
|
div(
|
|
1587
|
-
{
|
|
1609
|
+
{
|
|
1610
|
+
class: `nav-link ${
|
|
1611
|
+
builderSettings.entryPointType === "page"
|
|
1612
|
+
? "active"
|
|
1613
|
+
: ""
|
|
1614
|
+
}`,
|
|
1615
|
+
id: "pageNavLinkID",
|
|
1616
|
+
},
|
|
1588
1617
|
req.__("Page")
|
|
1589
1618
|
)
|
|
1590
1619
|
)
|
|
@@ -1592,25 +1621,56 @@ router.get(
|
|
|
1592
1621
|
// select entry-view
|
|
1593
1622
|
select(
|
|
1594
1623
|
{
|
|
1595
|
-
class:
|
|
1596
|
-
|
|
1624
|
+
class: `form-select ${
|
|
1625
|
+
builderSettings.entryPointType === "page"
|
|
1626
|
+
? "d-none"
|
|
1627
|
+
: ""
|
|
1628
|
+
}`,
|
|
1629
|
+
...(!builderSettings.entryPointType ||
|
|
1630
|
+
builderSettings.entryPointType === "view"
|
|
1631
|
+
? { name: "entryPoint" }
|
|
1632
|
+
: {}),
|
|
1597
1633
|
id: "viewInputID",
|
|
1598
1634
|
},
|
|
1599
1635
|
views
|
|
1600
1636
|
.map((view) =>
|
|
1601
|
-
option(
|
|
1637
|
+
option(
|
|
1638
|
+
{
|
|
1639
|
+
value: view.name,
|
|
1640
|
+
selected:
|
|
1641
|
+
builderSettings.entryPointType === "view" &&
|
|
1642
|
+
builderSettings.entryPoint === view.name,
|
|
1643
|
+
},
|
|
1644
|
+
view.name
|
|
1645
|
+
)
|
|
1602
1646
|
)
|
|
1603
1647
|
.join(",")
|
|
1604
1648
|
),
|
|
1605
1649
|
// select entry-page
|
|
1606
1650
|
select(
|
|
1607
1651
|
{
|
|
1608
|
-
class:
|
|
1652
|
+
class: `form-select ${
|
|
1653
|
+
!builderSettings.entryPointType ||
|
|
1654
|
+
builderSettings.entryPointType === "view"
|
|
1655
|
+
? "d-none"
|
|
1656
|
+
: ""
|
|
1657
|
+
}`,
|
|
1658
|
+
...(builderSettings.entryPointType === "page"
|
|
1659
|
+
? { name: "entryPoint" }
|
|
1660
|
+
: {}),
|
|
1609
1661
|
id: "pageInputID",
|
|
1610
1662
|
},
|
|
1611
1663
|
pages
|
|
1612
1664
|
.map((page) =>
|
|
1613
|
-
option(
|
|
1665
|
+
option(
|
|
1666
|
+
{
|
|
1667
|
+
value: page.name,
|
|
1668
|
+
selected:
|
|
1669
|
+
builderSettings.entryPointType === "page" &&
|
|
1670
|
+
builderSettings.entryPoint === page.name,
|
|
1671
|
+
},
|
|
1672
|
+
page.name
|
|
1673
|
+
)
|
|
1614
1674
|
)
|
|
1615
1675
|
.join("")
|
|
1616
1676
|
)
|
|
@@ -1631,6 +1691,7 @@ router.get(
|
|
|
1631
1691
|
name: "androidPlatform",
|
|
1632
1692
|
id: "androidCheckboxId",
|
|
1633
1693
|
onClick: "toggle_android_platform()",
|
|
1694
|
+
checked: builderSettings.androidPlatform === "on",
|
|
1634
1695
|
})
|
|
1635
1696
|
)
|
|
1636
1697
|
),
|
|
@@ -1645,6 +1706,7 @@ router.get(
|
|
|
1645
1706
|
class: "form-check-input",
|
|
1646
1707
|
name: "iOSPlatform",
|
|
1647
1708
|
id: "iOSCheckboxId",
|
|
1709
|
+
checked: builderSettings.iOSPlatform === "on",
|
|
1648
1710
|
})
|
|
1649
1711
|
)
|
|
1650
1712
|
)
|
|
@@ -1658,7 +1720,8 @@ router.get(
|
|
|
1658
1720
|
class: "form-check-input",
|
|
1659
1721
|
name: "useDocker",
|
|
1660
1722
|
id: "dockerCheckboxId",
|
|
1661
|
-
hidden:
|
|
1723
|
+
hidden: builderSettings.androidPlatform !== "on",
|
|
1724
|
+
checked: builderSettings.useDocker === "on",
|
|
1662
1725
|
})
|
|
1663
1726
|
)
|
|
1664
1727
|
),
|
|
@@ -1680,6 +1743,7 @@ router.get(
|
|
|
1680
1743
|
name: "appName",
|
|
1681
1744
|
id: "appNameInputId",
|
|
1682
1745
|
placeholder: "SaltcornMobileApp",
|
|
1746
|
+
value: builderSettings.appName || "",
|
|
1683
1747
|
})
|
|
1684
1748
|
)
|
|
1685
1749
|
),
|
|
@@ -1701,6 +1765,7 @@ router.get(
|
|
|
1701
1765
|
name: "appVersion",
|
|
1702
1766
|
id: "appVersionInputId",
|
|
1703
1767
|
placeholder: "1.0.0",
|
|
1768
|
+
value: builderSettings.appVersion || "",
|
|
1704
1769
|
})
|
|
1705
1770
|
)
|
|
1706
1771
|
),
|
|
@@ -1721,6 +1786,7 @@ router.get(
|
|
|
1721
1786
|
class: "form-control",
|
|
1722
1787
|
name: "serverURL",
|
|
1723
1788
|
id: "serverURLInputId",
|
|
1789
|
+
value: builderSettings.serverURL || "",
|
|
1724
1790
|
placeholder: getState().getConfig("base_url") || "",
|
|
1725
1791
|
})
|
|
1726
1792
|
)
|
|
@@ -1746,7 +1812,14 @@ router.get(
|
|
|
1746
1812
|
[
|
|
1747
1813
|
option({ value: "" }, ""),
|
|
1748
1814
|
...images.map((image) =>
|
|
1749
|
-
option(
|
|
1815
|
+
option(
|
|
1816
|
+
{
|
|
1817
|
+
value: image.location,
|
|
1818
|
+
selected:
|
|
1819
|
+
builderSettings.appIcon === image.location,
|
|
1820
|
+
},
|
|
1821
|
+
image.filename
|
|
1822
|
+
)
|
|
1750
1823
|
),
|
|
1751
1824
|
].join("")
|
|
1752
1825
|
)
|
|
@@ -1772,7 +1845,14 @@ router.get(
|
|
|
1772
1845
|
[
|
|
1773
1846
|
option({ value: "" }, ""),
|
|
1774
1847
|
...pages.map((page) =>
|
|
1775
|
-
option(
|
|
1848
|
+
option(
|
|
1849
|
+
{
|
|
1850
|
+
value: page.name,
|
|
1851
|
+
selected:
|
|
1852
|
+
builderSettings.splashPage === page.name,
|
|
1853
|
+
},
|
|
1854
|
+
page.name
|
|
1855
|
+
)
|
|
1776
1856
|
),
|
|
1777
1857
|
].join("")
|
|
1778
1858
|
)
|
|
@@ -1788,8 +1868,7 @@ router.get(
|
|
|
1788
1868
|
id: "autoPublLoginId",
|
|
1789
1869
|
class: "form-check-input me-2",
|
|
1790
1870
|
name: "autoPublicLogin",
|
|
1791
|
-
|
|
1792
|
-
checked: false,
|
|
1871
|
+
checked: builderSettings.autoPublicLogin === "on",
|
|
1793
1872
|
}),
|
|
1794
1873
|
label(
|
|
1795
1874
|
{
|
|
@@ -1810,9 +1889,8 @@ router.get(
|
|
|
1810
1889
|
id: "offlineModeBoxId",
|
|
1811
1890
|
class: "form-check-input me-2",
|
|
1812
1891
|
name: "allowOfflineMode",
|
|
1813
|
-
value: "allowOfflineMode",
|
|
1814
1892
|
onClick: "toggle_tbl_sync()",
|
|
1815
|
-
checked:
|
|
1893
|
+
checked: builderSettings.allowOfflineMode === "on",
|
|
1816
1894
|
}),
|
|
1817
1895
|
label(
|
|
1818
1896
|
{
|
|
@@ -1828,6 +1906,7 @@ router.get(
|
|
|
1828
1906
|
{
|
|
1829
1907
|
id: "tblSyncSelectorId",
|
|
1830
1908
|
class: "row pb-3",
|
|
1909
|
+
hidden: builderSettings.allowOfflineMode !== "on",
|
|
1831
1910
|
},
|
|
1832
1911
|
div(
|
|
1833
1912
|
label(
|
|
@@ -1864,6 +1943,12 @@ router.get(
|
|
|
1864
1943
|
id: `${table.name}_unsynched_opt`,
|
|
1865
1944
|
value: table.name,
|
|
1866
1945
|
label: table.name,
|
|
1946
|
+
hidden:
|
|
1947
|
+
builderSettings.synchedTables?.indexOf(
|
|
1948
|
+
table.name
|
|
1949
|
+
) >= 0
|
|
1950
|
+
? true
|
|
1951
|
+
: false,
|
|
1867
1952
|
})
|
|
1868
1953
|
)
|
|
1869
1954
|
)
|
|
@@ -1908,7 +1993,12 @@ router.get(
|
|
|
1908
1993
|
id: `${table.name}_synched_opt`,
|
|
1909
1994
|
value: table.name,
|
|
1910
1995
|
label: table.name,
|
|
1911
|
-
hidden:
|
|
1996
|
+
hidden:
|
|
1997
|
+
builderSettings.synchedTables?.indexOf(
|
|
1998
|
+
table.name
|
|
1999
|
+
) >= 0
|
|
2000
|
+
? false
|
|
2001
|
+
: true,
|
|
1912
2002
|
})
|
|
1913
2003
|
)
|
|
1914
2004
|
)
|
|
@@ -1954,7 +2044,12 @@ router.get(
|
|
|
1954
2044
|
id: `${plugin.name}_excluded_opt`,
|
|
1955
2045
|
value: plugin.name,
|
|
1956
2046
|
label: plugin.name,
|
|
1957
|
-
hidden:
|
|
2047
|
+
hidden:
|
|
2048
|
+
builderSettings.excludedPlugins?.indexOf(
|
|
2049
|
+
plugin.name
|
|
2050
|
+
) >= 0
|
|
2051
|
+
? false
|
|
2052
|
+
: true,
|
|
1958
2053
|
})
|
|
1959
2054
|
)
|
|
1960
2055
|
)
|
|
@@ -1999,7 +2094,12 @@ router.get(
|
|
|
1999
2094
|
id: `${plugin.name}_included_opt`,
|
|
2000
2095
|
value: plugin.name,
|
|
2001
2096
|
label: plugin.name,
|
|
2002
|
-
|
|
2097
|
+
hidden:
|
|
2098
|
+
builderSettings.excludedPlugins?.indexOf(
|
|
2099
|
+
plugin.name
|
|
2100
|
+
) >= 0
|
|
2101
|
+
? true
|
|
2102
|
+
: false,
|
|
2003
2103
|
})
|
|
2004
2104
|
)
|
|
2005
2105
|
)
|
|
@@ -2116,6 +2216,8 @@ router.post(
|
|
|
2116
2216
|
synchedTables,
|
|
2117
2217
|
includedPlugins,
|
|
2118
2218
|
} = req.body;
|
|
2219
|
+
if (!includedPlugins) includedPlugins = [];
|
|
2220
|
+
if (!synchedTables) synchedTables = [];
|
|
2119
2221
|
if (!androidPlatform && !iOSPlatform) {
|
|
2120
2222
|
return res.json({
|
|
2121
2223
|
error: req.__("Please select at least one platform (android or iOS)."),
|
|
@@ -2167,9 +2269,9 @@ router.post(
|
|
|
2167
2269
|
if (splashPage) spawnParams.push("--splashPage", splashPage);
|
|
2168
2270
|
if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
|
|
2169
2271
|
if (autoPublicLogin) spawnParams.push("--autoPublicLogin");
|
|
2170
|
-
if (synchedTables
|
|
2272
|
+
if (synchedTables.length > 0)
|
|
2171
2273
|
spawnParams.push("--synchedTables", ...synchedTables.map((tbl) => tbl));
|
|
2172
|
-
if (includedPlugins
|
|
2274
|
+
if (includedPlugins.length > 0)
|
|
2173
2275
|
spawnParams.push(
|
|
2174
2276
|
"--includedPlugins",
|
|
2175
2277
|
...includedPlugins.map((pluginName) => pluginName)
|
|
@@ -2180,6 +2282,30 @@ router.post(
|
|
|
2180
2282
|
) {
|
|
2181
2283
|
spawnParams.push("--tenantAppName", db.getTenantSchema());
|
|
2182
2284
|
}
|
|
2285
|
+
const excludedPlugins = (await Plugin.find())
|
|
2286
|
+
.filter(
|
|
2287
|
+
(plugin) =>
|
|
2288
|
+
["base", "sbadmin2"].indexOf(plugin.name) < 0 &&
|
|
2289
|
+
includedPlugins.indexOf(plugin.name) < 0
|
|
2290
|
+
)
|
|
2291
|
+
.map((plugin) => plugin.name);
|
|
2292
|
+
await getState().setConfig("mobile_builder_settings", {
|
|
2293
|
+
entryPoint,
|
|
2294
|
+
entryPointType,
|
|
2295
|
+
androidPlatform,
|
|
2296
|
+
iOSPlatform,
|
|
2297
|
+
useDocker,
|
|
2298
|
+
appName,
|
|
2299
|
+
appVersion,
|
|
2300
|
+
appIcon,
|
|
2301
|
+
serverURL,
|
|
2302
|
+
splashPage,
|
|
2303
|
+
autoPublicLogin,
|
|
2304
|
+
allowOfflineMode,
|
|
2305
|
+
synchedTables: synchedTables,
|
|
2306
|
+
includedPlugins: includedPlugins,
|
|
2307
|
+
excludedPlugins,
|
|
2308
|
+
});
|
|
2183
2309
|
// end http call, return the out directory name
|
|
2184
2310
|
// the gui polls for results
|
|
2185
2311
|
res.json({ build_dir_name: outDirName });
|
package/routes/fields.js
CHANGED
|
@@ -409,7 +409,7 @@ const fieldFlow = (req) =>
|
|
|
409
409
|
instance_options[model.name].push(...instances.map((i) => i.name));
|
|
410
410
|
|
|
411
411
|
const outputs = await applyAsync(
|
|
412
|
-
model.templateObj
|
|
412
|
+
model.templateObj?.prediction_outputs || [], // unit tests can have templateObj undefined
|
|
413
413
|
{ table, configuration: model.configuration }
|
|
414
414
|
);
|
|
415
415
|
output_options[model.name] = outputs.map((o) => o.name);
|
|
@@ -840,6 +840,11 @@ router.post(
|
|
|
840
840
|
const table = Table.findOne({ name: tableName });
|
|
841
841
|
const role = req.user && req.user.id ? req.user.role_id : 100;
|
|
842
842
|
|
|
843
|
+
getState().log(
|
|
844
|
+
5,
|
|
845
|
+
`Route /fields/show-calculated/${tableName}/${fieldName}/${fieldview} user=${req.user?.id}`
|
|
846
|
+
);
|
|
847
|
+
|
|
843
848
|
const fields = table.getFields();
|
|
844
849
|
let row = { ...req.body };
|
|
845
850
|
if (row && Object.keys(row).length > 0) readState(row, fields);
|
|
@@ -1018,6 +1023,13 @@ router.post(
|
|
|
1018
1023
|
const { tableName, fieldName, fieldview } = req.params;
|
|
1019
1024
|
const table = Table.findOne({ name: tableName });
|
|
1020
1025
|
const fields = table.getFields();
|
|
1026
|
+
const state = getState();
|
|
1027
|
+
|
|
1028
|
+
state.log(
|
|
1029
|
+
5,
|
|
1030
|
+
`Route /fields/preview/${tableName}/${fieldName}/${fieldview} user=${req.user?.id}`
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1021
1033
|
let field, row, value;
|
|
1022
1034
|
if (fieldName.includes(".")) {
|
|
1023
1035
|
const [refNm, targetNm] = fieldName.split(".");
|
|
@@ -1048,9 +1060,9 @@ router.post(
|
|
|
1048
1060
|
}
|
|
1049
1061
|
const fieldviews =
|
|
1050
1062
|
field.type === "Key"
|
|
1051
|
-
?
|
|
1063
|
+
? state.keyFieldviews
|
|
1052
1064
|
: field.type === "File"
|
|
1053
|
-
?
|
|
1065
|
+
? state.fileviews
|
|
1054
1066
|
: field.type.fieldviews;
|
|
1055
1067
|
if (!field.type || !fieldviews) {
|
|
1056
1068
|
res.send("");
|
package/routes/menu.js
CHANGED
|
@@ -44,7 +44,7 @@ const menuForm = async (req) => {
|
|
|
44
44
|
const views = await View.find({}, { orderBy: "name", nocase: true });
|
|
45
45
|
const pages = await Page.find({}, { orderBy: "name", nocase: true });
|
|
46
46
|
const roles = await User.get_roles();
|
|
47
|
-
const tables = await Table.
|
|
47
|
+
const tables = await Table.find_with_external({});
|
|
48
48
|
const dynTableOptions = tables.map((t) => t.name);
|
|
49
49
|
const dynOrderFieldOptions = {},
|
|
50
50
|
dynSectionFieldOptions = {};
|
package/routes/packs.js
CHANGED
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
const Router = require("express-promise-router");
|
|
8
8
|
const { isAdmin, error_catcher } = require("./utils.js");
|
|
9
|
-
const {
|
|
10
|
-
const { getState } = require("@saltcorn/data/db/state");
|
|
9
|
+
const { renderForm } = require("@saltcorn/markup");
|
|
11
10
|
const Table = require("@saltcorn/data/models/table");
|
|
12
11
|
const Form = require("@saltcorn/data/models/form");
|
|
13
12
|
const View = require("@saltcorn/data/models/view");
|
|
14
|
-
const Field = require("@saltcorn/data/models/field");
|
|
15
13
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
16
14
|
const Page = require("@saltcorn/data/models/page");
|
|
15
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
16
|
+
const EventLog = require("@saltcorn/data/models/eventlog");
|
|
17
|
+
const Model = require("@saltcorn/data/models/model");
|
|
18
|
+
const ModelInstance = require("@saltcorn/data/models/model_instance");
|
|
17
19
|
const load_plugins = require("../load_plugins");
|
|
18
20
|
|
|
19
21
|
const { is_pack } = require("@saltcorn/data/contracts");
|
|
20
|
-
const { contract, is } = require("contractis");
|
|
21
22
|
const {
|
|
22
23
|
table_pack,
|
|
23
24
|
view_pack,
|
|
@@ -26,15 +27,20 @@ const {
|
|
|
26
27
|
role_pack,
|
|
27
28
|
library_pack,
|
|
28
29
|
trigger_pack,
|
|
30
|
+
tag_pack,
|
|
31
|
+
model_pack,
|
|
32
|
+
model_instance_pack,
|
|
29
33
|
install_pack,
|
|
30
34
|
fetch_pack_by_name,
|
|
31
35
|
can_install_pack,
|
|
32
36
|
uninstall_pack,
|
|
37
|
+
event_log_pack,
|
|
33
38
|
} = require("@saltcorn/admin-models/models/pack");
|
|
34
|
-
const {
|
|
39
|
+
const { pre, code, p, text, text_attr } = require("@saltcorn/markup/tags");
|
|
35
40
|
const Library = require("@saltcorn/data/models/library");
|
|
36
41
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
37
42
|
const Role = require("@saltcorn/data/models/role");
|
|
43
|
+
const fs = require("fs");
|
|
38
44
|
|
|
39
45
|
/**
|
|
40
46
|
* @type {object}
|
|
@@ -98,6 +104,52 @@ router.get(
|
|
|
98
104
|
name: `role.${l.role}`,
|
|
99
105
|
type: "Bool",
|
|
100
106
|
}));
|
|
107
|
+
const tags = await Tag.find({});
|
|
108
|
+
const tagFields = tags.map((t) => ({
|
|
109
|
+
label: `${t.name} tag`,
|
|
110
|
+
name: `tag.${t.name}`,
|
|
111
|
+
type: "Bool",
|
|
112
|
+
}));
|
|
113
|
+
const models = await Model.find({});
|
|
114
|
+
const modelFields = models.map((m) => {
|
|
115
|
+
const modelTbl = Table.findOne({ id: m.table_id });
|
|
116
|
+
return {
|
|
117
|
+
label: `${m.name} model, table: ${
|
|
118
|
+
modelTbl.name || req.__("Table not found")
|
|
119
|
+
}`,
|
|
120
|
+
name: `model.${m.name}.${modelTbl.name}`,
|
|
121
|
+
type: "Bool",
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
const modelInstances = await ModelInstance.find({});
|
|
125
|
+
const modelInstanceFields = (
|
|
126
|
+
await Promise.all(
|
|
127
|
+
modelInstances.map(async (instance) => {
|
|
128
|
+
const model = await Model.findOne({ id: instance.model_id });
|
|
129
|
+
if (!model) {
|
|
130
|
+
req.flash(
|
|
131
|
+
"warning",
|
|
132
|
+
req.__(`Model with '${instance.model_id}' not found`)
|
|
133
|
+
);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const mTable = await Table.findOne({ id: model.table_id });
|
|
137
|
+
if (!mTable) {
|
|
138
|
+
req.flash(
|
|
139
|
+
"warning",
|
|
140
|
+
req.__(`Table of model '${model.name}' not found`)
|
|
141
|
+
);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
label: `${instance.name} model instance, model: ${model.name}, table: ${mTable.name}`,
|
|
146
|
+
name: `model_instance.${instance.name}.${model.name}.${mTable.name}`,
|
|
147
|
+
type: "Bool",
|
|
148
|
+
};
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
).filter((f) => f);
|
|
152
|
+
|
|
101
153
|
const form = new Form({
|
|
102
154
|
action: "/packs/create",
|
|
103
155
|
fields: [
|
|
@@ -108,6 +160,14 @@ router.get(
|
|
|
108
160
|
...trigFields,
|
|
109
161
|
...roleFields,
|
|
110
162
|
...libFields,
|
|
163
|
+
...tagFields,
|
|
164
|
+
...modelFields,
|
|
165
|
+
...modelInstanceFields,
|
|
166
|
+
{
|
|
167
|
+
name: "with_event_logs",
|
|
168
|
+
label: req.__("Include Event Logs"),
|
|
169
|
+
type: "Bool",
|
|
170
|
+
},
|
|
111
171
|
],
|
|
112
172
|
});
|
|
113
173
|
res.sendWrap(req.__(`Create Pack`), {
|
|
@@ -140,7 +200,7 @@ router.post(
|
|
|
140
200
|
"/create",
|
|
141
201
|
isAdmin,
|
|
142
202
|
error_catcher(async (req, res) => {
|
|
143
|
-
|
|
203
|
+
const pack = {
|
|
144
204
|
tables: [],
|
|
145
205
|
views: [],
|
|
146
206
|
plugins: [],
|
|
@@ -148,9 +208,13 @@ router.post(
|
|
|
148
208
|
roles: [],
|
|
149
209
|
library: [],
|
|
150
210
|
triggers: [],
|
|
211
|
+
tags: [],
|
|
212
|
+
models: [],
|
|
213
|
+
model_instances: [],
|
|
214
|
+
event_logs: [],
|
|
151
215
|
};
|
|
152
216
|
for (const k of Object.keys(req.body)) {
|
|
153
|
-
const [type, name] = k.split(".");
|
|
217
|
+
const [type, name, ...rest] = k.split(".");
|
|
154
218
|
switch (type) {
|
|
155
219
|
case "table":
|
|
156
220
|
pack.tables.push(await table_pack(name));
|
|
@@ -173,7 +237,32 @@ router.post(
|
|
|
173
237
|
case "trigger":
|
|
174
238
|
pack.triggers.push(await trigger_pack(name));
|
|
175
239
|
break;
|
|
176
|
-
|
|
240
|
+
case "tag":
|
|
241
|
+
pack.tags.push(await tag_pack(name));
|
|
242
|
+
break;
|
|
243
|
+
case "model": {
|
|
244
|
+
const table = rest[0];
|
|
245
|
+
if (!table) throw new Error(`Table for model '${name}' not found`);
|
|
246
|
+
pack.models.push(await model_pack(name, table));
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case "model_instance": {
|
|
250
|
+
const model = rest[0];
|
|
251
|
+
if (!model)
|
|
252
|
+
throw new Error(`Model of Model Instance '${name}' not found`);
|
|
253
|
+
const table = rest[1];
|
|
254
|
+
if (!table) throw new Error(`Table of Model '${model}' not found`);
|
|
255
|
+
pack.model_instances.push(
|
|
256
|
+
await model_instance_pack(name, model, table)
|
|
257
|
+
);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case "with_event_logs":
|
|
261
|
+
const logs = await EventLog.find({});
|
|
262
|
+
pack.event_logs = await Promise.all(
|
|
263
|
+
logs.map(async (l) => await event_log_pack(l))
|
|
264
|
+
);
|
|
265
|
+
break;
|
|
177
266
|
default:
|
|
178
267
|
break;
|
|
179
268
|
}
|
|
@@ -217,11 +306,33 @@ const install_pack_form = (req) =>
|
|
|
217
306
|
action: "/packs/install",
|
|
218
307
|
submitLabel: req.__("Install"),
|
|
219
308
|
fields: [
|
|
309
|
+
{
|
|
310
|
+
name: "source",
|
|
311
|
+
label: req.__("Source"),
|
|
312
|
+
type: "String",
|
|
313
|
+
attributes: {
|
|
314
|
+
options: [
|
|
315
|
+
{ label: "from text", name: "from_text" },
|
|
316
|
+
{ label: "from file", name: "from_file" },
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
default: "from_text",
|
|
320
|
+
required: true,
|
|
321
|
+
},
|
|
220
322
|
{
|
|
221
323
|
name: "pack",
|
|
222
324
|
label: req.__("Pack"),
|
|
223
325
|
type: "String",
|
|
224
326
|
fieldview: "textarea",
|
|
327
|
+
showIf: { source: "from_text" },
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "pack_file",
|
|
331
|
+
label: req.__("Pack file"),
|
|
332
|
+
class: "form-control",
|
|
333
|
+
type: "File",
|
|
334
|
+
sublabel: req.__("Upload a pack file"),
|
|
335
|
+
showIf: { source: "from_file" },
|
|
225
336
|
},
|
|
226
337
|
],
|
|
227
338
|
});
|
|
@@ -267,8 +378,22 @@ router.post(
|
|
|
267
378
|
isAdmin,
|
|
268
379
|
error_catcher(async (req, res) => {
|
|
269
380
|
var pack, error;
|
|
381
|
+
const source = req.body.source || "from_text";
|
|
270
382
|
try {
|
|
271
|
-
|
|
383
|
+
switch (source) {
|
|
384
|
+
case "from_text":
|
|
385
|
+
pack = JSON.parse(req.body.pack);
|
|
386
|
+
break;
|
|
387
|
+
case "from_file":
|
|
388
|
+
if (req.files?.pack_file?.tempFilePath)
|
|
389
|
+
pack = JSON.parse(
|
|
390
|
+
fs.readFileSync(req.files?.pack_file?.tempFilePath)
|
|
391
|
+
);
|
|
392
|
+
else throw new Error(req.__("No file uploaded"));
|
|
393
|
+
break;
|
|
394
|
+
default:
|
|
395
|
+
throw new Error(req.__("Invalid source"));
|
|
396
|
+
}
|
|
272
397
|
} catch (e) {
|
|
273
398
|
error = e.message;
|
|
274
399
|
}
|