@saltcorn/server 0.7.4-beta.3 → 0.8.0-beta.0
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 +43 -22
- package/auth/admin.js +173 -74
- package/auth/routes.js +67 -28
- package/locales/en.json +54 -2
- package/locales/es.json +134 -134
- package/locales/ru.json +32 -5
- package/markup/admin.js +40 -38
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- package/public/diagram_utils.js +530 -0
- package/public/gridedit.js +4 -1
- package/public/jquery-menu-editor.min.js +112 -112
- package/public/saltcorn-common.js +114 -26
- package/public/saltcorn.css +27 -10
- package/public/saltcorn.js +223 -76
- package/restart_watcher.js +1 -0
- package/routes/actions.js +20 -6
- package/routes/admin.js +243 -82
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/diagram.js +362 -35
- package/routes/fields.js +4 -1
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +4 -4
- package/routes/page.js +16 -3
- package/routes/pageedit.js +22 -14
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +4 -5
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +36 -32
- package/routes/tenant.js +98 -36
- package/routes/utils.js +72 -20
- package/routes/view.js +0 -1
- package/routes/viewedit.js +55 -22
- package/serve.js +5 -0
- package/tests/admin.test.js +2 -0
- package/tests/auth.test.js +20 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +4 -2
package/markup/forms.js
CHANGED
|
@@ -50,7 +50,7 @@ const editRoleForm = ({ url, current_role, roles, req }) =>
|
|
|
50
50
|
* @param {object} req
|
|
51
51
|
* @returns {Form}
|
|
52
52
|
*/
|
|
53
|
-
const fileUploadForm = (req) =>
|
|
53
|
+
const fileUploadForm = (req, folder) =>
|
|
54
54
|
form(
|
|
55
55
|
{
|
|
56
56
|
action: "/files/upload",
|
|
@@ -61,11 +61,12 @@ const fileUploadForm = (req) =>
|
|
|
61
61
|
label(req.__("Upload file ")),
|
|
62
62
|
input({
|
|
63
63
|
name: "file",
|
|
64
|
-
class: "form-control-
|
|
64
|
+
class: "form-control ms-1 w-unset d-inline",
|
|
65
65
|
type: "file",
|
|
66
66
|
onchange: "form.submit()",
|
|
67
67
|
multiple: true,
|
|
68
|
-
})
|
|
68
|
+
}),
|
|
69
|
+
folder && input({ type: "hidden", name: "folder", value: folder })
|
|
69
70
|
);
|
|
70
71
|
|
|
71
72
|
/**
|
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-beta.0",
|
|
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.
|
|
10
|
-
"@saltcorn/builder": "0.
|
|
11
|
-
"@saltcorn/data": "0.
|
|
12
|
-
"@saltcorn/admin-models": "0.
|
|
13
|
-
"@saltcorn/
|
|
14
|
-
"@saltcorn/
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.0-beta.0",
|
|
10
|
+
"@saltcorn/builder": "0.8.0-beta.0",
|
|
11
|
+
"@saltcorn/data": "0.8.0-beta.0",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.0-beta.0",
|
|
13
|
+
"@saltcorn/filemanager": "0.7.4",
|
|
14
|
+
"@saltcorn/markup": "0.8.0-beta.0",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.0-beta.0",
|
|
15
16
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
16
17
|
"@socket.io/sticky": "^1.0.1",
|
|
17
18
|
"aws-sdk": "^2.1037.0",
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
const entityFilter = {
|
|
2
|
+
showViews: true,
|
|
3
|
+
showPages: true,
|
|
4
|
+
showTables: true,
|
|
5
|
+
showTrigger: true,
|
|
6
|
+
};
|
|
7
|
+
const tagFilterIds = [];
|
|
8
|
+
let tagFilterEnabled = false;
|
|
9
|
+
|
|
10
|
+
let activePopper = null;
|
|
11
|
+
|
|
12
|
+
function initMouseOver() {
|
|
13
|
+
cy.on("mouseover", "node", (event) => {
|
|
14
|
+
const node = event.target;
|
|
15
|
+
const cardPopper = node.popper({
|
|
16
|
+
content: () => {
|
|
17
|
+
const popperDiv = document.getElementById(`${node.id()}_popper`);
|
|
18
|
+
if (popperDiv) {
|
|
19
|
+
popperDiv.setAttribute("style", "");
|
|
20
|
+
return popperDiv;
|
|
21
|
+
} else return buildCard(node);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
activePopper = cardPopper;
|
|
25
|
+
const update = () => {
|
|
26
|
+
cardPopper.update();
|
|
27
|
+
};
|
|
28
|
+
node.on("position", update);
|
|
29
|
+
cy.on("pan zoom resize", update);
|
|
30
|
+
buildPreview(node);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
cy.on("mouseout", "node", (event) => {
|
|
34
|
+
const node = event.target;
|
|
35
|
+
activePopper.destroy();
|
|
36
|
+
const popperDiv = document.getElementById(`${node.id()}_popper`);
|
|
37
|
+
popperDiv.setAttribute("style", "display: none;");
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildCard(node) {
|
|
42
|
+
const { type, label } = node.data();
|
|
43
|
+
const html = `
|
|
44
|
+
<div class="card" style="width: 18rem;">
|
|
45
|
+
<div class="card-header">
|
|
46
|
+
<h5 class="card-title">${type}</h5>
|
|
47
|
+
<h6 class="card-subtitle text-muted">${label}</h6>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="card-body">
|
|
50
|
+
${buildTagBadges(node)}
|
|
51
|
+
${buildCardBody(node)}
|
|
52
|
+
${type === "page" || type === "view" ? buildPreviewDiv(node) : ""}
|
|
53
|
+
${type === "page" || type === "view" ? buildMinRoleSelect(node) : ""}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
`;
|
|
57
|
+
const div = document.createElement("div");
|
|
58
|
+
div.id = `${node.id()}_popper`;
|
|
59
|
+
document.body.appendChild(div);
|
|
60
|
+
div.innerHTML = html;
|
|
61
|
+
return div;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildPreview(node) {
|
|
65
|
+
const { name, type } = node.data();
|
|
66
|
+
const previewId = `preview_${node.id()}`;
|
|
67
|
+
$.ajax(`/${type}/${name}/preview`, {
|
|
68
|
+
type: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
71
|
+
},
|
|
72
|
+
success: (res) => {
|
|
73
|
+
$(`#${previewId}`).html(`
|
|
74
|
+
<div
|
|
75
|
+
id="preview_wrapper"
|
|
76
|
+
style="min-height: 70px;"
|
|
77
|
+
>
|
|
78
|
+
${res}
|
|
79
|
+
</div></div></div>`);
|
|
80
|
+
const previewDiv = $(`#${previewId}`);
|
|
81
|
+
const pos = previewDiv.position();
|
|
82
|
+
const cssBase = `
|
|
83
|
+
position: absolute; top: ${pos.top}px; left: ${pos.left}px;
|
|
84
|
+
width: ${previewDiv.width()}px; height: ${previewDiv.height() + 12}px;`;
|
|
85
|
+
$(`#${previewId}`).after(`
|
|
86
|
+
<div
|
|
87
|
+
style="${cssBase}
|
|
88
|
+
background-color: black; opacity: 0.1;
|
|
89
|
+
z-index: 10;"
|
|
90
|
+
>
|
|
91
|
+
</div>
|
|
92
|
+
<div style="${cssBase} opacity: 0.5;">
|
|
93
|
+
<h2 class="preview-text fw-bold text-danger">
|
|
94
|
+
Preview
|
|
95
|
+
</h2>
|
|
96
|
+
</div>`);
|
|
97
|
+
},
|
|
98
|
+
error: (res) => {
|
|
99
|
+
console.log("error");
|
|
100
|
+
console.log(res);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function buildPreviewDiv(node) {
|
|
106
|
+
const previewId = `preview_${node.id()}`;
|
|
107
|
+
return `
|
|
108
|
+
<div class="my-2" id="${previewId}" style="min-height: 70px;">
|
|
109
|
+
<div style="opacity: 0.5;">
|
|
110
|
+
<h2>
|
|
111
|
+
<span class="fw-bold text-danger">Preview</span>
|
|
112
|
+
<i class="fas fa-spinner fa-spin"></i>
|
|
113
|
+
</h2>
|
|
114
|
+
</div>
|
|
115
|
+
</div>`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildMinRoleSelect(node) {
|
|
119
|
+
let { type, objectId, min_role } = node.data();
|
|
120
|
+
min_role = parseInt(min_role);
|
|
121
|
+
const selectId = `_${type}_${objectId}_access_id`;
|
|
122
|
+
return `
|
|
123
|
+
<form
|
|
124
|
+
action="${type === "view" ? "viewedit" : "pageedit"}/setrole/${objectId}"
|
|
125
|
+
method="post"
|
|
126
|
+
>
|
|
127
|
+
<div class="row">
|
|
128
|
+
<div class="col-sm-3">
|
|
129
|
+
<label
|
|
130
|
+
class="form-label"
|
|
131
|
+
for="${selectId}"
|
|
132
|
+
>
|
|
133
|
+
Access
|
|
134
|
+
</label>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="col-sm-7">
|
|
137
|
+
<select
|
|
138
|
+
class="form-select"
|
|
139
|
+
id="${selectId}"
|
|
140
|
+
name="role"
|
|
141
|
+
onchange="setRole(this, '${node.id()}')"
|
|
142
|
+
>
|
|
143
|
+
${roles.map(
|
|
144
|
+
(role) =>
|
|
145
|
+
`<option
|
|
146
|
+
value="${role.id}"
|
|
147
|
+
${role.id === min_role ? "selected" : ""}
|
|
148
|
+
>
|
|
149
|
+
${role.role}
|
|
150
|
+
</option>`
|
|
151
|
+
)}
|
|
152
|
+
</select>
|
|
153
|
+
</div>
|
|
154
|
+
</form>`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function setRole(srcElement, nodeId) {
|
|
158
|
+
const form = $(srcElement).closest("form");
|
|
159
|
+
const newRole = parseInt(form.serializeArray()[0].value);
|
|
160
|
+
const node = cy.nodes().find((node) => node.id() === nodeId);
|
|
161
|
+
const { type, objectId } = node.data();
|
|
162
|
+
$.ajax(`/${type}edit/setrole/${objectId}`, {
|
|
163
|
+
type: "POST",
|
|
164
|
+
headers: {
|
|
165
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
166
|
+
},
|
|
167
|
+
data: { role: newRole },
|
|
168
|
+
success: (res) => {
|
|
169
|
+
node.data().min_role = newRole;
|
|
170
|
+
// TODO disabled alerts, the element conflicts with cy
|
|
171
|
+
console.log(res.responseText);
|
|
172
|
+
},
|
|
173
|
+
error: (res) => {
|
|
174
|
+
// TODO disabled alerts, the element conflicts with cy
|
|
175
|
+
console.log(res.responseText);
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function buildTagBadges(node) {
|
|
181
|
+
const { type, tags, objectId } = node.data();
|
|
182
|
+
return `
|
|
183
|
+
<div
|
|
184
|
+
id="_${type}_${objectId}_badges_id"
|
|
185
|
+
class="mb-3"
|
|
186
|
+
>
|
|
187
|
+
${existingTagBadges(node)}
|
|
188
|
+
<button
|
|
189
|
+
class="badge bg-primary"
|
|
190
|
+
data-bs-toggle="dropdown"
|
|
191
|
+
aria-expanded=false
|
|
192
|
+
>
|
|
193
|
+
<i class="fas fa-plus"></i>
|
|
194
|
+
<i class="fas fa-caret-down"></i>
|
|
195
|
+
</button>
|
|
196
|
+
<div class="dropdown-menu">
|
|
197
|
+
<form id="${type}_${objectId}_options_form">
|
|
198
|
+
<input
|
|
199
|
+
type="hidden"
|
|
200
|
+
name="objectType"
|
|
201
|
+
value="${type}"
|
|
202
|
+
/>
|
|
203
|
+
<input
|
|
204
|
+
type="hidden"
|
|
205
|
+
name="objectId"
|
|
206
|
+
value="${objectId}"
|
|
207
|
+
/>
|
|
208
|
+
${newTagOptions(tags, type, objectId)}
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
onClick="addToTag(this, '${node.id()}')"
|
|
212
|
+
class="ms-3 mt-2 mb-1 btn btn-warning"
|
|
213
|
+
>
|
|
214
|
+
Add to tags
|
|
215
|
+
</button>
|
|
216
|
+
</form>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function addToTag(srcButton, nodeId) {
|
|
223
|
+
const form = $(srcButton).closest("form");
|
|
224
|
+
let objectType;
|
|
225
|
+
let objectId;
|
|
226
|
+
const tag_ids = [];
|
|
227
|
+
for (const param of form.serializeArray()) {
|
|
228
|
+
switch (param.name) {
|
|
229
|
+
case "objectType": {
|
|
230
|
+
objectType = param.value;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case "objectId": {
|
|
234
|
+
objectId = param.value;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case "tagId": {
|
|
238
|
+
tag_ids.push(param.value);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (tag_ids.length > 0) {
|
|
244
|
+
$.ajax(`/tag-entries/add/multiple_tags/${objectType}/${objectId}`, {
|
|
245
|
+
type: "POST",
|
|
246
|
+
headers: {
|
|
247
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
248
|
+
},
|
|
249
|
+
data: { tag_ids },
|
|
250
|
+
success: (res) => {
|
|
251
|
+
const badgesDiv = document.getElementById(
|
|
252
|
+
`_${objectType}_${objectId}_badges_id`
|
|
253
|
+
);
|
|
254
|
+
for (const tag of res.tags) {
|
|
255
|
+
// create new badge
|
|
256
|
+
const tagBadge = document.createElement("div");
|
|
257
|
+
tagBadge.classList.add("badge", "bg-primary");
|
|
258
|
+
tagBadge.innerHTML = `${tag.name}
|
|
259
|
+
<i
|
|
260
|
+
class="fas fa-times ms-1"
|
|
261
|
+
onClick="removeObjectFromTag(this, '${objectType}', ${objectId},
|
|
262
|
+
${tag.id}, '${tag.name}', '${nodeId}')"
|
|
263
|
+
></i>`;
|
|
264
|
+
tagBadge.id = `_${objectType}_badge_${tag.id}_${objectId}`;
|
|
265
|
+
const allChildren = badgesDiv.childNodes;
|
|
266
|
+
badgesDiv.insertBefore(tagBadge, allChildren[allChildren.length - 4]);
|
|
267
|
+
// remove the add option
|
|
268
|
+
const addOptionDiv = document.getElementById(
|
|
269
|
+
`tag_add_div_${tag.id}_${objectType}_${objectId}`
|
|
270
|
+
);
|
|
271
|
+
addOptionDiv.remove();
|
|
272
|
+
// add the tag to the cy node
|
|
273
|
+
const node = cy.nodes().find((node) => node.id() === nodeId);
|
|
274
|
+
node.data().tags.push(tag);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
error: (res) => {
|
|
278
|
+
// TODO disabled alerts, the element conflicts with cy
|
|
279
|
+
console.log(res.responseText);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildCardBody(node) {
|
|
286
|
+
switch (node.data().type) {
|
|
287
|
+
case "view": {
|
|
288
|
+
return buildViewContent(node);
|
|
289
|
+
}
|
|
290
|
+
case "page": {
|
|
291
|
+
return buildPageContent(node);
|
|
292
|
+
}
|
|
293
|
+
case "table": {
|
|
294
|
+
return buildTableContent(node);
|
|
295
|
+
}
|
|
296
|
+
case "trigger": {
|
|
297
|
+
return buildTriggerContent(node);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function buildViewContent(node) {
|
|
303
|
+
const { table, viewtemplate } = node.data();
|
|
304
|
+
return `
|
|
305
|
+
<div class="container mb-2">
|
|
306
|
+
<div class="row">
|
|
307
|
+
<div class="col">${viewtemplate}</div>
|
|
308
|
+
<div class="col">${table}</div>
|
|
309
|
+
</div>
|
|
310
|
+
</div>`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function buildPageContent(node) {
|
|
314
|
+
// TODO
|
|
315
|
+
return `
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function buildTableContent(node) {
|
|
320
|
+
const { fields } = node.data();
|
|
321
|
+
return `
|
|
322
|
+
<div class="container">
|
|
323
|
+
${fields
|
|
324
|
+
.map((field) => {
|
|
325
|
+
return `
|
|
326
|
+
<div class="row">
|
|
327
|
+
<div class="col">${field.name} :</div>
|
|
328
|
+
<div class="col ps-0">${field.typeName}</div>
|
|
329
|
+
</div>`;
|
|
330
|
+
})
|
|
331
|
+
.join("")}
|
|
332
|
+
</div>`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function buildTriggerContent(node) {
|
|
336
|
+
// TODO
|
|
337
|
+
return `
|
|
338
|
+
`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function newTagOptions(existingTags, type, objectId) {
|
|
342
|
+
const existingTagIds = new Set(existingTags.map((tag) => tag.id));
|
|
343
|
+
const newTagOptions = allTags.filter((tag) => !existingTagIds.has(tag.id));
|
|
344
|
+
return newTagOptions
|
|
345
|
+
.map((tag) => {
|
|
346
|
+
const inputId = `tag_add_box_${tag.id}`;
|
|
347
|
+
const divId = `tag_add_div_${tag.id}_${type}_${objectId}`;
|
|
348
|
+
return `
|
|
349
|
+
<div
|
|
350
|
+
class="ms-3 mt-3 form-check"
|
|
351
|
+
id="${divId}"
|
|
352
|
+
>
|
|
353
|
+
<label
|
|
354
|
+
class="form-check-label"
|
|
355
|
+
for="${inputId}"
|
|
356
|
+
>
|
|
357
|
+
${tag.name}
|
|
358
|
+
</label>
|
|
359
|
+
<input
|
|
360
|
+
type="checkbox"
|
|
361
|
+
class="form-check-input"
|
|
362
|
+
id="${inputId}"
|
|
363
|
+
name="tagId"
|
|
364
|
+
value="${tag.id}"
|
|
365
|
+
checked=false
|
|
366
|
+
autocomplete="off"
|
|
367
|
+
/>
|
|
368
|
+
</div>
|
|
369
|
+
`;
|
|
370
|
+
})
|
|
371
|
+
.join("");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function removeObjectFromTag(src, type, objectId, tagId, tagName, nodeId) {
|
|
375
|
+
$.ajax(`/tag-entries/remove/${type}/${objectId}/${tagId}`, {
|
|
376
|
+
type: "POST",
|
|
377
|
+
headers: {
|
|
378
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
379
|
+
},
|
|
380
|
+
data: { tag_id: tagId },
|
|
381
|
+
success: (res) => {
|
|
382
|
+
// remove tag badge
|
|
383
|
+
src.parentNode.remove();
|
|
384
|
+
// add option checkbox
|
|
385
|
+
const divId = `tag_add_div_${tagId}_${type}_${objectId}`;
|
|
386
|
+
const inputId = `tag_add_box_${tagId}`;
|
|
387
|
+
const optionsDiv = document.createElement("div");
|
|
388
|
+
optionsDiv.innerHTML = `
|
|
389
|
+
<div
|
|
390
|
+
class="ms-3 mt-3 form-check"
|
|
391
|
+
id="${divId}"
|
|
392
|
+
>
|
|
393
|
+
<label
|
|
394
|
+
class="form-check-label"
|
|
395
|
+
for="${inputId}"
|
|
396
|
+
>
|
|
397
|
+
${tagName}
|
|
398
|
+
</label>
|
|
399
|
+
<input
|
|
400
|
+
type="checkbox"
|
|
401
|
+
class="form-check-input"
|
|
402
|
+
id="${inputId}"
|
|
403
|
+
name="tagId"
|
|
404
|
+
value="${tagId}"
|
|
405
|
+
checked=false
|
|
406
|
+
autocomplete="off"
|
|
407
|
+
/>
|
|
408
|
+
</div>
|
|
409
|
+
`;
|
|
410
|
+
const optionsContainer = document.getElementById(
|
|
411
|
+
`${type}_${objectId}_options_form`
|
|
412
|
+
);
|
|
413
|
+
const childNodes = optionsContainer.childNodes;
|
|
414
|
+
const addButton = childNodes[childNodes.length - 2];
|
|
415
|
+
optionsContainer.insertBefore(optionsDiv, addButton);
|
|
416
|
+
// remove tag from cy node
|
|
417
|
+
const node = cy.nodes().find((node) => node.id() === nodeId);
|
|
418
|
+
const { tags } = node.data();
|
|
419
|
+
const index = tags.findIndex((tag) => tag.id === tagId);
|
|
420
|
+
tags.splice(index, 1);
|
|
421
|
+
},
|
|
422
|
+
error: (res) => {
|
|
423
|
+
// TODO disabled alerts, the element conflicts with cy
|
|
424
|
+
console.log(res.responseText);
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function existingTagBadges(node) {
|
|
430
|
+
const { tags, type, objectId } = node.data();
|
|
431
|
+
return tags
|
|
432
|
+
.map((tag) => {
|
|
433
|
+
return `
|
|
434
|
+
<div
|
|
435
|
+
class="badge bg-primary"
|
|
436
|
+
id="_${type}_badge_${tag.id}_${objectId}"
|
|
437
|
+
>
|
|
438
|
+
${tag.name}
|
|
439
|
+
<i
|
|
440
|
+
class="fas fa-times ms-1"
|
|
441
|
+
onClick="removeObjectFromTag(
|
|
442
|
+
this, '${type}', ${objectId},
|
|
443
|
+
${tag.id}, '${tag.name}', '${node.id()}')"
|
|
444
|
+
>
|
|
445
|
+
</i>
|
|
446
|
+
</div>`;
|
|
447
|
+
})
|
|
448
|
+
.join("");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function reloadCy(keepViewPos) {
|
|
452
|
+
const currentZoom = keepViewPos ? cy.zoom() : undefined;
|
|
453
|
+
const currentPan = keepViewPos ? cy.pan() : undefined;
|
|
454
|
+
$.ajax("/diagram/data", {
|
|
455
|
+
dataType: "json",
|
|
456
|
+
type: "GET",
|
|
457
|
+
headers: { "CSRF-Token": _sc_globalCsrf },
|
|
458
|
+
data: !tagFilterEnabled ? entityFilter : { ...entityFilter, tagFilterIds },
|
|
459
|
+
}).done((res) => {
|
|
460
|
+
const cfg = {
|
|
461
|
+
container: document.getElementById("cy"),
|
|
462
|
+
maxZoom: 2,
|
|
463
|
+
wheelSensitivity: 0.3,
|
|
464
|
+
...res,
|
|
465
|
+
};
|
|
466
|
+
window.cy = cytoscape(cfg);
|
|
467
|
+
if (keepViewPos) {
|
|
468
|
+
cy.pan(currentPan);
|
|
469
|
+
cy.zoom(currentZoom);
|
|
470
|
+
}
|
|
471
|
+
initMouseOver();
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function toggleEntityFilter(type) {
|
|
476
|
+
switch (type) {
|
|
477
|
+
case "views": {
|
|
478
|
+
entityFilter.showViews = !entityFilter.showViews;
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
case "pages": {
|
|
482
|
+
entityFilter.showPages = !entityFilter.showPages;
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
case "tables": {
|
|
486
|
+
entityFilter.showTables = !entityFilter.showTables;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case "triggers": {
|
|
490
|
+
entityFilter.showTrigger = !entityFilter.showTrigger;
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function toggleTagFilter(id) {
|
|
497
|
+
if (!tagFilterEnabled) enableTagFilter();
|
|
498
|
+
const index = tagFilterIds.indexOf(id);
|
|
499
|
+
if (index > -1) {
|
|
500
|
+
tagFilterIds.splice(index, 1);
|
|
501
|
+
} else {
|
|
502
|
+
tagFilterIds.push(id);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function enableTagFilter() {
|
|
507
|
+
tagFilterEnabled = true;
|
|
508
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_box_"]')) {
|
|
509
|
+
node.style = "";
|
|
510
|
+
}
|
|
511
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_label_"]')) {
|
|
512
|
+
node.style = "";
|
|
513
|
+
}
|
|
514
|
+
const box = document.getElementById("noTagsId");
|
|
515
|
+
box.checked = false;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function toggleTagFilterMode() {
|
|
519
|
+
if (tagFilterEnabled) {
|
|
520
|
+
tagFilterEnabled = false;
|
|
521
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_box_"]')) {
|
|
522
|
+
node.style = "opacity: 0.5;";
|
|
523
|
+
}
|
|
524
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_label_"]')) {
|
|
525
|
+
node.style = "opacity: 0.5;";
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
enableTagFilter();
|
|
529
|
+
}
|
|
530
|
+
}
|
package/public/gridedit.js
CHANGED
|
@@ -19,12 +19,15 @@ function deleteIcon() {
|
|
|
19
19
|
function flatpickerEditor(cell, onRendered, success, cancel, editorParams) {
|
|
20
20
|
var input = $("<input type='text'/>");
|
|
21
21
|
const dayOnly = editorParams && editorParams.dayOnly;
|
|
22
|
+
let defaultDate = cell.getValue()
|
|
23
|
+
|
|
24
|
+
if (!defaultDate) defaultDate = new Date()
|
|
22
25
|
input.flatpickr({
|
|
23
26
|
enableTime: !dayOnly,
|
|
24
27
|
dateFormat: dayOnly ? "Y-m-d" : "Z",
|
|
25
28
|
time_24hr: true,
|
|
26
29
|
locale: "en", // global variable with locale 'en', 'fr', ...
|
|
27
|
-
defaultDate
|
|
30
|
+
defaultDate,
|
|
28
31
|
onClose: function (selectedDates, dateStr, instance) {
|
|
29
32
|
evt = window.event;
|
|
30
33
|
var isEscape = false;
|