From d812f3bb2b2ad62b0b87c5f86f86ee66641f7bb7 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 2 May 2014 07:44:53 +0200 Subject: [PATCH] include bootstrap html framework --- PVE/API2/Formatter/Bootstrap.pm | 232 ++++++++++++++++++++++++++++++++ bin/pveproxy | 1 + defines.mk | 1 + www/bootstrap/Makefile | 30 +++++ 4 files changed, 264 insertions(+) create mode 100644 PVE/API2/Formatter/Bootstrap.pm create mode 100644 www/bootstrap/Makefile diff --git a/PVE/API2/Formatter/Bootstrap.pm b/PVE/API2/Formatter/Bootstrap.pm new file mode 100644 index 00000000..75baf98a --- /dev/null +++ b/PVE/API2/Formatter/Bootstrap.pm @@ -0,0 +1,232 @@ +package PVE::API2::Formatter::Bootstrap; + +use strict; +use warnings; +use URI::Escape; +use HTML::Entities; +use JSON; + +use PVE::AccessControl; # to generate CSRF token + +# Helpers to generate simple html pages using Bootstrap markup. + +my $jssrc = <<_EOJS; +PVE = { + delete_auth_cookie: function() { + document.cookie = "PVEAuthCookie=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; secure;"; + }, + open_vm_console: function(node, vmid) { + console.log("open vm " + vmid + " on node " + node); + + var downloadWithName = function(uri, name) { + var link = jQuery('#pve_console_anchor'); + link.attr("href", uri); + + // Note: we need to tell android the correct file name extension + // but we do not set 'download' tag for other environments, because + // It can have strange side effects (additional user prompt on firefox) + var andriod = navigator.userAgent.match(/Android/i) ? true : false; + if (andriod) { + link.attr("download", name); + } + + if (document.createEvent) { + var evt = document.createEvent("MouseEvents"); + evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); + link.get(0).dispatchEvent(evt); + } else { + link.get(0).fireEvent('onclick'); + } + }; + + jQuery.ajax("/api2/json/console", { + data: { vmid: vmid, node: node }, + headers: { CSRFPreventionToken: PVE.CSRFPreventionToken }, + dataType: 'json', + type: 'POST', + error: function(jqXHR, textStatus, errorThrown) { + // fixme: howto view JS errors ? + console.log("ERROR " + textStatus + ": " + errorThrown); + }, + success: function(data) { + var raw = "[virt-viewer]\\n"; + jQuery.each(data.data, function(k, v) { + raw += k + "=" + v + "\\n"; + }); + var url = 'data:application/x-virt-viewer;charset=UTF-8,' + + encodeURIComponent(raw); + + downloadWithName(url, "pve-spice.vv"); + } + }); + } +}; +_EOJS + +sub new { + my ($class, $res, $url) = @_; + + my $self = bless { + url => $url, + js => '', + }; + + if (my $username = $res->{auth}->{userid}) { + $self->{csrftoken} = PVE::AccessControl::assemble_csrf_prevention_token($username); + } + + return $self; +} + +sub body { + my ($self, $html) = @_; + + my $jssetup = ''; + + if ($self->{csrftoken}) { + $jssetup .= "PVE.CSRFPreventionToken = '$self->{csrftoken}';\n"; + } + + return <<_EOD; + + + + + + + Proxmox VE Portal at '$hostname' + + + + + + + + + + + + + + + + + + + $html + + + +_EOD +} + +my $comp_id_counter = 0; + +sub el { + my ($self, %param) = @_; + + $param{tag} = 'div' if !$param{tag}; + + my $id; + + my $html = "<$param{tag}"; + + if (wantarray) { + $comp_id_counter++; + $id = "pveid$comp_id_counter"; + $html .= " id=$id"; + } + + my $skip = { + tag => 1, + cn => 1, + html => 1, + text => 1, + }; + + my $boolattr = { + required => 1, + autofocus => 1, + }; + + my $noescape = { + placeholder => 1, + }; + + foreach my $attr (keys %param) { + next if $skip->{$attr}; + my $v = $noescape->{$attr} ? $param{$attr} : uri_escape_utf8($param{$attr},"[^\/\ A-Za-z0-9\-\._~]"); + next if !defined($v); + if ($boolattr->{$attr}) { + $html .= " $attr" if $v; + } else { + $html .= " $attr=\"$v\""; + } + } + + $html .= ">"; + + + if (my $cn = $param{cn}) { + if(ref($cn) eq 'ARRAY'){ + foreach my $rec (@$cn) { + $html .= $self->el(%$rec); + } + } else { + $html .= $self->el(%$cn); + } + } elsif ($param{html}) { + $html .= $param{html}; + } elsif ($param{text}) { + $html .= encode_entities($param{text}); + } + + $html .= ""; + + return wantarray ? ($html, $id) : $html; +} + +sub alert { + my ($self, %param) = @_; + + return $self->el(class => "alert alert-danger", %param); +} + +sub add_js { + my ($self, $js) = @_; + + $self->{js} .= $js . "\n"; +} + +my $format_event_callback = sub { + my ($info) = @_; + + my $pstr = encode_json($info->{param}); + return "function(e){$info->{fn}.apply(e, $pstr);}"; +}; + +sub button { + my ($self, %param) = @_; + + $param{tag} = 'button'; + $param{class} = "btn btn-default btn-xs"; + + if (my $click = delete $param{click}) { + my ($html, $id) = $self->el(%param); + my $cb = &$format_event_callback($click); + $self->add_js("jQuery('#$id').on('click', $cb);"); + return $html; + } else { + return $self->el(%param); + } +} + +1; diff --git a/bin/pveproxy b/bin/pveproxy index 970de993..15225315 100755 --- a/bin/pveproxy +++ b/bin/pveproxy @@ -79,6 +79,7 @@ eval { add_dirs($dirs, '/pve2/ext4/', '/usr/share/pve-manager/ext4/'); add_dirs($dirs, '/pve2/images/' => '/usr/share/pve-manager/images/'); add_dirs($dirs, '/pve2/css/' => '/usr/share/pve-manager/css/'); + add_dirs($dirs, '/pve2/js/' => '/usr/share/pve-manager/js/'); add_dirs($dirs, '/vncterm/' => '/usr/share/vncterm/'); $daemon = PVE::APIDaemon->new( diff --git a/defines.mk b/defines.mk index d832684f..410c7dfa 100644 --- a/defines.mk +++ b/defines.mk @@ -17,3 +17,4 @@ WWWROOTDIR=${WWWBASEDIR}/root WWWIMAGEDIR=${WWWBASEDIR}/images WWWEXT4DIR=${WWWBASEDIR}/ext4 WWWCSSDIR=${WWWBASEDIR}/css +WWWJSDIR=${WWWBASEDIR}/js diff --git a/www/bootstrap/Makefile b/www/bootstrap/Makefile new file mode 100644 index 00000000..be493fd5 --- /dev/null +++ b/www/bootstrap/Makefile @@ -0,0 +1,30 @@ +include ../../defines.mk + +BTDIR=bootstrap-3.1.1-dist +BTSRC=${BTDIR}.zip + +BTDATA = \ + ${BTDIR}/css/bootstrap.min.css \ + ${BTDIR}/js/bootstrap.min.js + +${BTDATA}: ${BTSRC} + rm -rf ${BTDIR} + unzip -x ${BTSRC} + touch $@ + +all: ${BTDATA} + +.PHONY: install +install: ${BTDATA} + install -d ${WWWCSSDIR} + install -m 0644 -o www-data -g www-data ${BTDIR}/css/bootstrap.min.css ${WWWCSSDIR} + install -d ${WWWJSDIR} + install -m 0644 -o www-data -g www-data ${BTDIR}/js/bootstrap.min.js ${WWWJSDIR} + +.PHONY: distclean +distclean: clean + +.PHONY: clean +clean: + rm -rf *~ ${BTDIR} +