waibu-db 2.26.3 → 2.27.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/extend/waibuBootstrap/theme/component/wdb-base.js +5 -0
- package/extend/waibuBootstrap/theme/component/widget/btn-add.js +3 -3
- package/extend/waibuBootstrap/theme/component/widget/btn-clone.js +3 -3
- package/extend/waibuBootstrap/theme/component/widget/btn-columns.js +1 -1
- package/extend/waibuBootstrap/theme/component/widget/btn-delete.js +3 -3
- package/extend/waibuBootstrap/theme/component/widget/btn-details.js +3 -3
- package/extend/waibuBootstrap/theme/component/widget/btn-edit.js +3 -3
- package/extend/waibuBootstrap/theme/component/widget/data-table.js +35 -29
- package/extend/waibuBootstrap/theme/component/widget/form.js +1 -1
- package/lib/crud/add-handler.js +1 -0
- package/lib/crud/delete-handler.js +1 -0
- package/lib/crud/edit-handler.js +1 -0
- package/package.json +1 -1
- package/wiki/CHANGES.md +10 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
async function wdbBase () {
|
|
2
2
|
return class WdbBase extends this.app.baseClass.MpaWidget {
|
|
3
|
+
isActionAllowed = async (action) => {
|
|
4
|
+
const { req } = this.component
|
|
5
|
+
if (!this.app.sumba || !this.model) return true
|
|
6
|
+
return await this.app.sumba.isActionAllowed(action, this.model, req)
|
|
7
|
+
}
|
|
3
8
|
}
|
|
4
9
|
}
|
|
5
10
|
|
|
@@ -5,11 +5,11 @@ async function btnAdd () {
|
|
|
5
5
|
|
|
6
6
|
return class WdbBtnAdd extends WdbBase {
|
|
7
7
|
build = async () => {
|
|
8
|
-
const { isEmpty
|
|
8
|
+
const { isEmpty } = this.app.lib._
|
|
9
9
|
const { req } = this.component
|
|
10
10
|
this.params.noTag = true
|
|
11
|
-
const
|
|
12
|
-
if (schema.view.disabled.includes('create')) {
|
|
11
|
+
const allowed = await this.isActionAllowed('CREATE')
|
|
12
|
+
if (!allowed || this.schema.view.disabled.includes('create')) {
|
|
13
13
|
this.params.html = ''
|
|
14
14
|
return
|
|
15
15
|
}
|
|
@@ -6,10 +6,10 @@ async function btnClone () {
|
|
|
6
6
|
return class WdbBtnClone extends WdbBase {
|
|
7
7
|
build = async () => {
|
|
8
8
|
const { req } = this.component
|
|
9
|
-
const { isEmpty
|
|
9
|
+
const { isEmpty } = this.app.lib._
|
|
10
10
|
this.params.noTag = true
|
|
11
|
-
const
|
|
12
|
-
if (schema.view.disabled.includes('create')) {
|
|
11
|
+
const allowed = await this.isActionAllowed('CREATE')
|
|
12
|
+
if (!allowed || this.schema.view.disabled.includes('create')) {
|
|
13
13
|
this.params.html = ''
|
|
14
14
|
return
|
|
15
15
|
}
|
|
@@ -23,7 +23,7 @@ async function btnColumns () {
|
|
|
23
23
|
if (isEmpty(this.params.attr.content)) this.params.attr.content = req.t('columns')
|
|
24
24
|
for (const f of schema.view.fields) {
|
|
25
25
|
// if (allFields.length > 0 && !allFields.includes(f)) continue
|
|
26
|
-
const prop = schema.properties.find(p => p.name === f
|
|
26
|
+
const prop = schema.properties.find(p => p.name === f)
|
|
27
27
|
if (!prop) continue
|
|
28
28
|
const attr = { 'x-model': 'selected', label: req.t(get(schema, `view.label.${f}`, `field.${f}`)), value: f, labelText: 'nowrap' }
|
|
29
29
|
if (fields.includes(f)) attr.checked = true
|
|
@@ -7,10 +7,10 @@ async function btnDelete () {
|
|
|
7
7
|
build = async () => {
|
|
8
8
|
const { req } = this.component
|
|
9
9
|
const { generateId } = this.app.lib.aneka
|
|
10
|
-
const { isEmpty
|
|
10
|
+
const { isEmpty } = this.app.lib._
|
|
11
11
|
this.params.noTag = true
|
|
12
|
-
const
|
|
13
|
-
if (schema.view.disabled.includes('remove')) {
|
|
12
|
+
const allowed = await this.isActionAllowed('REMOVE')
|
|
13
|
+
if (!allowed || this.schema.view.disabled.includes('remove')) {
|
|
14
14
|
this.params.html = ''
|
|
15
15
|
return
|
|
16
16
|
}
|
|
@@ -7,10 +7,10 @@ async function btnDetails () {
|
|
|
7
7
|
build = async () => {
|
|
8
8
|
const { req } = this.component
|
|
9
9
|
const { generateId } = this.app.lib.aneka
|
|
10
|
-
const { isEmpty
|
|
10
|
+
const { isEmpty } = this.app.lib._
|
|
11
11
|
this.params.noTag = true
|
|
12
|
-
const
|
|
13
|
-
if (schema.view.disabled.includes('get')) {
|
|
12
|
+
const allowed = await this.isActionAllowed('READ')
|
|
13
|
+
if (!allowed || this.schema.view.disabled.includes('get')) {
|
|
14
14
|
this.params.html = ''
|
|
15
15
|
return
|
|
16
16
|
}
|
|
@@ -7,10 +7,10 @@ async function btnEdit () {
|
|
|
7
7
|
build = async () => {
|
|
8
8
|
const { req } = this.component
|
|
9
9
|
const { generateId } = this.app.lib.aneka
|
|
10
|
-
const { isEmpty
|
|
10
|
+
const { isEmpty } = this.app.lib._
|
|
11
11
|
this.params.noTag = true
|
|
12
|
-
const
|
|
13
|
-
if (schema.view.disabled.includes('update')) {
|
|
12
|
+
const allowed = await this.isActionAllowed('UPDATE')
|
|
13
|
+
if (!allowed || this.schema.view.disabled.includes('update')) {
|
|
14
14
|
this.params.html = ''
|
|
15
15
|
return
|
|
16
16
|
}
|
|
@@ -4,19 +4,19 @@ async function table () {
|
|
|
4
4
|
const WdbBase = await wdbBase.call(this)
|
|
5
5
|
|
|
6
6
|
return class WdbDataTable extends WdbBase {
|
|
7
|
-
isRightAligned = (field
|
|
7
|
+
isRightAligned = (field) => {
|
|
8
8
|
const { get, find } = this.app.lib._
|
|
9
|
-
const prop = find(schema.properties, { name: field })
|
|
9
|
+
const prop = find(this.schema.properties, { name: field })
|
|
10
10
|
if (!prop) return false
|
|
11
|
-
let value = get(schema, 'view.alignEnd', []).includes(field)
|
|
11
|
+
let value = get(this.schema, 'view.alignEnd', []).includes(field)
|
|
12
12
|
if (!value) value = ['smallint', 'integer', 'float', 'double'].includes(prop.type)
|
|
13
13
|
return value
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
isNoWrap = (field,
|
|
16
|
+
isNoWrap = (field, bodyNowrap) => {
|
|
17
17
|
if (bodyNowrap) return true
|
|
18
18
|
const { get } = this.app.lib._
|
|
19
|
-
return get(schema, 'view.noWrap', []).includes(field)
|
|
19
|
+
return get(this.schema, 'view.noWrap', []).includes(field)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
build = async () => {
|
|
@@ -32,24 +32,30 @@ async function table () {
|
|
|
32
32
|
const prettyUrl = this.params.attr.prettyUrl
|
|
33
33
|
|
|
34
34
|
const hasXSite = get(req, 'routeOptions.config.xSite')
|
|
35
|
-
const schema = get(locals, 'schema', {})
|
|
36
35
|
const data = get(locals, 'list.data', [])
|
|
37
36
|
const filter = get(locals, 'list.filter', {})
|
|
38
37
|
const count = get(locals, 'list.count', 0)
|
|
38
|
+
const allowed = await this.isActionAllowed('READ')
|
|
39
|
+
if (!allowed) {
|
|
40
|
+
const alert = '<c:alert color="error" t:content="accessDenied" margin="top-4"/>'
|
|
41
|
+
this.params.noTag = true
|
|
42
|
+
this.params.html = await buildSentence(alert)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
39
45
|
if (count === 0 || data.length === 0) {
|
|
40
46
|
const alert = '<c:alert color="warning" t:content="noRecordFound" margin="top-4"/>'
|
|
41
47
|
this.params.noTag = true
|
|
42
48
|
this.params.html = await buildSentence(alert)
|
|
43
49
|
return
|
|
44
50
|
}
|
|
45
|
-
const disableds = get(schema, 'view.disabled', [])
|
|
51
|
+
const disableds = get(this.schema, 'view.disabled', [])
|
|
46
52
|
if (disableds.includes('find')) {
|
|
47
53
|
this.params.html = ''
|
|
48
54
|
return
|
|
49
55
|
}
|
|
50
56
|
const qsKey = this.app.waibu.config.qsKey
|
|
51
57
|
let fields = without(get(locals, `_meta.query.${qsKey.fields}`, '').split(','), '')
|
|
52
|
-
if (isEmpty(fields)) fields = without(schema.view.fields, 'id')
|
|
58
|
+
if (isEmpty(fields)) fields = without(this.schema.view.fields, 'id')
|
|
53
59
|
if (data.length > 0) {
|
|
54
60
|
fields = intersection(fields, Object.keys(data[0]))
|
|
55
61
|
}
|
|
@@ -64,9 +70,9 @@ async function table () {
|
|
|
64
70
|
|
|
65
71
|
let selection = this.params.attr.selection
|
|
66
72
|
if (!isSet(selection)) {
|
|
67
|
-
const canDelete = !disableds.includes('remove')
|
|
68
|
-
const canEdit = !disableds.includes('update')
|
|
69
|
-
const canDetails = !disableds.includes('get')
|
|
73
|
+
const canDelete = !disableds.includes('remove') && (await this.isActionAllowed('REMOVE'))
|
|
74
|
+
const canEdit = !disableds.includes('update') && (await this.isActionAllowed('UPDATE'))
|
|
75
|
+
const canDetails = !disableds.includes('get') && (await this.isActionAllowed('READ'))
|
|
70
76
|
if (canEdit || canDetails) selection = 'single'
|
|
71
77
|
if (canDelete) selection = 'multi'
|
|
72
78
|
if (selection) this.params.attr.hover = true
|
|
@@ -76,13 +82,13 @@ async function table () {
|
|
|
76
82
|
const html = []
|
|
77
83
|
let items = []
|
|
78
84
|
// head
|
|
79
|
-
for (const f of schema.view.fields) {
|
|
85
|
+
for (const f of this.schema.view.fields) {
|
|
80
86
|
if (!fields.includes(f)) continue
|
|
81
|
-
let prop = find(schema.properties, { name: f })
|
|
82
|
-
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
87
|
+
let prop = find(this.schema.properties, { name: f })
|
|
88
|
+
if (!prop) prop = find(this.schema.view.calcFields, { name: f })
|
|
83
89
|
if (!prop) continue
|
|
84
|
-
let head = req.t(get(schema, `view.label.${f}`, `field.${f}`))
|
|
85
|
-
if (!this.params.attr.noSort && (schema.sortables ?? []).includes(f)) {
|
|
90
|
+
let head = req.t(get(this.schema, `view.label.${f}`, `field.${f}`))
|
|
91
|
+
if (!this.params.attr.noSort && (this.schema.sortables ?? []).includes(f)) {
|
|
86
92
|
let sortItem = `${f}:-1`
|
|
87
93
|
let icon = this.params.attr.sortUpIcon ?? 'caretUp'
|
|
88
94
|
if (f === sortCol) {
|
|
@@ -91,7 +97,7 @@ async function table () {
|
|
|
91
97
|
}
|
|
92
98
|
const item = set({ page: 1 }, qsKey.sort, sortItem)
|
|
93
99
|
const href = buildUrl({ params: item })
|
|
94
|
-
const attr = this.isRightAligned(f, schema) ? { text: 'align:end' } : {}
|
|
100
|
+
const attr = this.isRightAligned(f, this.schema) ? { text: 'align:end' } : {}
|
|
95
101
|
const content = [
|
|
96
102
|
await buildTag({ tag: 'div', attr, html: head }),
|
|
97
103
|
await buildTag({ tag: 'a', attr: { icon, href, noIconLink: true }, prepend: '<div class="ms-1">', append: '</div>' })
|
|
@@ -99,8 +105,8 @@ async function table () {
|
|
|
99
105
|
head = await buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
|
|
100
106
|
}
|
|
101
107
|
let text = this.params.attr.headerNowrap ? '' : 'nowrap'
|
|
102
|
-
if (text === '' && this.isNoWrap(f,
|
|
103
|
-
if (this.isRightAligned(f
|
|
108
|
+
if (text === '' && this.isNoWrap(f, group.body.nowrap)) text = 'nowrap'
|
|
109
|
+
if (this.isRightAligned(f)) text += ' align:end'
|
|
104
110
|
const attr = { dataKey: f, dataType: prop.type, text }
|
|
105
111
|
items.push(await buildTag({ tag: 'th', attr, html: head }))
|
|
106
112
|
}
|
|
@@ -126,13 +132,13 @@ async function table () {
|
|
|
126
132
|
if (selection) {
|
|
127
133
|
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
128
134
|
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
129
|
-
const type = find(schema.properties, { name: 'id' }).type
|
|
135
|
+
const type = find(this.schema.properties, { name: 'id' }).type
|
|
130
136
|
const prepend = `<td data-value="${d.id}" data-key="id" data-type="${type}">`
|
|
131
137
|
lines.push(await buildTag({ tag, attr, prepend, append: '</td>' }))
|
|
132
138
|
}
|
|
133
|
-
for (const f of schema.view.fields) {
|
|
139
|
+
for (const f of this.schema.view.fields) {
|
|
134
140
|
if (!fields.includes(f)) continue
|
|
135
|
-
const prop = find(schema.properties, { name: f })
|
|
141
|
+
const prop = find(this.schema.properties, { name: f })
|
|
136
142
|
if (!prop) continue
|
|
137
143
|
let dataValue = d[f]
|
|
138
144
|
if (['datetime'].includes(prop.type) && dataValue instanceof Date && !isNaN(dataValue)) dataValue = escape(dataValue.toISOString())
|
|
@@ -140,16 +146,16 @@ async function table () {
|
|
|
140
146
|
let value = this.getRefValue({ field: f, data: d, refName: this.getRefName(f) }) ?? get(d, `_fmt.${f}`, d[f])
|
|
141
147
|
const attr = { dataValue, dataKey: prop.name, dataType: prop.type }
|
|
142
148
|
if (!disableds.includes('get')) attr.style = { cursor: 'pointer' }
|
|
143
|
-
const formatCell = get(schema, `view.formatCell.${f}`)
|
|
149
|
+
const formatCell = get(this.schema, `view.formatCell.${f}`)
|
|
144
150
|
if (formatCell) merge(attr, await formatCell.call(this, value, d, { params: this.params, req }))
|
|
145
|
-
const noWrap = this.isNoWrap(f,
|
|
146
|
-
if (this.isRightAligned(f
|
|
151
|
+
const noWrap = this.isNoWrap(f, group.body.nowrap) ? 'nowrap' : ''
|
|
152
|
+
if (this.isRightAligned(f)) attr.text = `align:end ${noWrap}`
|
|
147
153
|
else attr.text = `${noWrap}`
|
|
148
154
|
if (!hasXSite && d._immutable && d._immutable.length > 0) {
|
|
149
155
|
if (d._immutable[0] === '*' || d._immutable.includes(f)) attr.text += ' color:body-tertiary'
|
|
150
156
|
}
|
|
151
157
|
if (f === 'id') attr.text += ' color:body-tertiary'
|
|
152
|
-
const format = get(schema, `view.format.${f}`)
|
|
158
|
+
const format = get(this.schema, `view.format.${f}`)
|
|
153
159
|
if (format) {
|
|
154
160
|
const formatted = await format.call(this, value, d, { params: this.params, req })
|
|
155
161
|
if (isPlainObject(formatted) && formatted.href) {
|
|
@@ -163,7 +169,7 @@ async function table () {
|
|
|
163
169
|
value = formatted
|
|
164
170
|
}
|
|
165
171
|
if (['object', 'array'].includes(prop.type) && !isHtmlLink(value)) value = getTruncated(value, 20) // TODO: should be handle by css instead
|
|
166
|
-
if (!get(schema, 'view.noEscape', []).includes(f) && !isHtmlLink(value)) value = escape(value)
|
|
172
|
+
if (!get(this.schema, 'view.noEscape', []).includes(f) && !isHtmlLink(value)) value = escape(value)
|
|
167
173
|
const line = await buildTag({ tag: 'td', attr, html: value })
|
|
168
174
|
lines.push(line)
|
|
169
175
|
}
|
|
@@ -183,10 +189,10 @@ async function table () {
|
|
|
183
189
|
window.location.href = url
|
|
184
190
|
}
|
|
185
191
|
`
|
|
186
|
-
const xDataView = get(schema, 'view.x.data', '')
|
|
192
|
+
const xDataView = get(this.schema, 'view.x.data', '')
|
|
187
193
|
if (!isEmpty(xDataView)) xData += `, ${xDataView}`
|
|
188
194
|
let xInit = ''
|
|
189
|
-
const xInitView = get(schema, 'view.x.init', '')
|
|
195
|
+
const xInitView = get(this.schema, 'view.x.init', '')
|
|
190
196
|
if (!isEmpty(xInitView)) xInit += `${xInitView}\n`
|
|
191
197
|
|
|
192
198
|
if (selection === 'multi') {
|
|
@@ -45,7 +45,7 @@ async function form () {
|
|
|
45
45
|
if (v === true) attr[k] = true
|
|
46
46
|
else attr[k] = v
|
|
47
47
|
})
|
|
48
|
-
if (['array', 'object'].includes(prop.type)) attr.col = '12-md'
|
|
48
|
+
if (['array', 'object', 'text'].includes(prop.type)) attr.col = '12-md'
|
|
49
49
|
attr.label = this.component.req.t(attr.label)
|
|
50
50
|
const xon = filter(xOns, { field: widget.name })
|
|
51
51
|
for (const o of xon) {
|
package/lib/crud/add-handler.js
CHANGED
|
@@ -35,6 +35,7 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
|
|
|
35
35
|
if (!form._addmore) return reply.redirectTo(buildUrl({ url: req.url, base: 'list', params: { page: 1 }, exclude: ['id', 'mode'] }))
|
|
36
36
|
if (!form._cloneprev) form = pick(form, ['_addmore', '_cloneprev'])
|
|
37
37
|
} catch (err) {
|
|
38
|
+
if (err.orgMessage === 'accessDenied') throw err
|
|
38
39
|
error = err
|
|
39
40
|
}
|
|
40
41
|
} else {
|
|
@@ -20,6 +20,7 @@ async function deleteHandler ({ req, reply, model, params = {}, templateDisabled
|
|
|
20
20
|
await removeRecord({ model, id, req, reply, options: opts })
|
|
21
21
|
result.push(true)
|
|
22
22
|
} catch (err) {
|
|
23
|
+
if (err.orgMessage === 'accessDenied') throw err
|
|
23
24
|
console.log(err)
|
|
24
25
|
result.push(err.message)
|
|
25
26
|
}
|
package/lib/crud/edit-handler.js
CHANGED
|
@@ -43,6 +43,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
|
|
|
43
43
|
form = resp.data
|
|
44
44
|
return reply.redirectTo(buildUrl({ url: req.url, base: req.params.base ?? req.query.base ?? 'list', params: { page: 1 }, exclude: ['id'] }))
|
|
45
45
|
} catch (err) {
|
|
46
|
+
if (err.orgMessage === 'accessDenied') throw err
|
|
46
47
|
error = err
|
|
47
48
|
}
|
|
48
49
|
}
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-06-29
|
|
4
|
+
|
|
5
|
+
- [2.27.0] Add ```isActionAllowed()``` in ```wdb-base```
|
|
6
|
+
- [2.27.0] Add all related action permission on all widgets & page handlers
|
|
7
|
+
|
|
8
|
+
## 2026-06-24
|
|
9
|
+
|
|
10
|
+
- [2.26.4] Bug fix in ```wdb-btn-columns``` widget
|
|
11
|
+
- [2.26.4] Bug fix in ```wdb-forms``` widget
|
|
12
|
+
|
|
3
13
|
## 2026-06-19
|
|
4
14
|
|
|
5
15
|
- [2.26.2] Bug fix in ```add-handler.js```
|