marty 2.9.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +6 -5
  3. data/Gemfile.lock +1 -1
  4. data/app/components/marty/data_grid_view.rb +64 -1
  5. data/app/components/marty/data_grid_view/client/data_grid_edit.js +550 -0
  6. data/app/components/marty/import_type_view.rb +11 -2
  7. data/app/components/marty/main_auth_app.rb +23 -23
  8. data/app/components/marty/promise_view.rb +1 -1
  9. data/app/components/marty/report_select.rb +1 -0
  10. data/app/components/marty/script_form.rb +1 -1
  11. data/app/components/marty/user_view.rb +30 -18
  12. data/app/models/marty/data_grid.rb +34 -8
  13. data/app/models/marty/import_type.rb +2 -4
  14. data/app/models/marty/role_type.rb +10 -0
  15. data/app/models/marty/user.rb +9 -4
  16. data/app/models/marty/user_role.rb +2 -3
  17. data/app/models/marty/vw_promise.rb +2 -2
  18. data/app/services/marty/data_grid/constraint.rb +73 -0
  19. data/app/services/marty/data_grid_view/save_grid.rb +63 -0
  20. data/config/locales/en.yml +1 -0
  21. data/db/migrate/107_add_data_grid_constraint.rb +8 -0
  22. data/db/migrate/507_migrate_marty_roles_to_enum.rb +72 -0
  23. data/db/seeds.rb +1 -6
  24. data/lib/marty/permissions.rb +6 -16
  25. data/lib/marty/version.rb +1 -1
  26. data/spec/dummy/config/locales/en.yml +1 -0
  27. data/spec/features/data_grid_spec.rb +499 -0
  28. data/spec/features/data_import_spec.rb +11 -8
  29. data/spec/features/user_view_spec.rb +1 -1
  30. data/spec/fixtures/json/data_grid.json +210 -0
  31. data/spec/fixtures/misc/data_grid_1.txt +15 -0
  32. data/spec/fixtures/misc/data_grid_2.txt +17 -0
  33. data/spec/fixtures/misc/data_grid_3.txt +9 -0
  34. data/spec/fixtures/misc/data_grid_4.txt +5 -0
  35. data/spec/fixtures/misc/data_grid_5.txt +19 -0
  36. data/spec/fixtures/misc/grid1_final_data.json +23 -0
  37. data/spec/fixtures/misc/grid1_final_meta.json +182 -0
  38. data/spec/fixtures/misc/grid2_final_data.json +11 -0
  39. data/spec/fixtures/misc/grid2_final_meta.json +181 -0
  40. data/spec/fixtures/misc/grid5_final_data.json +142 -0
  41. data/spec/fixtures/misc/grid5_final_meta.json +152 -0
  42. data/spec/fixtures/misc/grid_log_errs.json +418 -0
  43. data/spec/models/data_grid_spec.rb +689 -626
  44. data/spec/models/import_type_spec.rb +5 -5
  45. data/spec/spec_helper.rb +9 -7
  46. data/spec/support/users.rb +1 -1
  47. metadata +22 -3
  48. data/app/models/marty/role.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dcf52a2940fd4cdfa856cf82c2d77dd2ed9536b88d192addfed0d1ab3a9a9d0
4
- data.tar.gz: 8becb3090ad127df9823f11f1fa4b8719a22f1401cf6e02f6121c11e650e99c2
3
+ metadata.gz: 46bed6f3169beec2514318a672e7b116ef0031f679f774cb51bb14dc9fa935fd
4
+ data.tar.gz: 74d430112df6bdf70bf9fa91d0efae7fbba4a0de08d53cb8f6cc9ab0d8029945
5
5
  SHA512:
6
- metadata.gz: 50b7892041ea4f9d4ff6a63f982f29e7edeff67f313fa241b36d24fe18df79126f6d8caaa2ace032696c629f188413be9023e2582403951a5835ce83e448e7d1
7
- data.tar.gz: f0fe264a2c2150ca4e85d30f2592d65c75f9fab66cad38307007361dd9b3eda7c1ca21c0cc5fa25bc70cf86d4c50182d32d150b0f37dd1f5f7cc92baf1f0f4fe
6
+ metadata.gz: 8ba1fa3171e1887fe79331dcb623ef1936557e16b238117c84c3b6dff4d0b519db0596f64e0dd4fa4ec08bd2703e715c3d21bcf6d5d11c8cb46d1d4e1991a46d
7
+ data.tar.gz: cdf887a7e66c5ce77f9c23f6467460d6b71b74f2e146fa7c7e1bfe79ad6d5534803a9930221376101683df68e360c0f30aba75072a7574c641fab4252210f818
@@ -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
- DOCKER_AUTH_CONFIG: '{ "credsStore": "ecr-login" }'
50
+ RSPEC_AUTO_RETRY_JS: "true"
50
51
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marty (2.9.3)
4
+ marty (2.10.0)
5
5
  aws-sigv4 (~> 1.0, >= 1.0.2)
6
6
  axlsx (= 3.0.0pre)
7
7
  coderay
@@ -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
+ }