]> git.proxmox.com Git - proxmox-backup.git/blame - www/NavigationTree.js
ui: nav tree: code cleanup and unification between datastore and tapes
[proxmox-backup.git] / www / NavigationTree.js
CommitLineData
7ece65a0
DC
1Ext.define('pbs-datastore-list', {
2 extend: 'Ext.data.Model',
3 fields: ['name', 'comment'],
4 proxy: {
5 type: 'proxmox',
6 url: "/api2/json/admin/datastore",
7 },
8 idProperty: 'store',
9});
10
2e268e31
DC
11Ext.define('pbs-tape-drive-list', {
12 extend: 'Ext.data.Model',
13 fields: ['name', 'changer'],
14 proxy: {
15 type: 'proxmox',
16 url: "/api2/json/tape/drive",
17 },
18 idProperty: 'name',
19});
20
b0ee976f
DM
21Ext.define('PBS.store.NavigationStore', {
22 extend: 'Ext.data.TreeStore',
23
24 storeId: 'NavigationStore',
25
26 root: {
27 expanded: true,
28 children: [
84b9eced
TL
29 {
30 text: gettext('Dashboard'),
31 iconCls: 'fa fa-tachometer',
32 path: 'pbsDashboard',
264c1958 33 leaf: true,
84b9eced 34 },
b0ee976f
DM
35 {
36 text: gettext('Configuration'),
37 iconCls: 'fa fa-gears',
38 path: 'pbsSystemConfiguration',
39 expanded: true,
40 children: [
88acc861 41 {
2f1a46f7
TL
42 text: gettext('Access Control'),
43 iconCls: 'fa fa-key',
44 path: 'pbsAccessControlPanel',
264c1958 45 leaf: true,
0542cfdf 46 },
9e2a4653
DC
47 {
48 text: gettext('Remotes'),
49 iconCls: 'fa fa-server',
50 path: 'pbsRemoteView',
51 leaf: true,
52 },
b0ee976f
DM
53 {
54 text: gettext('Subscription'),
55 iconCls: 'fa fa-support',
56 path: 'pbsSubscription',
264c1958
TL
57 leaf: true,
58 },
59 ],
b0ee976f
DM
60 },
61 {
62 text: gettext('Administration'),
63 iconCls: 'fa fa-wrench',
64 path: 'pbsServerAdministration',
7f17f744
DC
65 expanded: true,
66 leaf: false,
67 children: [
8e12e86f
DC
68 {
69 text: gettext('Shell'),
70 iconCls: 'fa fa-terminal',
71 path: 'pbsXtermJsConsole',
72 leaf: true,
73 },
7f17f744 74 {
ee0ab12d 75 text: gettext('Storage / Disks'),
7f17f744 76 iconCls: 'fa fa-hdd-o',
ee0ab12d
TL
77 path: 'pbsStorageAndDiskPanel',
78 leaf: true,
264c1958
TL
79 },
80 ],
98bb3b90 81 },
a21f9852
TL
82 {
83 text: "Tape Backup",
84 iconCls: 'pbs-icon-tape',
85 id: 'tape_management',
86 path: 'pbsTapeManagement',
87 expanded: true,
88 children: [],
89 },
98bb3b90 90 {
58f950c5 91 text: gettext('Datastore'),
98bb3b90 92 iconCls: 'fa fa-archive',
7ece65a0 93 id: 'datastores',
ed1329ec 94 path: 'pbsDataStores',
98bb3b90 95 expanded: true,
7ece65a0 96 expandable: false,
264c1958 97 leaf: false,
7ece65a0
DC
98 children: [
99 {
100 text: gettext('Add Datastore'),
101 iconCls: 'fa fa-plus-circle',
102 leaf: true,
103 id: 'addbutton',
104 },
105 ],
98bb3b90 106 },
264c1958
TL
107 ],
108 },
b0ee976f
DM
109});
110
111Ext.define('PBS.view.main.NavigationTree', {
112 extend: 'Ext.list.Tree',
113 xtype: 'navigationtree',
114
ca23a97f
DM
115 controller: {
116 xclass: 'Ext.app.ViewController',
117
118 init: function(view) {
ca23a97f
DM
119 view.rstore = Ext.create('Proxmox.data.UpdateStore', {
120 autoStart: true,
121 interval: 15 * 1000,
122 storeid: 'pbs-datastore-list',
264c1958 123 model: 'pbs-datastore-list',
ca23a97f
DM
124 });
125
126 view.rstore.on('load', this.onLoad, this);
127 view.on('destroy', view.rstore.stopUpdate);
2e268e31 128
fa29d7eb
TL
129 if (view.tapeStore === undefined) {
130 view.tapeStore = Ext.create('Proxmox.data.UpdateStore', {
a21f9852
TL
131 autoStart: true,
132 interval: 60 * 1000,
133 storeid: 'pbs-tape-drive-list',
134 model: 'pbs-tape-drive-list',
135 });
fa29d7eb
TL
136 view.tapeStore.on('load', this.onTapeDriveLoad, this);
137 view.on('destroy', view.tapeStore.stopUpdate);
2e268e31 138 }
ca23a97f
DM
139 },
140
2e268e31 141 onTapeDriveLoad: function(store, records, success) {
98bb3b90 142 if (!success) return;
ca23a97f 143
2e268e31 144 let view = this.getView();
ca23a97f
DM
145 let root = view.getStore().getRoot();
146
2e268e31 147 records.sort((a, b) => a.data.name.localeCompare(b.data.name));
2e268e31 148
94b7f56e
TL
149 let list = root.findChild('id', 'tape_management', false);
150 let existingChildren = {};
2e268e31
DC
151 for (const drive of records) {
152 let path, text, iconCls;
153 if (drive.data.changer !== undefined) {
154 text = drive.data.changer;
155 path = `Changer-${text}`;
9a8bf2ca 156 iconCls = 'fa fa-exchange';
2e268e31
DC
157 } else {
158 text = drive.data.name;
159 path = `Drive-${text}`;
9a8bf2ca 160 iconCls = 'pbs-icon-tape-drive';
2e268e31 161 }
94b7f56e 162 existingChildren[path] = {
2e268e31
DC
163 text,
164 path,
165 iconCls,
166 leaf: true,
167 };
168 }
169
94b7f56e 170 let paths = Object.keys(existingChildren).sort();
2e268e31
DC
171
172 let oldIdx = 0;
173 for (let newIdx = 0; newIdx < paths.length; newIdx++) {
174 let newPath = paths[newIdx];
175 // find index to insert
176 while (oldIdx < list.childNodes.length && newPath > list.getChildAt(oldIdx).data.path) {
177 oldIdx++;
178 }
179
180 if (oldIdx >= list.childNodes.length || list.getChildAt(oldIdx).data.path !== newPath) {
94b7f56e 181 list.insertChild(oldIdx, existingChildren[newPath]);
ff50c07e
DM
182 }
183 }
184
94b7f56e 185 let toRemove = [];
2e268e31 186 list.eachChild((child) => {
94b7f56e
TL
187 if (!existingChildren[child.data.path]) {
188 toRemove.push(child);
2e268e31
DC
189 }
190 });
94b7f56e 191 toRemove.forEach((child) => list.removeChild(child, true));
2e268e31
DC
192
193 if (view.pathToSelect !== undefined) {
194 let path = view.pathToSelect;
195 delete view.pathToSelect;
196 view.select(path, true);
197 }
198 },
199
200 onLoad: function(store, records, success) {
94b7f56e
TL
201 if (!success) {
202 return;
203 }
204 let view = this.getView();
2e268e31
DC
205 let root = view.getStore().getRoot();
206
7ece65a0 207 records.sort((a, b) => a.id.localeCompare(b.id));
8277f4ac 208
94b7f56e
TL
209 let list = root.findChild('id', 'datastores', false);
210 let getChildTextAt = i => list.getChildAt(i).data.text;
211 let existingChildren = {};
212 for (let i = 0, j = 0, length = records.length; i < length; i++) {
264c1958 213 let name = records[i].id;
94b7f56e 214 existingChildren[name] = true;
7ece65a0 215
94b7f56e 216 while (name.localeCompare(getChildTextAt(j)) > 0 && (j+1) < list.childNodes.length) {
7ece65a0
DC
217 j++;
218 }
219
94b7f56e 220 if (getChildTextAt(j).localeCompare(name) !== 0) {
7ece65a0 221 list.insertChild(j, {
ca23a97f 222 text: name,
c0ac2074 223 path: `DataStore-${name}`,
bc9c306c 224 iconCls: 'fa fa-database',
264c1958 225 leaf: true,
ca23a97f
DM
226 });
227 }
228 }
229
94b7f56e
TL
230 // remove entries which are not existing anymore
231 let toRemove = [];
232 list.eachChild(child => {
233 if (!existingChildren[child.data.text] && child.data.id !== 'addbutton') {
234 toRemove.push(child);
ca23a97f
DM
235 }
236 });
94b7f56e 237 toRemove.forEach(child => list.removeChild(child, true));
2565fdd0
DC
238
239 if (view.pathToSelect !== undefined) {
240 let path = view.pathToSelect;
241 delete view.pathToSelect;
242 view.select(path, true);
243 }
7ece65a0
DC
244 },
245 },
246
247 listeners: {
248 itemclick: function(tl, info) {
7ece65a0
DC
249 if (info.node.data.id === 'addbutton') {
250 let me = this;
251 Ext.create('PBS.DataStoreEdit', {
252 listeners: {
253 destroy: function() {
254 me.rstore.reload();
255 },
256 },
257 }).show();
258 return false;
259 }
260 return true;
264c1958 261 },
ca23a97f
DM
262 },
263
dcf155da
DC
264 reloadTapeStore: function() {
265 let me = this;
fa29d7eb 266 me.tapeStore.load();
dcf155da
DC
267 },
268
2565fdd0 269 select: function(path, silent) {
b0ee976f 270 var me = this;
fa29d7eb 271 if (me.rstore.isLoaded() && me.tapeStore.isLoaded()) {
2565fdd0
DC
272 if (silent) {
273 me.suspendEvents(false);
274 }
275 var item = me.getStore().findRecord('path', path, 0, false, true, true);
276 me.setSelection(item);
277 if (silent) {
278 me.resumeEvents(true);
279 }
280 } else {
281 me.pathToSelect = path;
282 }
b0ee976f
DM
283 },
284
285 animation: false,
286 expanderOnly: true,
287 expanderFirst: false,
288 store: 'NavigationStore',
264c1958 289 ui: 'nav',
b0ee976f 290});