]>
Commit | Line | Data |
---|---|---|
7ece65a0 DC |
1 | Ext.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 |
11 | Ext.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 |
21 | Ext.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', | |
bc42bb3c | 104 | virtualEntry: true, |
7ece65a0 DC |
105 | }, |
106 | ], | |
98bb3b90 | 107 | }, |
264c1958 TL |
108 | ], |
109 | }, | |
b0ee976f DM |
110 | }); |
111 | ||
112 | Ext.define('PBS.view.main.NavigationTree', { | |
113 | extend: 'Ext.list.Tree', | |
114 | xtype: 'navigationtree', | |
115 | ||
ca23a97f DM |
116 | controller: { |
117 | xclass: 'Ext.app.ViewController', | |
118 | ||
119 | init: function(view) { | |
ca23a97f DM |
120 | view.rstore = Ext.create('Proxmox.data.UpdateStore', { |
121 | autoStart: true, | |
122 | interval: 15 * 1000, | |
123 | storeid: 'pbs-datastore-list', | |
264c1958 | 124 | model: 'pbs-datastore-list', |
ca23a97f DM |
125 | }); |
126 | ||
127 | view.rstore.on('load', this.onLoad, this); | |
128 | view.on('destroy', view.rstore.stopUpdate); | |
2e268e31 | 129 | |
fa29d7eb TL |
130 | if (view.tapeStore === undefined) { |
131 | view.tapeStore = Ext.create('Proxmox.data.UpdateStore', { | |
a21f9852 TL |
132 | autoStart: true, |
133 | interval: 60 * 1000, | |
134 | storeid: 'pbs-tape-drive-list', | |
135 | model: 'pbs-tape-drive-list', | |
136 | }); | |
fa29d7eb TL |
137 | view.tapeStore.on('load', this.onTapeDriveLoad, this); |
138 | view.on('destroy', view.tapeStore.stopUpdate); | |
2e268e31 | 139 | } |
ca23a97f DM |
140 | }, |
141 | ||
2e268e31 | 142 | onTapeDriveLoad: function(store, records, success) { |
98bb3b90 | 143 | if (!success) return; |
ca23a97f | 144 | |
2e268e31 | 145 | let view = this.getView(); |
ca23a97f DM |
146 | let root = view.getStore().getRoot(); |
147 | ||
2e268e31 | 148 | records.sort((a, b) => a.data.name.localeCompare(b.data.name)); |
2e268e31 | 149 | |
94b7f56e TL |
150 | let list = root.findChild('id', 'tape_management', false); |
151 | let existingChildren = {}; | |
2e268e31 DC |
152 | for (const drive of records) { |
153 | let path, text, iconCls; | |
154 | if (drive.data.changer !== undefined) { | |
155 | text = drive.data.changer; | |
156 | path = `Changer-${text}`; | |
9a8bf2ca | 157 | iconCls = 'fa fa-exchange'; |
2e268e31 DC |
158 | } else { |
159 | text = drive.data.name; | |
160 | path = `Drive-${text}`; | |
9a8bf2ca | 161 | iconCls = 'pbs-icon-tape-drive'; |
2e268e31 | 162 | } |
94b7f56e | 163 | existingChildren[path] = { |
2e268e31 DC |
164 | text, |
165 | path, | |
166 | iconCls, | |
167 | leaf: true, | |
168 | }; | |
169 | } | |
170 | ||
94b7f56e | 171 | let paths = Object.keys(existingChildren).sort(); |
2e268e31 DC |
172 | |
173 | let oldIdx = 0; | |
174 | for (let newIdx = 0; newIdx < paths.length; newIdx++) { | |
175 | let newPath = paths[newIdx]; | |
176 | // find index to insert | |
177 | while (oldIdx < list.childNodes.length && newPath > list.getChildAt(oldIdx).data.path) { | |
178 | oldIdx++; | |
179 | } | |
180 | ||
181 | if (oldIdx >= list.childNodes.length || list.getChildAt(oldIdx).data.path !== newPath) { | |
94b7f56e | 182 | list.insertChild(oldIdx, existingChildren[newPath]); |
ff50c07e DM |
183 | } |
184 | } | |
185 | ||
94b7f56e | 186 | let toRemove = []; |
2e268e31 | 187 | list.eachChild((child) => { |
94b7f56e TL |
188 | if (!existingChildren[child.data.path]) { |
189 | toRemove.push(child); | |
2e268e31 DC |
190 | } |
191 | }); | |
94b7f56e | 192 | toRemove.forEach((child) => list.removeChild(child, true)); |
2e268e31 DC |
193 | |
194 | if (view.pathToSelect !== undefined) { | |
195 | let path = view.pathToSelect; | |
196 | delete view.pathToSelect; | |
197 | view.select(path, true); | |
198 | } | |
199 | }, | |
200 | ||
201 | onLoad: function(store, records, success) { | |
94b7f56e TL |
202 | if (!success) { |
203 | return; | |
204 | } | |
205 | let view = this.getView(); | |
2e268e31 DC |
206 | let root = view.getStore().getRoot(); |
207 | ||
7ece65a0 | 208 | records.sort((a, b) => a.id.localeCompare(b.id)); |
8277f4ac | 209 | |
94b7f56e TL |
210 | let list = root.findChild('id', 'datastores', false); |
211 | let getChildTextAt = i => list.getChildAt(i).data.text; | |
212 | let existingChildren = {}; | |
213 | for (let i = 0, j = 0, length = records.length; i < length; i++) { | |
264c1958 | 214 | let name = records[i].id; |
94b7f56e | 215 | existingChildren[name] = true; |
7ece65a0 | 216 | |
94b7f56e | 217 | while (name.localeCompare(getChildTextAt(j)) > 0 && (j+1) < list.childNodes.length) { |
7ece65a0 DC |
218 | j++; |
219 | } | |
220 | ||
94b7f56e | 221 | if (getChildTextAt(j).localeCompare(name) !== 0) { |
7ece65a0 | 222 | list.insertChild(j, { |
ca23a97f | 223 | text: name, |
c0ac2074 | 224 | path: `DataStore-${name}`, |
bc9c306c | 225 | iconCls: 'fa fa-database', |
264c1958 | 226 | leaf: true, |
ca23a97f DM |
227 | }); |
228 | } | |
229 | } | |
230 | ||
94b7f56e TL |
231 | // remove entries which are not existing anymore |
232 | let toRemove = []; | |
233 | list.eachChild(child => { | |
bc42bb3c | 234 | if (!existingChildren[child.data.text] && !child.data.virtualEntry) { |
94b7f56e | 235 | toRemove.push(child); |
ca23a97f DM |
236 | } |
237 | }); | |
94b7f56e | 238 | toRemove.forEach(child => list.removeChild(child, true)); |
2565fdd0 DC |
239 | |
240 | if (view.pathToSelect !== undefined) { | |
241 | let path = view.pathToSelect; | |
242 | delete view.pathToSelect; | |
243 | view.select(path, true); | |
244 | } | |
7ece65a0 DC |
245 | }, |
246 | }, | |
247 | ||
248 | listeners: { | |
249 | itemclick: function(tl, info) { | |
7ece65a0 DC |
250 | if (info.node.data.id === 'addbutton') { |
251 | let me = this; | |
252 | Ext.create('PBS.DataStoreEdit', { | |
253 | listeners: { | |
f05085ab | 254 | destroy: () => me.rstore.reload(), |
7ece65a0 DC |
255 | }, |
256 | }).show(); | |
257 | return false; | |
258 | } | |
259 | return true; | |
264c1958 | 260 | }, |
ca23a97f DM |
261 | }, |
262 | ||
dcf155da DC |
263 | reloadTapeStore: function() { |
264 | let me = this; | |
fa29d7eb | 265 | me.tapeStore.load(); |
dcf155da DC |
266 | }, |
267 | ||
2565fdd0 | 268 | select: function(path, silent) { |
b0ee976f | 269 | var me = this; |
fa29d7eb | 270 | if (me.rstore.isLoaded() && me.tapeStore.isLoaded()) { |
2565fdd0 DC |
271 | if (silent) { |
272 | me.suspendEvents(false); | |
273 | } | |
274 | var item = me.getStore().findRecord('path', path, 0, false, true, true); | |
275 | me.setSelection(item); | |
276 | if (silent) { | |
277 | me.resumeEvents(true); | |
278 | } | |
279 | } else { | |
280 | me.pathToSelect = path; | |
281 | } | |
b0ee976f DM |
282 | }, |
283 | ||
284 | animation: false, | |
285 | expanderOnly: true, | |
286 | expanderFirst: false, | |
287 | store: 'NavigationStore', | |
264c1958 | 288 | ui: 'nav', |
b0ee976f | 289 | }); |