]> git.datanom.net - omvzfs.git/blob - gui/js/omv/module/admin/storage/zfs/TreePanel.js
004a246aea2a3a535850cf27bf574ccc24d1e3b7
[omvzfs.git] / gui / js / omv / module / admin / storage / zfs / TreePanel.js
1 /**
2 * This file is part of OpenMediaVault.
3 *
4 * @license http://www.gnu.org/licenses/gpl.html GPL Version 3
5 * @author Volker Theile <volker.theile@openmediavault.org>
6 * @copyright Copyright (c) 2009-2014 Volker Theile
7 *
8 * OpenMediaVault is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * any later version.
12 *
13 * OpenMediaVault is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with OpenMediaVault. If not, see <http://www.gnu.org/licenses/>.
20 */
21 // require("js/omv/tree/Panel.js")
22 // require("js/omv/grid/Panel.js")
23 // require("js/omv/grid/column/BinaryUnit.js")
24 // require("js/omv/grid/column/BooleanIcon.js")
25 // require("js/omv/grid/column/BooleanText.js")
26 // require("js/omv/grid/column/Empty.js")
27 // require("js/omv/grid/column/Hyperlink.js")
28 // require("js/omv/grid/column/UnixTimestamp.js")
29 // require("js/omv/grid/column/WhiteSpace.js")
30 // require("js/omv/window/MessageBox.js")
31
32 /**
33 * @ingroup webgui
34 * @class OMV.workspace.grid.Panel
35 * @derived OMV.grid.Panel
36 * An enhanced grid panel. This grid provides 'Add', 'Edit' and 'Delete'
37 * buttons in the toolbar by default. The basic delete functionality is also
38 * implemented, simply overwrite the 'doDeletion' and 'afterDeletion'
39 * functions to implement fit your requirements. To implement the 'Add' and
40 * 'Edit' functionality overwrite the 'onAdd' and 'onEdit' callback
41 * functions. A paging toolbar which is displayed at the bottom of the grid
42 * can be displayed also. It is also possible to reload the grid
43 * automatically in a given interval.
44 * @param hideTopToolbar TRUE to hide the whole toolbar. Defaults to FALSE.
45 * @param hidePagingToolbar TRUE to hide the paging toolbar at the bottom of
46 * the grid. Defaults to TRUE.
47 * @param hideAddButton Hide the 'Add' button in the top toolbar.
48 * Defaults to FALSE.
49 * @param hideEditButton Hide the 'Edit' button in the top toolbar.
50 * Defaults to FALSE.
51 * @param hideDeleteButton Hide the 'Delete' button in the top toolbar.
52 * Defaults to FALSE.
53 * @param hideUpButton Hide the 'Up' button in the top toolbar.
54 * Defaults to TRUE.
55 * @param hideDownButton Hide the 'Down' button in the top toolbar.
56 * Defaults to TRUE.
57 * @param hideApplyButton Hide the 'Apply' button in the top toolbar.
58 * Defaults to TRUE.
59 * @param hideRefreshButton Hide the 'Refresh' button in the top toolbar.
60 * Defaults to TRUE.
61 * @param addButtonText The button text. Defaults to 'Add'.
62 * @param editButtonText The button text. Defaults to 'Edit'.
63 * @param deleteButtonText The button text. Defaults to 'Delete'.
64 * @param upButtonText The button text. Defaults to 'Up'.
65 * @param downButtonText The button text. Defaults to 'Down'.
66 * @param applyButtonText The button text. Defaults to 'Save'.
67 * @param refreshButtonText The button text. Defaults to 'Refresh'.
68 * @param deletionConfirmRequired Set to TRUE to force the user to confirm
69 * the deletion request. Defaults to TRUE.
70 * @param deletionWaitMsg The message displayed during the deletion process.
71 * @param mode The mode how to retrieve the data displayed in the grid panel.
72 * This can be 'local' or 'remote' which means the data is requested via
73 * RPC. Defaults to 'remote'.
74 * @param rememberSelected TRUE to reselect the previous selected rows
75 * after the grid content has been reloaded/refreshed. Defaults to FALSE.
76 */
77 Ext.define("OMV.module.admin.storage.zfs.TreePanel", {
78 extend: "OMV.tree.Panel",
79 requires: [
80 "OMV.window.MessageBox",
81 "OMV.grid.column.BinaryUnit",
82 "OMV.grid.column.BooleanIcon",
83 "OMV.grid.column.BooleanText",
84 "OMV.grid.column.Empty",
85 "OMV.grid.column.Hyperlink",
86 "OMV.grid.column.UnixTimestamp",
87 "OMV.grid.column.WhiteSpace"
88 ],
89
90 border: false,
91 rowLines: false,
92 columnLines: true,
93 selModel: {
94 allowDeselect: true,
95 mode: "SINGLE"
96 },
97
98 hideTopToolbar: false,
99 hidePagingToolbar: true,
100 hideAddButton: false,
101 hideAddObjButton: false,
102 hideEditButton: false,
103 hideDeleteButton: false,
104 hideUpButton: true,
105 hideDownButton: true,
106 hideApplyButton: true,
107 hideRefreshButton: true,
108 addButtonText: _("Add Pool"),
109 addObjButtonText: _("Add Object"),
110 editButtonText: _("Edit"),
111 deleteButtonText: _("Delete"),
112 upButtonText: _("Up"),
113 downButtonText: _("Down"),
114 applyButtonText: _("Save"),
115 refreshButtonText: _("Refresh"),
116 deletionConfirmRequired: true,
117 deletionWaitMsg: _("Deleting selected item(s)"),
118 mode: "remote",
119 rememberSelected: false,
120
121 initComponent: function() {
122 var me = this;
123 // Initialize toolbars.
124 me.dockedItems = [];
125 if(!me.hideTopToolbar) {
126 me.dockedItems.push(me.topToolbar = Ext.widget({
127 xtype: "toolbar",
128 dock: "top",
129 items: me.getTopToolbarItems(me)
130 }));
131 }
132 if(!me.hidePagingToolbar) {
133 me.dockedItems.push({
134 xtype: "toolbar",
135 dock: "bottom",
136 items: [ me.pagingToolbar = Ext.widget({
137 xtype: "pagingtoolbar",
138 store: me.store,
139 displayInfo: true,
140 displayMsg: _("Displaying items {0} - {1} of {2}"),
141 emptyMsg: _("No items to display")
142 }) ]
143 });
144 }
145 me.callParent(arguments);
146 // Register event handler.
147 // Process double clicks in grid.
148 me.on("itemdblclick", me.onItemDblClick, me);
149 // Process selections in grid, e.g. to update the toolbar.
150 var selModel = me.getSelectionModel();
151 selModel.on("selectionchange", me.onSelectionChange, me);
152 // Remember selection to restore it after the grid has been
153 // refreshed.
154 if(me.rememberSelected) {
155 me.getStore().on("beforeload", function() {
156 if(!me.rendered || Ext.isEmpty(me.getEl()))
157 return;
158 if(!selModel.hasSelection())
159 return;
160 me.previousSelected = selModel.getSelection();
161 });
162 me.getView().on("refresh", function(view) {
163 if(Ext.isEmpty(me.previousSelected))
164 return;
165 var select = [];
166 Ext.Array.each(me.previousSelected, function(r) {
167 var record = me.getStore().getById(r.getId());
168 if(!Ext.isEmpty(record))
169 select.push(record);
170 });
171 delete me.previousSelected;
172 if(select.length > 0) {
173 selModel.select(select, false, false);
174 selModel.view.focusNode(select[0]);
175 }
176 });
177 }
178 },
179
180 /**
181 * Returns the items displayed in the top toolbar.
182 * @param c This component object.
183 * @return An array of buttons displayed in the top toolbar.
184 */
185 getTopToolbarItems: function(c) {
186 var me = this;
187 return [{
188 id: me.getId() + "-add",
189 xtype: "button",
190 text: me.addButtonText,
191 icon: "images/add.png",
192 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
193 hidden: me.hideAddButton,
194 handler: Ext.Function.bind(me.onAddButton, me, [ me ]),
195 scope: me
196 },{
197 id: me.getId() + "-addobj",
198 xtype: "button",
199 text: me.addObjButtonText,
200 icon: "images/add.png",
201 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
202 hidden: me.hideAddObjButton,
203 handler: Ext.Function.bind(me.onAddObjButton, me, [ me ]),
204 scope: me,
205 disabled: true
206 },{
207 id: me.getId() + "-edit",
208 xtype: "button",
209 text: me.editButtonText,
210 icon: "images/edit.png",
211 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
212 hidden: me.hideEditButton,
213 handler: Ext.Function.bind(me.onEditButton, me, [ me ]),
214 scope: me,
215 disabled: true
216 },{
217 id: me.getId() + "-delete",
218 xtype: "button",
219 text: me.deleteButtonText,
220 icon: "images/delete.png",
221 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
222 hidden: me.hideDeleteButton,
223 handler: Ext.Function.bind(me.onDeleteButton, me, [ me ]),
224 scope: me,
225 disabled: true
226 },{
227 id: me.getId() + "-up",
228 xtype: "button",
229 text: me.upButtonText,
230 icon: "images/arrow-up.png",
231 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
232 hidden: me.hideUpButton,
233 handler: Ext.Function.bind(me.onUpButton, me, [ me ]),
234 scope: me,
235 disabled: true
236 },{
237 id: me.getId() + "-down",
238 xtype: "button",
239 text: me.downButtonText,
240 icon: "images/arrow-down.png",
241 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
242 hidden: me.hideDownButton,
243 handler: Ext.Function.bind(me.onDownButton, me, [ me ]),
244 scope: me,
245 disabled: true
246 },{
247 id: me.getId() + "-apply",
248 xtype: "button",
249 text: me.applyButtonText,
250 icon: "images/checkmark.png",
251 hidden: me.hideApplyButton,
252 handler: Ext.Function.bind(me.onApplyButton, me, [ me ]),
253 scope: me
254 },{
255 id: me.getId() + "-refresh",
256 xtype: "button",
257 text: me.refreshButtonText,
258 icon: "images/refresh.png",
259 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
260 hidden: me.hideRefreshButton,
261 handler: Ext.Function.bind(me.onRefreshButton, me, [ me ]),
262 scope: me
263 }]
264 },
265
266 /**
267 * Handler that is called whenever the selection in the grid has
268 * been changed. The top toolbar buttons will be enabled/disabled
269 * depending on how much rows has been selected.
270 * @param model The selection model
271 */
272 onSelectionChange: function(model, records) {
273 var me = this;
274 if(me.hideTopToolbar)
275 return;
276 var tbarBtnName = [ "addobj", "edit", "delete", "up", "down" ];
277 var tbarBtnDisabled = {
278 "addobj": false,
279 "edit": false,
280 "delete": false,
281 "up": true,
282 "down": true
283 };
284 // Enable/disable buttons depending on the number of selected rows.
285 if(records.length <= 0) {
286 tbarBtnDisabled["addobj"] = true;
287 tbarBtnDisabled["edit"] = true;
288 tbarBtnDisabled["delete"] = true;
289 tbarBtnDisabled["up"] = true;
290 tbarBtnDisabled["down"] = true;
291 } else if(records.length == 1) {
292 tbarBtnDisabled["addobj"] = false;
293 tbarBtnDisabled["edit"] = false;
294 tbarBtnDisabled["delete"] = false;
295 tbarBtnDisabled["up"] = false;
296 tbarBtnDisabled["down"] = false;
297 } else {
298 tbarBtnDisabled["addobj"] = true;
299 tbarBtnDisabled["edit"] = true;
300 tbarBtnDisabled["delete"] = false;
301 tbarBtnDisabled["up"] = false;
302 tbarBtnDisabled["down"] = false;
303 }
304 //Disable 'AddObj' button if selected row is a Poool or a Snapshot
305 Ext.Array.each(records, function(record) {
306 if(("Pool" == record.get("type")) ||
307 ("Snapshot" == record.get("type"))) {
308 tbarBtnDisabled["addobj"] = true;
309 return false;
310 }
311 });
312
313 // Disable 'Delete' button if a selected node is not a leaf
314 Ext.Array.each(records, function(record) {
315 if((false == record.get("leaf"))) {
316 tbarBtnDisabled["delete"] = true;
317 return false;
318 }
319 });
320
321 // Update the button controls.
322 Ext.Array.each(tbarBtnName, function(name) {
323 var tbarBtnCtrl = me.queryById(me.getId() + "-" + name);
324 if(!Ext.isEmpty(tbarBtnCtrl)) {
325 if(true == tbarBtnDisabled[name]) {
326 tbarBtnCtrl.disable();
327 } else {
328 tbarBtnCtrl.enable();
329 }
330 }
331 });
332 },
333
334 onItemDblClick: function() {
335 var me = this;
336 if(!me.hideTopToolbar && !me.hideEditButton) {
337 me.onEditButton(me);
338 }
339 },
340
341 /**
342 * Load the grid content.
343 */
344 doLoad: function() {
345 var me = this;
346 if(me.mode === "remote") {
347 me.store.load();
348 }
349 },
350
351 /**
352 * Reload the grid content.
353 */
354 doReload: function() {
355 var me = this;
356 if(me.mode === "remote") {
357 me.store.reload();
358 }
359 },
360
361 /**
362 * Handler that is called when the 'Add' button in the top toolbar
363 * is pressed. Override this method to customize the default behaviour.
364 * @param this The grid itself.
365 */
366 onAddButton: function() {
367 // Nothing to do here
368 },
369
370 /**
371 * * Handler that is called when the 'AddObj' button in the top toolbar
372 * is pressed. Override this method to customize the default behaviour.
373 * @param this The grid itself.
374 */
375 onAddObjButton: function() {
376 // Nothing to do here
377 },
378
379
380 /**
381 * Handler that is called when the 'Edit' button in the top toolbar
382 * is pressed. Override this method to customize the default behaviour.
383 * @param this The grid itself.
384 */
385 onEditButton: function() {
386 // Nothing to do here
387 },
388
389 /**
390 * Handler that is called when the 'Up' button in the top toolbar
391 * is pressed. Override this method to customize the default behaviour.
392 * @param this The grid itself.
393 */
394 onUpButton: function() {
395 var me = this;
396 var sm = me.getSelectionModel();
397 var records = sm.getSelection();
398 if(records.length > 0) {
399 // Find the smallest index of the selected rows.
400 var ltIndex = me.store.indexOf(records[0]);
401 Ext.Array.each(records, function(record) {
402 var index = me.store.indexOf(record);
403 if(ltIndex > index)
404 ltIndex = index;
405 });
406 // Calculate the index where to insert the rows.
407 var index = ltIndex - 1;
408 if(index < 0)
409 index = 0;
410 me.doMoveRows(records, index);
411 }
412 },
413
414 /**
415 * Handler that is called when the 'Down' button in the top toolbar
416 * is pressed.
417 * @param this The grid itself.
418 */
419 onDownButton: function() {
420 var me = this;
421 var sm = me.getSelectionModel();
422 var records = sm.getSelection();
423 if(records.length > 0) {
424 // Find the smallest index of the selected rows.
425 var ltIndex = me.store.indexOf(records[0]);
426 Ext.Array.each(records, function(record) {
427 var index = me.store.indexOf(record);
428 if(ltIndex > index)
429 ltIndex = index;
430 });
431 // Calculate the index where to insert the rows.
432 var index = ltIndex + records.length;
433 var count = me.store.getCount() - 1;
434 if(index > count)
435 index = count;
436 me.doMoveRows(records, index);
437 }
438 },
439
440 /**
441 * Handler that is called when the 'Apply' button in the top toolbar
442 * is pressed. Override this method to customize the default behaviour.
443 * @param this The grid itself.
444 */
445 onApplyButton: function() {
446 // Nothing to do here
447 },
448
449 /**
450 * Handler that is called when the 'Refresh' button in the top toolbar
451 * is pressed. Override this method to customize the default behaviour.
452 * @param this The grid itself.
453 */
454 onRefreshButton: function() {
455 this.doReload();
456 },
457
458 /**
459 * Move the given rows to the given index.
460 * @param records The records to move.
461 * @param index The index where to insert the rows to be moved.
462 */
463 doMoveRows: function(records, index) {
464 var me = this;
465 if(!Ext.isNumber(index))
466 return;
467 records = Ext.Array.from(records);
468 me.store.suspendEvents();
469 Ext.Array.each(records, function(record) {
470 me.store.remove(record);
471 me.store.insert(index, record);
472 });
473 me.store.resumeEvents();
474 me.afterMoveRows(records, index);
475 me.getView().refresh();
476 },
477
478 /**
479 * Function that is called after the selected rows have been moved.
480 * Override this method to customize the default behaviour.
481 * @param records The records that have been move.
482 * @param index The index where the rows have been inserted.
483 */
484 afterMoveRows: function(records, index) {
485 var sm = this.getSelectionModel();
486 sm.select(records);
487 },
488
489 /**
490 * Handler that is called when the 'Delete' button in the top toolbar
491 * is pressed.
492 */
493 onDeleteButton: function() {
494 var me = this;
495 var sm = me.getSelectionModel();
496 var records = sm.getSelection();
497 if(me.deletionConfirmRequired === true) {
498 var msg = _("Do you really want to delete the selected item(s)?");
499 OMV.MessageBox.show({
500 title: _("Confirmation"),
501 msg: msg,
502 buttons: Ext.Msg.YESNO,
503 fn: function(answer) {
504 if(answer !== "yes")
505 return;
506 me.startDeletion(records);
507 },
508 scope: me,
509 icon: Ext.Msg.QUESTION
510 });
511 } else {
512 me.startDeletion(records);
513 }
514 },
515
516 /**
517 * @private
518 * Private method that is called when the deletion of the selected records
519 * has been aggreed.
520 * @param records The records to delete.
521 */
522 startDeletion: function(records) {
523 var me = this;
524 if(records.length <= 0)
525 return;
526 // Store selected records in a local variable
527 me.delActionInfo = {
528 records: records,
529 count: records.length
530 }
531 // Get first record to be deleted
532 var record = me.delActionInfo.records.pop();
533 // Display progress dialog
534 OMV.MessageBox.progress("", me.deletionWaitMsg, "");
535 me.updateDeletionProgress();
536 // Execute deletion function
537 me.doDeletion(record);
538 },
539
540 /**
541 * The method that is called to delete a selected record. Override this
542 * method to customize the default behaviour. This is necessary in
543 * 'remote' mode.
544 */
545 doDeletion: function(record) {
546 var me = this;
547 if(me.mode === "local") {
548 // Remove record from store
549 me.store.remove(record);
550 // Continue deletion process
551 me.onDeletion(null, true, null);
552 }
553 },
554
555 /**
556 * The method that is called by the 'doDeletion' method. The progress
557 * bar will be updated and the deletion progress will be continued if
558 * there are still records to delete.
559 */
560 onDeletion: function(id, success, response) {
561 var me = this;
562 if(!success) {
563 // Remove temporary local variables
564 delete me.delActionInfo;
565 // Hide progress dialog
566 OMV.MessageBox.hide();
567 // Display error message
568 OMV.MessageBox.error(null, response);
569 } else {
570 if(me.delActionInfo.records.length > 0) {
571 var record = me.delActionInfo.records.pop();
572 // Update progress dialog
573 me.updateDeletionProgress();
574 // Execute deletion function
575 me.doDeletion(record);
576 } else {
577 // Remove temporary local variables
578 delete me.delActionInfo;
579 // Update and hide progress dialog
580 OMV.MessageBox.updateProgress(1, _("100% completed ..."));
581 OMV.MessageBox.hide();
582 me.afterDeletion();
583 }
584 }
585 },
586
587 /**
588 * Function that is called after the deletion has been successful finished.
589 */
590 afterDeletion: function() {
591 var me = this;
592 if(me.mode === "remote") {
593 me.doReload();
594 }
595 },
596
597 /**
598 * @private
599 * Private helper function to update the progress dialog.
600 */
601 updateDeletionProgress: function() {
602 var me = this;
603 // Calculate percentage
604 var p = (me.delActionInfo.count - me.delActionInfo.records.length) /
605 me.delActionInfo.count;
606 // Create message text
607 var text = Math.round(100 * p) + _("% completed ...");
608 // Update progress dialog
609 OMV.MessageBox.updateProgress(p, text);
610 },
611
612 /**
613 * Convenience function for setting the given toolbar button
614 * disabled/enabled.
615 * @param name The name of the toolbar button.
616 * @param disabled TRUE to disable the button, FALSE to enable.
617 * @return The button component, otherwise FALSE.
618 */
619 setToolbarButtonDisabled: function(name, disabled) {
620 var me = this;
621 var result = false;
622 var btnCtrl = me.queryById(me.getId() + "-" + name);
623 if(!Ext.isEmpty(btnCtrl) && btnCtrl.isButton)
624 result = btnCtrl.setDisabled(disabled);
625 return result;
626 },
627
628 /**
629 * Helper function to get the top toolbar object.
630 * @return The paging toolbar object or NULL.
631 */
632 getTopToolbar: function() {
633 return this.topToolbar;
634 },
635
636 /**
637 * Helper function to get the paging toolbar object.
638 * @return The paging toolbar object or NULL.
639 */
640 getPagingToolbar: function() {
641 return this.pagingToolbar;
642 }
643 });
644
645
646
This page took 0.141265 seconds and 4 git commands to generate.