]> git.proxmox.com Git - proxmox-widget-toolkit.git/commitdiff
add panel/JournalView
authorDominik Csapak <d.csapak@proxmox.com>
Wed, 15 May 2019 09:15:12 +0000 (11:15 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 15 May 2019 09:44:21 +0000 (11:44 +0200)
similar to LogView, but expects the result from the /node/journal api call,
which is an array of strings, with start/endcursor instead of
an array of objects with line numbers

the new api call also does not accept start line numbers and limit,
so it is necessary to handle it differently

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Makefile
panel/JournalView.js [new file with mode: 0644]

index d2b905cc22a3e74f6fb3d0e3db4694f0a30d24cb..049a1fb16665dc5d269eef82eb9f3f47187a0c74 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,7 @@ JSSRC=                                        \
        grid/PendingObjectGrid.js       \
        panel/InputPanel.js             \
        panel/LogView.js                \
+       panel/JournalView.js            \
        panel/RRDChart.js               \
        panel/GaugeWidget.js            \
        window/Edit.js                  \
diff --git a/panel/JournalView.js b/panel/JournalView.js
new file mode 100644 (file)
index 0000000..79edaeb
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Display log entries in a panel with scrollbar
+ * The log entries are automatically refreshed via a background task,
+ * with newest entries comming at the bottom
+ */
+Ext.define('Proxmox.panel.JournalView', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'proxmoxJournalView',
+
+    numEntries: 500,
+    lineHeight: 16,
+
+    scrollToEnd: true,
+
+    controller: {
+       xclass: 'Ext.app.ViewController',
+
+       updateParams: function() {
+           var me = this;
+           var viewModel = me.getViewModel();
+           var since = viewModel.get('since');
+           var until = viewModel.get('until');
+
+           since.setHours(0, 0, 0, 0);
+           until.setHours(0, 0, 0, 0);
+           until.setDate(until.getDate()+1);
+
+           me.getView().loadTask.delay(200, undefined, undefined, [
+               false,
+               false,
+               Ext.Date.format(since, "U"),
+               Ext.Date.format(until, "U")
+           ]);
+       },
+
+       scrollPosBottom: function() {
+           var view = this.getView();
+           var pos = view.getScrollY();
+           var maxPos = view.getScrollable().getMaxPosition().y;
+           return maxPos - pos;
+       },
+
+       scrollPosTop: function() {
+           var view = this.getView();
+           return view.getScrollY();
+       },
+
+       updateScroll: function(livemode, num, scrollPos, scrollPosTop) {
+           var me = this;
+           var view = me.getView();
+
+           if (!livemode) {
+               setTimeout(function() { view.scrollTo(0, 0); }, 10);
+           } else if (view.scrollToEnd && scrollPos <= 0) {
+               setTimeout(function() { view.scrollTo(0, Infinity); }, 10);
+           } else if (!view.scrollToEnd && scrollPosTop < 20*view.lineHeight) {
+               setTimeout(function() { view.scrollTo(0, num*view.lineHeight + scrollPosTop); }, 10);
+           }
+       },
+
+       updateView: function(lines, livemode, top) {
+           var me = this;
+           var view = me.getView();
+           var viewmodel = me.getViewModel();
+           if (viewmodel.get('livemode') !== livemode) {
+               return; // we switched mode, do not update the content
+           }
+           var contentEl = me.lookup('content');
+
+           // save old scrollpositions
+           var scrollPos = me.scrollPosBottom();
+           var scrollPosTop = me.scrollPosTop();
+
+           var newend = lines.shift();
+           var newstart = lines.pop();
+
+           var num = lines.length;
+           var text = lines.map(Ext.htmlEncode).join('<br>');
+
+           if (!livemode) {
+               view.content = num ? text : 'no content';
+           } else {
+               // update content
+               if (top && num) {
+                   view.content = view.content ? text + '<br>' + view.content : text;
+               } else if (!top && num) {
+                   view.content = view.content ? view.content + '<br>' + text : text;
+               }
+
+               // update cursors
+               if (!top || !view.startcursor) {
+                   view.startcursor = newstart;
+               }
+
+               if (top || !view.endcursor) {
+                   view.endcursor = newend;
+               }
+           }
+
+           contentEl.update(view.content);
+
+           me.updateScroll(livemode, num, scrollPos, scrollPosTop);
+       },
+
+       doLoad: function(livemode, top, since, until) {
+           var me = this;
+           if (me.running) {
+               me.requested = true;
+               return;
+           }
+           me.running = true;
+           var view = me.getView();
+           var params = {
+               lastentries: view.numEntries || 500,
+           };
+           if (livemode) {
+               if (!top && view.startcursor) {
+                   params = {
+                       startcursor: view.startcursor
+                   };
+               } else if (view.endcursor) {
+                   params.endcursor = view.endcursor;
+               }
+           } else {
+               params = {
+                   since: since,
+                   until: until
+               };
+           }
+           Proxmox.Utils.API2Request({
+               url: view.url,
+               params: params,
+               waitMsgTarget: (!livemode) ? view : undefined,
+               method: 'GET',
+               success: function(response) {
+                   Proxmox.Utils.setErrorMask(me, false);
+                   var lines = response.result.data;
+                   me.updateView(lines, livemode, top);
+                   me.running = false;
+                   if (me.requested) {
+                       me.requested = false;
+                       view.loadTask.delay(200);
+                   }
+               },
+               failure: function(response) {
+                   var msg = response.htmlStatus;
+                   Proxmox.Utils.setErrorMask(me, msg);
+                   me.running = false;
+                   if (me.requested) {
+                       me.requested = false;
+                       view.loadTask.delay(200);
+                   }
+               }
+           });
+       },
+
+       onScroll: function(x, y) {
+           var me = this;
+           var view = me.getView();
+           var viewmodel = me.getViewModel();
+           var livemode = viewmodel.get('livemode');
+           if (!livemode) {
+               return;
+           }
+
+           if (me.scrollPosTop() < 20*view.lineHeight) {
+               view.scrollToEnd = false;
+               view.loadTask.delay(200, undefined, undefined, [true, true]);
+           } else if (me.scrollPosBottom() <= 1) {
+               view.scrollToEnd = true;
+           }
+       },
+
+       init: function(view) {
+           var me = this;
+
+           if (!view.url) {
+               throw "no url specified";
+           }
+
+           var viewmodel = me.getViewModel();
+           var viewModel = this.getViewModel();
+           var since = new Date();
+           since.setDate(since.getDate() - 3);
+           viewModel.set('until', new Date());
+           viewModel.set('since', since);
+           me.lookup('content').setStyle('line-height', view.lineHeight + 'px');
+
+           view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]);
+
+           me.updateParams();
+           view.task = Ext.TaskManager.start({
+               run: function() {
+                   if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) {
+                       return;
+                   }
+
+                   if (me.scrollPosBottom() <= 1) {
+                       view.loadTask.delay(200, undefined, undefined, [true, false]);
+                   }
+               },
+               interval: 1000
+           });
+       },
+
+       onLiveMode: function() {
+           var me = this;
+           var view = me.getView();
+           delete view.startcursor;
+           delete view.endcursor;
+           delete view.content;
+           me.getViewModel().set('livemode', true);
+           view.scrollToEnd = true;
+           me.updateView([], true, false);
+       },
+
+       onTimespan: function() {
+           var me = this;
+           me.getViewModel().set('livemode', false);
+           me.updateView([], false);
+       }
+    },
+
+    onDestroy: function() {
+       var me = this;
+       me.loadTask.cancel();
+       Ext.TaskManager.stop(me.task);
+       delete me.content;
+    },
+
+    // for user to initiate a load from outside
+    requestUpdate: function() {
+       var me = this;
+       me.loadTask.delay(200);
+    },
+
+    viewModel: {
+       data: {
+           livemode: true,
+           until: null,
+           since: null
+       }
+    },
+
+    layout: 'auto',
+    bodyPadding: 5,
+    scrollable: {
+       x: 'auto',
+       y: 'auto',
+       listeners: {
+           // we have to have this here, since we cannot listen to events
+           // of the scroller in the viewcontroller (extjs bug?), nor does
+           // the panel have a 'scroll' event'
+           scroll: {
+               fn: function(scroller, x, y) {
+                   var controller = this.component.getController();
+                   if (controller) { // on destroy, controller can be gone
+                       controller.onScroll(x,y);
+                   }
+               },
+               buffer: 200
+           },
+       }
+    },
+
+    tbar: {
+
+       items: [
+           '->',
+           {
+               xtype: 'segmentedbutton',
+               items: [
+                   {
+                       text: gettext('Live Mode'),
+                       bind: {
+                           pressed: '{livemode}'
+                       },
+                       handler: 'onLiveMode',
+                   },
+                   {
+                       text: gettext('Select Timespan'),
+                       bind: {
+                           pressed: '{!livemode}'
+                       },
+                       handler: 'onTimespan',
+                   }
+               ]
+           },
+           {
+               xtype: 'datefield',
+               fieldLabel: gettext('Since'),
+               name: 'since_date',
+               reference: 'since',
+               format: 'Y-m-d',
+               bind: {
+                   disabled: '{livemode}',
+                   value: '{since}',
+                   maxValue: '{until}'
+               }
+           },
+           {
+               xtype: 'datefield',
+               fieldLabel: gettext('Until'),
+               name: 'until_date',
+               reference: 'until',
+               format: 'Y-m-d',
+               bind: {
+                   disabled: '{livemode}',
+                   value: '{until}',
+                   minValue: '{since}'
+               }
+           },
+           {
+               xtype: 'button',
+               text: 'Update',
+               reference: 'updateBtn',
+               handler: 'updateParams',
+               bind: {
+                   disabled: '{livemode}'
+               }
+           }
+       ]
+    },
+
+    items: [
+       {
+           xtype: 'box',
+           reference: 'content',
+           style: {
+               font: 'normal 11px tahoma, arial, verdana, sans-serif',
+               'white-space': 'pre'
+           },
+       }
+    ]
+});