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.
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
+ }