Issues (27)

1
<?php
2
3
// Copyright (C) 2014-2023 Universitätsbibliothek Mannheim
4
// See file LICENSE for license details.
5
6
session_start();
7
if (isset($_REQUEST['monitor'])) {
8
  $monitor = $_REQUEST['monitor'];
9
  $_SESSION['monitor'] = $monitor;
10
} elseif (!isset($_SESSION['monitor'])) {
11
  $_SESSION['monitor'] = '???';
12
}
13
$_SESSION['referer'] = 'index.php';
14
require_once 'auth.php';
15
16
// Connect to database and get configuration constants.
17
require_once 'DBConnector.class.php';
18
$dbcon = palma\DBConnector::getInstance();
19
20
// Support localisation.
21
$locale = '';
22
require_once 'i12n.php';
23
24
$user = false;
25
$username = '';
26
$address = '';
27
if (isset($_SESSION['username'])) {
28
  # PHP session based authorization.
29
  $username = $_SESSION['username'];
30
  $address = $_SESSION['address'];
31
  $user = "$username@$address";
32
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
33
  # .htaccess basic authorization.
34
  $user = $_SERVER['PHP_AUTH_USER'];
35
}
36
37
require_once 'globals.php';
38
39
// file paths for vnc downloads
40
$winvnc = CONFIG_START_URL . "theme/" . CONFIG_THEME . "/winvnc-palma.exe";
41
$macvnc = CONFIG_START_URL . "theme/" . CONFIG_THEME . "/osx-vnc.dmg";
42
$linuxsh = CONFIG_START_URL . "theme/" . CONFIG_THEME . "/x11.sh";
43
44
$starturl = htmlspecialchars($_SESSION['starturl']);
45
$pin = htmlspecialchars($_SESSION['pin']);
46
?>
47
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
48
          "http://www.w3.org/TR/html4/strict.dtd">
49
50
<html>
51
52
  <head>
53
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
54
    <meta name="viewport" content="width=device-width, initial-scale=1">
55
    <title>PalMA</title>
56
57
    <link rel="icon" href="theme/<?=CONFIG_THEME?>/favicon.ico" type="image/x-icon">
58
    <link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
59
    <link rel="stylesheet" href="pure-min.css">
60
    <link rel="stylesheet" href="palma.css" type="text/css">
61
62
    <script type="text/javascript" src="jquery.min.js"></script>
63
64
    <script src="dropzone.min.js"></script>
65
    <link
66
      rel="stylesheet"
67
      href="dropzone.min.css"
68
      type="text/css"
69
    />
70
71
    <script type="text/javascript">
72
73
     // Screen section which responds to keyboard input.
74
     var focus_section = '1';
75
76
     function sendToNuc(command) {
77
       var xmlHttp = new XMLHttpRequest();
78
       if (!xmlHttp) {
79
         // TODO
80
         alert('XMLHttpRequest failed!');
81
         return;
82
       }
83
       var url = 'control.php?' + command;
84
       var response = "";
85
       xmlHttp.open("get", url, true);
86
       xmlHttp.onreadystatechange = function () {
87
         if (xmlHttp.readyState == 1) {
88
           // Server connection established (IE only).
89
         } else if (xmlHttp.readyState == 2) {
90
           // Data transferred to server.
91
         } else if (xmlHttp.readyState == 3) {
92
           // Server is answering.
93
         } else if (xmlHttp.readyState == 4) {
94
           // Received all data from server.
95
           return xmlHttp.responseText;
96
         } else {
97
           alert("Got xmlHttp.readyState " + xmlHttp.readyState);
98
         }
99
       };
100
       xmlHttp.send(null);
101
       //console.log("sendToNuc " + url);
102
     }
103
104
     function keyControl(number, image, controlClass, key, handler, disabled, title) {
105
       // "number" refers to the selected screensection
106
107
       var keyHandler = getHandlerCommand(handler, key);
108
       if ( (keyHandler == "") || (keyHandler == null) ) {
109
         keyHandler = "default";
110
       }
111
       var button = document.createElement('button');
112
       var icon = document.createElement('i');
113
       if (!disabled) {
114
         icon.setAttribute('class', image);
115
         button.appendChild(icon);
116
         button.setAttribute('class', controlClass);
117
         button.setAttribute('onmousedown',
118
                             'sendToNuc("window=' + number + '&keydown=' + encodeURIComponent(keyHandler) + '")');
119
         button.setAttribute('onmouseup',
120
                             'sendToNuc("window=' + number + '&keyup=' + encodeURIComponent(keyHandler) + '")');
121
         button.setAttribute('title', title);
122
       } else {
123
         icon.setAttribute('class', image + " unavailable");
124
         button.appendChild(icon);
125
         button.setAttribute("class", "unavailable");
126
         button.setAttribute('title', title + " " + "<?=addslashes(__('not available'))?>");
0 ignored issues
show
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
         button.setAttribute('title', title + " " + "<?=addslashes(/** @scrutinizer ignore-call */ __('not available'))?>");
Loading history...
127
       }
128
       return button;
129
     }
130
131
     function downloadFile(screensection) {
132
133
       // wrong path if copied to /home/directory
134
       // TODO: check file and path
135
136
       var url = document.URL;
137
       var url_path = url.split("/");
138
139
       var file = document.getElementById("file" + screensection).innerHTML;
140
       var file = document.getElementById("file" + screensection).getAttribute("title");
141
142
       // Download with download.php
143
       var download = url_path.slice(0,4).join('/')
144
                    + "/download.php?file=" + encodeURIComponent(file);
145
146
       var name = "Download";
147
148
       if(file.indexOf("www.") > -1) {
149
         window.open(file, name);
150
       } else {
151
         window.open(download, name);
152
       }
153
     }
154
155
     function is_valid_url(url)
156
     {
157
       return url.match(/(^(ht|f)tps?:\/\/)([a-zA-Z0-9\.-])+(\.([a-zA-Z]{2,}))?(\:([0-9]{1,5}))?(\/([^\s\<\>\,\{\}\\\|\^\[\]\'])*)?$/);
158
     }
159
160
     function urlToNuc() {
161
       var url = document.getElementById('url_field').value;
162
       if (is_valid_url(url)) {
163
         // Encode special characters
164
         url = encodeURIComponent(url);
165
         sendToNuc('openURL='+url);
166
       } else {
167
         var urlfield = document.getElementById('url_field');
168
         urlfield.setAttribute('value', '<?=addslashes(__("Enter valid URL"))?>');
169
       }
170
171
       setTimeout(function(){location.reload()}, 1000);
172
     }
173
174
     function showLayout(layout, controls, window) {
175
       //~ console.log("Layout: " + layout);
176
       //~ for (i = 0; i < controls.length; i++) {
177
       //~     console.log("SL " + i + ": " + controls[i]);
178
       //~ }
179
180
       document.onkeydown = function(evt) {
181
         evt = evt || window.event;
182
         var section = focus_section;
183
         var handler = controls[focus_section][0];
184
         var keyHandler;
185
         //~ console.log("Key down: " + evt.keyCode);
186
         switch (evt.keyCode) {
187
           case 33: // page up
188
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'prior'));
189
             sendToNuc('window=' + section + '&key=' + keyHandler);
190
             break;
191
           case 34: // page down
192
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'next'));
193
             sendToNuc('window=' + section + '&key=' + keyHandler);
194
             break;
195
           case 35: // end
196
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'end'));
197
             sendToNuc('window=' + section + '&key=' + keyHandler);
198
             break;
199
           case 36: // home
200
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'home'));
201
             sendToNuc('window=' + section + '&key=' + keyHandler);
202
             break;
203
           case 37: // left
204
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'left'));
205
             sendToNuc('window=' + section + '&key=' + keyHandler);
206
             break;
207
           case 38: // up
208
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'up'));
209
             sendToNuc('window=' + section + '&key=' + keyHandler);
210
             break;
211
           case 39: // right
212
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'right'));
213
             sendToNuc('window=' + section + '&key=' + keyHandler);
214
             break;
215
           case 40: // down
216
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'down'));
217
             sendToNuc('window=' + section + '&key=' + keyHandler);
218
             break;
219
         }
220
       };
221
222
       document.onkeypress = function(evt) {
223
         evt = evt || window.event;
224
         //~ console.log("Key press: " + evt.keyCode);
225
         var charCode = evt.which || evt.keyCode;
226
         var charStr = String.fromCharCode(charCode);
227
         var section = focus_section;
228
         var handler = controls[focus_section][0];
229
         var keyHandler;
230
         switch (charStr) {
231
           case "1": // select section 1
232
           case "2": // select section 2
233
           case "3": // select section 3
234
           case "4": // select section 4
235
             focus_section = charStr;
236
             break;
237
           case "+": // zoom in
238
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'zoomin'));
239
             sendToNuc('window=' + section + '&key=' + keyHandler);
240
             break;
241
           case "-": // zoom out
242
             keyHandler = encodeURIComponent(getHandlerCommand(handler, 'zoomout'));
243
             sendToNuc('window=' + section + '&key=' + keyHandler);
244
             break;
245
         }
246
       };
247
248
       var windowlist = document.getElementById('windowlist');
249
       var entries = windowlist.getElementsByClassName('window_entry');
250
       var screensection, file, status;
251
       markCurrentLayout(layout);
252
       for (var n = 0; n < window.length; n++) {
253
         screensection = window[n].section;
254
         file = window[n].file;
255
         status = window[n].state;
256
         entries[n].appendChild(addWindowControls(layout, controls, screensection, file, status));
257
       }
258
     }
259
260
     function markCurrentLayout(layout) {
261
       var layoutDivs = document.getElementsByClassName("screenlayout");
262
       for (var i = 0 ; i < layoutDivs.length ; i++) {
263
         var children = layoutDivs[i].getElementsByClassName("pure-button");
264
         for (var k = 0 ; k < children.length ; k++) {
265
           children[k].style.backgroundColor = "";
266
         }
267
       }
268
       document.getElementById(layout).style.backgroundColor = "#232e58";
269
     }
270
271
     function miniDisplaySelect(element) {
272
       sendToNuc('layout=' + element.id);
273
       markCurrentLayout(element.id);
274
     }
275
276
     function getHandlerCommand(handle, task) {
277
278
       // console.log("getHandlerCommand "+handle+" - "+task);
279
       // to deactivate buttons just add 'undefined' as keystroke
280
281
       var handler = [];
282
283
       handler["default"] = {};
284
       // handler["default"]["init"] = "";
285
       handler["default"]["up"] = "Up";
286
       handler["default"]["down"] = "Down";
287
       handler["default"]["left"] = "Left";
288
       handler["default"]["right"] = "Right";
289
       handler["default"]["next"] = "Next";
290
       handler["default"]["prior"] = "Prior";
291
       handler["default"]["home"] = "Home";
292
       handler["default"]["end"] = "End";
293
       handler["default"]["zoomin"] = "ctrl+plus";
294
       handler["default"]["zoomout"] = "ctrl+minus";
295
       handler["default"]["download"] = "download";
296
       handler["default"]["counterclockwise"] = "undefined";
297
       handler["default"]["clockwise"] = "undefined";
298
299
       // Handler for web pages.
300
       handler["palma-browser"] = {};
301
       handler["palma-browser"]["up"] = "Up";
302
       handler["palma-browser"]["down"] = "Down";
303
       handler["palma-browser"]["left"] = "Left";
304
       handler["palma-browser"]["right"] = "Right";
305
       handler["palma-browser"]["next"] = "Next";
306
       handler["palma-browser"]["prior"] = "Prior";
307
       handler["palma-browser"]["home"] = "Home";
308
       handler["palma-browser"]["end"] = "End";
309
       handler["palma-browser"]["zoomin"] = "ctrl+plus";
310
       handler["palma-browser"]["zoomout"] = "ctrl+minus";
311
       handler["palma-browser"]["download"] = "undefined";
312
       handler["palma-browser"]["counterclockwise"] = "undefined";
313
       handler["palma-browser"]["clockwise"] = "undefined";
314
315
       // Handler for images.
316
       handler["feh"] = {};
317
       handler["feh"]["up"] = "alt+Up";
318
       handler["feh"]["down"] = "alt+Down";
319
       handler["feh"]["left"] = "alt+Left";
320
       handler["feh"]["right"] = "alt+Right";
321
       handler["feh"]["next"] = "alt+Next";
322
       handler["feh"]["prior"] = "alt+Prior";
323
       handler["feh"]["home"] = "undefined";
324
       handler["feh"]["end"] = "undefined";
325
       handler["feh"]["zoomin"] = "KP_Add";
326
       handler["feh"]["zoomout"] = "KP_Subtract";
327
       handler["feh"]["download"] = "download";
328
       handler["feh"]["counterclockwise"] = "less";
329
       handler["feh"]["clockwise"] = "greater";
330
331
       // Controls in LibreOffice: no zoom in calc and writer, has to be activated first
332
       // by pressing <Ctrl+Shift+o> (switch view mode on/off) not implemented yet
333
       handler["libreoffice"] = {};
334
       handler["libreoffice"]["up"] = "Up";
335
       handler["libreoffice"]["down"] = "Down";
336
       handler["libreoffice"]["left"] = "Left";
337
       handler["libreoffice"]["right"] = "Right";
338
       handler["libreoffice"]["next"] = "Next";
339
       handler["libreoffice"]["prior"] = "Prior";
340
       handler["libreoffice"]["home"] = "undefined";
341
       handler["libreoffice"]["end"] = "undefined";
342
       handler["libreoffice"]["zoomin"] = "undefined";
343
       handler["libreoffice"]["zoomout"] = "undefined";
344
       handler["libreoffice"]["download"] = "download";
345
       handler["libreoffice"]["counterclockwise"] = "undefined";
346
       handler["libreoffice"]["clockwise"] = "undefined";
347
348
       // Handler for MS Excel and LibreOffice Calc documents.
349
       handler["libreoffice-calc"] = {};
350
       handler["libreoffice-calc"]["up"] = "Up";
351
       handler["libreoffice-calc"]["down"] = "Down";
352
       handler["libreoffice-calc"]["left"] = "Left";
353
       handler["libreoffice-calc"]["right"] = "Right";
354
       handler["libreoffice-calc"]["next"] = "Next";
355
       handler["libreoffice-calc"]["prior"] = "Prior";
356
       handler["libreoffice-calc"]["home"] = "Home";
357
       handler["libreoffice-calc"]["end"] = "End";
358
       handler["libreoffice-calc"]["zoomin"] = "undefined";
359
       handler["libreoffice-calc"]["zoomout"] = "undefined";
360
       handler["libreoffice-calc"]["download"] = "download";
361
       handler["libreoffice-calc"]["counterclockwise"] = "undefined";
362
       handler["libreoffice-calc"]["clockwise"] = "undefined";
363
364
       // Handler for MS Powerpoint and LibreOffice Impress documents.
365
       handler["libreoffice-impress"] = {};
366
       handler["libreoffice-impress"]["up"] = "Up";
367
       handler["libreoffice-impress"]["down"] = "Down";
368
       handler["libreoffice-impress"]["left"] = "Left";
369
       handler["libreoffice-impress"]["right"] = "Right";
370
       handler["libreoffice-impress"]["next"] = "Next";
371
       handler["libreoffice-impress"]["prior"] = "Prior";
372
       handler["libreoffice-impress"]["home"] = "Home";
373
       handler["libreoffice-impress"]["end"] = "End";
374
       handler["libreoffice-impress"]["zoomin"] = "plus";
375
       handler["libreoffice-impress"]["zoomout"] = "minus";
376
       handler["libreoffice-impress"]["download"] = "download";
377
       handler["libreoffice-impress"]["counterclockwise"] = "undefined";
378
       handler["libreoffice-impress"]["clockwise"] = "undefined";
379
380
       // Handler for MS Word and LibreOffice Writer documents.
381
       handler["libreoffice-writer"] = {};
382
       handler["libreoffice-writer"]["up"] = "Up";
383
       handler["libreoffice-writer"]["down"] = "Down";
384
       handler["libreoffice-writer"]["left"] = "Left";
385
       handler["libreoffice-writer"]["right"] = "Right";
386
       handler["libreoffice-writer"]["next"] = "Next";
387
       handler["libreoffice-writer"]["prior"] = "Prior";
388
       handler["libreoffice-writer"]["home"] = "undefined";
389
       handler["libreoffice-writer"]["end"] = "undefined";
390
       handler["libreoffice-writer"]["zoomin"] = "undefined";
391
       handler["libreoffice-writer"]["zoomout"] = "undefined";
392
       handler["libreoffice-writer"]["download"] = "download";
393
       handler["libreoffice-writer"]["counterclockwise"] = "undefined";
394
       handler["libreoffice-writer"]["clockwise"] = "undefined";
395
396
       // Handler for videos.
397
       handler["vlc"] = {};
398
       handler["vlc"]["up"] = "undefined";
399
       handler["vlc"]["down"] = "undefined";
400
       handler["vlc"]["left"] = "undefined";
401
       handler["vlc"]["right"] = "space";
402
       handler["vlc"]["next"] = "undefined";
403
       handler["vlc"]["prior"] = "undefined";
404
       handler["vlc"]["home"] = "undefined";
405
       handler["vlc"]["end"] = "undefined";
406
       handler["vlc"]["zoomin"] = "undefined";
407
       handler["vlc"]["zoomout"] = "undefined";
408
       handler["vlc"]["download"] = "undefined";
409
       handler["vlc"]["counterclockwise"] = "undefined";
410
       handler["vlc"]["clockwise"] = "undefined";
411
412
       // Handler for shared desktops (VNC).
413
       handler["vnc"] = {};
414
       handler["vnc"]["up"] = "Up";
415
       handler["vnc"]["down"] = "Down";
416
       handler["vnc"]["left"] = "Left";
417
       handler["vnc"]["right"] = "Right";
418
       handler["vnc"]["next"] = "undefined";
419
       handler["vnc"]["prior"] = "undefined";
420
       handler["vnc"]["home"] = "undefined";
421
       handler["vnc"]["end"] = "undefined";
422
       handler["vnc"]["zoomin"] = "plus";
423
       handler["vnc"]["zoomout"] = "minus";
424
       handler["vnc"]["download"] = "undefined";
425
       handler["vnc"]["counterclockwise"] = "undefined";
426
       handler["vnc"]["clockwise"] = "undefined";
427
428
       // Handler for PDF documents.
429
       handler["zathura"] = {};
430
       handler["zathura"]["up"] = "Up";
431
       handler["zathura"]["down"] = "Down";
432
       handler["zathura"]["left"] = "Left";
433
       handler["zathura"]["right"] = "Right";
434
       handler["zathura"]["next"] = "Next";
435
       handler["zathura"]["prior"] = "Prior";
436
       handler["zathura"]["home"] = "Home";
437
       handler["zathura"]["end"] = "End";
438
       handler["zathura"]["zoomin"] = "plus";
439
       handler["zathura"]["zoomout"] = "minus";
440
       handler["zathura"]["download"] = "download";
441
       handler["zathura"]["counterclockwise"] = "undefined";
442
       handler["zathura"]["clockwise"] = "r";
443
444
       var send_keys = handler["default"]["up"];
445
446
       if (typeof(handler[handle]) !== "undefined") {
447
         send_keys = handler[handle][task];
448
       }
449
450
       // console.log(send_keys);
451
452
       return send_keys;
453
     }
454
455
     Dropzone.options.palmaDropzone = {
456
       init: function() {
457
         this.on("complete", function() {
458
           if (this.getQueuedFiles().length == 0 && this.getUploadingFiles().length == 0) {
459
             // File finished uploading, and there aren't any left in the queue.
460
             //console.log("File(s) uploaded");
461
             setTimeout(function() {
462
               location.reload();
463
             }, 1);
464
           }
465
         });
466
       }
467
     };
468
469
     function updateUserList(address, user) {
470
       // Update the user list on screen from the table in the database.
471
472
       // Get the <tbody> element which contains the user entries.
473
       var list = document.getElementById('userlist');
474
475
       // First we remove all existing <tr> elements.
476
       while (list.firstChild) {
477
         list.removeChild(list.firstChild);
478
       }
479
480
       if (address.length > 0) {
481
         // Add an entry for each user. Iterate over addresses:
482
         // One user may be connected several times with different devices.
483
         // We don't expect more than one user from the same device.
484
         var m;
485
         for (m = 0; m < address.length; m++) {
486
           var n;
487
           for (n = 0; n < user.length; n++) {
488
             if (address[m].userid == user[n].userid) {
489
               break;
490
             }
491
           }
492
           var device = address[m].device;
493
           var tr = document.createElement('tr');
494
           var td = document.createElement('td');
495
           var i = document.createElement('i');
496
           i.setAttribute('class', 'fa fa-fw fa-' + device);
497
           td.appendChild(i);
498
           td.appendChild(document.createTextNode(user[n].name));
499
           tr.appendChild(td);
500
           list.appendChild(tr);
501
         }
502
       } else {
503
         <?php
504
          if ($user) {
505
            ?>
506
         // All users were disconnected.
507
         alert("<?=addslashes(__('You were disconnected!'))?>");
508
         window.location = 'logout.php';
509
            <?php
510
          } else {
511
            ?>
512
         // If there is no user, we display an empty entry.
513
         var tr = document.createElement('tr');
514
         var td = document.createElement('td');
515
         td.appendChild(document.createTextNode("\u00a0"));
516
         tr.appendChild(td);
517
         list.appendChild(tr);
518
            <?php
519
          }
520
          ?>
521
       }
522
     }
523
524
525
     function addWindowPosition(layout, screensection) {
526
       var position = document.createElement("div");
527
       position.setAttribute("class", "position " + layout);
528
       position.setAttribute('title', '<?=addslashes(__("Select screen section for display"))?>');
529
530
       var s;
531
       var button;
532
       var icon;
533
       var br;
534
535
       switch (layout) {
536
         case 'g1x1':
537
           s = 1;
538
           break;
539
         case 'g1x2':
540
           s = 2;
541
           break;
542
         case 'g2x1':
543
           s = 2;
544
           break;
545
         case 'g1a2':
546
           s = 3;
547
           var layout_left = document.createElement('div');
548
           layout_left.setAttribute("class", "layout_left");
549
           var layout_right = document.createElement('div');
550
           layout_right.setAttribute("class", "layout_right");
551
           break;
552
         case 'g2x2':
553
           s = 4;
554
           break;
555
       }
556
557
       for (var n = 1; n <= s; n++) {
558
         br = '';
559
         button = document.createElement("button");
560
         button.setAttribute('value', n);
561
         if (n == screensection) {
562
           button.setAttribute("class", "selected");
563
         }
564
         button.setAttribute(
565
           'onclick',
566
           "sendToNuc('switchWindows=TRUE&before=" + screensection + "&after='+(this.value))"
567
         );
568
         icon = document.createElement("i");
569
         if (s == 1) {
570
           icon.setAttribute("class", "fa fa-desktop fa-2x");
571
         } else {
572
           icon.setAttribute("class", "fa fa-desktop fa-1x");
573
         }
574
         button.appendChild(icon);
575
576
         if ((layout == 'g1x2' && n == 1) || ((layout == 'g1a2' || layout == 'g2x2') && n == 2 )) {
577
           br = document.createElement("br");
578
         }
579
580
         if (layout == 'g1a2' && n == 1) {
581
           layout_left.appendChild(button);
582
         } else if (layout == 'g1a2' && n > 1) {
583
           layout_right.appendChild(button);
584
           if (br) {
585
             layout_right.appendChild(br);
586
           }
587
         } else {
588
           position.appendChild(button);
589
           if (br) {
590
             position.appendChild(br);
591
           }
592
         }
593
       }
594
595
       if (layout == 'g1a2') {
596
         position.appendChild(layout_left);
597
         position.appendChild(layout_right);
598
       }
599
       return position;
600
     }
601
602
603
     function addWindowControls(layout, controls, screensection, file, status) {
604
       var control = controls[screensection];
605
       if (typeof control == "undefined") {
606
         control = ["default", false, false, false, false,
607
                    false, false, false, false, false, false, false];
608
       }
609
610
       // get handler
611
       var handler = control[0];
612
       // up down left right zoomin zoomout home end prior next download
613
       var up = control[1];
614
       var down = control[2];
615
       var left = control[3];
616
       var right = control[4];
617
       var zoomin = control[5];
618
       var zoomout = control[6];
619
       var home = control[7];
620
       var end = control[8];
621
       var prior = control[9];
622
       var next = control[10];
623
       var download = control[11];
624
       var counterclockwise = control[12];
625
       var clockwise = control[13];
626
627
       var windowcontrols = document.createElement("div");
628
       windowcontrols.setAttribute("class", "windowcontrols");
629
630
       var topbar = document.createElement("div");
631
       topbar.setAttribute("class", "topbar");
632
       var button = document.createElement('button');
633
       button.setAttribute("class", "toggle");
634
       icon = document.createElement('i');
635
       if (status == 'active') {
636
         icon.setAttribute("class", "fa fa-eye");
637
       } else {
638
         icon.setAttribute("class", "fa fa-eye-slash");
639
       }
640
       icon.setAttribute('id', 'status_' + screensection);
641
       button.setAttribute('title', '<?=addslashes(__("Toggle visibility"))?>');
642
       button.setAttribute('onclick', "sendToNuc('window=" + screensection + "&toggle=TRUE')");
643
       button.appendChild(icon);
644
       topbar.appendChild(button);
645
       topbar.appendChild(keyControl(
646
         screensection, 'fa fa-search-plus', 'zoomin', 'zoomin',
647
         handler, !zoomin, '<?=addslashes(__("Zoom in"))?>'
648
       ));
649
       topbar.appendChild(keyControl(
650
         screensection, 'fa fa-search-minus', 'zoomout', 'zoomout',
651
         handler, !zoomout, '<?=addslashes(__("Zoom out"))?>'
652
       ));
653
       topbar.appendChild(keyControl(
654
         screensection, 'fa fa-rotate-left', 'counterclockwise', 'counterclockwise',
655
         handler, !counterclockwise, '<?=addslashes(__("Rotate counterclockwise"))?>'
656
       ));
657
       topbar.appendChild(keyControl(
658
         screensection, 'fa fa-rotate-right', 'clockwise', 'clockwise',
659
         handler, !clockwise, '<?=addslashes(__("Rotate clockwise"))?>'
660
       ));
661
       var downloadbutton = keyControl(
662
         screensection, 'fa fa-download', 'download', 'download',
663
         handler, !download, '<?=addslashes(__("Download this item"))?>'
664
       );
665
       if(download) {
666
         downloadbutton.setAttribute('onclick', 'downloadFile(' + screensection + ')');
667
       }
668
       topbar.appendChild(downloadbutton);
669
       button = document.createElement('button');
670
       button.setAttribute("class", "trash");
671
       button.setAttribute('onclick', "sendToNuc('window=" + screensection + "&delete=" + file + "')");
672
       button.setAttribute('title', '<?=addslashes(__("Remove this item"))?>');
673
       icon = document.createElement('i');
674
       icon.setAttribute("class", "fa fa-trash-o");
675
       button.appendChild(icon);
676
       topbar.appendChild(button);
677
678
       var movement = document.createElement("div");
679
       movement.setAttribute("class", "movement");
680
681
       var jump = document.createElement("div");
682
       jump.setAttribute("class", "jump");
683
       jump.appendChild(keyControl(
684
         screensection, 'fa fa-angle-double-up', 'jumpbeginning', 'home',
685
         handler, !home, '<?=addslashes(__("Jump to start"))?>'
686
       ));
687
       jump.appendChild(keyControl(
688
         screensection, 'fa fa-angle-up', 'pageback', 'prior',
689
         handler, !prior, '<?=addslashes(__("Page up"))?>'
690
       ));
691
       jump.appendChild(keyControl(
692
         screensection, 'fa fa-angle-down', 'pageforward', 'next',
693
         handler, !next, '<?=addslashes(__("Page down"))?>'
694
       ));
695
       jump.appendChild(keyControl(
696
         screensection, 'fa fa-angle-double-down', 'jumpend', 'end',
697
         handler, !end, '<?=addslashes(__("Jump to end"))?>'
698
       ));
699
700
       var arrows = document.createElement("div");
701
       arrows.setAttribute("class", "arrows");
702
       arrows.appendChild(keyControl(
703
         screensection, 'fa fa-toggle-up', 'arrowup', 'up',
704
         handler, !up, '<?=addslashes(__("Up"))?>'
705
       ));
706
       arrows.appendChild(document.createElement("br"));
707
       arrows.appendChild(keyControl(
708
         screensection, 'fa fa-toggle-left', 'arrowleft', 'left',
709
         handler, !left, '<?=addslashes(__("Left"))?>'
710
       ));
711
       arrows.appendChild(keyControl(
712
         screensection, 'fa fa-toggle-right', 'arrowright', 'right',
713
         handler, !right, '<?=addslashes(__("Right"))?>'
714
       ));
715
       arrows.appendChild(document.createElement("br"));
716
       arrows.appendChild(keyControl(
717
         screensection, 'fa fa-toggle-down', 'arrowdown', 'down',
718
         handler, !down, '<?=addslashes(__("Down"))?>'
719
       ));
720
721
       movement.appendChild(jump);
722
       movement.appendChild(arrows);
723
724
       var position = addWindowPosition(layout, screensection);
725
726
       // Putting it all together
727
       windowcontrols.appendChild(topbar);
728
       windowcontrols.appendChild(movement);
729
       windowcontrols.appendChild(position);
730
       return windowcontrols;
731
     }
732
733
734
     function updateWindowList(window){
735
       var windowlist = document.getElementById('windowlist');
736
       // remove old entries
737
       while (windowlist.firstChild) {
738
         windowlist.removeChild(windowlist.firstChild);
739
       }
740
741
       if (window.length == 0) {
742
         var entry = document.createElement('div');
743
         entry.setAttribute("class", "description");
744
         entry.appendChild(document.createTextNode(
745
           '<?=addslashes(__('Welcome'))?>, <?=htmlspecialchars($username)?>!'
746
         ));
747
         entry.appendChild(document.createElement("br"));
748
         entry.appendChild(document.createTextNode(
749
           '<?=addslashes(__('There is no shared content yet. Click below to get started!'))?>'
750
         ));
751
         var addbutton = document.createElement('button');
752
         addbutton.setAttribute("class", "splash_add pure-button");
753
         addbutton.setAttribute("onclick", "openTab(event, 'Add')");
754
         var addtext = (document.createTextNode('<?=addslashes(__("Add"))?>'));
755
         addbutton.appendChild(addtext);
756
         var addicon = document.createElement("i");
757
         addicon.setAttribute("class", "fa fa-plus");
758
         addbutton.appendChild(addicon);
759
         windowlist.appendChild(entry);
760
         windowlist.appendChild(addbutton);
761
         document.getElementById("closeWindows").style.display = "none";
762
         document.getElementById("Layout").style.display = "none";
763
         document.getElementById("controlbtn").className = "tablinks";
764
       } else {
765
         document.getElementById("closeWindows").style.display = "inline-block";
766
         document.getElementById("Layout").style.display = "block";
767
         document.getElementById("controlbtn").className = "tablinks active";
768
         // Add an entry for each window.
769
         var n;
770
         for (n = 0; n < window.length; n++) {
771
           var file = window[n].file;
772
           var handler = window[n].handler;
773
           var screensection = window[n].section;
774
           var entry = document.createElement('div');
775
           entry.setAttribute("class", "window_entry");
776
           var divID = 'file' + screensection;
777
           entry.setAttribute('id', divID);
778
779
           var button = document.createElement('button');
780
           button.setAttribute("class", "window_entry_button");
781
           button.setAttribute('onclick', "openAccordion('" + divID + "')");
782
           var icon = document.createElement('i');
783
           if (handler.indexOf("palma-browser") > -1) {
784
             icon.setAttribute("class", "fa fa-globe");
785
           } else if (handler.indexOf("vnc") > -1) {
786
             icon.setAttribute("class", "fa fa-video-camera");
787
           } else if (handler.indexOf("vlc") > -1) {
788
             icon.setAttribute("class", "fa fa-film");
789
           } else if (handler.indexOf("feh") > -1) {
790
             icon.setAttribute("class", "fa fa-picture-o");
791
           } else if (handler.indexOf("zathura") > -1) {
792
             icon.setAttribute("class", "fa fa-file-pdf-o");
793
           } else {
794
             icon.setAttribute("class", "fa fa-file");
795
           }
796
           button.appendChild(icon);
797
           var title = decodeURI(decodeURIComponent(file));
798
           // display only the last part of the URL or file name.
799
           // Long names are truncated, and the truncation is indicated.
800
           if (title.substring(0, 4) == 'http') {
801
             // Remove a terminating slash from an URL.
802
             // The full URL will be shown as a tooltip.
803
             title = title.replace(/\/$/, '');
804
             title = title.replace(/^.*\//, '');
805
             entry.setAttribute('title', file);
806
           } else {
807
             // For files only the full base name is shown as a tooltip.
808
             var fname = file;
809
             title = title.replace(/^.*\//, '');
810
             entry.setAttribute('title', fname);
811
           }
812
           if (title.length > 25) {
813
             title = title.substring(0, 25) + '...';
814
           }
815
           button.appendChild(document.createTextNode(title));
816
           var icon = document.createElement('i');
817
           icon.setAttribute("class", "fa fa-caret-down accordion-icon");
818
           button.appendChild(icon);
819
           entry.appendChild(button);
820
           windowlist.appendChild(entry);
821
         }
822
       }
823
     }
824
825
     function updateControlsBySection(window) {
826
827
       // get section and handler for each window
828
       var sectionControls = [];
829
830
       for (n = 0; n < window.length; n++) {
831
         var win_id = window[n].win_id;
832
         var section = window[n].section;
833
         var handler = window[n].handler;
834
835
         // alert("Section: " + section + " - Handler: " + handler);
836
837
         if (handler.indexOf("feh") > -1) {
838
           // up down left right zoomin zoomout home end prior next download rotateleft rotateright
839
           control = ["feh", true, true, true, true, true, true, false, false, false, false, true, true, true];
840
         } else if (handler.indexOf("libreoffice") > -1) {
841
           // Controls in LibreOffice: no zoom in calc and writer, has to be activated first
842
           // by pressing <Ctrl+Shift+o> (switch view mode on/off) not implemented yet
843
           control = [
844
             "libreoffice", true, true, true, true, false, false,
845
             false, false, true, true, true, false, false
846
           ];
847
           if (handler.indexOf("--calc") > -1) {
848
             control = [
849
               "libreoffice-calc", true, true, true, true, false, false,
850
               true, true, true, true, true, false, false
851
             ];
852
           }
853
           if (handler.indexOf("--impress") > -1) {
854
             control = [
855
               "libreoffice-impress", true, true, true, true, true, true,
856
               true, true, true, true, true, false, false
857
             ];
858
           }
859
           if (handler.indexOf("--writer") > -1) {
860
             control = [
861
               "libreoffice-writer", true, true, true, true, false, false,
862
               false, false, true, true, true, false, false
863
             ];
864
           }
865
         } else if (handler.indexOf("palma-browser") > -1) {
866
           control = [
867
             "palma-browser", true, true, true, true, true, true,
868
             true, true, true, true, false, false, false
869
           ];
870
         } else if (handler.indexOf("vlc") > -1) {
871
           control = [
872
             "vlc", false, false, false, true, false, false,
873
             false, false, false, false, false, false, false
874
           ];
875
         } else if (handler.indexOf("vnc") > -1) {
876
           control = [
877
             "vnc", true, true, true, true, true, true,
878
             false, false, false, false, false, false, false
879
           ];
880
         } else if (handler.indexOf("zathura") > -1) {
881
           control = [
882
             "zathura", true, true, true, true, true, true,
883
             true, true, true, true, true, false, true
884
           ];
885
         } else {
886
           control = [
887
             "undefined", false, false, false, false, false, false,
888
             false, false, false, false, false, false, false
889
           ];
890
         }
891
892
         sectionControls[section] = control;
893
       }
894
895
       // Fill empty sections with default values.
896
       for (i = sectionControls.length; i < 5; i++) {
897
         sectionControls[i] = ["default",
898
                               false, false, false, false,
899
                               false, false, false, false,
900
                               false, false, false, false, false];
901
       }
902
903
       return sectionControls;
904
     }
905
906
     function updateScreen() {
907
       sendToNuc('window=all&closeOrphans=true');
908
     }
909
910
     function clearURLField(defaultText) {
911
       var browseto = document.getElementById('url_field');
912
       browseto.setAttribute('value', 'https://');
913
     }
914
915
     // lastJSON is used to reduce the database polling rate.
916
     var lastJSON = '';
917
918
     function pollDatabase() {
919
       var xmlHttp = new XMLHttpRequest();
920
       if (xmlHttp) {
921
         xmlHttp.open("get", 'db.php?json=' + lastJSON, true);
922
         xmlHttp.onreadystatechange = function () {
923
           if (xmlHttp.readyState == 2) {
924
             // Data transferred to server.
925
           } else if (xmlHttp.readyState == 3) {
926
             // Server is answering.
927
           } else if (xmlHttp.readyState == 4) {
928
             // Received all data from server.
929
             var status = xmlHttp.status;
930
             if (status == 200) {
931
               // Got valid response.
932
               var text = xmlHttp.responseText;
933
               lastJSON = text;
934
               var db = JSON.parse(text);
935
               if (db === null || Object.keys(db).length === 0) {
936
                 return;
937
               }
938
               var i;
939
               var layout = '';
940
               for (i = 0; i < db.setting.length; i++) {
941
                 if (db.setting[i].key == 'layout') {
942
                   layout = db.setting[i].value;
943
                   break;
944
                 }
945
               }
946
               var controls = updateControlsBySection(db.window);
947
               // for (i = 0; i < controls.length; i++) {
948
               //    console.log(i + ": " + controls[i]);
949
               //    }
950
               updateUserList(db.address, db.user);
951
               updateWindowList(db.window);
952
               showLayout(layout, controls, db.window);
953
               // updateScreen(db.window);
954
               setTimeout("pollDatabase()", 1);
955
             } else {
956
               // Got error. TODO: handle it.
957
             }
958
           } else {
959
             // Got unexpected xmlHttp.readyState. TODO: handle it.
960
           }
961
         };
962
         xmlHttp.send(null);
963
       }
964
     }
965
966
     // Start polling the database.
967
     pollDatabase();
968
969
970
     function getOS() {
971
       var OSName="Unknown OS";
972
       if (navigator.appVersion.indexOf("Win") != -1) {
973
         OSName = "Windows";
974
       }
975
       if (navigator.appVersion.indexOf("Mac") != -1) {
976
         OSName = "MacOS";
977
       }
978
       if (navigator.appVersion.indexOf("X11") != -1) {
979
         OSName = "UNIX";
980
       }
981
       if (navigator.appVersion.indexOf("Linux") != -1) {
982
         OSName="Linux";
983
       }
984
       if (navigator.userAgent.indexOf("Android") != -1) {
985
         OSName="Android";
986
       }
987
       if ((navigator.userAgent.match(/iPhone/i))
988
           || (navigator.userAgent.match(/iPod/i))
989
           || (navigator.userAgent.match(/iPad/i))) {
990
         OSName="iOS";
991
       }
992
       /* Source for Android and iOS:
993
          https://davidwalsh.name/detect-android
994
          https://davidwalsh.name/detect-iphone
995
          https://davidwalsh.name/detect-ipad */
996
       return OSName;
997
     }
998
999
     function getFilePathByOS() {
1000
       var OSName = getOS();
1001
       var windows = 'download-winvnc';
1002
       var macOS = 'download-macvnc';
1003
       var linux = 'download-linux';
1004
       var download = '';
1005
       switch(OSName) {
1006
         case 'Windows': download = windows;
1007
           break;
1008
         case 'MacOS': download = macOS;
1009
           break;
1010
         case 'Linux': download = linux;
1011
           break;
1012
         case 'UNIX': download = linux;
1013
           break;
1014
         case 'Android':
1015
         case 'iOS':
1016
           document.getElementById("Screen").innerHTML =
1017
             '<div class="description">'
1018
             + "<?=addslashes(__('Sorry! Screensharing for your device is currently not supported.'))?>"
1019
             + '</div>';
1020
           break;
1021
         default: download = null;
1022
       }
1023
       document.getElementById(download).click();
1024
     }
1025
1026
     function openTab(evt, tabName) {
1027
       var i, tabcontent, tablinks;
1028
       // Hide all elements with class="tabcontent"
1029
       tabcontent = document.getElementsByClassName("tabcontent");
1030
       for (i = 0; i < tabcontent.length; i++) {
1031
         tabcontent[i].style.display = "none";
1032
       }
1033
       // Remove "active" class from all elements with class="tablinks"
1034
       tablinks = document.getElementsByClassName("tablinks");
1035
       for (i = 0; i < tablinks.length; i++) {
1036
         tablinks[i].className = tablinks[i].className.replace(" active", "");
1037
       }
1038
1039
       // Show current tab and add "active" class to the opening button
1040
       document.getElementById(tabName).style.display = "block";
1041
       evt.currentTarget.className += " active";
1042
     }
1043
1044
     function openSubtab(evt, tabName, subtabName) {
1045
       var i, tab, subtabcontent, subtablinks;
1046
       // Hide all elements with class="subtabcontent"
1047
       tab = document.getElementById(tabName);
1048
       subtabcontent = tab.getElementsByClassName("subtabcontent");
1049
       for (i = 0; i < subtabcontent.length; i++) {
1050
         subtabcontent[i].style.display = "none";
1051
       }
1052
1053
       // Remove "active" class from all elements with class="subtablinks"
1054
       subtablinks = tab.getElementsByClassName("subtablinks");
1055
       for (i = 0; i < subtablinks.length; i++) {
1056
         subtablinks[i].className = subtablinks[i].className.replace(" active", "");
1057
       }
1058
1059
       // Show current subtab and add "active" class to the opening button
1060
       document.getElementById(subtabName).style.display = "block";
1061
       evt.currentTarget.className += " active";
1062
     }
1063
1064
     function openAccordion(divID) {
1065
       var button = document.getElementById(divID).firstChild;
1066
       var windowcontrols = document.getElementById(divID).lastChild;
1067
       if (windowcontrols.style.display == "block") {
1068
         windowcontrols.style.display = "none";
1069
         button.setAttribute("class", "window_entry_button");
1070
       } else if (windowcontrols.style.display == "none") {
1071
         windowcontrols.style.display = "block";
1072
         button.setAttribute("class", "window_entry_button active");
1073
       } else {
1074
         windowcontrols.style.display = "block";
1075
         button.setAttribute("class", "window_entry_button active");
1076
       }
1077
     }
1078
1079
     function showDropdown() {
1080
       document.getElementById("languageSelection").classList.toggle("show");
1081
     }
1082
     // Close the dropdown menu if the user clicks outside of it
1083
     // Must use some workaround to support IE.
1084
     window.onclick = function(event) {
1085
       var classes = event.target.className.split(' ');
1086
       var found = false; var i = 0;
1087
       while (i < classes.length && !found) {
1088
         if (classes[i]=='dropbutton') found = true;
1089
         else ++i;
1090
       }
1091
       if (!found) {
1092
         var dropdowns = document.getElementsByClassName("dropdown-content");
1093
         var i;
1094
         for (i = 0; i < dropdowns.length; i++) {
1095
           var openDropdown = dropdowns[i];
1096
           if (openDropdown.classList.contains('show')) {
1097
             openDropdown.classList.remove('show');
1098
           }
1099
         }
1100
       }
1101
     }
1102
    </script>
1103
  </head>
1104
1105
  <body>
1106
1107
    <!-- This formatting is used to prevent empty textnodes that interfere with the design -->
1108
    <div class="tab">
1109
      <button class="tablinks" onclick="openTab(event, 'Add')">
1110
        <?=addslashes(__('Add'))?>
1111
        <i class="fa fa-plus"></i>
1112
      </button>
1113
      <button class="tablinks" id="controlbtn" onclick="openTab(event, 'Control')">
1114
        <?=addslashes(__('Control'))?>
1115
        <i class="fa fa-arrows"></i>
1116
      </button>
1117
      <button class="tablinks" onclick="openTab(event, 'Extras')">
1118
        <?=addslashes(__('Extras'))?>
1119
        <i class="fa fa-info-circle"></i>
1120
      </button>
1121
    </div>
1122
1123
    <div id="workbench">
1124
      <div id="Add" class="tabcontent">
1125
        <div class="subtab">
1126
          <button class="subtablinks" onclick="openSubtab(event, 'Add', 'File')">
1127
            <?=addslashes(__('File'))?>
1128
            <i class="fa fa-file"></i>
1129
          </button>
1130
          <button class="subtablinks" onclick="openSubtab(event, 'Add', 'URL')">
1131
            <?=addslashes(__('URL'))?>
1132
            <i class="fa fa-globe"></i>
1133
          </button>
1134
          <button class="subtablinks" onclick="openSubtab(event, 'Add', 'Screen')">
1135
            <?=addslashes(__('Screen'))?>
1136
            <i class="fa fa-video-camera"></i>
1137
          </button>
1138
        </div>
1139
        <div id="File" class="subtabcontent">
1140
          <div id="file_upload">
1141
            <form action="upload.php"
1142
                  class="dropzone"
1143
                  id="palma-dropzone"
1144
                  title="<?=addslashes(__('Drag and drop files or click here to upload.'))?>">
1145
              <div class="dz-default dz-message">
1146
                <?=__('Add file (click or drop here)')?>
1147
                <i class="fa fa-file fa-2x"></i>
1148
              </div>
1149
            </form>
1150
            <div class="dz-preview dz-file-preview">
1151
              <div class="dz-details">
1152
                <div class="dz-filename"><span data-dz-name></span></div>
1153
                <div class="dz-size" data-dz-size></div>
1154
                <img data-dz-thumbnail src="" alt="">
1155
              </div>
1156
              <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
1157
              <div class="dz-success-mark"><span> </span></div>
1158
              <div class="dz-error-mark"><span> </span></div>
1159
              <div class="dz-error-message"><span data-dz-errormessage></span></div>
1160
            </div>
1161
          </div>
1162
        </div>
1163
        <div id="URL" class="subtabcontent">
1164
          <div>
1165
            <input type="text" value="<?=addslashes(__('Add webpage'))?>"
1166
                   id="url_field" maxlength="256" size="46"
1167
                   onkeydown="if (event.keyCode == 13) document.getElementById('url_button').click()"
1168
                   onfocus="clearURLField('<?=addslashes(__('Enter URL'))?>')">
1169
            <button class="pure-button pure-button-primary pure-input-rounded"
1170
                    id="url_button"
1171
                    onClick="urlToNuc()" title="<?=addslashes(__('Click here to show the webpage on the screen.'))?>">
1172
              <?=addslashes(__('Enter'))?><i class="fa fa-globe"></i>
1173
            </button>
1174
          </div>
1175
        </div>
1176
        <div id="Screen" class="subtabcontent">
1177
          <div id="vnc-button" onclick="javascript:getFilePathByOS()">
1178
            <div id="vnc-button-container">
1179
              <div id="vnc-button-label">
1180
                <?=addslashes(__('Add your screen'))?>
1181
                <i class="fa fa-video-camera fa-2x" aria-hidden="true"></i>
1182
              </div>
1183
              <div id="vnc-button-label-subtext">
1184
                <?=addslashes(__('Download screensharing tool'))?>
1185
              </div>
1186
            </div>
1187
            <a href="<?php echo $winvnc; ?>" download id="download-winvnc" hidden></a>
1188
            <a href="<?php echo $macvnc; ?>" download id="download-macvnc" hidden></a>
1189
            <a href="<?php echo $linuxsh; ?>" download id="download-linux" hidden></a>
1190
          </div>
1191
          <div class="description">
1192
            <?=addslashes(__('Download your screensharing tool'
1193
                           . ' (Windows, Mac and Linux only).'
1194
                           . ' Visit the help section for further information.'))?>
1195
          </div>
1196
        </div>
1197
      </div>
1198
      <div id="Control" class="tabcontent">
1199
        <div class="subtab">
1200
          <button class="subtablinks" onclick="openSubtab(event, 'Control', 'Layout')">
1201
            <?=addslashes(__('Layout'))?>
1202
            <i class="fa fa-desktop"></i>
1203
          </button>
1204
          <button class="subtablinks" onclick="openSubtab(event, 'Control', 'Navigate')">
1205
            <?=addslashes(__('Navigate'))?>
1206
            <i class="fa fa-arrows"></i>
1207
          </button>
1208
        </div>
1209
        <div id="Layout" class="subtabcontent">
1210
          <div class="screenlayout">
1211
            <button class="pure-button pure-button-primary pure-input-rounded"
1212
                    id="g1x1" onclick="miniDisplaySelect(this)"
1213
                    title="<?=addslashes(__('Choose screen layout'))?>">
1214
              <i alt="1" class="fa fa-desktop fa-2x" aria-hidden="true"></i>
1215
            </button>
1216
          </div>
1217
          <div class="screenlayout vertical">
1218
            <button class="pure-button pure-button-primary pure-input-rounded"
1219
                    id="g1x2" onclick="miniDisplaySelect(this)"
1220
                    title="<?=addslashes(__('Choose screen layout'))?>">
1221
              <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1222
              <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1223
            </button>
1224
          </div>
1225
          <div class="screenlayout">
1226
            <button class="pure-button pure-button-primary pure-input-rounded"
1227
                    id="g2x1" onclick="miniDisplaySelect(this)"
1228
                    title="<?=addslashes(__('Choose screen layout'))?>">
1229
              <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1230
              <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1231
            </button>
1232
          </div>
1233
          <div class="screenlayout">
1234
            <button class="pure-button pure-button-primary pure-input-rounded"
1235
                    id="g1a2" onclick="miniDisplaySelect(this)"
1236
                    title="<?=addslashes(__('Choose screen layout'))?>">
1237
              <div class="layout_left">
1238
                <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1239
              </div>
1240
              <div class="layout_right vertical">
1241
                <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1242
                <i alt="3" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1243
              </div>
1244
            </button>
1245
          </div>
1246
          <div class="screenlayout">
1247
            <button class="pure-button pure-button-primary pure-input-rounded"
1248
                    id="g2x2" onclick="miniDisplaySelect(this)"
1249
                    title="<?=addslashes(__('Choose screen layout'))?>">
1250
              <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1251
              <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1252
              <br />
1253
              <i alt="3" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1254
              <i alt="4" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1255
            </button>
1256
          </div>
1257
        </div>
1258
        <div id="Navigate" class="subtabcontent">
1259
          <div id="windowlist">
1260
            <!-- filled by updateWindowList and showLayout -->
1261
          </div>
1262
          <button id="closeWindows"
1263
                  class="pure-button pure-button-primary pure-input-rounded"
1264
                  onClick="sendToNuc('closeAll=TRUE')"
1265
                  title="<?=addslashes(__('Close all windows and remove uploaded files'))?>">
1266
            <?=addslashes(__('Delete all items'))?>
1267
          </button>
1268
        </div>
1269
      </div>
1270
      <div id="Extras" class="tabcontent">
1271
        <div class="subtab">
1272
          <button class="subtablinks active" onclick="openSubtab(event, 'Extras', 'Help')">
1273
            <?=addslashes(__('Help'))?>
1274
            <i class="fa fa-question-circle"></i>
1275
          </button>
1276
          <button class="subtablinks" onclick="openSubtab(event, 'Extras', 'Feedback')">
1277
            <?=addslashes(__('Feedback'))?>
1278
            <i class="fa fa-thumbs-o-up"></i>
1279
          </button>
1280
          <button class="subtablinks" onclick="openSubtab(event, 'Extras', 'Users')">
1281
            <?=addslashes(__('Users'))?>
1282
            <i class="fa fa-users"></i>
1283
          </button>
1284
        </div>
1285
        <div id="Help" class="subtabcontent">
1286
          <p>
1287
            <?=addslashes(__('With PalMA, you can share documents, websites'
1288
                           . ' and your desktop with your learning group.'))?>
1289
            <?=addslashes(__('The PalMA team monitor shows up to four'
1290
                           . ' contributions simultaneously.'))?>
1291
          </p>
1292
          <?php
1293
          if (CONFIG_INSTITUTION_URL) { ?>
1294
            <p>
1295
              <?=addslashes(__('For further information about PalMA in this'
1296
                             . ' institution'))?>
1297
              <a href=<?=CONFIG_INSTITUTION_URL?> target="_blank"><?=__('see here.')?></a>
1298
            </p>
1299
            <?php
1300
          }
1301
          ?>
1302
          <h4><?=addslashes(__('Connect'))?><i class="fa fa-wifi"></i></h4>
1303
          <p>
1304
            <?=addslashes(__('Team members can join the session at any time with this URL or QR-Code:'))?>
1305
          </p>
1306
          <p class="url_hint">
1307
            <?=$starturl?><br />
1308
            <?php
1309
            if (!empty($pin)) {
1310
              echo addslashes(__('PIN: '));
1311
              echo $pin;
1312
            }
1313
            ?>
1314
          </p>
1315
          <img class="qr-code"
1316
               src="qrcode/php/qr_img.php?d=<?=$starturl?>?pin=<?=$pin?>"
1317
               alt="QR Code">
1318
          <h4><?=addslashes(__('Add'))?><i class="fa fa-plus"></i></h4>
1319
          <p>
1320
            <?=addslashes(__('Use the Add-Section to share content on the PalMA monitor.'))?>
1321
          </p>
1322
          <ul>
1323
            <li>
1324
              <i class="fa fa-file"></i>
1325
              <?=addslashes(__('For PDF files, office files, images or videos use the file section.'))?>
1326
            </li>
1327
            <li>
1328
              <i class="fa fa-globe"></i>
1329
              <?=addslashes(__('To display a website use the URL field.'))?>
1330
            </li>
1331
            <li>
1332
              <i class="fa fa-video-camera"></i>
1333
              <?=addslashes(__('To share your desktop in real time download the VNC screen sharing software and'))?>
1334
            </li>
1335
            <ul>
1336
              <li>
1337
                <i class="fa fa-windows"></i>
1338
                <?=addslashes(__('Windows:'))?>
1339
              </li>
1340
              <ul>
1341
                <li><?=addslashes(__('Run the downloaded file.'))?></li>
1342
                <li><?=addslashes(__('Double-click the name of your PalMA station in the appearing list.'))?></li>
1343
              </ul>
1344
              <li><i class="fa fa-apple"> </i> <?=addslashes(__('Mac:'))?></li>
1345
              <ul>
1346
                <li><?=addslashes(__('CTRL + click the downloaded file and run it.'))?></li>
1347
                <li><?=addslashes(__('A second window opens, in which you can start &quot;VineServer&quot;.'))?></li>
1348
                <li><?=addslashes(__('In the upper left corner, click on &quot;VNC Server&quot;.'))?></li>
1349
                <li><?=addslashes(__('Select &quot;Reverse Connection&quot;.'))?></li>
1350
                <li><?=addslashes(__('Enter the URL of your PalMA station and click &quot;Connect&quot;.'))?></li>
1351
              </ul>
1352
              <li><i class="fa fa-linux"></i> <?=addslashes(__('Linux:'))?></li>
1353
              <ul>
1354
                <li><?=addslashes(__('Run the downloaded shell script.'))?></li>
1355
                <li><?=addslashes(__('Or use this shell command:'))?>
1356
                  <code>x11vnc -connect <?=$_SERVER['HTTP_HOST']?></code>
1357
                </li>
1358
              </ul>
1359
            </ul>
1360
          </ul>
1361
          <h4><?=addslashes(__('Control'))?><i class="fa fa-arrows"></i></h4>
1362
          <p><?=addslashes(__('With the grey monitor buttons at the top you'
1363
             . ' can choose how the shared content should be arranged on the PalMA monitor.'))?></p>
1364
          <p><?=addslashes(__('Below that you find the controls for each item:'))?></p>
1365
          <ul>
1366
            <li><?=addslashes(__('In the top bar you can'))?></li>
1367
            <ul>
1368
              <li><?=addslashes(__('Hide and show'))?>
1369
                <i class="fa fa-eye"></i>,
1370
              </li>
1371
              <li><?=addslashes(__('Zoom in and out'))?>
1372
                <i class="fa fa-search-plus"></i>
1373
                <i class="fa fa-search-minus"></i>,
1374
              </li>
1375
              <li><?=addslashes(__('Rotate'))?>
1376
                <i class="fa fa-rotate-left"></i>
1377
                <i class="fa fa-rotate-right"></i>,
1378
              </li>
1379
              <li><?=addslashes(__('Download'))?>
1380
                <i class="fa fa-download"></i>,
1381
              </li>
1382
              <li><?=addslashes(__('Delete the item'))?>
1383
                <i class="fa fa-trash-o"></i>.
1384
              </li>
1385
            </ul>
1386
            <li><?=addslashes(__('Below you find the navigation controls.'))?></li>
1387
            <ul>
1388
              <li><?=addslashes(__('Buttons on the left jump to the top, to the end, a page up or a page down'))?>
1389
                <i class="fa fa-angle-double-up"></i>
1390
                <i class="fa fa-angle-up"></i>
1391
                <i class="fa fa-angle-down"></i>
1392
                <i class="fa fa-angle-double-up"></i>.
1393
              </li>
1394
              <li><?=addslashes(__('Arrow buttons in the middle scroll gradually'))?>
1395
                <i class="fa fa-toggle-up"></i>
1396
                <i class="fa fa-toggle-down"></i>
1397
                <i class="fa fa-toggle-left"></i>
1398
                <i class="fa fa-toggle-right"></i>.
1399
              </li>
1400
            </ul>
1401
    </li>
1402
    <li><?=addslashes(__('On the right you can choose the position on the PalMA monitor'))?>
1403
      <i class="fa fa-desktop"></i>.
1404
    </li>
1405
          </ul>
1406
          <p><?=addslashes(__('Controls that are not available for certain kinds of content are marked grey.'))?></p>
1407
          <h4><?=addslashes(__('Extras'))?><i class="fa fa-info-circle"></i></h4>
1408
          <p><?=addslashes(__('Some additional features are:'))?></p>
1409
          <ul>
1410
            <li><i class="fa fa-question-circle"></i>
1411
              <?=addslashes(__('This help,'))?>
1412
            </li>
1413
            <li><i class="fa fa-thumbs-o-up"></i>
1414
              <?=addslashes(__('Your chance to recommend us or give us your'
1415
                . ' thoughts in the &quot;Feedback&quot; section,'))?>
1416
            </li>
1417
            <li><i class="fa fa-users"></i>
1418
              <?=addslashes(__('A list of all logged-in users as well as a'
1419
                . ' button to disconnect everyone and therefore end the session.'))?>
1420
            </li>
1421
          </ul>
1422
        </div>
1423
        <div id="Feedback" class="subtabcontent">
1424
          <div id="recommendcontainer">
1425
            <h3>
1426
              <?=addslashes(__('Recommend us'))?>
1427
            </h3>
1428
            <div>
1429
              <p><?=addslashes(__('If you like PalMA, please recommend us by'
1430
                                . ' sharing in your social networks.<br />Enjoy PalMA!'))?>
1431
              </p>
1432
              <!-- Social Media Button Integration, Source: http://sharingbuttons.io/ -->
1433
              <?php $github_url = "https%3A%2F%2Fgithub.com/UB-Mannheim/PalMA/blob/master/README.md"; ?>
1434
1435
              <!-- Sharingbutton Facebook -->
1436
              <a class="resp-sharing-button__link"
1437
                 href="https://facebook.com/sharer/sharer.php?u=<?=$github_url?>"
1438
                 target="_blank"
1439
                 aria-label="">
1440
                <div class="resp-sharing-button resp-sharing-button--facebook resp-sharing-button--small">
1441
                  <div aria-hidden="true"
1442
                       class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1443
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.77 7.46H14.5v-1.9c0-.9.6-1.1 1-1.1h3V.5h-4.33C10.24.5 9.5 3.44 9.5 5.32v2.15h-3v4h3v12h5v-12h3.85l.42-4z"/></svg>
1444
                </div>
1445
                </div>
1446
              </a>
1447
1448
              <!-- Sharingbutton Twitter -->
1449
              <a class="resp-sharing-button__link"
1450
                 href="https://twitter.com/intent/tweet/?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;url=<?=$github_url?>"
1451
                 target="_blank"
1452
                 aria-label="">
1453
                <div class="resp-sharing-button resp-sharing-button--twitter resp-sharing-button--small">
1454
                  <div aria-hidden="true"
1455
                       class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1456
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.44 4.83c-.8.37-1.5.38-2.22.02.93-.56.98-.96 1.32-2.02-.88.52-1.86.9-2.9 1.1-.82-.88-2-1.43-3.3-1.43-2.5 0-4.55 2.04-4.55 4.54 0 .36.03.7.1 1.04-3.77-.2-7.12-2-9.36-4.75-.4.67-.6 1.45-.6 2.3 0 1.56.8 2.95 2 3.77-.74-.03-1.44-.23-2.05-.57v.06c0 2.2 1.56 4.03 3.64 4.44-.67.2-1.37.2-2.06.08.58 1.8 2.26 3.12 4.25 3.16C5.78 18.1 3.37 18.74 1 18.46c2 1.3 4.4 2.04 6.97 2.04 8.35 0 12.92-6.92 12.92-12.93 0-.2 0-.4-.02-.6.9-.63 1.96-1.22 2.56-2.14z"/></svg>
1457
                </div>
1458
                </div>
1459
              </a>
1460
1461
              <!-- Sharingbutton E-Mail -->
1462
              <a class="resp-sharing-button__link"
1463
                 href="mailto:?subject=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;body=<?=$github_url?>"
1464
                 target="_self"
1465
                 aria-label="">
1466
                <div class="resp-sharing-button resp-sharing-button--email resp-sharing-button--small">
1467
                  <div aria-hidden="true"
1468
                       class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1469
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 4H2C.9 4 0 4.9 0 6v12c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7.25 14.43l-3.5 2c-.08.05-.17.07-.25.07-.17 0-.34-.1-.43-.25-.14-.24-.06-.55.18-.68l3.5-2c.24-.14.55-.06.68.18.14.24.06.55-.18.68zm4.75.07c-.1 0-.2-.03-.27-.08l-8.5-5.5c-.23-.15-.3-.46-.15-.7.15-.22.46-.3.7-.14L12 13.4l8.23-5.32c.23-.15.54-.08.7.15.14.23.07.54-.16.7l-8.5 5.5c-.08.04-.17.07-.27.07zm8.93 1.75c-.1.16-.26.25-.43.25-.08 0-.17-.02-.25-.07l-3.5-2c-.24-.13-.32-.44-.18-.68s.44-.32.68-.18l3.5 2c.24.13.32.44.18.68z"/></svg>
1470
                </div>
1471
                </div>
1472
              </a>
1473
1474
              <!-- Sharingbutton WhatsApp -->
1475
              <a class="resp-sharing-button__link"
1476
                 href="whatsapp://send?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.%20<?=$github_url?>"
1477
                 target="_blank"
1478
                 aria-label="">
1479
                <div class="resp-sharing-button resp-sharing-button--whatsapp resp-sharing-button--small">
1480
                  <div aria-hidden="true"
1481
                       class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1482
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20.1 3.9C17.9 1.7 15 .5 12 .5 5.8.5.7 5.6.7 11.9c0 2 .5 3.9 1.5 5.6L.6 23.4l6-1.6c1.6.9 3.5 1.3 5.4 1.3 6.3 0 11.4-5.1 11.4-11.4-.1-2.8-1.2-5.7-3.3-7.8zM12 21.4c-1.7 0-3.3-.5-4.8-1.3l-.4-.2-3.5 1 1-3.4L4 17c-1-1.5-1.4-3.2-1.4-5.1 0-5.2 4.2-9.4 9.4-9.4 2.5 0 4.9 1 6.7 2.8 1.8 1.8 2.8 4.2 2.8 6.7-.1 5.2-4.3 9.4-9.5 9.4zm5.1-7.1c-.3-.1-1.7-.9-1.9-1-.3-.1-.5-.1-.7.1-.2.3-.8 1-.9 1.1-.2.2-.3.2-.6.1s-1.2-.5-2.3-1.4c-.9-.8-1.4-1.7-1.6-2-.2-.3 0-.5.1-.6s.3-.3.4-.5c.2-.1.3-.3.4-.5.1-.2 0-.4 0-.5C10 9 9.3 7.6 9 7c-.1-.4-.4-.3-.5-.3h-.6s-.4.1-.7.3c-.3.3-1 1-1 2.4s1 2.8 1.1 3c.1.2 2 3.1 4.9 4.3.7.3 1.2.5 1.6.6.7.2 1.3.2 1.8.1.6-.1 1.7-.7 1.9-1.3.2-.7.2-1.2.2-1.3-.1-.3-.3-.4-.6-.5z"/></svg>
1483
                </div>
1484
                </div>
1485
              </a>
1486
1487
              <!-- Sharingbutton Telegram -->
1488
              <a class="resp-sharing-button__link"
1489
                 href="https://telegram.me/share/url?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;url=<?=$github_url?>"
1490
                 target="_blank"
1491
                 aria-label="">
1492
                <div class="resp-sharing-button resp-sharing-button--telegram resp-sharing-button--small">
1493
                  <div aria-hidden="true"
1494
                       class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1495
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M.707 8.475C.275 8.64 0 9.508 0 9.508s.284.867.718 1.03l5.09 1.897 1.986 6.38a1.102 1.102 0 0 0 1.75.527l2.96-2.41a.405.405 0 0 1 .494-.013l5.34 3.87a1.1 1.1 0 0 0 1.046.135 1.1 1.1 0 0 0 .682-.803l3.91-18.795A1.102 1.102 0 0 0 22.5.075L.706 8.475z"/></svg>
1496
                </div>
1497
                </div>
1498
              </a>
1499
            </div> <!-- Social media buttons and description -->
1500
          </div> <!-- recommendcontainer -->
1501
          <div id="contactcontainer">
1502
            <h3><?=addslashes(__('Tell us what you think'))?></h3>
1503
            <div>
1504
              <p>
1505
                <?=addslashes(__('Please let us know about problems or ideas'
1506
                               . ' to improve PalMA. Help us directly by'
1507
                               . ' sending crash reports or contributing on'))?>
1508
                <a href="https://github.com/UB-Mannheim/PalMA" target="_blank">GitHub</a>.
1509
                <br />
1510
                <?=__('Thank you!')?>
1511
              </p>
1512
            </div>
1513
          </div> <!-- contactcontainer -->
1514
        </div> <!-- feedback -->
1515
        <div id="Users" class="subtabcontent">
1516
          <div class="dropdown">
1517
            <button onclick="showDropdown()"
1518
                    class="dropbutton pure-button pure-button-primary">
1519
              <?=addslashes(__('Select language:'))?> <?=substr($locale, 0, 2)?>
1520
              <i class="fa fa-caret-down"></i></button>
1521
            <div id="languageSelection" class="dropdown-content">
1522
              <?php
1523
              $dirs = array_slice(scandir('locale'), 2);
1524
              $langs = [];
1525
              for ($n = 0; $n < count($dirs); $n++) {
1526
                if (is_dir("locale/$dirs[$n]")) {
1527
                  array_push($langs, substr($dirs[$n], 0, 2));
1528
                }
1529
              }
1530
              for ($i = 0; $i < count($langs); $i++) {
1531
                echo "<a href=$_SERVER[PHP_SELF]?lang=$langs[$i]>$langs[$i]</a>";
1532
              }
1533
              ?>
1534
            </div>
1535
          </div>
1536
          <div class="list_container">
1537
            <table class="userlist"
1538
                   summary="<?=addslashes(__('User list'))?>"
1539
                   title="<?=__('List of connected users')?>">
1540
              <tbody id="userlist">
1541
                <tr><td><!-- filled by updateUserList() --></td></tr>
1542
              </tbody>
1543
            </table>
1544
            <div class="description">
1545
              <?=addslashes(__('New users can join at'))?><br /><?=$starturl?><br />
1546
              <?php
1547
              if (!empty($pin)) {
1548
                echo addslashes(__('PIN: '));
1549
                echo $pin;
1550
              }
1551
              ?>
1552
              <img class="qr-code" src="qrcode/php/qr_img.php?d=<?=$starturl?>?pin=<?=$pin?>" alt="QR Code">
1553
            </div>
1554
            <button class="pure-button pure-button-primary pure-input-rounded"
1555
                    onClick="sendToNuc('logout=ALL')"
1556
                    title="<?=addslashes(__('Disconnect all users and end the session'))?>">
1557
              <?=addslashes(__('End the session'))?>
1558
            </button>
1559
          </div>
1560
        </div>
1561
      </div>
1562
    </div> <!-- workbench -->
1563
1564
    <div id="footer">
1565
      <?php
1566
      # Show authorized user name (and address) and allow logout.
1567
      if ($user) {
1568
        echo("<a href=\"logout.php\" title=\"" .
1569
             addslashes(__('Disconnect the current user')) .
1570
             "\">" . addslashes(__('Log out')) . "<i class=\"fa fa-sign-out\"></i></a>");
1571
      }
1572
      ?>
1573
    </div> <!-- Footer -->
1574
1575
  </body>
1576
</html>
1577