[pve-devel] [RFC widget-toolkit 1/1] add panel/JournalView

Dominik Csapak d.csapak at proxmox.com
Mon May 13 16:29:58 CEST 2019


there is an error in this patch(comment inline)

On 5/13/19 2:49 PM, Dominik Csapak wrote:
> 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 at proxmox.com>
> ---
>   Makefile             |   1 +
>   panel/JournalView.js | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 336 insertions(+)
>   create mode 100644 panel/JournalView.js
> 
> diff --git a/Makefile b/Makefile
> index d2b905c..049a1fb 100644
> --- 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
> index 0000000..21c1598
> --- /dev/null
> +++ b/panel/JournalView.js
> @@ -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 = {
> +		last: view.numEntries || 500,

this should be lastentries

> +	    };
> +	    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'
> +	    },
> +	}
> +    ]
> +});
> 





More information about the pve-devel mailing list