marty 2.9.3 → 3.0.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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +6 -5
- data/Gemfile.lock +1 -1
- data/app/components/marty/data_grid_view.rb +64 -1
- data/app/components/marty/data_grid_view/client/data_grid_edit.js +550 -0
- data/app/components/marty/import_type_view.rb +11 -2
- data/app/components/marty/main_auth_app.rb +23 -23
- data/app/components/marty/promise_view.rb +1 -1
- data/app/components/marty/report_select.rb +1 -0
- data/app/components/marty/script_form.rb +1 -1
- data/app/components/marty/user_view.rb +30 -18
- data/app/models/marty/data_grid.rb +34 -8
- data/app/models/marty/import_type.rb +2 -4
- data/app/models/marty/role_type.rb +10 -0
- data/app/models/marty/user.rb +9 -4
- data/app/models/marty/user_role.rb +2 -3
- data/app/models/marty/vw_promise.rb +2 -2
- data/app/services/marty/data_grid/constraint.rb +73 -0
- data/app/services/marty/data_grid_view/save_grid.rb +63 -0
- data/config/locales/en.yml +1 -0
- data/db/migrate/107_add_data_grid_constraint.rb +8 -0
- data/db/migrate/507_migrate_marty_roles_to_enum.rb +72 -0
- data/db/seeds.rb +1 -6
- data/lib/marty/permissions.rb +6 -16
- data/lib/marty/version.rb +1 -1
- data/spec/dummy/config/locales/en.yml +1 -0
- data/spec/features/data_grid_spec.rb +499 -0
- data/spec/features/data_import_spec.rb +11 -8
- data/spec/features/user_view_spec.rb +1 -1
- data/spec/fixtures/json/data_grid.json +210 -0
- data/spec/fixtures/misc/data_grid_1.txt +15 -0
- data/spec/fixtures/misc/data_grid_2.txt +17 -0
- data/spec/fixtures/misc/data_grid_3.txt +9 -0
- data/spec/fixtures/misc/data_grid_4.txt +5 -0
- data/spec/fixtures/misc/data_grid_5.txt +19 -0
- data/spec/fixtures/misc/grid1_final_data.json +23 -0
- data/spec/fixtures/misc/grid1_final_meta.json +182 -0
- data/spec/fixtures/misc/grid2_final_data.json +11 -0
- data/spec/fixtures/misc/grid2_final_meta.json +181 -0
- data/spec/fixtures/misc/grid5_final_data.json +142 -0
- data/spec/fixtures/misc/grid5_final_meta.json +152 -0
- data/spec/fixtures/misc/grid_log_errs.json +418 -0
- data/spec/models/data_grid_spec.rb +689 -626
- data/spec/models/import_type_spec.rb +5 -5
- data/spec/spec_helper.rb +9 -7
- data/spec/support/users.rb +1 -1
- metadata +22 -3
- data/app/models/marty/role.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46bed6f3169beec2514318a672e7b116ef0031f679f774cb51bb14dc9fa935fd
|
4
|
+
data.tar.gz: 74d430112df6bdf70bf9fa91d0efae7fbba4a0de08d53cb8f6cc9ab0d8029945
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ba1fa3171e1887fe79331dcb623ef1936557e16b238117c84c3b6dff4d0b519db0596f64e0dd4fa4ec08bd2703e715c3d21bcf6d5d11c8cb46d1d4e1991a46d
|
7
|
+
data.tar.gz: cdf887a7e66c5ce77f9c23f6467460d6b71b74f2e146fa7c7e1bfe79ad6d5534803a9930221376101683df68e360c0f30aba75072a7574c641fab4252210f818
|
data/.gitlab-ci.yml
CHANGED
@@ -36,15 +36,16 @@ cache:
|
|
36
36
|
- vendor/ruby
|
37
37
|
|
38
38
|
variables:
|
39
|
-
GIT_SSL_NO_VERIFY: "true"
|
40
39
|
BUNDLER_VERSION: "2.0.1"
|
40
|
+
DOCKER_AUTH_CONFIG: '{ "credsStore": "ecr-login" }'
|
41
|
+
GIT_SSL_NO_VERIFY: "true"
|
42
|
+
HEADLESS: "true"
|
43
|
+
HEADLESS_WINDOW_SIZE: "1400,1400"
|
44
|
+
PGTZ: "America/Los_Angeles"
|
41
45
|
POSTGRES_USER: "runner"
|
42
46
|
POSTGRES_PASSWORD: ""
|
43
47
|
RAILS_ENV: "test"
|
44
48
|
RAILS_DUMP_SCHEMA: "false"
|
45
|
-
PGTZ: "America/Los_Angeles"
|
46
|
-
HEADLESS: "true"
|
47
|
-
HEADLESS_WINDOW_SIZE: "1400,1400"
|
48
49
|
REPOSITORY_URL: '415596832415.dkr.ecr.us-west-2.amazonaws.com/cm_tech/docker-ruby-ci-image'
|
49
|
-
|
50
|
+
RSPEC_AUTO_RETRY_JS: "true"
|
50
51
|
|
data/Gemfile.lock
CHANGED
@@ -62,9 +62,27 @@ module Marty; class DataGridView < McflyGridPanel
|
|
62
62
|
javascript
|
63
63
|
end
|
64
64
|
|
65
|
+
def self.edit_grid_js(options = {})
|
66
|
+
dg = options[:data_grid] || 'data_grid'
|
67
|
+
title_str = options[:title_str] || 'Data Grid'
|
68
|
+
|
69
|
+
javascript = l(<<-JS)
|
70
|
+
function() {
|
71
|
+
var sel = this.getSelectionModel().getSelection()[0];
|
72
|
+
var record_id = sel && sel.getId();
|
73
|
+
this.server.editGrid({record_id: record_id,
|
74
|
+
data_grid: "#{dg}",
|
75
|
+
title_str: "#{title_str}"});
|
76
|
+
}
|
77
|
+
JS
|
78
|
+
javascript
|
79
|
+
end
|
80
|
+
|
65
81
|
client_class do |c|
|
82
|
+
c.include :data_grid_edit
|
66
83
|
c.netzke_show_grid = DataGridView.show_grid_js
|
67
84
|
c.netzke_client_show_grid = DataGridView.client_show_grid_js
|
85
|
+
c.netzke_edit_grid = DataGridView.edit_grid_js
|
68
86
|
end
|
69
87
|
|
70
88
|
def configure(c)
|
@@ -79,6 +97,7 @@ module Marty; class DataGridView < McflyGridPanel
|
|
79
97
|
:hcols,
|
80
98
|
:lenient,
|
81
99
|
:data_type,
|
100
|
+
:constraint,
|
82
101
|
:created_dt,
|
83
102
|
]
|
84
103
|
|
@@ -123,6 +142,12 @@ module Marty; class DataGridView < McflyGridPanel
|
|
123
142
|
a.handler = :netzke_show_grid
|
124
143
|
end
|
125
144
|
|
145
|
+
action :edit_grid do |a|
|
146
|
+
a.text = 'Edit Grid'
|
147
|
+
a.icon_cls = 'fa fa-th-large glyph'
|
148
|
+
a.handler = :netzke_edit_grid
|
149
|
+
end
|
150
|
+
|
126
151
|
endpoint :show_grid do |params|
|
127
152
|
record_id = params[:record_id]
|
128
153
|
|
@@ -143,8 +168,42 @@ module Marty; class DataGridView < McflyGridPanel
|
|
143
168
|
client.netzke_client_show_grid maxcount, res, 'Data Grid'
|
144
169
|
end
|
145
170
|
|
171
|
+
# placeholders for grid editing permission logic.
|
172
|
+
# for now, this allows the rspec to control the permission
|
173
|
+
def self.get_edit_edit_permission
|
174
|
+
Marty::Config['grid_edit_edit_perm'] || 'edit_all'
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.get_edit_save_permission
|
178
|
+
Marty::Config['grid_edit_save_perm'] || 'edit_all'
|
179
|
+
end
|
180
|
+
|
181
|
+
endpoint :edit_grid do |params|
|
182
|
+
record_id = params[:record_id]
|
183
|
+
|
184
|
+
dg = DataGrid.find_by_id(record_id)
|
185
|
+
|
186
|
+
return client.netzke_notify('No data grid.') unless dg
|
187
|
+
|
188
|
+
meta_rows_raw, h_key_rows, data_rows = dg.export_array
|
189
|
+
res = h_key_rows + data_rows
|
190
|
+
|
191
|
+
md = dg.metadata
|
192
|
+
hdim = md.map { |m| m['dir'] == 'h' && m['attr'] }.select { |v| v }
|
193
|
+
vdim = md.map { |m| m['dir'] == 'v' && m['attr'] }.select { |v| v }
|
194
|
+
hdim_en = hdim.map { |d| I18n.t('attributes.' + d, default: d) }
|
195
|
+
vdim_en = vdim.map { |d| I18n.t('attributes.' + d, default: d) }
|
196
|
+
name = "Editing Data Grid '#{dg.name}'"
|
197
|
+
permission = Marty::DataGridView.get_edit_edit_permission
|
198
|
+
client.edit_grid(record_id, hdim_en, vdim_en, res, name, permission)
|
199
|
+
end
|
200
|
+
|
201
|
+
endpoint :save_grid do |params|
|
202
|
+
SaveGrid.call(params)
|
203
|
+
end
|
204
|
+
|
146
205
|
def default_bbar
|
147
|
-
[:show_grid] + super
|
206
|
+
[:show_grid, :edit_grid] + super
|
148
207
|
end
|
149
208
|
|
150
209
|
def default_context_menu
|
@@ -172,6 +231,10 @@ module Marty; class DataGridView < McflyGridPanel
|
|
172
231
|
c.width = 120
|
173
232
|
end
|
174
233
|
|
234
|
+
attribute :constraint do |c|
|
235
|
+
c.width = 100
|
236
|
+
end
|
237
|
+
|
175
238
|
attribute :hcols do |c|
|
176
239
|
c.label = 'Horizontal Attrs'
|
177
240
|
c.width = 200
|
@@ -0,0 +1,550 @@
|
|
1
|
+
{
|
2
|
+
createStoreAndColumns:
|
3
|
+
function(data, hdim, vdim, hcol, vcol, extra_attrs) {
|
4
|
+
var fields = [];
|
5
|
+
var columns = [];
|
6
|
+
for (var i=0; i< data[0].length; i++) {
|
7
|
+
fields.push("a" + i);
|
8
|
+
columns.push({dataIndex: "a" + i, text: i,
|
9
|
+
sortable: false,
|
10
|
+
editor: 'textfield',
|
11
|
+
renderer: function(value, meta) {
|
12
|
+
var hlen = hdim.length,
|
13
|
+
vlen = vdim.length,
|
14
|
+
row = meta.rowIndex,
|
15
|
+
col = meta.column.fullColumnIndex;
|
16
|
+
if (extra_attrs[row][col]) {
|
17
|
+
meta.tdStyle = extra_attrs[row][col][0]
|
18
|
+
meta.tdAttr = Ext.String.format('data-qtip="{0}"',
|
19
|
+
extra_attrs[row][col][1]);
|
20
|
+
}
|
21
|
+
if (row < hlen && col >= vlen)
|
22
|
+
{
|
23
|
+
meta.tdStyle = hcol[row];
|
24
|
+
meta.tdAttr = Ext.String.format('data-qtip="{0}"',
|
25
|
+
hdim[row]);
|
26
|
+
}
|
27
|
+
if (col < vlen && row >= hlen)
|
28
|
+
{
|
29
|
+
meta.tdStyle = vcol[col];
|
30
|
+
meta.tdAttr = Ext.String.format('data-qtip="{0}"',
|
31
|
+
vdim[col]);
|
32
|
+
}
|
33
|
+
|
34
|
+
return value;
|
35
|
+
},
|
36
|
+
autoSizeColumn: true});
|
37
|
+
}
|
38
|
+
var thestore = Ext.create('Ext.data.ArrayStore', {
|
39
|
+
fields: fields,
|
40
|
+
data: data,
|
41
|
+
});
|
42
|
+
return [columns, thestore];
|
43
|
+
},
|
44
|
+
editGrid:
|
45
|
+
function(record_id, hdim, vdim, data, title_str, permission) {
|
46
|
+
var colors = [
|
47
|
+
|
48
|
+
'background-color: #FFA1A1;',
|
49
|
+
'background-color: #FF9D5C;',
|
50
|
+
'background-color: #A1A1FF;',
|
51
|
+
'background-color: #FFFFA1;',
|
52
|
+
'background-color: #A1FFFF;',
|
53
|
+
'background-color: #FFA1FF;',
|
54
|
+
'background-color: #A1A187;',
|
55
|
+
'background-color: #D1FFD1;',
|
56
|
+
'background-color: #FFD1D1;',
|
57
|
+
'background-color: #FF9D7C;',
|
58
|
+
'background-color: #D1D1FF;',
|
59
|
+
'background-color: #FFFFD1;',
|
60
|
+
'background-color: #D1FFFF;',
|
61
|
+
'background-color: #FFD1FF;',
|
62
|
+
'background-color: #D1D1B7;',
|
63
|
+
];
|
64
|
+
var hcol = [];
|
65
|
+
var vcol = [];
|
66
|
+
|
67
|
+
var me = this;
|
68
|
+
// setup colors for hdims
|
69
|
+
for (var i=0; i<hdim.length; i++)
|
70
|
+
hcol[i] = colors.pop();
|
71
|
+
// setup colors for vdims
|
72
|
+
for (var i=0; i<vdim.length; i++)
|
73
|
+
vcol[i] = colors.pop();
|
74
|
+
var columns, thestore;
|
75
|
+
|
76
|
+
var createArray = function (length) {
|
77
|
+
var arr = new Array(length || 0),
|
78
|
+
i = length;
|
79
|
+
|
80
|
+
if (arguments.length > 1) {
|
81
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
82
|
+
while(i--) arr[length-1 - i] = createArray.apply(this, args);
|
83
|
+
}
|
84
|
+
|
85
|
+
return arr;
|
86
|
+
};
|
87
|
+
|
88
|
+
var extra_attrs = createArray(data.length, data[0].length);
|
89
|
+
|
90
|
+
[columns, thestore] = this.createStoreAndColumns(data, hdim, vdim, hcol, vcol, extra_attrs);
|
91
|
+
|
92
|
+
var dirty = false;
|
93
|
+
var setDirty = function() { dirty = true; };
|
94
|
+
var getDirty = function() { return dirty; };
|
95
|
+
var me = this;
|
96
|
+
var dataUpdate = function(grid, modFunc) {
|
97
|
+
var store_data = grid.getStore().data.items;
|
98
|
+
var newcolumns, newstore;
|
99
|
+
var mod_data = [];
|
100
|
+
modFunc(store_data, mod_data);
|
101
|
+
var extra_attrs = createArray(mod_data.length, mod_data[0].length);
|
102
|
+
[newcolumns, newstore] = me.createStoreAndColumns(mod_data, hdim, vdim, hcol, vcol, extra_attrs);
|
103
|
+
grid.reconfigure(newstore, newcolumns);
|
104
|
+
Ext.each(grid.getColumns(), function(column) {
|
105
|
+
column.autoSize();
|
106
|
+
});
|
107
|
+
setDirty();
|
108
|
+
};
|
109
|
+
var insertRow = function(inData, outData, rowIdx, up) {
|
110
|
+
var width = inData[0].fields.length -1
|
111
|
+
var newa = Array.apply(null, Array(width)).map(function () { return ""; });
|
112
|
+
var target = up ? rowIdx : rowIdx + 1;
|
113
|
+
for (var i=0; i<= inData.length; ++i) {
|
114
|
+
if (i == target) {
|
115
|
+
outData.push(newa);
|
116
|
+
}
|
117
|
+
if (i < inData.length) {
|
118
|
+
var row = [];
|
119
|
+
for (const [key, value] of Object.entries(inData[i].data)) {
|
120
|
+
if (key != 'id') row.push(value);
|
121
|
+
}
|
122
|
+
outData.push(row);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
};
|
126
|
+
var insertCol = function(inData, outData, colIdx, left) {
|
127
|
+
var target = left ? colIdx : colIdx + 1
|
128
|
+
var row_width = inData[0].fields.length;
|
129
|
+
for (var i=0; i< inData.length; ++i) {
|
130
|
+
var row = [];
|
131
|
+
var idx = 0;
|
132
|
+
for (const [key, value] of Object.entries(inData[i].data)) {
|
133
|
+
if (idx == target) row.push(null);
|
134
|
+
if (key != 'id') row.push(value);
|
135
|
+
++idx;
|
136
|
+
}
|
137
|
+
if (target == row_width) row.push(null);
|
138
|
+
outData.push(row);
|
139
|
+
}
|
140
|
+
};
|
141
|
+
var deleteRow = function(inData, outData, rowIdx) {
|
142
|
+
for (var i=0; i< inData.length; ++i) {
|
143
|
+
if (i != rowIdx) {
|
144
|
+
var row = [];
|
145
|
+
for (const [key, value] of Object.entries(inData[i].data)) {
|
146
|
+
if (key != 'id')
|
147
|
+
row.push(value);
|
148
|
+
}
|
149
|
+
outData.push(row);
|
150
|
+
}
|
151
|
+
}
|
152
|
+
};
|
153
|
+
var deleteCol = function(inData, outData, colIdx) {
|
154
|
+
for (var i=0; i< inData.length; ++i) {
|
155
|
+
var row = [];
|
156
|
+
var idx = 0;
|
157
|
+
for (const [key, value] of Object.entries(inData[i].data)) {
|
158
|
+
if (idx != colIdx && key != 'id')
|
159
|
+
row.push(value);
|
160
|
+
++idx;
|
161
|
+
}
|
162
|
+
outData.push(row);
|
163
|
+
}
|
164
|
+
};
|
165
|
+
var lookup_grid = function () {
|
166
|
+
return Ext.ComponentQuery.query('grid').find(function(v) {
|
167
|
+
return v.name=='data_grid_edit_grid'
|
168
|
+
});
|
169
|
+
};
|
170
|
+
var lookup_win = function () {
|
171
|
+
return Ext.ComponentQuery.query('window').find(function(v) {
|
172
|
+
return v.name=='data_grid_edit_window'
|
173
|
+
});
|
174
|
+
};
|
175
|
+
var insertRowAboveAction = Ext.create('Ext.Action', {
|
176
|
+
text: 'Insert Row Above',
|
177
|
+
handler: function(widget , event) {
|
178
|
+
var grid = lookup_grid();
|
179
|
+
dataUpdate(grid, function(data, mod_data) {
|
180
|
+
insertRow(data, mod_data, widget.position.row, true);
|
181
|
+
});
|
182
|
+
}
|
183
|
+
});
|
184
|
+
var insertRowBelowAction = Ext.create('Ext.Action', {
|
185
|
+
text: 'Insert Row Below',
|
186
|
+
handler: function(widget , event) {
|
187
|
+
var grid = lookup_grid();
|
188
|
+
dataUpdate(grid, function(data, mod_data) {
|
189
|
+
insertRow(data, mod_data, widget.position.row, false);
|
190
|
+
});
|
191
|
+
}
|
192
|
+
});
|
193
|
+
var insertColLeftAction = Ext.create('Ext.Action', {
|
194
|
+
text: 'Insert Column Left',
|
195
|
+
handler: function(widget , event) {
|
196
|
+
var grid = lookup_grid();
|
197
|
+
dataUpdate(grid, function(data, mod_data) {
|
198
|
+
insertCol(data, mod_data, widget.position.col, true);
|
199
|
+
});
|
200
|
+
}
|
201
|
+
});
|
202
|
+
var insertColRightAction = Ext.create('Ext.Action', {
|
203
|
+
text: 'Insert Column Right',
|
204
|
+
handler: function(widget , event) {
|
205
|
+
var grid = lookup_grid();
|
206
|
+
dataUpdate(grid, function(data, mod_data) {
|
207
|
+
insertCol(data, mod_data, widget.position.col, false);
|
208
|
+
});
|
209
|
+
}
|
210
|
+
});
|
211
|
+
var deleteRowAction = Ext.create('Ext.Action', {
|
212
|
+
text: 'Delete Row',
|
213
|
+
handler: function(widget , event) {
|
214
|
+
var grid = lookup_grid();
|
215
|
+
dataUpdate(grid, function(data, mod_data) {
|
216
|
+
deleteRow(data, mod_data, widget.position.row);
|
217
|
+
});
|
218
|
+
}
|
219
|
+
});
|
220
|
+
var deleteColAction = Ext.create('Ext.Action', {
|
221
|
+
text: 'Delete Column',
|
222
|
+
handler: function(widget , event) {
|
223
|
+
var grid = lookup_grid();
|
224
|
+
dataUpdate(grid, function(data, mod_data) {
|
225
|
+
deleteCol(data, mod_data, widget.position.col);
|
226
|
+
});
|
227
|
+
}
|
228
|
+
});
|
229
|
+
var itemContextMenu = Ext.create('Ext.menu.Menu' , {
|
230
|
+
items: [insertRowAboveAction, insertRowBelowAction, insertColLeftAction,
|
231
|
+
insertColRightAction, deleteRowAction, deleteColAction]
|
232
|
+
});
|
233
|
+
|
234
|
+
// Selection Models
|
235
|
+
var spSel = Ext.create("Ext.grid.selection.SpreadsheetModel", {
|
236
|
+
cellSelect: true,
|
237
|
+
columnSelect: true,
|
238
|
+
rowSelect: false,
|
239
|
+
extensible: true,
|
240
|
+
mode: "MULTI"
|
241
|
+
});
|
242
|
+
|
243
|
+
var get_area = function(row, col) {
|
244
|
+
var row_hdim = row < hdim.length,
|
245
|
+
row_vdim = col < vdim.length;
|
246
|
+
|
247
|
+
var a;
|
248
|
+
if (row_hdim && row_vdim)
|
249
|
+
a = 'blank_area';
|
250
|
+
else if (!row_hdim && !row_vdim)
|
251
|
+
a = 'data_area';
|
252
|
+
else if (row_hdim && !row_vdim)
|
253
|
+
a = 'hdim_area';
|
254
|
+
else a = 'vdim_area';
|
255
|
+
return a;
|
256
|
+
};
|
257
|
+
|
258
|
+
var can_edit = function(row, col) {
|
259
|
+
var area = get_area(row, col);
|
260
|
+
if (area == 'blank_area')
|
261
|
+
return false;
|
262
|
+
if (area == 'data_area')
|
263
|
+
return permission != 'view';
|
264
|
+
if (area == 'vdim_area' || area == 'hdim_area')
|
265
|
+
return permission == 'edit_all';
|
266
|
+
}
|
267
|
+
|
268
|
+
// Plugins
|
269
|
+
var cellEditor = Ext.create("Ext.grid.plugin.CellEditing", {
|
270
|
+
|
271
|
+
clicksToEdit: 2,
|
272
|
+
listeners: {
|
273
|
+
beforeedit: function(editor, context, eOpts) {
|
274
|
+
if (!can_edit(context.rowIdx, context.colIdx))
|
275
|
+
return false;
|
276
|
+
},
|
277
|
+
afteredit: function() {
|
278
|
+
setDirty();
|
279
|
+
}
|
280
|
+
}
|
281
|
+
});
|
282
|
+
|
283
|
+
var context_disable_fn = function(menu, label, x, y, fn) {
|
284
|
+
var mi = menu.items.items.find(function (mi) {
|
285
|
+
return mi.text == label;
|
286
|
+
});
|
287
|
+
if (fn(x, y))
|
288
|
+
mi.enable();
|
289
|
+
else
|
290
|
+
mi.disable();
|
291
|
+
};
|
292
|
+
// check to see if menu item should be enabled based on row, col
|
293
|
+
var row_menu_chk = function (row, col) {
|
294
|
+
var area = get_area(row, col);
|
295
|
+
if (vdim.length == 0)
|
296
|
+
return false;
|
297
|
+
return (area == 'vdim_area' || area == 'data_area') &&
|
298
|
+
permission == 'edit_all';
|
299
|
+
};
|
300
|
+
var col_menu_chk = function (row, col) {
|
301
|
+
var area = get_area(row, col);
|
302
|
+
if (hdim.length == 0)
|
303
|
+
return false;
|
304
|
+
return (area == 'hdim_area' || area == 'data_area') &&
|
305
|
+
permission == 'edit_all';
|
306
|
+
};
|
307
|
+
var disable_conds = [
|
308
|
+
['Insert Row Above', row_menu_chk],
|
309
|
+
['Insert Row Below', row_menu_chk],
|
310
|
+
['Delete Row', row_menu_chk],
|
311
|
+
['Insert Column Left', col_menu_chk],
|
312
|
+
['Insert Column Right', col_menu_chk],
|
313
|
+
['Delete Column', col_menu_chk]
|
314
|
+
];
|
315
|
+
Ext.define('DGEdit.grid.plugin.Clipboard',{
|
316
|
+
override: 'Ext.grid.plugin.Clipboard',
|
317
|
+
beforepaste: Ext.emptyFn,
|
318
|
+
mixins: [
|
319
|
+
'Ext.mixin.Observable'
|
320
|
+
],
|
321
|
+
constructor: function(config) {
|
322
|
+
var me = this;
|
323
|
+
|
324
|
+
me.callParent([config]);
|
325
|
+
me.mixins.observable.constructor.call(me);
|
326
|
+
},
|
327
|
+
privates : {
|
328
|
+
onPaste: function (keyCode, event) {
|
329
|
+
var me = this,
|
330
|
+
sharedData = me.shared.data,
|
331
|
+
source = me.getSource(),
|
332
|
+
i, n, s,
|
333
|
+
rowIdx = event.position.rowIdx,
|
334
|
+
colIdx = event.position.colIdx;
|
335
|
+
if (!can_edit(rowIdx, colIdx)) {
|
336
|
+
return;
|
337
|
+
}
|
338
|
+
if (me.fireEvent('beforepaste',keyCode,event,me.cmp) !== false) {
|
339
|
+
if (source) {
|
340
|
+
for (i = 0, n = source.length; i < n; ++i) {
|
341
|
+
s = source[i];
|
342
|
+
if (s === 'system') {
|
343
|
+
// get the format used by the system clipboard.
|
344
|
+
s = me.getSystem();
|
345
|
+
me.pasteClipboardData(s);
|
346
|
+
break;
|
347
|
+
} else if (sharedData && (s in sharedData)) {
|
348
|
+
me.doPaste(s, sharedData[s]);
|
349
|
+
break;
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
}
|
355
|
+
}
|
356
|
+
});
|
357
|
+
Ext.define('DGEditController', {
|
358
|
+
extend : 'Ext.app.ViewController',
|
359
|
+
alias: 'controller.dataGridEdit',
|
360
|
+
onBeforePaste:function(keyCode,event,grid){
|
361
|
+
return false;
|
362
|
+
}
|
363
|
+
});
|
364
|
+
Ext.tip.Tip.prototype.minWidth = void 0;
|
365
|
+
Ext.tip.QuickTipManager.init();
|
366
|
+
var grid = {
|
367
|
+
xtype: 'grid',
|
368
|
+
name: 'data_grid_edit_grid',
|
369
|
+
border: false,
|
370
|
+
hideHeaders: false,
|
371
|
+
autoEncode: true,
|
372
|
+
controller: 'dataGridEdit',
|
373
|
+
columns: columns,
|
374
|
+
scrollable: true,
|
375
|
+
anchor: '100% 100%',
|
376
|
+
forceFit: true,
|
377
|
+
store: thestore,
|
378
|
+
hideHeaders: true,
|
379
|
+
columnLines: true,
|
380
|
+
plugins: [cellEditor,
|
381
|
+
{
|
382
|
+
ptype: 'clipboard',
|
383
|
+
system: 'raw',
|
384
|
+
// listeners: {
|
385
|
+
// beforepaste: 'onBeforePaste'
|
386
|
+
// }
|
387
|
+
}],
|
388
|
+
selModel: spSel,
|
389
|
+
listeners: {
|
390
|
+
containercontextmenu: function(view, e) {
|
391
|
+
e.preventDefault();
|
392
|
+
},
|
393
|
+
contextmenu: function(e, element, options) {
|
394
|
+
e.preventDefault();
|
395
|
+
},
|
396
|
+
itemcontextmenu: function(view, record, item, index, e) {
|
397
|
+
e.stopEvent();
|
398
|
+
var items = itemContextMenu.items.items;
|
399
|
+
var ctn = items.length;
|
400
|
+
var y = e.position.colIdx;
|
401
|
+
var x = e.position.rowIdx;
|
402
|
+
for (var i=0; i<ctn; ++i) {
|
403
|
+
items[i].position = {col: y, row: x};
|
404
|
+
}
|
405
|
+
for (const [label, fn] of disable_conds) {
|
406
|
+
context_disable_fn(itemContextMenu, label, x, y, fn);
|
407
|
+
}
|
408
|
+
itemContextMenu.showAt(e.getXY());
|
409
|
+
},
|
410
|
+
|
411
|
+
}
|
412
|
+
};
|
413
|
+
var fbsave = {
|
414
|
+
text: 'Save',
|
415
|
+
handler: function () {
|
416
|
+
this.up('window').submit();
|
417
|
+
}};
|
418
|
+
var fbcancel = {
|
419
|
+
text: 'Cancel',
|
420
|
+
handler: function () {
|
421
|
+
this.up('window').close();
|
422
|
+
}};
|
423
|
+
var fbar = permission == 'view' ?
|
424
|
+
[fbcancel] : [fbsave, fbcancel];
|
425
|
+
|
426
|
+
Ext.create('Ext.Window', {
|
427
|
+
name: 'data_grid_edit_window',
|
428
|
+
height: "90%",
|
429
|
+
width: "90%",
|
430
|
+
x: 100,
|
431
|
+
y: 100,
|
432
|
+
autoWidth: true,
|
433
|
+
modal: true,
|
434
|
+
autoScroll: true,
|
435
|
+
title: title_str,
|
436
|
+
layout: 'anchor',
|
437
|
+
items: grid,
|
438
|
+
submit: function() {
|
439
|
+
Ext.getBody().mask('Saving data...').setZIndex(99999).setStyle('cursor', 'wait');
|
440
|
+
var grid = Ext.ComponentQuery.query('grid').find(function(v) {
|
441
|
+
return v.name=='data_grid_edit_grid'
|
442
|
+
});
|
443
|
+
var server = me.server;
|
444
|
+
|
445
|
+
if (getDirty() || grid.getStore().getModifiedRecords().length > 0) {
|
446
|
+
var store = grid.getStore().data.items;
|
447
|
+
var ret = [];
|
448
|
+
|
449
|
+
for (var i = 0; i < store.length; ++i) {
|
450
|
+
var row = {};
|
451
|
+
|
452
|
+
var col_idx = 0;
|
453
|
+
|
454
|
+
// or remove label fields we added in top left
|
455
|
+
var col_null = i < hdim.length ? vdim.length : 0;
|
456
|
+
|
457
|
+
for (const [key, value] of Object.entries(store[i].data)) {
|
458
|
+
if (key != 'id') {
|
459
|
+
if (col_idx < col_null)
|
460
|
+
row[key] = "";
|
461
|
+
else
|
462
|
+
row[key] = value;
|
463
|
+
}
|
464
|
+
col0_skip = false;
|
465
|
+
col_idx++;
|
466
|
+
}
|
467
|
+
ret.push(row);
|
468
|
+
}
|
469
|
+
server.saveGrid({record_id: record_id, data: ret}, function(res) {
|
470
|
+
if (res) {
|
471
|
+
if (res['errorMessage']) {
|
472
|
+
Ext.MessageBox.show({
|
473
|
+
title:'Error in data',
|
474
|
+
msg: 'error: ' + res['errorMessage'],
|
475
|
+
buttons: Ext.Msg.OK
|
476
|
+
});
|
477
|
+
} else if (res['problemArray']) {
|
478
|
+
Ext.MessageBox.show({
|
479
|
+
title:'Error in data',
|
480
|
+
msg: 'error: some entries failed constraint or data type check',
|
481
|
+
buttons: Ext.Msg.OK
|
482
|
+
});
|
483
|
+
for (const [type, x, y] of res['problemArray']) {
|
484
|
+
var real_x = vdim.length + x,
|
485
|
+
real_y = hdim.length + y;
|
486
|
+
if (type == 'constraint')
|
487
|
+
extra_attrs[real_y][real_x] = ['background-color: #FF8181;','failed constraint check'];
|
488
|
+
else if (type == 'type')
|
489
|
+
extra_attrs[real_y][real_x] = ['background-color: #FFB181;','failed type check'];
|
490
|
+
}
|
491
|
+
grid.getView().refresh();
|
492
|
+
}
|
493
|
+
}
|
494
|
+
else
|
495
|
+
{
|
496
|
+
var win = lookup_win();
|
497
|
+
win.destroy();
|
498
|
+
}
|
499
|
+
Ext.getBody().unmask();
|
500
|
+
});
|
501
|
+
}
|
502
|
+
else
|
503
|
+
{
|
504
|
+
Ext.getBody().unmask();
|
505
|
+
Ext.MessageBox.show({
|
506
|
+
title:'Nothing to Save',
|
507
|
+
msg: 'No changes made',
|
508
|
+
buttons: Ext.Msg.OK
|
509
|
+
});
|
510
|
+
}
|
511
|
+
},
|
512
|
+
closeAction: 'destroy',
|
513
|
+
listeners: {
|
514
|
+
beforeclose: function (win) {
|
515
|
+
var grid = lookup_grid();
|
516
|
+
if(win.closeMe) {
|
517
|
+
win.closeMe = false;
|
518
|
+
grid.destroy();
|
519
|
+
return true;
|
520
|
+
}
|
521
|
+
if (getDirty() || grid.getStore().getModifiedRecords().length > 0) {
|
522
|
+
Ext.MessageBox.show({
|
523
|
+
title:'Discard Changes?',
|
524
|
+
msg: 'You are closing a window that has unsaved changes. Are you sure?',
|
525
|
+
buttons: Ext.Msg.YESNO,
|
526
|
+
icon: Ext.Msg.QUESTION,
|
527
|
+
callback: function(btn) {
|
528
|
+
if('yes' === btn) {
|
529
|
+
win.closeMe = true;
|
530
|
+
win.close();
|
531
|
+
}
|
532
|
+
}
|
533
|
+
});
|
534
|
+
return false;
|
535
|
+
}
|
536
|
+
grid.destroy();
|
537
|
+
return true;
|
538
|
+
}
|
539
|
+
},
|
540
|
+
fbar: fbar
|
541
|
+
}).show();
|
542
|
+
var gridobj = Ext.ComponentQuery.query('grid').find(function(v) {
|
543
|
+
return v.name=='data_grid_edit_grid'
|
544
|
+
});
|
545
|
+
Ext.each(gridobj.getColumns(), function(column) {
|
546
|
+
column.autoSize();
|
547
|
+
column.setWidth(column.getWidth()+20);
|
548
|
+
});
|
549
|
+
}
|
550
|
+
}
|