Passed
Push — master ( 3e6070...95d24c )
by Stefan
06:34
created

UIElements::errorPage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
namespace web\lib\admin;
24
25
use Exception;
26
27
/**
28
 * This class provides various HTML snippets and other UI-related convenience functions.
29
 * 
30
 * @author Stefan Winter <[email protected]>
31
 */
32
class UIElements extends \core\common\Entity {
33
34
    /**
35
     * the custom displayable variant of the term 'federation'
36
     * 
37
     * @var string
38
     */
39
    public $nomenclatureFed;
40
41
    /**
42
     * the custom displayable variant of the term 'institution'
43
     * 
44
     * @var string
45
     */
46
    public $nomenclatureInst;
47
48
    /**
49
     * the custom displayable variant of the term 'hotspot'
50
     * 
51
     * @var string
52
     */
53
    public $nomenclatureHotspot;
54
55
    /**
56
     * the custom displayable variant of the term 'hotspot'
57
     * 
58
     * @var string
59
     */
60
    public $nomenclatureParticipant;
61
62
    /**
63
     * Initialises the class.
64
     * 
65
     * Mainly fetches various nomenclature from the config and attempts to translate those into local language. Needs pre-loading some terms.
66
     */
67
    public function __construct() {
68
        // pick up the nomenclature translations from core - no need to repeat
69
        // them here in this catalogue
70
        parent::__construct();
71
        $this->nomenclatureFed = \core\common\Entity::$nomenclature_fed;
72
        $this->nomenclatureInst = \core\common\Entity::$nomenclature_inst;
73
        $this->nomenclatureHotspot = \core\common\Entity::$nomenclature_hotspot;
74
        $this->nomenclatureParticipant = \core\common\Entity::$nomenclature_participant;
75
    }
76
77
    /**
78
     * provides human-readable text for the various option names as stored in DB.
79
     * 
80
     * @param string $input raw text in need of a human-readable display variant
81
     * @return string the human-readable variant
82
     * @throws \Exception
83
     */
84
    public function displayName($input) {
85
        \core\common\Entity::intoThePotatoes();
86
        $ssidText = _("SSID");
87
        $ssidLegacyText = _("SSID (with WPA/TKIP)");
88
        $passpointOiText = _("HS20 Consortium OI");
89
90
        if (count(\config\ConfAssistant::CONFIG['CONSORTIUM']['ssid']) > 0) {
91
            $ssidText = _("Additional SSID");
92
            $ssidLegacyText = _("Additional SSID (with WPA/TKIP)");
93
        }
94
        if (!empty(\config\ConfAssistant::CONFIG['CONSORTIUM']['interworking-consortium-oi']) && count(\config\ConfAssistant::CONFIG['CONSORTIUM']['interworking-consortium-oi']) > 0) {
95
            $passpointOiText = _("Additional HS20 Consortium OI");
96
        }
97
98
        $displayNames = [_("Support: Web") => "support:url",
99
            _("Support: EAP Types") => "support:eap_types",
100
            _("Support: Phone") => "support:phone",
101
            _("Support: E-Mail") => "support:email",
102
            sprintf(_("%s Name"), $this->nomenclatureParticipant) => "general:instname",
103
            _("Location") => "general:geo_coordinates",
104
            _("Logo URL") => "general:logo_url",
105
            _("Logo image") => "general:logo_file",
106
            _("Configure Wired Ethernet") => "media:wired",
107
            _("Name (CN) of Authentication Server") => "eap:server_name",
108
            _("Enable device assessment") => "eap:enable_nea",
109
            _("Terms of Use") => "support:info_file",
110
            _("CA Certificate URL") => "eap:ca_url",
111
            _("CA Certificate File") => "eap:ca_file",
112
            _("Profile Display Name") => "profile:name",
113
            _("Production-Ready") => "profile:production",
114
            _("Admin Accepted Terms of Use") => 'hiddenprofile:tou_accepted',
115
            _("Extra text on downloadpage for device") => "device-specific:customtext",
116
            _("Redirection Target") => "device-specific:redirect",
117
            _("Extra text on downloadpage for EAP method") => "eap-specific:customtext",
118
            _("Turn on selection of EAP-TLS User-Name") => "eap-specific:tls_use_other_id",
119
            _("Use GEANTlink TTLS supplicant for W8") => "device-specific:geantlink",
120
            _("Use builtin TTLS supplicant for Windows 10") => "device-specific:builtin_ttls",
121
            _("Profile Description") => "profile:description",
122
            _("Custom Installer Name Suffix") => "profile:customsuffix",
123
            sprintf(_("%s Administrator"), $this->nomenclatureFed) => "user:fedadmin",
124
            _("Real Name") => "user:realname",
125
            _("E-Mail Address") => "user:email",
126
            _("Remove/Disable SSID") => "media:remove_SSID",
127
            _("Mandatory Content Filtering Proxy") => "media:force_proxy",
128
            _("Custom CSS file for User Area") => "fed:css_file",
129
            sprintf(_("%s Logo"), $this->nomenclatureFed) => "fed:logo_file",
130
            _("Preferred Skin for User Area") => "fed:desired_skin",
131
            sprintf(_("Include %s branding in installers"), $this->nomenclatureFed) => "fed:include_logo_installers",
132
            sprintf(_("%s Name"), $this->nomenclatureFed) => "fed:realname",
133
            sprintf(_("%s Homepage"), $this->nomenclatureFed) => "fed:url",
134
            sprintf(_("Custom text in %s Invitations"), $this->nomenclatureInst) => "fed:custominvite",
135
            sprintf(_("Enable %s"), \core\ProfileSilverbullet::PRODUCTNAME) => "fed:silverbullet",
136
            sprintf(_("%s: Do not terminate EAP"), \core\ProfileSilverbullet::PRODUCTNAME) => "fed:silverbullet-noterm",
137
            sprintf(_("%s: max users per profile"), \core\ProfileSilverbullet::PRODUCTNAME) => "fed:silverbullet-maxusers",
138
            sprintf(_("Mint %s with CA on creation"), $this->nomenclatureInst) => "fed:minted_ca_file",
139
            $ssidText => "media:SSID",
140
            $ssidLegacyText => "media:SSID_with_legacy",
141
            $passpointOiText => "media:consortium_OI",
142
            _("VLAN for own users") => "managedsp:vlan",
143
            _("Realm to be considered own users") => "managedsp:realmforvlan",
144
            _("Custom Operator-Name attribute") => "managedsp:operatorname",
145
        ];
146
147
        $find = array_keys($displayNames, $input, TRUE);
148
149
        if (count($find) == 0) { // this is an error! throw an Exception
150
            throw new \Exception("The translation of an option name was requested, but the option is not known to the system: " . htmlentities($input));
151
        }
152
        \core\common\Entity::outOfThePotatoes();
153
        return $find[0];
154
    }
155
156
    /**
157
     * creates an HTML information block with a list of options from a given category and level
158
     * @param array  $optionlist list of options
159
     * @param string $class      option class of interest
160
     * @param string $level      option level of interest
161
     * @return string HTML code
162
     */
163
    public function infoblock(array $optionlist, string $class, string $level) {
164
        \core\common\Entity::intoThePotatoes();
165
        $locationMarkers = [];
166
        $retval = "";
167
        $optioninfo = \core\Options::instance();
168
169
        foreach ($optionlist as $option) {
170
            $type = $optioninfo->optionType($option['name']);
171
            if (preg_match('/^' . $class . '/', $option['name']) && $option['level'] == "$level") {
172
                // all non-multilang attribs get this assignment ...
173
                $language = "";
174
                $content = $option['value'];
175
                // ... override them with multilang tags if needed
176
                if ($type["flag"] == "ML") {
177
                    $language = _("default/other languages");
178
                    if ($option['lang'] != 'C') {
179
                        $language = \config\Master::CONFIG['LANGUAGES'][$option['lang']]['display'] ?? "(unsupported language)";
180
                    }
181
                }
182
183
                switch ($type["type"]) {
184
                    case "coordinates":
185
                        $coords = json_decode($option['value'], true);
186
                        $locationMarkers[] = $coords;
187
                        break;
188
                    case "file":
189
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td>";
190
                        switch ($option['name']) {
191
                            case "general:logo_file":
192
                            case "fed:logo_file":
193
                                $retval .= $this->previewImageinHTML('ROWID-' . $option['level'] . '-' . $option['row']);
194
                                break;
195
                            case "eap:ca_file":
196
                            // fall-through intended: display both the same way
197
                            case "fed:minted_ca_file":
198
                                $retval .= $this->previewCAinHTML('ROWID-' . $option['level'] . '-' . $option['row']);
199
                                break;
200
                            case "support:info_file":
201
                                $retval .= $this->previewInfoFileinHTML('ROWID-' . $option['level'] . '-' . $option['row']);
202
                                break;
203
                            default:
204
                        }
205
                        break;
206
                    case "boolean":
207
                        if ($option['name'] == "fed:silverbullet" && \config\Master::CONFIG['FUNCTIONALITY_LOCATIONS']['CONFASSISTANT_SILVERBULLET'] == "LOCAL" && \config\Master::CONFIG['FUNCTIONALITY_LOCATIONS']['CONFASSISTANT_RADIUS'] != "LOCAL") {
208
                            // do not display the option at all; it gets auto-set by the ProfileSilverbullet constructor and doesn't have to be seen
209
                            break;
210
                        }
211
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>" . ($content == "on" ? _("on") : _("off") ) . "</strong></td></tr>";
212
                        break;
213
                    default:
214
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>$content</strong></td></tr>";
215
                }
216
            }
217
        }
218
        if (count($locationMarkers)) {
219
            $marker = '<markers>';
220
            $locationCount = 0;
221
            foreach ($locationMarkers as $g) {
222
                $locationCount++;
223
                $marker .= '<marker name="' . $locationCount . '" lat="' . $g['lat'] . '" lng="' . $g['lon'] . '" />';
224
            }
225
            $marker .= '<\/markers>'; // some validator says this should be escaped
226
            $jMarker = json_encode($locationMarkers);
227
            $retval .= '<tr><td><script>markers=\'' . $marker . '\'; jmarkers = \'' . $jMarker . '\';</script></td><td></td><td></td></tr>';
228
        }
229
        \core\common\Entity::outOfThePotatoes();
230
        return $retval;
231
    }
232
233
    /**
234
     * creates HTML code to display all information boxes for an IdP
235
     * 
236
     * @param \core\IdP $myInst the IdP in question
237
     * @return string HTML code
238
     */
239
    public function instLevelInfoBoxes(\core\IdP $myInst) {
240
        \core\common\Entity::intoThePotatoes();
241
        $idpoptions = $myInst->getAttributes();
242
        $retval = "<div class='infobox'>
243
        <h2>" . sprintf(_("General %s details"), $this->nomenclatureInst) . "</h2>
244
        <table>
245
            <tr>
246
                <td>
247
                    " . _("Country:") . "
248
                </td>
249
                <td>
250
                </td>
251
                <td>
252
                    <strong>";
253
        $myFed = new \core\Federation($myInst->federation);
254
        $retval .= $myFed->name;
255
        $retval .= "</strong>
256
                </td>
257
            </tr>" . $this->infoblock($idpoptions, "general", "IdP") . "
258
        </table>
259
    </div>";
260
261
        $blocks = [["support", _("Global Helpdesk Details")], ["media", _("Media Properties")]];
262
        foreach ($blocks as $block) {
263
            $retval .= "<div class='infobox'>
264
            <h2>" . $block[1] . "</h2>
265
            <table>" .
266
                    $this->infoblock($idpoptions, $block[0], "IdP") .
267
                    "</table>
268
        </div>";
269
        }
270
        \core\common\Entity::outOfThePotatoes();
271
        return $retval;
272
    }
273
274
    /**
275
     * pretty-prints a file size number in SI "bi" units
276
     * @param int $number the size of the file
277
     * @return string the pretty-print representation of the file size
278
     */
279
    private function displaySize(int $number) {
280
        if ($number > 1024 * 1024) {
281
            return round($number / 1024 / 1024, 2) . " MiB";
282
        }
283
        if ($number > 1024) {
284
            return round($number / 1024, 2) . " KiB";
285
        }
286
        return $number . " B";
287
    }
288
289
    /**
290
     * 
291
     * @param string  $ref         the database reference string
292
     * @param boolean $checkpublic should we check if the requested piece of data is public?
293
     * @return string|boolean the requested data, or FALSE if something went wrong
294
     */
295
    public static function getBlobFromDB($ref, $checkpublic) {
296
        $validator = new \web\lib\common\InputValidation();
297
        $reference = $validator->databaseReference($ref);
298
299
        if ($reference === FALSE) {
300
            return FALSE;
301
        }
302
303
        // the data is either public (just give it away) or not; in this case, only
304
        // release if the data belongs to admin himself
305
        if ($checkpublic) {
306
307
            $owners = \core\EntityWithDBProperties::isDataRestricted($reference["table"], $reference["rowindex"]);
308
309
            $ownersCondensed = [];
310
311
            if ($owners !== FALSE) { // restricted data, see if we're authenticated and owners of the data
312
                $auth = new \web\lib\admin\Authentication();
313
                if (!$auth->isAuthenticated()) {
314
                    return FALSE; // admin-only, but we are not an admin
315
                }
316
                // we might be called without session context (filepreview) so get the
317
                // context if needed
318
                \core\CAT::sessionStart();
319
320
                foreach ($owners as $oneowner) {
321
                    $ownersCondensed[] = $oneowner['ID'];
322
                }
323
                if (array_search($_SESSION['user'], $ownersCondensed) === FALSE) {
324
                    return FALSE; // wrong guy
325
                }
326
                // carry on and get the data
327
            }
328
        }
329
330
        $blob = \core\EntityWithDBProperties::fetchRawDataByIndex($reference["table"], $reference["rowindex"]);
331
        return $blob; // this means we might return FALSE here if something was wrong with the original requested reference
332
    }
333
334
    /**
335
     * 
336
     * @param string $reference a reference pointer to a database entry
337
     * @return void
338
     * @throws Exception
339
     */
340
    private function checkROWIDpresence($reference) {
341
        $found = preg_match("/^ROWID-.*/", $reference);
342
        if ($found != 1) { // get excited on not-found AND on execution error
343
            throw new Exception("Error, ROWID expected.");
344
        }
345
    }
346
347
    /**
348
     * creates HTML code to display a nice UI representation of a CA
349
     * 
350
     * @param string $cAReference ROWID pointer to the CA to display
351
     * @return string HTML code
352
     */
353
    public function previewCAinHTML($cAReference) {
354
        \core\common\Entity::intoThePotatoes();
355
        $this->checkROWIDpresence($cAReference);
356
        $rawResult = UIElements::getBlobFromDB($cAReference, FALSE);
357
        if (is_bool($rawResult)) { // we didn't actually get a CA!
358
            $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the certificate from the database!") . "</div>";
359
            \core\common\Entity::outOfThePotatoes();
360
            return $retval;
361
        }
362
        $cAblob = base64_decode($rawResult);
363
364
        $func = new \core\common\X509;
365
        $details = $func->processCertificate($cAblob);
366
        if ($details === FALSE) {
367
            $retval = _("There was an error processing the certificate!");
368
            \core\common\Entity::outOfThePotatoes();
369
            return $retval;
370
        }
371
372
        $details['name'] = preg_replace('/(.)\/(.)/', "$1<br/>$2", $details['name']);
373
        $details['name'] = preg_replace('/\//', "", $details['name']);
374
        $certstatus = ( $details['root'] == 1 ? "R" : "I");
375
        if ($details['ca'] == 0 && $details['root'] != 1) {
376
            $retval = "<div class='ca-summary' style='background-color:red'><div style='position:absolute; right: 0px; width:20px; height:20px; background-color:maroon;  border-radius:10px; text-align: center;'><div style='padding-top:3px; font-weight:bold; color:#ffffff;'>S</div></div>" . _("This is a <strong>SERVER</strong> certificate!") . "<br/>" . $details['name'] . "</div>";
377
            \core\common\Entity::outOfThePotatoes();
378
            return $retval;
379
        }
380
        $retval = "<div class='ca-summary'                                ><div style='position:absolute; right: 0px; width:20px; height:20px; background-color:#0000ff; border-radius:10px; text-align: center;'><div style='padding-top:3px; font-weight:bold; color:#ffffff;'>$certstatus</div></div>" . $details['name'] . "</div>";
381
        \core\common\Entity::outOfThePotatoes();
382
        return $retval;
383
    }
384
385
    /**
386
     * creates HTML code to display a nice UI representation of an image
387
     * 
388
     * @param string $imageReference ROWID pointer to the image to display
389
     * @return string HTML code
390
     */
391
    public function previewImageinHTML($imageReference) {
392
        \core\common\Entity::intoThePotatoes();
393
        $this->checkROWIDpresence($imageReference);
394
        $retval = "<img style='max-width:150px' src='inc/filepreview.php?id=" . $imageReference . "' alt='" . _("Preview of logo file") . "'/>";
395
        \core\common\Entity::outOfThePotatoes();
396
        return $retval;
397
    }
398
399
    /**
400
     * creates HTML code to display a nice UI representation of a TermsOfUse file
401
     * 
402
     * @param string $fileReference ROWID pointer to the file to display
403
     * @return string HTML code
404
     */
405
    public function previewInfoFileinHTML($fileReference) {
406
        \core\common\Entity::intoThePotatoes();
407
        $this->checkROWIDpresence($fileReference);
408
        $fileBlob = UIElements::getBlobFromDB($fileReference, FALSE);
409
        if (is_bool($fileBlob)) { // we didn't actually get a file!
410
            $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the file from the database!") . "</div>";
411
            \core\common\Entity::outOfThePotatoes();
412
            return $retval;
413
        }
414
        $decodedFileBlob = base64_decode($fileBlob);
415
        $fileinfo = new \finfo();
416
        $retval = "<div class='ca-summary'>" . _("File exists") . " (" . $fileinfo->buffer($decodedFileBlob, FILEINFO_MIME_TYPE) . ", " . $this->displaySize(strlen($decodedFileBlob)) . ")<br/><a href='inc/filepreview.php?id=$fileReference'>" . _("Preview") . "</a></div>";
417
        \core\common\Entity::outOfThePotatoes();
418
        return $retval;
419
    }
420
421
    /**
422
     * creates HTML code for a UI element which informs the user about something.
423
     * 
424
     * @param int    $level         what kind of information is to be displayed?
425
     * @param string $text          the text to display
426
     * @param string $caption       the caption to display
427
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
428
     * @return string
429
     */
430
    public function boxFlexible(int $level, string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
431
        \core\common\Entity::intoThePotatoes();
432
        $uiMessages = [
433
            \core\common\Entity::L_OK => ['icon' => '../resources/images/icons/Quetto/check-icon.png', 'text' => _("OK")],
434
            \core\common\Entity::L_REMARK => ['icon' => '../resources/images/icons/Quetto/info-icon.png', 'text' => _("Remark")],
435
            \core\common\Entity::L_WARN => ['icon' => '../resources/images/icons/Quetto/danger-icon.png', 'text' => _("Warning!")],
436
            \core\common\Entity::L_ERROR => ['icon' => '../resources/images/icons/Quetto/no-icon.png', 'text' => _("Error!")],
437
        ];
438
439
        $retval = "";
440
        if (!$omittabletags) {
441
            $retval .= "<tr><td>";
442
        }
443
        $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']);
444
        $retval .= "<img class='icon' src='" . $uiMessages[$level]['icon'] . "' alt='" . $finalCaption . "' title='" . $finalCaption . "'/>";
445
        if (!$omittabletags) {
446
            $retval .= "</td><td>";
447
        }
448
        if ($text !== NULL) {
449
            $retval .= $text;
450
        }
451
        if (!$omittabletags) {
452
            $retval .= "</td></tr>";
453
        }
454
        \core\common\Entity::outOfThePotatoes();
455
        return $retval;
456
    }
457
458
    /**
459
     * creates HTML code to display an "all is okay" message
460
     * 
461
     * @param string $text          the text to display
462
     * @param string $caption       the caption to display
463
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
464
     * @return string HTML: the box
465
     */
466
    public function boxOkay(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
467
        return $this->boxFlexible(\core\common\Entity::L_OK, $text, $caption, $omittabletags);
468
    }
469
470
    /**
471
     * creates HTML code to display a "smartass comment" message
472
     * 
473
     * @param string $text          the text to display
474
     * @param string $caption       the caption to display
475
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
476
     * @return string HTML: the box
477
     */
478
    public function boxRemark(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
479
        return $this->boxFlexible(\core\common\Entity::L_REMARK, $text, $caption, $omittabletags);
480
    }
481
482
    /**
483
     * creates HTML code to display a "something's a bit wrong" message
484
     * 
485
     * @param string $text          the text to display
486
     * @param string $caption       the caption to display
487
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
488
     * @return string HTML: the box
489
     */
490
    public function boxWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
491
        return $this->boxFlexible(\core\common\Entity::L_WARN, $text, $caption, $omittabletags);
492
    }
493
494
    /**
495
     * creates HTML code to display a "Whoa! Danger, Will Robinson!" message
496
     * 
497
     * @param string $text          the text to display
498
     * @param string $caption       the caption to display
499
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
500
     * @return string HTML: the box
501
     */
502
    public function boxError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
503
        return $this->boxFlexible(\core\common\Entity::L_ERROR, $text, $caption, $omittabletags);
504
    }
505
506
    /**
507
     * Injects the consortium logo in the middle of a given PNG.
508
     * 
509
     * Usually used on QR code PNGs - the parameters inform about the structure of
510
     * the QR code so that the logo does not prevent parsing of the QR code.
511
     * 
512
     * @param string $inputpngstring the PNG to edit
513
     * @param int    $symbolsize     size in pixels of one QR "pixel"
514
     * @param int    $marginsymbols  size in pixels of border around the actual QR
515
     * @return string the image with logo centered in the middle
516
     */
517
    public function pngInjectConsortiumLogo(string $inputpngstring, int $symbolsize, int $marginsymbols = 4) {
518
        $loggerInstance = new \core\common\Logging();
519
        $inputgd = imagecreatefromstring($inputpngstring);
520
        if ($inputgd === FALSE) { // source image is bogus; don't do anything
521
            return "";
522
        }
523
524
        $loggerInstance->debug(4, "Consortium logo is at: " . ROOT . "/web/resources/images/consortium_logo_large.png");
525
        $logogd = imagecreatefrompng(ROOT . "/web/resources/images/consortium_logo_large.png");
526
        if ($logogd === FALSE) { // consortium logo is bogus; don't do anything
527
            return "";
528
        }
529
        $sizeinput = [imagesx($inputgd), imagesy($inputgd)];
530
        $sizelogo = [imagesx($logogd), imagesy($logogd)];
531
        // Q level QR-codes can sustain 25% "damage"
532
        // make our logo cover approx 15% of area to be sure; mind that there's a $symbolsize * $marginsymbols pixel white border around each edge
533
        $totalpixels = ($sizeinput[0] - $symbolsize * $marginsymbols) * ($sizeinput[1] - $symbolsize * $marginsymbols);
534
        $totallogopixels = ($sizelogo[0]) * ($sizelogo[1]);
535
        $maxoccupy = $totalpixels * 0.04;
536
        // find out how much we have to scale down logo to reach 10% QR estate
537
        $scale = sqrt($maxoccupy / $totallogopixels);
538
        $loggerInstance->debug(4, "Scaling info: $scale, $maxoccupy, $totallogopixels\n");
539
        // determine final pixel size - round to multitude of $symbolsize to match exact symbol boundary
540
        $targetwidth = (int) ($symbolsize * round($sizelogo[0] * $scale / $symbolsize));
541
        $targetheight = (int) ($symbolsize * round($sizelogo[1] * $scale / $symbolsize));
542
        // paint white below the logo, in case it has transparencies (looks bad)
543
        // have one symbol in each direction extra white space
544
        $whiteimage = imagecreate($targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize);
545
        if ($whiteimage === FALSE) { // we can't create an empty canvas. Weird. Stop processing.
546
            return "";
547
        }
548
        imagecolorallocate($whiteimage, 255, 255, 255);
549
        // also make sure the initial placement is a multitude of 12; otherwise "two half" symbols might be affected
550
        $targetplacementx = (int) ($symbolsize * round(($sizeinput[0] / 2 - ($targetwidth - $symbolsize) / 2) / $symbolsize));
551
        $targetplacementy = (int) ($symbolsize * round(($sizeinput[1] / 2 - ($targetheight - $symbolsize) / 2) / $symbolsize));
552
        imagecopyresized($inputgd, $whiteimage, $targetplacementx - $symbolsize, $targetplacementy - $symbolsize, 0, 0, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize);
553
        imagecopyresized($inputgd, $logogd, $targetplacementx, $targetplacementy, 0, 0, $targetwidth, $targetheight, $sizelogo[0], $sizelogo[1]);
554
        ob_start();
555
        imagepng($inputgd);
556
        return ob_get_clean();
557
    }
558
559
    /**
560
     * Something went wrong. We display the error cause and then throw an Exception.
561
     * 
562
     * @param string $headerDisplay error to put in the page header
563
     * @param string $uiDisplay     error string to display
564
     * @return void direct output
565
     * @throws Exception
566
     */
567
    function errorPage($headerDisplay, $uiDisplay) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
568
        $decoObject = new PageDecoration();
569
        echo $decoObject->pageheader($headerDisplay, "ADMIN-IDP");
570
        echo "<h1>$uiDisplay</h1>";
571
        echo $decoObject->footer();
572
        throw new Exception("Error page raised: $headerDisplay - $uiDisplay.");
573
    }
574
575
}
576