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