Passed
Push — master ( a54c9d...b24210 )
by
unknown
39s queued 10s
created

index.php (2 issues)

1
<?php
2
/*
3
Copyright (C) 2014-2015 Universitätsbibliothek Mannheim
4
See file LICENSE for license details.
5
6
Authors: Alexander Wagner, Stefan Weil, Dennis Müller
7
8
References:
9
10
File upload (general)
11
12
* http://www.php.net/manual/en/features.file-upload.post-method.php
13
14
File upload with dropzone
15
16
* http://www.dropzonejs.com/
17
* http://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php
18
* http://maxoffsky.com/code-blog/howto-ajax-multiple-file-upload-in-laravel/
19
20
Websockets:
21
22
* https://en.wikipedia.org/wiki/Server-sent_events
23
* https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications
24
* http://code.google.com/p/phpwebsocket/
25
* http://dharman.eu/?menu=phpWebSocketsTutorial
26
27
Keyboard input
28
29
* http://jsfiddle.net/angusgrant/E3tE6/
30
* http://stackoverflow.com/questions/3181648/how-can-i-handle-arrowkeys-and-greater-than-in-a-javascript-function-which
31
* http://stackoverflow.com/questions/5597060/detecting-arrow-key-presses-in-javascript
32
* http://www.quirksmode.org/js/keys.html
33
34
Key symbols
35
36
* http://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm
37
38
* wmctrl, suckless-tools (lsw, sprop, wmname, ...)
39
40
* display.im6, evince
41
42
Authorization
43
44
* http://aktuell.de.selfhtml.org/artikel/php/loginsystem/
45
46
Overlays
47
48
* http://answers.oreilly.com/topic/1823-adding-a-page-overlay-in-javascript/
49
50
*/
51
52
    session_start();
53
if (isset($_REQUEST['monitor'])) {
54
    $monitor = $_REQUEST['monitor'];
55
    $_SESSION['monitor'] = $monitor;
56
} elseif (!isset($_SESSION['monitor'])) {
57
    $_SESSION['monitor'] = '???';
58
}
59
    $_SESSION['referer'] = 'index.php';
60
    require_once('auth.php');
61
62
    // Connect to database and get configuration constants.
63
    require_once('DBConnector.class.php');
64
    $dbcon = new palma\DBConnector();
65
66
    // Support localisation.
67
    require_once('i12n.php');
68
69
    $user = false;
70
if (isset($_SESSION['username'])) {
71
    # PHP session based authorization.
72
    $username = $_SESSION['username'];
73
    $address = $_SESSION['address'];
74
    $user = "$username@$address";
75
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
76
    # .htaccess basic authorization.
77
    $user = $_SERVER['PHP_AUTH_USER'];
78
}
79
80
    require_once('globals.php');
81
    /*
82
     * file paths for vnc downloads
83
     */
84
    $winvnc = CONFIG_START_URL . "theme/" . CONFIG_THEME . "/winvnc-palma.exe";
85
    $macvnc = "https://www.bib.uni-mannheim.de/palma/VineServer.dmg";
86
    $linuxsh = CONFIG_START_URL . "theme/" . CONFIG_THEME . "/x11.sh";
87
88
    /*
89
     * contact form elements
90
     * might be sourced out and included
91
     */
92
if (isset($_POST['submit'])) {
93
    $to = "[email protected]";
94
    $from = escapeshellcmd($_POST['email']);
95
    $name = escapeshellcmd($_POST['name']);
96
    $subject = "Feedback for PalMA";
97
    $message = $name . " wrote the following:" . "\n\n" . escapeshellcmd($_POST['message']);
98
99
    $headers = "From:" . $from;
100
    mail($to, $subject, $message, $headers);
101
    echo "Mail Sent. Thank you for your feedback " . $name . ", we will get in touch with you shortly.";
102
}
103
104
?>
105
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
106
       "http://www.w3.org/TR/html4/strict.dtd">
107
108
<html>
109
110
<head>
111
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
112
<meta name="viewport" content="width=device-width, initial-scale=1">
113
<title>PalMA</title>
114
115
<link rel="icon" href="theme/<?=CONFIG_THEME?>/favicon.ico" type="image/x-icon">
116
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
117
<link rel="stylesheet" href="pure-min.css">
118
<link rel="stylesheet" href="palma.css" type="text/css">
119
120
<script type="text/javascript" src="jquery.min.js"></script>
121
122
<script type="text/javascript" src="dropzone.js"></script>
123
124
<script type="text/javascript">
125
126
// Screen section which responds to keyboard input.
127
var focus_section = '1';
128
129
function sendToNuc(command) {
130
  var xmlHttp = new XMLHttpRequest();
131
  if (!xmlHttp) {
132
    // TODO
133
    alert('XMLHttpRequest failed!');
134
    return;
135
  }
136
  var url = 'control.php?' + command;
137
  var response = "";
138
  xmlHttp.open("get", url, true);
139
  xmlHttp.onreadystatechange = function () {
140
      if (xmlHttp.readyState == 1) {
141
          // Server connection established (IE only).
142
      } else if (xmlHttp.readyState == 2) {
143
          // Data transferred to server.
144
      } else if (xmlHttp.readyState == 3) {
145
          // Server is answering.
146
      } else if (xmlHttp.readyState == 4) {
147
          // Received all data from server.
148
          return xmlHttp.responseText;
149
      } else {
150
          alert("Got xmlHttp.readyState " + xmlHttp.readyState);
151
      }
152
  };
153
  xmlHttp.send(null);
154
  //~ alert("sendToNuc " + url);
155
}
156
157
function keyControl(number, image, controlClass, key, handler, disabled, title) {
158
    // "number" refers to the slected screensection
159
160
  var keyHandler = getHandlerCommand(handler, key);
161
  if ( (keyHandler == "") || (keyHandler == null) ) {
162
    keyHandler = "default";
163
  }
164
165
  var button = document.createElement('button');
166
  var icon = document.createElement('i');
167
  if (!disabled) {
168
    icon.setAttribute('class', image);
169
    button.appendChild(icon);
170
    button.setAttribute('class', controlClass);
171
    button.setAttribute('onmousedown',
172
                     'sendToNuc("window=' + number + '&keydown=' + encodeURIComponent(keyHandler) + '")');
173
    button.setAttribute('onmouseup',
174
                     'sendToNuc("window=' + number + '&keyup=' + encodeURIComponent(keyHandler) + '")');
175
    button.setAttribute('title', title);
176
  } else {
177
    icon.setAttribute('class', image + " unavailable");
178
    button.appendChild(icon);
179
    button.setAttribute("class", "unavailable");
180
    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

180
    button.setAttribute('title', title + " " + "<?=addslashes(/** @scrutinizer ignore-call */ __('not available'))?>");
Loading history...
181
  }
182
  return button;
183
}
184
185
function downloadFile(screensection) {
186
187
    // wrong path if copied to /home/directory
188
    // TODO: check file and path
189
190
   var url = document.URL;
191
   var url_path = url.split("/");
192
193
   var file = document.getElementById("file" + screensection).innerHTML;
194
   var file = document.getElementById("file" + screensection).getAttribute("title");
195
196
   // Download with download.php
197
   var download = url_path[0]+"/"+url_path[1]+"/"+url_path[2]+"/"+url_path[3]+"/download.php?file="+encodeURIComponent(file);
198
199
   var name = "Download";
200
201
   if(file.indexOf("www.") > -1) {
202
        window.open(file, name);
203
   } else {
204
        window.open(download, name);
205
    }
206
}
207
208
function is_valid_url(url)
209
{
210
    return url.match(/(^(ht|f)tps?:\/\/)([a-zA-Z0-9\.-])+(\.([a-zA-Z]{2,}))(\/([^\s\<\>\,\{\}\\\|\^\[\]\'])*)?$/);
211
}
212
213
function urlToNuc() {
214
215
    var url = document.getElementById('url_field').value;
216
    if (is_valid_url(url)) {
217
        // Encode special characters
218
        url = encodeURIComponent(url);
219
        sendToNuc('openURL='+url);
220
    } else {
221
        var urlfield = document.getElementById('url_field');
222
        urlfield.setAttribute('value', '<?=addslashes(__("Enter valid URL"))?>');
223
    }
224
225
    setTimeout(function(){location.reload()}, 1000);
226
}
227
228
function showLayout(layout, controls, window) {
229
    //~ console.log("Layout: " + layout);
230
    //~ for (i = 0; i < controls.length; i++) {
231
    //~     console.log("SL " + i + ": " + controls[i]);
232
    //~ }
233
234
    document.onkeydown = function(evt) {
235
        evt = evt || window.event;
236
        var section = focus_section;
237
        var handler = controls[focus_section][0];
238
        var keyHandler;
239
        //~ console.log("Key down: " + evt.keyCode);
240
        switch (evt.keyCode) {
241
        case 33: // page up
242
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'prior'));
243
            sendToNuc('window=' + section + '&key=' + keyHandler);
244
            break;
245
        case 34: // page down
246
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'next'));
247
            sendToNuc('window=' + section + '&key=' + keyHandler);
248
            break;
249
        case 35: // end
250
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'end'));
251
            sendToNuc('window=' + section + '&key=' + keyHandler);
252
            break;
253
        case 36: // home
254
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'home'));
255
            sendToNuc('window=' + section + '&key=' + keyHandler);
256
            break;
257
        case 37: // left
258
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'left'));
259
            sendToNuc('window=' + section + '&key=' + keyHandler);
260
            break;
261
        case 38: // up
262
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'up'));
263
            sendToNuc('window=' + section + '&key=' + keyHandler);
264
            break;
265
        case 39: // right
266
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'right'));
267
            sendToNuc('window=' + section + '&key=' + keyHandler);
268
            break;
269
        case 40: // down
270
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'down'));
271
            sendToNuc('window=' + section + '&key=' + keyHandler);
272
            break;
273
        }
274
    };
275
276
    document.onkeypress = function(evt) {
277
        evt = evt || window.event;
278
        //~ console.log("Key press: " + evt.keyCode);
279
        var charCode = evt.which || evt.keyCode;
280
        var charStr = String.fromCharCode(charCode);
281
        var section = focus_section;
282
        var handler = controls[focus_section][0];
283
        var keyHandler;
284
        switch (charStr) {
285
        case "1": // select section 1
286
        case "2": // select section 2
287
        case "3": // select section 3
288
        case "4": // select section 4
289
            focus_section = charStr;
290
            break;
291
        case "+": // zoom in
292
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'zoomin'));
293
            sendToNuc('window=' + section + '&key=' + keyHandler);
294
            break;
295
        case "-": // zoom out
296
            keyHandler = encodeURIComponent(getHandlerCommand(handler, 'zoomout'));
297
            sendToNuc('window=' + section + '&key=' + keyHandler);
298
            break;
299
        }
300
    };
301
302
    var windowlist = document.getElementById('windowlist');
303
    var entries = windowlist.getElementsByClassName('window_entry');
304
    var screensection, file, status;
305
    markCurrentLayout(layout);
306
    for (var n = 0; n < window.length; n++) {
307
        screensection = window[n].section;
308
        file = window[n].file;
309
        status = window[n].state;
310
        entries[n].appendChild(addWindowControls(layout, controls, screensection, file, status));
311
    }
312
}
313
314
function markCurrentLayout(layout) {
315
    var layoutDivs = document.getElementsByClassName("screenlayout");
316
    for (var i = 0 ; i < layoutDivs.length ; i++) {
317
        var children = layoutDivs[i].getElementsByClassName("pure-button");
318
        for (var k = 0 ; k < children.length ; k++) {
319
            children[k].style.backgroundColor = "";
320
        }
321
    }
322
    document.getElementById(layout).style.backgroundColor = "#990000";
323
}
324
325
function miniDisplaySelect(element) {
326
    sendToNuc('layout=' + element.id);
327
    markCurrentLayout(element.id);
328
}
329
330
function getHandlerCommand(handle, task) {
331
332
    // console.log("getHandlerCommand "+handle+" - "+task);
333
    // to deactivate buttons just add 'undefined' as keystroke
334
335
    var handler = [];
336
337
    handler["default"] = {};
338
    // handler["default"]["init"] = "";
339
    handler["default"]["up"] = "Up";
340
    handler["default"]["down"] = "Down";
341
    handler["default"]["left"] = "Left";
342
    handler["default"]["right"] = "Right";
343
    handler["default"]["next"] = "Next";
344
    handler["default"]["prior"] = "Prior";
345
    handler["default"]["home"] = "Home";
346
    handler["default"]["end"] = "End";
347
    handler["default"]["zoomin"] = "ctrl+plus";
348
    handler["default"]["zoomout"] = "ctrl+minus";
349
    handler["default"]["download"] = "download";
350
    handler["default"]["counterclockwise"] = "undefined";
351
    handler["default"]["clockwise"] = "undefined";
352
353
    // Handler for web pages.
354
    handler["midori"] = {};
355
    handler["midori"]["up"] = "Up";
356
    handler["midori"]["down"] = "Down";
357
    handler["midori"]["left"] = "Left";
358
    handler["midori"]["right"] = "Right";
359
    handler["midori"]["next"] = "Next";
360
    handler["midori"]["prior"] = "Prior";
361
    handler["midori"]["home"] = "Home";
362
    handler["midori"]["end"] = "End";
363
    handler["midori"]["zoomin"] = "ctrl+plus";
364
    handler["midori"]["zoomout"] = "ctrl+minus";
365
    handler["midori"]["download"] = "undefined";
366
    handler["midori"]["counterclockwise"] = "undefined";
367
    handler["midori"]["clockwise"] = "undefined";
368
369
    // Handler for images.
370
    handler["feh"] = {};
371
    handler["feh"]["up"] = "alt+Up";
372
    handler["feh"]["down"] = "alt+Down";
373
    handler["feh"]["left"] = "alt+Left";
374
    handler["feh"]["right"] = "alt+Right";
375
    handler["feh"]["next"] = "alt+Next";
376
    handler["feh"]["prior"] = "alt+Prior";
377
    handler["feh"]["home"] = "undefined";
378
    handler["feh"]["end"] = "undefined";
379
    handler["feh"]["zoomin"] = "KP_Add";
380
    handler["feh"]["zoomout"] = "KP_Subtract";
381
    handler["feh"]["download"] = "download";
382
    handler["feh"]["counterclockwise"] = "less";
383
    handler["feh"]["clockwise"] = "greater";
384
385
    // Controls in LibreOffice: no zoom in calc and writer, has to be activated first
386
    // by pressing <Ctrl+Shift+o> (switch view mode on/off) not implemented yet
387
    handler["libreoffice"] = {};
388
    handler["libreoffice"]["up"] = "Up";
389
    handler["libreoffice"]["down"] = "Down";
390
    handler["libreoffice"]["left"] = "Left";
391
    handler["libreoffice"]["right"] = "Right";
392
    handler["libreoffice"]["next"] = "Next";
393
    handler["libreoffice"]["prior"] = "Prior";
394
    handler["libreoffice"]["home"] = "undefined";
395
    handler["libreoffice"]["end"] = "undefined";
396
    handler["libreoffice"]["zoomin"] = "undefined";
397
    handler["libreoffice"]["zoomout"] = "undefined";
398
    handler["libreoffice"]["download"] = "download";
399
    handler["libreoffice"]["counterclockwise"] = "undefined";
400
    handler["libreoffice"]["clockwise"] = "undefined";
401
402
    // Handler for MS Excel and LibreOffice Calc documents.
403
    handler["libreoffice-calc"] = {};
404
    handler["libreoffice-calc"]["up"] = "Up";
405
    handler["libreoffice-calc"]["down"] = "Down";
406
    handler["libreoffice-calc"]["left"] = "Left";
407
    handler["libreoffice-calc"]["right"] = "Right";
408
    handler["libreoffice-calc"]["next"] = "Next";
409
    handler["libreoffice-calc"]["prior"] = "Prior";
410
    handler["libreoffice-calc"]["home"] = "Home";
411
    handler["libreoffice-calc"]["end"] = "End";
412
    handler["libreoffice-calc"]["zoomin"] = "undefined";
413
    handler["libreoffice-calc"]["zoomout"] = "undefined";
414
    handler["libreoffice-calc"]["download"] = "download";
415
    handler["libreoffice-calc"]["counterclockwise"] = "undefined";
416
    handler["libreoffice-calc"]["clockwise"] = "undefined";
417
418
    // Handler for MS Powerpoint and LibreOffice Impress documents.
419
    handler["libreoffice-impress"] = {};
420
    handler["libreoffice-impress"]["up"] = "Up";
421
    handler["libreoffice-impress"]["down"] = "Down";
422
    handler["libreoffice-impress"]["left"] = "Left";
423
    handler["libreoffice-impress"]["right"] = "Right";
424
    handler["libreoffice-impress"]["next"] = "Next";
425
    handler["libreoffice-impress"]["prior"] = "Prior";
426
    handler["libreoffice-impress"]["home"] = "Home";
427
    handler["libreoffice-impress"]["end"] = "End";
428
    handler["libreoffice-impress"]["zoomin"] = "plus";
429
    handler["libreoffice-impress"]["zoomout"] = "minus";
430
    handler["libreoffice-impress"]["download"] = "download";
431
    handler["libreoffice-impress"]["counterclockwise"] = "undefined";
432
    handler["libreoffice-impress"]["clockwise"] = "undefined";
433
434
    // Handler for MS Word and LibreOffice Writer documents.
435
    handler["libreoffice-writer"] = {};
436
    handler["libreoffice-writer"]["up"] = "Up";
437
    handler["libreoffice-writer"]["down"] = "Down";
438
    handler["libreoffice-writer"]["left"] = "Left";
439
    handler["libreoffice-writer"]["right"] = "Right";
440
    handler["libreoffice-writer"]["next"] = "Next";
441
    handler["libreoffice-writer"]["prior"] = "Prior";
442
    handler["libreoffice-writer"]["home"] = "undefined";
443
    handler["libreoffice-writer"]["end"] = "undefined";
444
    handler["libreoffice-writer"]["zoomin"] = "undefined";
445
    handler["libreoffice-writer"]["zoomout"] = "undefined";
446
    handler["libreoffice-writer"]["download"] = "download";
447
    handler["libreoffice-writer"]["counterclockwise"] = "undefined";
448
    handler["libreoffice-writer"]["clockwise"] = "undefined";
449
450
    // Handler for videos.
451
    handler["vlc"] = {};
452
    handler["vlc"]["up"] = "undefined";
453
    handler["vlc"]["down"] = "undefined";
454
    handler["vlc"]["left"] = "undefined";
455
    handler["vlc"]["right"] = "space";
456
    handler["vlc"]["next"] = "undefined";
457
    handler["vlc"]["prior"] = "undefined";
458
    handler["vlc"]["home"] = "undefined";
459
    handler["vlc"]["end"] = "undefined";
460
    handler["vlc"]["zoomin"] = "undefined";
461
    handler["vlc"]["zoomout"] = "undefined";
462
    handler["vlc"]["download"] = "undefined";
463
    handler["vlc"]["counterclockwise"] = "undefined";
464
    handler["vlc"]["clockwise"] = "undefined";
465
466
    // Handler for shared desktops (VNC).
467
    handler["vnc"] = {};
468
    handler["vnc"]["up"] = "Up";
469
    handler["vnc"]["down"] = "Down";
470
    handler["vnc"]["left"] = "Left";
471
    handler["vnc"]["right"] = "Right";
472
    handler["vnc"]["next"] = "undefined";
473
    handler["vnc"]["prior"] = "undefined";
474
    handler["vnc"]["home"] = "undefined";
475
    handler["vnc"]["end"] = "undefined";
476
    handler["vnc"]["zoomin"] = "plus";
477
    handler["vnc"]["zoomout"] = "minus";
478
    handler["vnc"]["download"] = "undefined";
479
    handler["vnc"]["counterclockwise"] = "undefined";
480
    handler["vnc"]["clockwise"] = "undefined";
481
482
    // Handler for PDF documents.
483
    handler["zathura"] = {};
484
    handler["zathura"]["up"] = "Up";
485
    handler["zathura"]["down"] = "Down";
486
    handler["zathura"]["left"] = "Left";
487
    handler["zathura"]["right"] = "Right";
488
    handler["zathura"]["next"] = "Next";
489
    handler["zathura"]["prior"] = "Prior";
490
    handler["zathura"]["home"] = "Home";
491
    handler["zathura"]["end"] = "End";
492
    handler["zathura"]["zoomin"] = "plus";
493
    handler["zathura"]["zoomout"] = "minus";
494
    handler["zathura"]["download"] = "download";
495
    handler["zathura"]["counterclockwise"] = "undefined";
496
    handler["zathura"]["clockwise"] = "undefined";
497
498
    var send_keys = handler["default"]["up"];
499
500
    if (typeof(handler[handle]) !== "undefined") {
501
        send_keys = handler[handle][task];
502
    }
503
504
    // console.log(send_keys);
505
506
    return send_keys;
507
}
508
509
Dropzone.options.palmaDropzone = {
510
    init: function() {
511
      this.on("complete", function() {
512
        if (this.getQueuedFiles().length == 0 && this.getUploadingFiles().length == 0) {
513
          // File finished uploading, and there aren't any left in the queue.
514
          // console.log("File(s) uploaded");
515
          setTimeout(function() {
516
             location.reload();
517
          }, 1);
518
          // location.reload(); // verlangt Eingabe von Enter zum wiederholten Schicken der Daten
519
        }
520
      });
521
    }
522
};
523
524
function updateUserList(address, user) {
525
    // Update the user list on screen from the table in the database.
526
527
    // Get the <tbody> element which contains the user entries.
528
    var list = document.getElementById('userlist');
529
530
    // First we remove all existing <tr> elements.
531
    while (list.firstChild) {
532
        list.removeChild(list.firstChild);
533
    }
534
535
    if (address.length > 0) {
536
        // Add an entry for each user. Iterate over addresses:
537
        // One user may be connected several times with different devices.
538
        // We don't expect more than one user from the same device.
539
        var m;
540
        for (m = 0; m < address.length; m++) {
541
            var n;
542
            for (n = 0; n < user.length; n++) {
543
                if (address[m].userid == user[n].userid) {
544
                    break;
545
                }
546
            }
547
            var device = address[m].device;
548
            var tr = document.createElement('tr');
549
            var td = document.createElement('td');
550
            var i = document.createElement('i');
551
            i.setAttribute('class', 'fa fa-fw fa-' + device);
552
            td.appendChild(i);
553
            td.appendChild(document.createTextNode(user[n].name));
554
            tr.appendChild(td);
555
            list.appendChild(tr);
556
        }
557
    } else {
558
<?php
559
if ($user) {
560
    ?>
561
        // All users were disconnected.
562
        alert("<?=addslashes(__('You were disconnected!'))?>");
563
        window.location = 'logout.php';
564
    <?php
565
} else {
566
    ?>
567
        // If there is no user, we display an empty entry.
568
        var tr = document.createElement('tr');
569
        var td = document.createElement('td');
570
        td.appendChild(document.createTextNode("\u00a0"));
571
        tr.appendChild(td);
572
        list.appendChild(tr);
573
    <?php
574
}
575
?>
576
    }
577
}
578
579
580
function addWindowPosition(layout, screensection) {
581
    var position = document.createElement("div");
582
    position.setAttribute("class", "position " + layout);
583
    position.setAttribute('title', '<?=addslashes(__("Select screen section for display"))?>');
584
585
    var s;
586
    var button;
587
    var icon;
588
    var br;
589
590
    switch (layout) {
591
        case 'g1x1':
592
            s = 1;
593
            break;
594
        case 'g1x2':
595
            s = 2;
596
            break;
597
        case 'g2x1':
598
            s = 2;
599
            break;
600
        case 'g1a2':
601
            s = 3;
602
            var layout_left = document.createElement('div');
603
            layout_left.setAttribute("class", "layout_left");
604
            var layout_right = document.createElement('div');
605
            layout_right.setAttribute("class", "layout_right");
606
            break;
607
        case 'g2x2':
608
            s = 4;
609
            break;
610
    }
611
612
    for (var n = 1; n <= s; n++) {
613
        br = '';
614
        button = document.createElement("button");
615
        button.setAttribute('value', n);
616
        if (n == screensection) {
617
            button.setAttribute("class", "selected");
618
        }
619
        button.setAttribute('onclick', "sendToNuc('switchWindows=TRUE&before=" + screensection + "&after='+(this.value))");
620
        icon = document.createElement("i");
621
        if (s == 1) {
622
            icon.setAttribute("class", "fa fa-desktop fa-2x");
623
        } else {
624
            icon.setAttribute("class", "fa fa-desktop fa-1x");
625
        }
626
        button.appendChild(icon);
627
628
        if ((layout == 'g1x2' && n == 1) || ((layout == 'g1a2' || layout == 'g2x2') && n == 2 )) {
629
            br = document.createElement("br");
630
        }
631
632
        if (layout == 'g1a2' && n == 1) {
633
            layout_left.appendChild(button);
634
        } else if (layout == 'g1a2' && n > 1) {
635
            layout_right.appendChild(button);
636
            if (br) {
637
                layout_right.appendChild(br);
638
            }
639
        } else {
640
            position.appendChild(button);
641
            if (br) {
642
                position.appendChild(br);
643
            }
644
        }
645
    }
646
647
    if (layout == 'g1a2') {
648
        position.appendChild(layout_left);
649
        position.appendChild(layout_right);
650
    }
651
    return position;
652
}
653
654
655
function addWindowControls(layout, controls, screensection, file, status) {
656
    var control = controls[screensection];
657
    if (typeof control == "undefined") {
658
    control = ["default", false, false, false, false,
659
               false, false, false, false, false, false, false];
660
    }
661
662
    // get handler
663
    var handler = control[0];
664
    // up down left right zoomin zoomout home end prior next download
665
    var up = control[1];
666
    var down = control[2];
667
    var left = control[3];
668
    var right = control[4];
669
    var zoomin = control[5];
670
    var zoomout = control[6];
671
    var home = control[7];
672
    var end = control[8];
673
    var prior = control[9];
674
    var next = control[10];
675
    var download = control[11];
676
    var counterclockwise = control[12];
677
    var clockwise = control[13];
678
679
    var windowcontrols = document.createElement("div");
680
    windowcontrols.setAttribute("class", "windowcontrols");
681
682
    var topbar = document.createElement("div");
683
    topbar.setAttribute("class", "topbar");
684
    var button = document.createElement('button');
685
    button.setAttribute("class", "toggle");
686
    icon = document.createElement('i');
687
    if (status == 'active') {
688
        icon.setAttribute("class", "fa fa-eye");
689
    } else {
690
        icon.setAttribute("class", "fa fa-eye-slash");
691
    }
692
    icon.setAttribute('id', 'status_' + screensection);
693
    button.setAttribute('title', '<?=addslashes(__("Toggle visibility"))?>');
694
    button.setAttribute('onclick', "sendToNuc('window=" + screensection + "&toggle=TRUE')");
695
    button.appendChild(icon);
696
    topbar.appendChild(button);
697
    topbar.appendChild(keyControl(screensection, 'fa fa-search-plus', 'zoomin', 'zoomin', handler, !zoomin, '<?=addslashes(__("Zoom in"))?>'));
698
    topbar.appendChild(keyControl(screensection, 'fa fa-search-minus', 'zoomout', 'zoomout', handler, !zoomout, '<?=addslashes(__("Zoom out"))?>'));
699
    topbar.appendChild(keyControl(screensection, 'fa fa-rotate-left', 'counterclockwise', 'counterclockwise', handler, !counterclockwise, '<?=addslashes(__("Rotate counterclockwise"))?>'));
700
    topbar.appendChild(keyControl(screensection, 'fa fa-rotate-right', 'clockwise', 'clockwise', handler, !clockwise, '<?=addslashes(__("Rotate clockwise"))?>'));
701
    var downloadbutton = keyControl(screensection, 'fa fa-download', 'download', 'download', handler, !download, '<?=addslashes(__("Download this item"))?>');
702
    downloadbutton.setAttribute('onclick', 'downloadFile(' + screensection + ')');
703
    topbar.appendChild(downloadbutton);
704
    button = document.createElement('button');
705
    button.setAttribute("class", "trash");
706
    button.setAttribute('onclick', "sendToNuc('window=" + screensection + "&delete=" + file + "')");
707
    button.setAttribute('title', '<?=addslashes(__("Remove this item"))?>');
708
    icon = document.createElement('i');
709
    icon.setAttribute("class", "fa fa-trash-o");
710
    button.appendChild(icon);
711
    topbar.appendChild(button);
712
713
    var movement = document.createElement("div");
714
    movement.setAttribute("class", "movement");
715
716
    var jump = document.createElement("div");
717
    jump.setAttribute("class", "jump");
718
    jump.appendChild(keyControl(screensection, 'fa fa-angle-double-up', 'jumpbeginning', 'home', handler, !home, '<?=addslashes(__("Jump to start"))?>'));
719
    jump.appendChild(keyControl(screensection, 'fa fa-angle-up', 'pageback', 'prior', handler, !prior, '<?=addslashes(__("Page up"))?>'));
720
    jump.appendChild(keyControl(screensection, 'fa fa-angle-down', 'pageforward', 'next', handler, !next, '<?=addslashes(__("Page down"))?>'));
721
    jump.appendChild(keyControl(screensection, 'fa fa-angle-double-down', 'jumpend', 'end', handler, !end, '<?=addslashes(__("Jump to end"))?>'));
722
723
    var arrows = document.createElement("div");
724
    arrows.setAttribute("class", "arrows");
725
    arrows.appendChild(keyControl(screensection, 'fa fa-toggle-up', 'arrowup', 'up', handler, !up, '<?=addslashes(__("Up"))?>'));
726
    arrows.appendChild(document.createElement("br"));
727
    arrows.appendChild(keyControl(screensection, 'fa fa-toggle-left', 'arrowleft', 'left', handler, !left, '<?=addslashes(__("Left"))?>'));
728
    arrows.appendChild(keyControl(screensection, 'fa fa-toggle-right', 'arrowright', 'right', handler, !right, '<?=addslashes(__("Right"))?>'));
729
    arrows.appendChild(document.createElement("br"));
730
    arrows.appendChild(keyControl(screensection, 'fa fa-toggle-down', 'arrowdown', 'down', handler, !down, '<?=addslashes(__("Down"))?>'));
731
732
    movement.appendChild(jump);
733
    movement.appendChild(arrows);
734
735
    var position = addWindowPosition(layout, screensection);
736
737
    // Putting it all together
738
    windowcontrols.appendChild(topbar);
739
    windowcontrols.appendChild(movement);
740
    windowcontrols.appendChild(position);
741
    return windowcontrols;
742
}
743
744
745
function updateWindowList(window){
746
    var windowlist = document.getElementById('windowlist');
747
    // remove old entries
748
    while (windowlist.firstChild) {
749
        windowlist.removeChild(windowlist.firstChild);
750
    }
751
752
    if (window.length == 0) {
753
        var entry = document.createElement('div');
754
        entry.setAttribute("class", "description");
755
        entry.appendChild(document.createTextNode('<?=addslashes(__('Welcome'))?>, <?=htmlspecialchars($username)?>!'));
756
        entry.appendChild(document.createElement("br"));
757
        entry.appendChild(document.createTextNode('<?=addslashes(__('There is no shared content yet. Click below to get started!'))?>'));
758
        var addbutton = document.createElement('button');
759
        addbutton.setAttribute("class", "splash_add pure-button");
760
        addbutton.setAttribute("onclick", "openTab(event, 'Add')");
761
        var addtext = (document.createTextNode('<?=addslashes(__("Add"))?>'));
762
        addbutton.appendChild(addtext);
763
        var addicon = document.createElement("i");
764
        addicon.setAttribute("class", "fa fa-plus");
765
        addbutton.appendChild(addicon);
766
        windowlist.appendChild(entry);
767
        windowlist.appendChild(addbutton);
768
        document.getElementById("closeWindows").style.display = "none";
769
        document.getElementById("Layout").style.display = "none";
770
        document.getElementById("controlbtn").className = "tablinks";
771
    } else {
772
        document.getElementById("closeWindows").style.display = "inline-block";
773
        document.getElementById("Layout").style.display = "block";
774
        document.getElementById("controlbtn").className = "tablinks active";
775
        // Add an entry for each window.
776
        var n;
777
        for (n = 0; n < window.length; n++) {
778
            var file = window[n].file;
779
            var handler = window[n].handler;
780
            var screensection = window[n].section;
781
            var entry = document.createElement('div');
782
            entry.setAttribute("class", "window_entry");
783
            var divID = 'file' + screensection;
784
            entry.setAttribute('id', divID);
785
786
            var button = document.createElement('button');
787
            button.setAttribute("class", "window_entry_button");
788
            button.setAttribute('onclick', "openAccordion('" + divID + "')");
789
            var icon = document.createElement('i');
790
            if (handler.indexOf("midori") > -1) {
791
                icon.setAttribute("class", "fa fa-globe");
792
            } else if (handler.indexOf("vnc") > -1) {
793
                icon.setAttribute("class", "fa fa-video-camera");
794
            } else if (handler.indexOf("vlc") > -1) {
795
                icon.setAttribute("class", "fa fa-film");
796
            } else if (handler.indexOf("feh") > -1) {
797
                icon.setAttribute("class", "fa fa-picture-o");
798
            } else if (handler.indexOf("zathura") > -1) {
799
                icon.setAttribute("class", "fa fa-file-pdf-o");
800
            } else {
801
                icon.setAttribute("class", "fa fa-file");
802
            }
803
            button.appendChild(icon);
804
            var title = decodeURI(decodeURIComponent(file));
805
            // display only the last part of the URL or file name.
806
            // Long names are truncated, and the truncation is indicated.
807
            if (title.substring(0, 4) == 'http') {
808
                // Remove a terminating slash from an URL.
809
                // The full URL will be shown as a tooltip.
810
                title = title.replace(/\/$/, '');
811
                title = title.replace(/^.*\//, '');
812
                entry.setAttribute('title', file);
813
            } else {
814
                // For files only the full base name is shown as a tooltip.
815
                var fname = file;
816
                title = title.replace(/^.*\//, '');
817
                entry.setAttribute('title', fname);
818
            }
819
            if (title.length > 25) {
820
                title = title.substring(0, 25) + '...';
821
            }
822
            button.appendChild(document.createTextNode(title));
823
            var icon = document.createElement('i');
824
            icon.setAttribute("class", "fa fa-caret-down accordion-icon");
825
            button.appendChild(icon);
826
            entry.appendChild(button);
827
            windowlist.appendChild(entry);
828
        }
829
    }
830
}
831
832
function updateControlsBySection(window) {
833
834
    // get section and handler for each window
835
    var sectionControls = [];
836
837
    for (n = 0; n < window.length; n++) {
838
        var win_id = window[n].win_id;
839
        var section = window[n].section;
840
        var handler = window[n].handler;
841
842
        // alert("Section: " + section + " - Handler: " + handler);
843
844
        if (handler.indexOf("feh") > -1) {
845
            // up down left right zoomin zoomout home end prior next download rotateleft rotateright
846
            control = ["feh", true, true, true, true, true, true, false, false, false, false, true, true, true];
847
        } else if (handler.indexOf("libreoffice") > -1) {
848
            // Controls in LibreOffice: no zoom in calc and writer, has to be activated first
849
            // by pressing <Ctrl+Shift+o> (switch view mode on/off) not implemented yet
850
            control = ["libreoffice", true, true, true, true, false, false, false, false, true, true, true, false, false];
851
                if (handler.indexOf("--calc") > -1) {
852
                    control = ["libreoffice-calc", true, true, true, true, false, false, true, true, true, true, true, false, false];
853
                }
854
                if (handler.indexOf("--impress") > -1) {
855
                    control = ["libreoffice-impress", true, true, true, true, true, true, true, true, true, true, true, false, false];
856
                }
857
                if (handler.indexOf("--writer") > -1) {
858
                    control = ["libreoffice-writer", true, true, true, true, false, false, false, false, true, true, true, false, false];
859
                }
860
        } else if (handler.indexOf("midori") > -1) {
861
            control = ["midori", true, true, true, true, true, true, true, true, true, true, false, false, false];
862
        } else if (handler.indexOf("vlc") > -1) {
863
            control = ["vlc", false, false, false, true, false, false, false, false, false, false, false, false, false];
864
        } else if (handler.indexOf("vnc") > -1) {
865
            control = ["vnc", true, true, true, true, true, true, false, false, false, false, false, false, false];
866
        } else if (handler.indexOf("zathura") > -1) {
867
            control = ["zathura", true, true, true, true, true, true, true, true, true, true, true, false, false];
868
        } else {
869
            control = ["undefined", false, false, false, false, false, false, false, false, false, false, false, false, false];
870
        }
871
872
        sectionControls[section] = control;
873
    }
874
875
    // Fill empty sections with default values.
876
    for (i = sectionControls.length; i < 5; i++) {
877
        sectionControls[i] = ["default",
878
                              false, false, false, false,
879
                              false, false, false, false,
880
                              false, false, false, false, false];
881
    }
882
883
    return sectionControls;
884
}
885
886
function updateScreen() {
887
    sendToNuc('window=all&closeOrphans=true');
888
}
889
890
function clearURLField(defaultText) {
891
    var browseto = document.getElementById('url_field');
892
    browseto.setAttribute('value', 'http://');
893
}
894
895
// lastJSON is used to reduce the database polling rate.
896
var lastJSON = '';
897
898
function pollDatabase() {
899
    var xmlHttp = new XMLHttpRequest();
900
    if (xmlHttp) {
901
        xmlHttp.open("get", 'db.php?json=' + lastJSON, true);
902
        xmlHttp.onreadystatechange = function () {
903
            if (xmlHttp.readyState == 2) {
904
                // Data transferred to server.
905
            } else if (xmlHttp.readyState == 3) {
906
                // Server is answering.
907
            } else if (xmlHttp.readyState == 4) {
908
                // Received all data from server.
909
                var status = xmlHttp.status;
910
                if (status == 200) {
911
                    // Got valid response.
912
                    var text = xmlHttp.responseText;
913
                    lastJSON = text;
914
                    var db = JSON.parse(text);
915
                    var i;
916
                    var layout = '';
917
                    for (i = 0; i < db.setting.length; i++) {
918
                        if (db.setting[i].key == 'layout') {
919
                            layout = db.setting[i].value;
920
                            break;
921
                        }
922
                    }
923
                    var controls = updateControlsBySection(db.window);
924
                    // for (i = 0; i < controls.length; i++) {
925
                    //    console.log(i + ": " + controls[i]);
926
                    //    }
927
                    updateUserList(db.address, db.user);
928
                    updateWindowList(db.window);
929
                    showLayout(layout, controls, db.window);
930
                    // updateScreen(db.window);
931
                    setTimeout("pollDatabase()", 1);
932
                } else {
933
                    // Got error. TODO: handle it.
934
                }
935
            } else {
936
                // Got unexpected xmlHttp.readyState. TODO: handle it.
937
            }
938
        };
939
        xmlHttp.send(null);
940
    }
941
}
942
943
// Start polling the database.
944
pollDatabase();
945
946
947
function getOS() {
948
    var OSName="Unknown OS";
949
    if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
950
    if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
951
    if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
952
    if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
953
    if (navigator.userAgent.indexOf("Android")!=-1) OSName="Android";
954
    if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i))) OSName="iOS";
955
    /* Source for Android and iOS:
956
    https://davidwalsh.name/detect-android
957
    https://davidwalsh.name/detect-iphone
958
    https://davidwalsh.name/detect-ipad */
959
    return OSName;
960
}
961
962
function getFilePathByOS() {
963
    var OSName = getOS();
964
    var windows = 'download-winvnc';
965
    var macOS = 'download-macvnc';
966
    var linux = 'download-linux';
967
    var download = '';
968
    switch(OSName) {
969
        case 'Windows': download = windows;
970
            break;
971
        case 'MacOS': download = macOS;
972
            break;
973
        case 'Linux': download = linux;
974
            break;
975
        case 'UNIX': download = linux;
976
            break;
977
        case 'Android':
978
        case 'iOS':
979
            document.getElementById("Screen").innerHTML = '<div class="description"><?=addslashes(__('Sorry! Screensharing for your device is currently not supported.'))?></div>';
980
            break;
981
        default: download = null;
982
    }
983
  document.getElementById(download).click();
984
}
985
986
function openTab(evt, tabName) {
987
    var i, tabcontent, tablinks;
988
    // Hide all elements with class="tabcontent"
989
    tabcontent = document.getElementsByClassName("tabcontent");
990
    for (i = 0; i < tabcontent.length; i++) {
991
        tabcontent[i].style.display = "none";
992
    }
993
    // Remove "active" class from all elements with class="tablinks"
994
    tablinks = document.getElementsByClassName("tablinks");
995
    for (i = 0; i < tablinks.length; i++) {
996
        tablinks[i].className = tablinks[i].className.replace(" active", "");
997
    }
998
999
    // Show current tab and add "active" class to the opening button
1000
    document.getElementById(tabName).style.display = "block";
1001
    evt.currentTarget.className += " active";
1002
}
1003
1004
function openSubtab(evt, tabName, subtabName) {
1005
    var i, tab, subtabcontent, subtablinks;
1006
    // Hide all elements with class="subtabcontent"
1007
    tab = document.getElementById(tabName);
1008
    subtabcontent = tab.getElementsByClassName("subtabcontent");
1009
    for (i = 0; i < subtabcontent.length; i++) {
1010
        subtabcontent[i].style.display = "none";
1011
    }
1012
1013
    // Remove "active" class from all elements with class="subtablinks"
1014
    subtablinks = tab.getElementsByClassName("subtablinks");
1015
    for (i = 0; i < subtablinks.length; i++) {
1016
        subtablinks[i].className = subtablinks[i].className.replace(" active", "");
1017
    }
1018
1019
    // Show current subtab and add "active" class to the opening button
1020
    document.getElementById(subtabName).style.display = "block";
1021
    evt.currentTarget.className += " active";
1022
}
1023
1024
function openAccordion(divID) {
1025
    var button = document.getElementById(divID).firstChild;
1026
    var windowcontrols = document.getElementById(divID).lastChild;
1027
    if (windowcontrols.style.display == "block") {
1028
        windowcontrols.style.display = "none";
1029
        button.setAttribute("class", "window_entry_button");
1030
    } else if (windowcontrols.style.display == "none") {
1031
        windowcontrols.style.display = "block";
1032
        button.setAttribute("class", "window_entry_button active");
1033
    } else {
1034
        windowcontrols.style.display = "block";
1035
        button.setAttribute("class", "window_entry_button active");
1036
    }
1037
}
1038
1039
function showDropdown() {
1040
    document.getElementById("languageSelection").classList.toggle("show");
1041
}
1042
// Close the dropdown menu if the user clicks outside of it
1043
// Must use some workaround to support IE.
1044
window.onclick = function(event) {
1045
  var classes = event.target.className.split(' ');
1046
  var found = false; var i = 0;
1047
  while (i < classes.length && !found) {
1048
      if (classes[i]=='dropbutton') found = true;
1049
      else ++i;
1050
  }
1051
  if (!found) {
1052
    var dropdowns = document.getElementsByClassName("dropdown-content");
1053
    var i;
1054
    for (i = 0; i < dropdowns.length; i++) {
1055
      var openDropdown = dropdowns[i];
1056
      if (openDropdown.classList.contains('show')) {
1057
        openDropdown.classList.remove('show');
1058
      }
1059
    }
1060
  }
1061
}
1062
</script>
1063
</head>
1064
1065
<body>
1066
1067
<!-- This formating is used to prevent empty textnodes that interfere with the design -->
1068
<div class="tab"
1069
    ><button class="tablinks" onclick="openTab(event, 'Add')"><?=addslashes(__('Add'))?><i class="fa fa-plus"></i></button
1070
    ><button class="tablinks" id="controlbtn" onclick="openTab(event, 'Control')"><?=addslashes(__('Control'))?><i class="fa fa-arrows"></i></button
1071
    ><button class="tablinks" onclick="openTab(event, 'Extras')"><?=addslashes(__('Extras'))?><i class="fa fa-info-circle"></i></button
1072
></div>
1073
1074
<div id="workbench">
1075
    <div id="Add" class="tabcontent">
1076
        <div class="subtab"
1077
        ><button class="subtablinks" onclick="openSubtab(event, 'Add', 'File')"><?=addslashes(__('File'))?><i class="fa fa-file"></i></button
1078
        ><button class="subtablinks" onclick="openSubtab(event, 'Add', 'URL')"><?=addslashes(__('URL'))?><i class="fa fa-globe"></i></button
1079
        ><button class="subtablinks" onclick="openSubtab(event, 'Add', 'Screen')"><?=addslashes(__('Screen'))?><i class="fa fa-video-camera"></i></button
1080
    ></div>
1081
        <div id="File" class="subtabcontent">
1082
            <div id="file_upload">
1083
                <form action="upload.php"
1084
                    class="dropzone"
1085
                    id="palma-dropzone"
1086
                    title="<?=addslashes(__('Drag and drop files or click here to upload.'))?>">
1087
                    <div class="dz-default dz-message">
1088
                        <?=__('Add file (click or drop here)')?>
1089
                        <i class="fa fa-file fa-2x"></i>
1090
                    </div>
1091
                </form>
1092
                <div class="dz-preview dz-file-preview">
1093
                    <div class="dz-details">
1094
                        <div class="dz-filename"><span data-dz-name></span></div>
1095
                        <div class="dz-size" data-dz-size></div>
1096
                        <img data-dz-thumbnail src="" alt="">
1097
                    </div>
1098
                  <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
1099
                  <div class="dz-success-mark"><span> </span></div>
1100
                  <div class="dz-error-mark"><span> </span></div>
1101
                  <div class="dz-error-message"><span data-dz-errormessage></span></div>
1102
                </div>
1103
            </div>
1104
        </div>
1105
        <div id="URL" class="subtabcontent">
1106
            <div>
1107
                <input type="text" value="<?=addslashes(__('Add webpage'))?>"
1108
                    id="url_field" maxlength="256" size="46"
1109
                    onkeydown="if (event.keyCode == 13) document.getElementById('url_button').click()"
1110
                    onfocus="clearURLField('<?=addslashes(__('Enter URL'))?>')">
1111
                <button class="pure-button pure-button-primary pure-input-rounded"
1112
                    id="url_button"
1113
                    onClick="urlToNuc()" title="<?=addslashes(__('Click here to show the webpage on the screen.'))?>">
1114
                    <?=addslashes(__('Enter'))?><i class="fa fa-globe"></i>
1115
                </button>
1116
            </div>
1117
        </div>
1118
        <div id="Screen" class="subtabcontent">
1119
            <div id="vnc-button" onclick="javascript:getFilePathByOS()">
1120
                <div id="vnc-button-container">
1121
                    <div id="vnc-button-label"><?=addslashes(__('Add your screen'))?><i class="fa fa-video-camera fa-2x" aria-hidden="true"></i></div>
1122
                    <div id="vnc-button-label-subtext"><?=addslashes(__('Download screensharing tool'))?></div>
1123
                </div>
1124
                <a href="<?php echo $winvnc; ?>" download id="download-winvnc" hidden></a>
1125
                <a href="<?php echo $macvnc; ?>" download id="download-macvnc" hidden></a>
1126
                <a href="<?php echo $linuxsh; ?>" download id="download-linux" hidden></a>
1127
            </div>
1128
            <div class="description">
1129
            <?=addslashes(__('Download your screensharing tool (Windows, Mac and Linux only). Visit the help section for further information.'))?>
1130
            </div>
1131
        </div>
1132
    </div>
1133
    <div id="Control" class="tabcontent">
1134
        <div class="subtab"
1135
            ><button class="subtablinks" onclick="openSubtab(event, 'Control', 'Layout')"><?=addslashes(__('Layout'))?><i class="fa fa-desktop"></i></button
1136
            ><button class="subtablinks" onclick="openSubtab(event, 'Control', 'Navigate')"><?=addslashes(__('Navigate'))?><i class="fa fa-arrows"></i></button
1137
        ></div>
1138
        <div id="Layout" class="subtabcontent">
1139
            <div class="screenlayout">
1140
                <button class="pure-button pure-button-primary pure-input-rounded"
1141
                        id="g1x1" onclick="miniDisplaySelect(this)"
1142
                        title="<?=addslashes(__('Choose screen layout'))?>">
1143
                    <i alt="1" class="fa fa-desktop fa-2x" aria-hidden="true"></i>
1144
                </button>
1145
            </div
1146
            ><div class="screenlayout vertical">
1147
                <button class="pure-button pure-button-primary pure-input-rounded"
1148
                        id="g1x2" onclick="miniDisplaySelect(this)"
1149
                        title="<?=addslashes(__('Choose screen layout'))?>">
1150
                    <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1151
                    <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1152
                </button>
1153
            </div
1154
            ><div class="screenlayout">
1155
                <button class="pure-button pure-button-primary pure-input-rounded"
1156
                        id="g2x1" onclick="miniDisplaySelect(this)"
1157
                        title="<?=addslashes(__('Choose screen layout'))?>">
1158
                    <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1159
                    <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1160
                </button>
1161
            </div
1162
            ><div class="screenlayout">
1163
                <button class="pure-button pure-button-primary pure-input-rounded"
1164
                        id="g1a2" onclick="miniDisplaySelect(this)"
1165
                        title="<?=addslashes(__('Choose screen layout'))?>">
1166
                    <div class="layout_left">
1167
                    <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1168
                    </div>
1169
                    <div class="layout_right vertical">
1170
                    <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1171
                    <i alt="3" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1172
                    </div>
1173
                </button>
1174
            </div
1175
            ><div class="screenlayout">
1176
                <button class="pure-button pure-button-primary pure-input-rounded"
1177
                        id="g2x2" onclick="miniDisplaySelect(this)"
1178
                        title="<?=addslashes(__('Choose screen layout'))?>">
1179
                    <i alt="1" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1180
                    <i alt="2" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1181
                    <br />
1182
                    <i alt="3" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1183
                    <i alt="4" class="fa fa-desktop fa-1x" aria-hidden="true"></i>
1184
                </button>
1185
            </div>
1186
        </div>
1187
        <div id="Navigate" class="subtabcontent">
1188
            <div id="windowlist">
1189
            <!-- filled by updateWindowList and showLayout -->
1190
            </div>
1191
            <button id="closeWindows" class="pure-button pure-button-primary pure-input-rounded"
1192
                onClick="sendToNuc('closeAll=TRUE')"
1193
                title="<?=addslashes(__('Close all windows and remove uploaded files'))?>">
1194
                <?=addslashes(__('Delete all items'))?>
1195
            </button>
1196
        </div>
1197
    </div>
1198
    <div id="Extras" class="tabcontent">
1199
        <div class="subtab"
1200
            ><button class="subtablinks active" onclick="openSubtab(event, 'Extras', 'Help')"><?=addslashes(__('Help'))?><i class="fa fa-question-circle"></i></button
1201
            ><button class="subtablinks" onclick="openSubtab(event, 'Extras', 'Feedback')"><?=addslashes(__('Feedback'))?><i class="fa fa-thumbs-o-up"></i></button
1202
            ><button class="subtablinks" onclick="openSubtab(event, 'Extras', 'Users')"><?=addslashes(__('Users'))?><i class="fa fa-users"></i></button
1203
        ></div>
1204
        <div id="Help" class="subtabcontent">
1205
            <p><?=addslashes(__('With PalMA, you can share documents, websites and your desktop with your learning group.'))?>
1206
            <?=addslashes(__('The PalMA team monitor shows up to four contributions simultaneously.'))?></p>
1207
            <?php
1208
            if (CONFIG_INSTITUTION_URL) { ?>
1209
                <p><?=addslashes(__('For further information about PalMA in this institution'))?> <a href=<?=CONFIG_INSTITUTION_URL?> target="_blank"><?=__('see here.')?></a></p>
1210
                <?php
1211
            }
1212
            ?>
1213
            <h4><?=addslashes(__('Connect'))?><i class="fa fa-wifi"></i></h4>
1214
            <p><?=addslashes(__('Team members can join the session at any time with this URL or QR-Code:'))?></p>
1215
            <p class="url_hint"><?=htmlspecialchars($_SESSION['starturl'])?><br />
1216
            <?php
1217
            if (!empty($_SESSION['pin'])) {
1218
                echo addslashes(__('PIN: '));
1219
                echo htmlspecialchars($_SESSION['pin']);
1220
            }
1221
            ?>
1222
            </p>
1223
            <img class="qr-code" src="qrcode/php/qr_img.php?d=<?=htmlspecialchars($_SESSION['starturl'])?>?pin=<?=htmlspecialchars($_SESSION['pin'])?>" alt="QR Code">
1224
1225
            <h4><?=addslashes(__('Add'))?><i class="fa fa-plus"></i></h4>
1226
                <p><?=addslashes(__('Use the Add-Section to share content on the PalMA monitor.'))?></p>
1227
                <ul>
1228
                <li><i class="fa fa-file"></i> <?=addslashes(__('For PDF files, office files, images or videos use the file section.'))?></li>
1229
                <li><i class="fa fa-globe"></i> <?=addslashes(__('To display a website use the URL field.'))?></li>
1230
                <li><i class="fa fa-video-camera"></i> <?=addslashes(__('To share your desktop in real time download the VNC screen sharing software and'))?></li>
1231
                    <ul>
1232
                    <li><i class="fa fa-windows"></i> <?=addslashes(__('Windows:'))?></li>
1233
                        <ul>
1234
                        <li><?=addslashes(__('Run the downloaded file.'))?></li>
1235
                        <li><?=addslashes(__('Double-click the name of your PalMA station in the appearing list.'))?></li>
1236
                        </ul>
1237
                    <li><i class="fa fa-apple"> </i> <?=addslashes(__('Mac:'))?></li>
1238
                        <ul>
1239
                        <li><?=addslashes(__('CTRL + click the downloaded file and run it.'))?></li>
1240
                        <li><?=addslashes(__('A second window opens, in which you can start &quot;VineServer&quot;.'))?></li>
1241
                        <li><?=addslashes(__('In the upper left corner, click on &quot;VNC Server&quot;.'))?></li>
1242
                        <li><?=addslashes(__('Select &quot;Reverse Connection&quot;.'))?></li>
1243
                        <li><?=addslashes(__('Enter the URL of your PalMA station and click &quot;Connect&quot;.'))?></li>
1244
                        </ul>
1245
                    <li><i class="fa fa-linux"></i> <?=addslashes(__('Linux:'))?></li>
1246
                        <ul>
1247
                        <li><?=addslashes(__('Run the downloaded shell script.'))?></li>
1248
                        <li><?=addslashes(__('Or use this shell command:'))?> <code>x11vnc -connect <?=$_SERVER['HTTP_HOST']?></code></li>
1249
                        </ul>
1250
                    </ul>
1251
                </ul>
1252
            <h4><?=addslashes(__('Control'))?><i class="fa fa-arrows"></i></h4>
1253
            <p><?=addslashes(__('With the grey monitor buttons at the top you can choose how the shared content should be arranged on the PalMA monitor.'))?></p>
1254
            <p><?=addslashes(__('Below that you find the controls for each item:'))?></p>
1255
            <ul>
1256
            <li><?=addslashes(__('In the top bar you can'))?></li>
1257
            <ul>
1258
            <li><?=addslashes(__('Hide and show'))?><i class="fa fa-eye"></i>,</li>
1259
            <li><?=addslashes(__('Zoom in and out'))?><i class="fa fa-search-plus"></i><i class="fa fa-search-minus"></i>,</li>
1260
            <li><?=addslashes(__('Rotate'))?><i class="fa fa-rotate-left"></i><i class="fa fa-rotate-right"></i>,</li>
1261
            <li><?=addslashes(__('Download'))?><i class="fa fa-download"></i>,</li>
1262
            <li><?=addslashes(__('Delete the item'))?><i class="fa fa-trash-o"></i>.</li>
1263
            </ul>
1264
            <li><?=addslashes(__('Below you find the navigation controls.'))?></li>
1265
            <ul>
1266
            <li><?=addslashes(__('Buttons on the left jump to the top, to the end, a page up or a page down'))?><i class="fa fa-angle-double-up"></i><i class="fa fa-angle-up"></i><i class="fa fa-angle-down"></i><i class="fa fa-angle-double-up"></i>.</li>
1267
            <li><?=addslashes(__('Arrow buttons in the middle scroll gradually'))?><i class="fa fa-toggle-up"></i><i class="fa fa-toggle-down"></i><i class="fa fa-toggle-left"></i><i class="fa fa-toggle-right"></i>.</li>
1268
            </ul>
1269
            </li>
1270
            <li><?=addslashes(__('On the right you can choose the position on the PalMA monitor'))?><i class="fa fa-desktop"></i>.</li>
1271
            </ul>
1272
            <p><?=addslashes(__('Controls that are not available for certain kinds of content are marked grey.'))?></p>
1273
            <h4><?=addslashes(__('Extras'))?><i class="fa fa-info-circle"></i></h4>
1274
            <p><?=addslashes(__('Some additional features are:'))?></p>
1275
            <ul>
1276
            <li><i class="fa fa-question-circle"></i> <?=addslashes(__('This help,'))?></li>
1277
            <li><i class="fa fa-thumbs-o-up"></i> <?=addslashes(__('Your chance to recommend us or give us your thoughts in the &quot;Feedback&quot; section,'))?></li>
1278
            <li><i class="fa fa-users"></i> <?=addslashes(__('A list of all logged-in users as well as a button to disconnect everyone and therefore end the session.'))?></li>
1279
            </ul>
1280
        </div>
1281
        <div id="Feedback" class="subtabcontent">
1282
            <div id="recommendcontainer">
1283
                <h3>
1284
                    <?=addslashes(__('Recommend us'))?>
1285
                </h3>
1286
                <div>
1287
                    <p><?=addslashes(__('If you like PalMA, please recommend us by sharing in your social networks.<br />Enjoy PalMA!'))?></p>
1288
                    <!-- Social Media Button Integration, Source: http://sharingbuttons.io/ -->
1289
                    <?php $github_url = "https%3A%2F%2Fgithub.com/UB-Mannheim/PalMA/blob/master/README.md"; ?>
1290
1291
                    <!-- Sharingbutton Facebook -->
1292
                    <a class="resp-sharing-button__link" href="https://facebook.com/sharer/sharer.php?u=<?=$github_url?>" target="_blank" aria-label="">
1293
                      <div class="resp-sharing-button resp-sharing-button--facebook resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1294
                        <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>
1295
                        </div>
1296
                      </div>
1297
                    </a>
1298
1299
                    <!-- Sharingbutton Twitter -->
1300
                    <a class="resp-sharing-button__link" href="https://twitter.com/intent/tweet/?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;url=<?=$github_url?>" target="_blank" aria-label="">
1301
                      <div class="resp-sharing-button resp-sharing-button--twitter resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1302
                        <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>
1303
                        </div>
1304
                      </div>
1305
                    </a>
1306
1307
                    <!-- Sharingbutton Google+ -->
1308
                    <a class="resp-sharing-button__link" href="https://plus.google.com/share?url=<?=$github_url?>" target="_blank" aria-label="">
1309
                      <div class="resp-sharing-button resp-sharing-button--google resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1310
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.37 12.93c-.73-.52-1.4-1.27-1.4-1.5 0-.43.03-.63.98-1.37 1.23-.97 1.9-2.23 1.9-3.57 0-1.22-.36-2.3-1-3.05h.5c.1 0 .2-.04.28-.1l1.36-.98c.16-.12.23-.34.17-.54-.07-.2-.25-.33-.46-.33H7.6c-.66 0-1.34.12-2 .35-2.23.76-3.78 2.66-3.78 4.6 0 2.76 2.13 4.85 5 4.9-.07.23-.1.45-.1.66 0 .43.1.83.33 1.22h-.08c-2.72 0-5.17 1.34-6.1 3.32-.25.52-.37 1.04-.37 1.56 0 .5.13.98.38 1.44.6 1.04 1.84 1.86 3.55 2.28.87.23 1.82.34 2.8.34.88 0 1.7-.1 2.5-.34 2.4-.7 3.97-2.48 3.97-4.54 0-1.97-.63-3.15-2.33-4.35zm-7.7 4.5c0-1.42 1.8-2.68 3.9-2.68h.05c.45 0 .9.07 1.3.2l.42.28c.96.66 1.6 1.1 1.77 1.8.05.16.07.33.07.5 0 1.8-1.33 2.7-3.96 2.7-1.98 0-3.54-1.23-3.54-2.8zM5.54 3.9c.33-.38.75-.58 1.23-.58h.05c1.35.05 2.64 1.55 2.88 3.35.14 1.02-.08 1.97-.6 2.55-.32.37-.74.56-1.23.56h-.03c-1.32-.04-2.63-1.6-2.87-3.4-.13-1 .08-1.92.58-2.5zM23.5 9.5h-3v-3h-2v3h-3v2h3v3h2v-3h3"/></svg>
1311
                        </div>
1312
                      </div>
1313
                    </a>
1314
1315
                    <!-- Sharingbutton E-Mail -->
1316
                    <a class="resp-sharing-button__link" href="mailto:?subject=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;body=<?=$github_url?>" target="_self" aria-label="">
1317
                      <div class="resp-sharing-button resp-sharing-button--email resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1318
                        <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>
1319
                        </div>
1320
                      </div>
1321
                    </a>
1322
1323
                    <!-- Sharingbutton WhatsApp -->
1324
                    <a class="resp-sharing-button__link" href="whatsapp://send?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.%20<?=$github_url?>" target="_blank" aria-label="">
1325
                      <div class="resp-sharing-button resp-sharing-button--whatsapp resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1326
                        <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>
1327
                        </div>
1328
                      </div>
1329
                    </a>
1330
1331
                    <!-- Sharingbutton Telegram -->
1332
                    <a class="resp-sharing-button__link" href="https://telegram.me/share/url?text=Do%20you%20already%20know%20PalMA?%20Take%20a%20Look.&amp;url=<?=$github_url?>" target="_blank" aria-label="">
1333
                      <div class="resp-sharing-button resp-sharing-button--telegram resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid">
1334
                          <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>
1335
                        </div>
1336
                      </div>
1337
                    </a>
1338
                </div> <!-- Social media buttons and description -->
1339
            </div> <!-- recommendcontainer -->
1340
            <div id="contactcontainer">
1341
                <h3><?=addslashes(__('Tell us what you think'))?></h3>
1342
                <div>
1343
                    <p><?=addslashes(__('Please let us know about problems or ideas to improve PalMA. Help us directly by sending crash reports or contributing on '))?><a href="https://github.com/UB-Mannheim/PalMA" target="_blank">GitHub</a>.<br /><?=__('Thank you!')?></p>
1344
                    <iframe width="100%" height="500" frameborder="0" src="https://www.bib.uni-mannheim.de/palma/feedback/form"></iframe>
1345
                </div>
1346
            </div> <!-- contactcontainer -->
1347
        </div> <!-- feedback -->
1348
        <div id="Users" class="subtabcontent">
1349
            <div class="dropdown">
1350
                <button onclick="showDropdown()" class="dropbutton pure-button pure-button-primary"><?=addslashes(__('Select language:'))?> <?=substr($locale, 0, 2)?><i class="fa fa-caret-down"></i></button>
1351
                <div id="languageSelection" class="dropdown-content">
1352
                <?php
1353
                $dirs = array_slice(scandir('locale'), 2);
0 ignored issues
show
It seems like scandir('locale') can also be of type false; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1353
                $dirs = array_slice(/** @scrutinizer ignore-type */ scandir('locale'), 2);
Loading history...
1354
                $langs = [];
1355
                for ($n = 0; $n < count($dirs); $n++) {
1356
                    if (is_dir("locale/$dirs[$n]")) {
1357
                        array_push($langs, substr($dirs[$n], 0, 2));
1358
                    }
1359
                }
1360
                for ($i = 0; $i < count($langs); $i++) {
1361
                    echo "<a href=$_SERVER[PHP_SELF]?lang=$langs[$i]>$langs[$i]</a>";
1362
                }
1363
                ?>
1364
                </div>
1365
            </div>
1366
            <div class="list_container">
1367
                <table class="userlist" summary="<?=addslashes(__('User list'))?>" title="<?=__('List of connected users')?>">
1368
                    <tbody id="userlist">
1369
                        <tr><td><!-- filled by updateUserList() --></td></tr>
1370
                    </tbody>
1371
                </table>
1372
                <div class="description">
1373
                    <?=addslashes(__('New users can join at'))?><br /><?=htmlspecialchars($_SESSION['starturl'])?><br />
1374
                    <?php
1375
                    if (!empty($_SESSION['pin'])) {
1376
                        echo addslashes(__('PIN: '));
1377
                        echo htmlspecialchars($_SESSION['pin']);
1378
                    }
1379
                    ?>
1380
                    <img class="qr-code" src="qrcode/php/qr_img.php?d=<?=htmlspecialchars($_SESSION['starturl'])?>?pin=<?=htmlspecialchars($_SESSION['pin'])?>" alt="QR Code">
1381
                </div>
1382
                <button class="pure-button pure-button-primary pure-input-rounded"
1383
                        onClick="sendToNuc('logout=ALL')"
1384
                        title="<?=addslashes(__('Disconnect all users and end the session'))?>">
1385
                    <?=addslashes(__('End the session'))?>
1386
                </button>
1387
            </div>
1388
        </div>
1389
    </div>
1390
</div> <!-- workbench -->
1391
1392
<div id="footer">
1393
    <?php
1394
      # Show authorized user name (and address) and allow logout.
1395
    if ($user) {
1396
        echo("<a href=\"logout.php\" title=\"" .
1397
          addslashes(__('Disconnect the current user')) .
1398
          "\">" . addslashes(__('Log out')) . "<i class=\"fa fa-sign-out\"></i></a>");
1399
    }
1400
    ?>
1401
</div> <!-- Footer -->
1402
1403
<?php
1404
1405
    $plugin_dir = "plugins/";
1406
    $stats = "piwik.php";
1407
1408
if (file_exists($plugin_dir . $stats)) {
1409
    include $plugin_dir . $stats;
1410
}
1411
1412
?>
1413
1414
</body>
1415
</html>
1416