@saltcorn/server 0.7.4-beta.1 → 0.7.4-beta.3

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/routes/tags.js ADDED
@@ -0,0 +1,266 @@
1
+ const { a, text } = require("@saltcorn/markup/tags");
2
+
3
+ const Tag = require("@saltcorn/data/models/tag");
4
+ const Router = require("express-promise-router");
5
+ const Form = require("@saltcorn/data/models/form");
6
+ const User = require("@saltcorn/data/models/user");
7
+
8
+ const { isAdmin, error_catcher, csrfField } = require("./utils");
9
+ const { send_infoarch_page } = require("../markup/admin");
10
+
11
+ const {
12
+ mkTable,
13
+ post_delete_btn,
14
+ link,
15
+ renderForm,
16
+ } = require("@saltcorn/markup");
17
+
18
+ const {
19
+ tablesList,
20
+ setTableRefs,
21
+ viewsList,
22
+ getPageList,
23
+ getTriggerList,
24
+ } = require("./common_lists");
25
+
26
+ const router = new Router();
27
+ module.exports = router;
28
+
29
+ router.get(
30
+ "/",
31
+ isAdmin,
32
+ error_catcher(async (req, res) => {
33
+ const rows = await Tag.find();
34
+ send_infoarch_page({
35
+ res,
36
+ req,
37
+ active_sub: "Tags",
38
+ contents: [
39
+ mkTable(
40
+ [
41
+ {
42
+ label: req.__("Tagname"),
43
+ key: (r) =>
44
+ link(`/tag/${r.id || r.name}?show_list=tables`, text(r.name)),
45
+ },
46
+ {
47
+ label: req.__("Delete"),
48
+ key: (r) => post_delete_btn(`/tag/delete/${r.id}`, req, r.name),
49
+ },
50
+ ],
51
+ rows,
52
+ {}
53
+ ),
54
+ a(
55
+ {
56
+ href: `/tag/new`,
57
+ class: "btn btn-primary",
58
+ },
59
+ req.__("Create tag")
60
+ ),
61
+ ],
62
+ });
63
+ })
64
+ );
65
+
66
+ router.get(
67
+ "/new",
68
+ isAdmin,
69
+ error_catcher(async (req, res) => {
70
+ res.sendWrap(req.__(`New tag`), {
71
+ above: [
72
+ {
73
+ type: "card",
74
+ title: req.__(`New tag`),
75
+ contents: renderForm(
76
+ new Form({
77
+ action: "/tag",
78
+ submitLabel: req.__("Create"),
79
+ fields: [
80
+ {
81
+ label: req.__("Tag name"),
82
+ name: "name",
83
+ input_type: "text",
84
+ required: true,
85
+ },
86
+ ],
87
+ }),
88
+ req.csrfToken()
89
+ ),
90
+ },
91
+ ],
92
+ });
93
+ })
94
+ );
95
+
96
+ const headerWithCollapser = (title, cardId, showList) =>
97
+ a(
98
+ {
99
+ class: `card-header-left-collapse ${!showList ? "collapsed" : ""} ps-3`,
100
+ "data-bs-toggle": "collapse",
101
+ href: `#${cardId}`,
102
+ "aria-expanded": "false",
103
+ "aria-controls": cardId,
104
+ role: "button",
105
+ },
106
+ title
107
+ );
108
+
109
+ const isShowList = (showList, listType) => showList === listType;
110
+
111
+ router.get(
112
+ "/:idorname",
113
+ isAdmin,
114
+ error_catcher(async (req, res) => {
115
+ const { idorname } = req.params;
116
+ const { show_list } = req.query;
117
+ const id = parseInt(idorname);
118
+ const tag = await Tag.findOne(id ? { id } : { name: idorname });
119
+ if (!tag) {
120
+ req.flash("error", req.__("Tag not found"));
121
+ return res.redirect(`/tag`);
122
+ }
123
+ const tables = await tag.getTables();
124
+ const views = await tag.getViews();
125
+ await setTableRefs(views);
126
+ const pages = await tag.getPages();
127
+ const trigger = await tag.getTrigger();
128
+ const roles = await User.get_roles();
129
+
130
+ const tablesDomId = "tablesListId";
131
+ const viewsDomId = "viewsListId";
132
+ const pagesDomId = "pagesDomId";
133
+ const triggerDomId = "triggerDomId";
134
+ res.sendWrap(req.__("%s Tag", tag.name), {
135
+ above: [
136
+ {
137
+ type: "breadcrumbs",
138
+ crumbs: [{ text: `Tag: ${tag.name}` }],
139
+ },
140
+ {
141
+ type: "card",
142
+ title: headerWithCollapser(
143
+ req.__("Tables"),
144
+ tablesDomId,
145
+ isShowList(show_list, "tables")
146
+ ),
147
+ contents: [
148
+ await tablesList(tables, req, {
149
+ tagId: tag.id,
150
+ domId: tablesDomId,
151
+ showList: isShowList(show_list, "tables"),
152
+ }),
153
+ a(
154
+ {
155
+ href: `/tag-entries/add/tables/${tag.id}`,
156
+ class: "btn btn-primary",
157
+ },
158
+ req.__("Add tables")
159
+ ),
160
+ ],
161
+ },
162
+ {
163
+ type: "card",
164
+ title: headerWithCollapser(
165
+ req.__("Views"),
166
+ viewsDomId,
167
+ isShowList(show_list, "views")
168
+ ),
169
+ contents: [
170
+ await viewsList(views, req, {
171
+ tagId: tag.id,
172
+ domId: viewsDomId,
173
+ showList: isShowList(show_list, "views"),
174
+ }),
175
+ a(
176
+ {
177
+ href: `/tag-entries/add/views/${tag.id}`,
178
+ class: "btn btn-primary",
179
+ },
180
+ req.__("Add views")
181
+ ),
182
+ ],
183
+ },
184
+ {
185
+ type: "card",
186
+ title: headerWithCollapser(
187
+ req.__("Pages"),
188
+ pagesDomId,
189
+ isShowList(show_list, "pages")
190
+ ),
191
+ contents: [
192
+ getPageList(pages, roles, req, {
193
+ tagId: tag.id,
194
+ domId: pagesDomId,
195
+ showList: isShowList(show_list, "pages"),
196
+ }),
197
+ a(
198
+ {
199
+ href: `/tag-entries/add/pages/${tag.id}`,
200
+ class: "btn btn-primary",
201
+ },
202
+ req.__("Add pages")
203
+ ),
204
+ ],
205
+ },
206
+ {
207
+ type: "card",
208
+ bodyId: "collapseTriggerCard",
209
+ title: headerWithCollapser(
210
+ req.__("Trigger"),
211
+ triggerDomId,
212
+ isShowList(show_list, "trigger")
213
+ ),
214
+ contents: [
215
+ getTriggerList(trigger, req, {
216
+ tagId: tag.id,
217
+ domId: triggerDomId,
218
+ showList: isShowList(show_list, "trigger"),
219
+ }),
220
+ a(
221
+ {
222
+ href: `/tag-entries/add/trigger/${tag.id}`,
223
+ class: "btn btn-primary",
224
+ },
225
+ req.__("Add triggers")
226
+ ),
227
+ ],
228
+ },
229
+ ],
230
+ });
231
+ })
232
+ );
233
+
234
+ // create
235
+ router.post(
236
+ "/",
237
+ isAdmin,
238
+ error_catcher(async (req, res) => {
239
+ const { name } = req.body;
240
+ const tag = await Tag.create({ name });
241
+ req.flash("success", req.__(`Tag %s created`, name));
242
+ res.redirect(`/tag/${tag.id}?show_list=tables`);
243
+ })
244
+ );
245
+
246
+ // delete
247
+ router.post(
248
+ "/delete/:id",
249
+ isAdmin,
250
+ error_catcher(async (req, res) => {
251
+ const { id } = req.params;
252
+ const tag = await Tag.findOne({ id });
253
+ if (!tag) {
254
+ req.flash("error", req.__("Tag not found"));
255
+ return res.redirect("/tag");
256
+ }
257
+ try {
258
+ await tag.delete();
259
+ req.flash("success", req.__("Tag %s deleted", tag.name));
260
+ res.redirect(`/tag`);
261
+ } catch (error) {
262
+ req.flash("error", error.message);
263
+ res.redirect(`/tag`);
264
+ }
265
+ })
266
+ );
@@ -31,6 +31,7 @@ const {
31
31
 
32
32
  const { getState } = require("@saltcorn/data/db/state");
33
33
  const { isAdmin, error_catcher } = require("./utils.js");
34
+ const { setTableRefs, viewsList } = require("./common_lists");
34
35
  const Form = require("@saltcorn/data/models/form");
35
36
  const Field = require("@saltcorn/data/models/field");
36
37
  const Table = require("@saltcorn/data/models/table");
@@ -38,10 +39,10 @@ const View = require("@saltcorn/data/models/view");
38
39
  const Workflow = require("@saltcorn/data/models/workflow");
39
40
  const User = require("@saltcorn/data/models/user");
40
41
  const Page = require("@saltcorn/data/models/page");
42
+ const Tag = require("@saltcorn/data/models/tag");
41
43
  const db = require("@saltcorn/data/db");
42
44
 
43
45
  const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
44
- const { editRoleForm } = require("../markup/forms.js");
45
46
 
46
47
  /**
47
48
  * @type {object}
@@ -53,68 +54,6 @@ const { editRoleForm } = require("../markup/forms.js");
53
54
  const router = new Router();
54
55
  module.exports = router;
55
56
 
56
- /**
57
- * @param {object} view
58
- * @param {object[]} roles
59
- * @param {object} req
60
- * @returns {Form}
61
- */
62
- const editViewRoleForm = (view, roles, req) =>
63
- editRoleForm({
64
- url: `/viewedit/setrole/${view.id}`,
65
- current_role: view.min_role,
66
- roles,
67
- req,
68
- });
69
-
70
- /**
71
- * @param {object} view
72
- * @param {object} req
73
- * @returns {div}
74
- */
75
- const view_dropdown = (view, req) =>
76
- settingsDropdown(`dropdownMenuButton${view.id}`, [
77
- a(
78
- {
79
- class: "dropdown-item",
80
- href: `/view/${encodeURIComponent(view.name)}`,
81
- },
82
- '<i class="fas fa-running"></i>&nbsp;' + req.__("Run")
83
- ),
84
- a(
85
- {
86
- class: "dropdown-item",
87
- href: `/viewedit/edit/${encodeURIComponent(view.name)}`,
88
- },
89
- '<i class="fas fa-edit"></i>&nbsp;' + req.__("Edit")
90
- ),
91
- post_dropdown_item(
92
- `/viewedit/add-to-menu/${view.id}`,
93
- '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
94
- req
95
- ),
96
- post_dropdown_item(
97
- `/viewedit/clone/${view.id}`,
98
- '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
99
- req
100
- ),
101
- a(
102
- {
103
- class: "dropdown-item",
104
- href: `javascript:ajax_modal('/admin/snapshot-restore/view/${view.name}')`,
105
- },
106
- '<i class="fas fa-undo-alt"></i>&nbsp;' + req.__("Restore")
107
- ),
108
- div({ class: "dropdown-divider" }),
109
- post_dropdown_item(
110
- `/viewedit/delete/${view.id}`,
111
- '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
112
- req,
113
- true,
114
- view.name
115
- ),
116
- ]);
117
-
118
57
  /**
119
58
  * @name get
120
59
  * @function
@@ -125,77 +64,18 @@ router.get(
125
64
  "/",
126
65
  isAdmin,
127
66
  error_catcher(async (req, res) => {
128
- var orderBy = "name";
67
+ let orderBy = "name";
129
68
  if (req.query._sortby === "viewtemplate") orderBy = "viewtemplate";
69
+ const views = await View.find({}, { orderBy, nocase: true });
70
+ await setTableRefs(views);
130
71
 
131
- var views = await View.find({}, { orderBy, nocase: true });
132
- const tables = await Table.find();
133
- const getTable = (tid) => tables.find((t) => t.id === tid).name;
134
- views.forEach((v) => {
135
- if (v.table_id) v.table = getTable(v.table_id);
136
- else if (v.exttable_name) v.table = v.exttable_name;
137
- else v.table = "";
138
- });
139
72
  if (req.query._sortby === "table")
140
73
  views.sort((a, b) =>
141
74
  a.table.toLowerCase() > b.table.toLowerCase() ? 1 : -1
142
75
  );
143
- const roles = await User.get_roles();
144
76
 
145
- const viewMarkup =
146
- views.length > 0
147
- ? mkTable(
148
- [
149
- {
150
- label: req.__("Name"),
151
- key: (r) => link(`/view/${encodeURIComponent(r.name)}`, r.name),
152
- sortlink: `javascript:set_state_field('_sortby', 'name')`,
153
- },
154
- // description - currently I dont want to show description in view list
155
- // because description can be long
156
- /*
157
- {
158
- label: req.__("Description"),
159
- key: "description",
160
- // this is sorting by column
161
- sortlink: `javascript:set_state_field('_sortby', 'description')`,
162
- },
163
- */
164
- // template
165
- {
166
- label: req.__("Pattern"),
167
- key: "viewtemplate",
168
- sortlink: `javascript:set_state_field('_sortby', 'viewtemplate')`,
169
- },
170
- {
171
- label: req.__("Table"),
172
- key: (r) => link(`/table/${r.table}`, r.table),
173
- sortlink: `javascript:set_state_field('_sortby', 'table')`,
174
- },
175
- {
176
- label: req.__("Role to access"),
177
- key: (row) => editViewRoleForm(row, roles, req),
178
- },
179
- {
180
- label: "",
181
- key: (r) =>
182
- link(
183
- `/viewedit/config/${encodeURIComponent(r.name)}`,
184
- req.__("Configure")
185
- ),
186
- },
187
- {
188
- label: "",
189
- key: (r) => view_dropdown(r, req),
190
- },
191
- ],
192
- views,
193
- { hover: true }
194
- )
195
- : div(
196
- h4(req.__("No views defined")),
197
- p(req.__("Views define how table rows are displayed to the user"))
198
- );
77
+ const viewMarkup = await viewsList(views, req);
78
+ const tables = await Table.find();
199
79
  res.sendWrap(req.__(`Views`), {
200
80
  above: [
201
81
  {
@@ -210,14 +90,14 @@ router.get(
210
90
  viewMarkup,
211
91
  tables.length > 0
212
92
  ? a(
213
- { href: `/viewedit/new`, class: "btn btn-primary" },
214
- req.__("Create view")
215
- )
216
- : p(
217
- req.__(
218
- "You must create at least one table before you can create views."
93
+ { href: `/viewedit/new`, class: "btn btn-primary" },
94
+ req.__("Create view")
219
95
  )
220
- ),
96
+ : p(
97
+ req.__(
98
+ "You must create at least one table before you can create views."
99
+ )
100
+ ),
221
101
  ],
222
102
  },
223
103
  ],
@@ -273,7 +153,9 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
273
153
  label: req.__("View pattern"),
274
154
  name: "viewtemplate",
275
155
  input_type: "select",
276
- sublabel: req.__("The view pattern sets the foundation of how the view relates to the table and the behaviour of the view"),
156
+ sublabel: req.__(
157
+ "The view pattern sets the foundation of how the view relates to the table and the behaviour of the view"
158
+ ),
277
159
  options: Object.keys(getState().viewtemplates),
278
160
  attributes: {
279
161
  explainers: mapObjectValues(
@@ -362,7 +244,8 @@ router.get(
362
244
  }
363
245
  const tables = await Table.find_with_external();
364
246
  const currentTable = tables.find(
365
- (t) => (t.id && t.id === viewrow.table_id) || t.name === viewrow.exttable_name
247
+ (t) =>
248
+ (t.id && t.id === viewrow.table_id) || t.name === viewrow.exttable_name
366
249
  );
367
250
  viewrow.table_name = currentTable && currentTable.name;
368
251
  if (viewrow.slug && currentTable) {
@@ -146,6 +146,15 @@ describe("pageedit", () => {
146
146
  .send("id=1")
147
147
  .expect(toRedirect("/pageedit/"));
148
148
  });
149
+ it("show builder", async () => {
150
+ const app = await getApp({ disableCsrf: true });
151
+ const loginCookie = await getAdminLoginCookie();
152
+ await request(app)
153
+ .get("/pageedit/edit/a_page")
154
+ .set("Cookie", loginCookie)
155
+ .expect(toInclude("<script>builder.renderBuilder"));
156
+
157
+ });
149
158
 
150
159
  it("sets root page", async () => {
151
160
  const app = await getApp({ disableCsrf: true });
@@ -43,6 +43,22 @@ describe("viewedit edit endpoint", () => {
43
43
  .set("Cookie", loginCookie)
44
44
  .expect(toInclude("author"));
45
45
  });
46
+ it("show list editor", async () => {
47
+ const loginCookie = await getAdminLoginCookie();
48
+ const app = await getApp({ disableCsrf: true });
49
+ await request(app)
50
+ .get("/viewedit/config/authorlist")
51
+ .set("Cookie", loginCookie)
52
+ .expect(toInclude("author"));
53
+ });
54
+ it("show builder", async () => {
55
+ const loginCookie = await getAdminLoginCookie();
56
+ const app = await getApp({ disableCsrf: true });
57
+ await request(app)
58
+ .get("/viewedit/config/authorshow")
59
+ .set("Cookie", loginCookie)
60
+ .expect(toInclude("<script>builder.renderBuilder"));
61
+ });
46
62
  });
47
63
 
48
64
  describe("viewedit new List", () => {