]> git.proxmox.com Git - pve-http-server.git/blame - PVE/APIServer/Formatter/Bootstrap.pm
update jQuery to 3.4.1
[pve-http-server.git] / PVE / APIServer / Formatter / Bootstrap.pm
CommitLineData
63307beb
DM
1package PVE::APIServer::Formatter::Bootstrap;
2
3use strict;
4use warnings;
5use URI::Escape;
6use HTML::Entities;
7use JSON;
8
6189d2ef 9# FIXME: remove console code??
239710af 10
63307beb
DM
11# Helpers to generate simple html pages using Bootstrap markup.
12
13my $jssrc = <<_EOJS;
6189d2ef
DM
14PVE.open_vm_console = function(node, vmid) {
15 console.log("open vm " + vmid + " on node " + node);
16
17 var downloadWithName = function(uri, name) {
18 var link = jQuery('#pve_console_anchor');
19 link.attr("href", uri);
20
21 // Note: we need to tell android the correct file name extension
22 // but we do not set 'download' tag for other environments, because
23 // It can have strange side effects (additional user prompt on firefox)
24 var andriod = navigator.userAgent.match(/Android/i) ? true : false;
25 if (andriod) {
26 link.attr("download", name);
27 }
28
29 if (document.createEvent) {
30 var evt = document.createEvent("MouseEvents");
31 evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
32 link.get(0).dispatchEvent(evt);
33 } else {
34 link.get(0).fireEvent('onclick');
35 }
36 };
37
38 jQuery.ajax("/api2/json/console", {
39 data: { vmid: vmid, node: node },
40 headers: { CSRFPreventionToken: PVE.CSRFPreventionToken },
41 dataType: 'json',
42 type: 'POST',
43 error: function(jqXHR, textStatus, errorThrown) {
44 // fixme: howto view JS errors ?
45 console.log("ERROR " + textStatus + ": " + errorThrown);
46 },
47 success: function(data) {
48 var raw = "[virt-viewer]\\n";
49 jQuery.each(data.data, function(k, v) {
50 raw += k + "=" + v + "\\n";
51 });
52 var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
53 encodeURIComponent(raw);
54
55 downloadWithName(url, "pve-spice.vv");
56 }
57 });
63307beb
DM
58};
59_EOJS
60
61sub new {
ca304f91 62 my ($class, $res, $url, $auth, $config) = @_;
63307beb
DM
63
64 my $self = bless {
65 url => $url,
ca304f91
DM
66 title => $config->{title},
67 cookie_name => $config->{cookie_name},
63307beb
DM
68 js => '',
69 };
70
a3bb6070 71 if (my $username = $auth->{userid}) {
ca304f91 72 $self->{csrftoken} = $config->{csrfgen_func}->($username);
63307beb
DM
73 }
74
75 return $self;
76}
77
78sub body {
79 my ($self, $html) = @_;
80
6189d2ef 81 my $jssetup = "PVE = {};\n\n"; # create namespace
63307beb
DM
82
83 if ($self->{csrftoken}) {
84 $jssetup .= "PVE.CSRFPreventionToken = '$self->{csrftoken}';\n";
85 }
86
6189d2ef
DM
87 $jssetup .= "PVE.delete_auth_cookie = function() {\n";
88
89 if ($self->{cookie_name}) {
90 $jssetup .= " document.cookie = \"$self->{cookie_name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; secure;\";\n";
91 };
92 $jssetup .= "};\n";
93
63307beb
DM
94 return <<_EOD;
95<!DOCTYPE html>
96<html lang="en">
97 <head>
98 <meta charset="utf-8">
99 <meta http-equiv="X-UA-Compatible" content="IE=edge">
100 <meta name="viewport" content="width=device-width, initial-scale=1">
fc87cd5e 101 <title>$self->{title}</title>
63307beb
DM
102
103 <!-- Bootstrap -->
6edb39f6 104 <link href="/css/bootstrap.min.css" rel="stylesheet">
63307beb 105
6189d2ef
DM
106<script type="text/javascript">
107$jssetup
108$jssrc
109</script>
63307beb
DM
110
111 <style>
112body {
113 padding-top: 70px;
114}
115 </style>
116
63307beb 117 <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
6be73370 118 <script src="/js/jquery-3.4.1.min.js"></script>
63307beb 119 <!-- Include all compiled plugins (below), or include individual files as needed -->
6edb39f6 120 <script src="/js/bootstrap.min.js"></script>
63307beb
DM
121
122 </head>
123 <body>
124 <a class="hidden" id="pve_console_anchor"></a>
125 $html
126 <script type="text/javascript">
127 $self->{js}
128 </script>
129 </body>
130</html>
131_EOD
132}
133
134my $comp_id_counter = 0;
135
136sub el {
137 my ($self, %param) = @_;
138
139 $param{tag} = 'div' if !$param{tag};
140
141 my $id;
142
143 my $html = "<$param{tag}";
144
145 if (wantarray) {
146 $comp_id_counter++;
147 $id = "pveid$comp_id_counter";
148 $html .= " id=$id";
149 }
150
151 my $skip = {
152 tag => 1,
153 cn => 1,
154 html => 1,
155 text => 1,
156 };
157
158 my $boolattr = {
159 required => 1,
160 autofocus => 1,
161 };
162
163 my $noescape = {
164 placeholder => 1,
165 };
166
167 foreach my $attr (keys %param) {
168 next if $skip->{$attr};
169 my $v = $noescape->{$attr} ? $param{$attr} : uri_escape_utf8($param{$attr},"[^\/\ A-Za-z0-9\-\._~]");
170 next if !defined($v);
171 if ($boolattr->{$attr}) {
172 $html .= " $attr" if $v;
173 } else {
174 $html .= " $attr=\"$v\"";
175 }
176 }
177
178 $html .= ">";
179
180
181 if (my $cn = $param{cn}) {
182 if(ref($cn) eq 'ARRAY'){
183 foreach my $rec (@$cn) {
184 $html .= $self->el(%$rec);
185 }
186 } else {
187 $html .= $self->el(%$cn);
188 }
189 } elsif ($param{html}) {
190 $html .= $param{html};
191 } elsif ($param{text}) {
192 $html .= encode_entities($param{text});
193 }
194
195 $html .= "</$param{tag}>";
196
197 return wantarray ? ($html, $id) : $html;
198}
199
200sub alert {
201 my ($self, %param) = @_;
202
203 return $self->el(class => "alert alert-danger", %param);
204}
205
206sub add_js {
207 my ($self, $js) = @_;
208
209 $self->{js} .= $js . "\n";
210}
211
212my $format_event_callback = sub {
213 my ($info) = @_;
214
215 my $pstr = encode_json($info->{param});
216 return "function(e){$info->{fn}.apply(e, $pstr);}";
217};
218
219sub button {
220 my ($self, %param) = @_;
221
222 $param{tag} = 'button';
223 $param{class} = "btn btn-default btn-xs";
224
225 if (my $click = delete $param{click}) {
226 my ($html, $id) = $self->el(%param);
227 my $cb = &$format_event_callback($click);
228 $self->add_js("jQuery('#$id').on('click', $cb);");
229 return $html;
230 } else {
231 return $self->el(%param);
232 }
233}
234
2351;