Test Failed
Push — master ( d682dd...1b57e8 )
by Tomasz
09:19
created

UIElements::displayName()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 111
Code Lines 99

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 99
c 6
b 0
f 0
dl 0
loc 111
rs 7.7107
cc 5
nc 6
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 $nomenclatureIdP;
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->nomenclatureIdP = \core\common\Entity::$nomenclature_idp;
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, $fullDisplay = false) {
85
        \core\common\Entity::intoThePotatoes();
86
        $ssidText = _("SSID");
87
        $passpointOiText = _("HS20 Consortium OI");
88
89
        if (!empty(\config\ConfAssistant::CONSORTIUM['interworking-consortium-oi']) && count(\config\ConfAssistant::CONSORTIUM['interworking-consortium-oi']) > 0) {
90
            $passpointOiText = _("Additional HS20 Consortium OI");
91
        }
92
                
93
        $displayNames = [
94
            "support:url" => ['display' => _("Support: Web"), 'help' => ""],
95
            "support:eap_types" => ['display' => _("Support: EAP Types"), 'help' => ""],
96
            "support:phone" => ['display' => _("Support: Phone"), 'help' => ""],
97
            "support:email" => ['display' => _("Support: E-Mail"), 'help' => ""],
98
            "general:instname" => ['display' => _("Organisation Name"), 'help' => ""],
99
            "general:instshortname" => ['display' => _("Organisation Acronym"), 'help' => ""],
100
            "general:instaltname" => ['display' => _("Organisation Alt Name"), 'help' => ""],
101
            "general:geo_coordinates" => ['display' => _("Location"), 'help' => ""],
102
            "general:logo_url" => ['display' => _("Logo URL"), 'help' => ""],
103
            "general:logo_file" => ['display' => _("Logo image"), 'help' => ""],
104
            "media:wired" => ['display' => _("Configure Wired Ethernet"), 'help' => ""],
105
            "eap:server_name" => ['display' => _("Name (CN) of Authentication Server"), 'help' => ""],
106
            "eap:ca_vailduntil" => ['display' => _("Valid until"), 'help' => ""],
107
            "eap:ca_vaildfrom" => ['display' => _("Valid from"), 'help' => ""],
108
            "eap:enable_nea" => ['display' => _("Enable device assessment"), 'help' => ""],
109
            "support:info_file" => ['display' => _("Terms of Use"), 'help' => ""],
110
            "eap:ca_url" => ['display' => _("CA Certificate URL"), 'help' => ""],
111
            "eap:ca_file" => ['display' => _("CA Certificate File"), 'help' => ""],
112
            "profile:name" => ['display' => _("Profile Display Name"), 'help' => ""],
113
            "profile:production" => ['display' => _("Production-Ready"), 'help' => ""],
114
            "hiddenprofile:tou_accepted" => ['display' => _("Admin Accepted IdP Terms of Use"), 'help' => ""],
115
            "hiddenmanagedsp:tou_accepted" => ['display' => _("Admin Accepted SP Terms of Use"), 'help' => ""],
116
            "device-specific:customtext" => ['display' => _("Extra text on downloadpage for device"), 'help' => ""],
117
            "device-specific:redirect" => ['display' => _("Redirection Target"), 'help' => ""],
118
            "eap-specific:customtext" => ['display' => _("Extra text on downloadpage for device"), 'help' => ""],
119
            "eap-specific:tls_use_other_id" => ['display' => _("Turn on selection of EAP-TLS User-Name"), 'help' => ""],
120
            "device-specific:geantlink" => ['display' => _("Use GEANTlink for TTLS (Windows 8 and 10)"), 'help' => ""],
121
            "device-specific:geteduroam" => ['display' => _("Show the dedicated geteduroam download page for this device"), 'help' => ""],
122
            "profile:description" => ['display' => _("Profile Description"), 'help' => ""],
123
            "profile:customsuffix" => ['display' => _("Custom Installer Name Suffix"), 'help' => ""],
124
            "media:openroaming" => ['display' => _("OpenRoaming"), 'help' => ""],
125
            "user:fedadmin" => ['display' => sprintf(_("%s Administrator"), $this->nomenclatureFed), 'help' => ""],
126
            "user:realname" => ['display' => _("Real Name"), 'help' => ""],
127
            "user:email" => ['display' => _("E-Mail Address"), 'help' => ""],
128
            "media:remove_SSID" => ['display' => _("Remove/Disable SSID"), 'help' => ""],
129
            "media:force_proxy" => ['display' => _("Mandatory Content Filtering Proxy"), 'help' => ""],
130
            "fed:css_file" => ['display' => _("Custom CSS file for User Area"), 'help' => "not available"],
131
            "fed:logo_file" => [
132
                'display' => sprintf(_("%s Logo"), $this->nomenclatureFed),
133
                'help' => _("Your federation logo to be shown on CAT download pages and also on Windows installers if"
134
                . " the option to include branding in installers is set as well.")],
135
            "fed:desired_skin" => ['display' => _("Preferred Skin for User Area"), 'help' => "not available"],
136
            "fed:include_logo_installers" => [
137
                'display' => sprintf(_("Include %s branding in installers"), $this->nomenclatureFed),
138
                'help' => _("Add your federation logo to Windows installers.")],
139
            "fed:realname" => [
140
                'display' => sprintf(_("%s Name"), $this->nomenclatureFed),
141
                'help' => "The name of your federation."],
142
            "fed:url" => ['display' => sprintf(_("%s Homepage"), $this->nomenclatureFed), 'help' => ""],
143
            "fed:custominvite" => [
144
                'display' => sprintf(_("Custom text in %s Invitations"), $this->nomenclatureParticipant),
145
                'help' => _("Your text in invitation mails sent for new IdP")],
146
            "fed:silverbullet" => ['display' => sprintf(_("Enable %s"), \config\ConfAssistant::SILVERBULLET['product_name']), 'help' => ""],
147
            "fed:silverbullet-noterm" => ['display' => sprintf(_("%s: Do not terminate EAP"), \core\ProfileSilverbullet::PRODUCTNAME), 'help' => ""],
148
            "fed:silverbullet-maxusers" => ['display' => sprintf(_("%s: max users per profile"), \core\ProfileSilverbullet::PRODUCTNAME), 'help' => ""],
149
            "fed:minted_ca_file" => [
150
                'display' => sprintf(_("Mint %s with CA on creation"), $this->nomenclatureIdP),
151
                'help' => _("Set of default CAs to add to new IdPs on signup")],
152
            "fed:openroaming" => [
153
                'display' => sprintf(_("OpenRoaming: Allow %s Opt-In"),$this->nomenclatureParticipant),
154
                'help' => _("Allow IdP to set OpenRoaming support for its users.")],
155
            "fed:openroaming_customtarget" => ['display' => _("OpenRoaming: Custom NAPTR Target"), 'help' => "If you want your IdPs to use your own OpenRoaming → eduroam proxy then you can configure the hostname here; the realm check feature for IdPs will then warn them if the OpenRoaming destination server is not yours. This attribute does not need to be set, and the realm checks default to checking for the OpenRoaming → eduroam proxy operated by eduroam OT."],
156
            "fed:autoregister-synced" => [
157
                'display' => _("Self registration from eduroam DB: add listed admins to CAT institutions"),
158
                'help' => sprintf(_("With this option turned on if a CAT institution is synced to the eduroam DB it is possible to have automatic enlisting of CAT institution admins under some conditions described <a href='%s'>here</a>."), "https://wiki.eduroam.org/")],
159
            "fed:autoregister-new-inst" => [
160
                'display' => _("Self registration from eduroam DB: allow creating new institutions"),
161
                'help' => sprintf(_("Turn this on and eduroam DB listed institution admins will be allowed to create new institutions under some conditions described <a href='%s'>here</a>."), "https://wiki.eduroam.org/")],
162
            "fed:autoregister-entitlement" => [
163
                'display' => _("Self registration based on entitlement: add admins to CAT institutions"),
164
                'help' => _("With this option turned on the system will verify the eduGAIN login of the potential administrator and propose taking control over institutions which use the realm within the scope defined in the user's pairwise-id attribute.")
165
            ],
166
            "fed:entitlement-attr" => [
167
                'display' => _("Custom entitlement value for self-registration"),
168
                'help' => _("If you want to use the SAML eduPersonEntitlement based self-registration you may define a value that will be used in your federation for institutions admins. When this is not set the default value geant:eduroam:inst:admin will be used. This option makes sense only if you have 'Self registration based on entitlement' set")
169
            ],
170
            "fed:max-inactivity" => [
171
                'display' => _("Custom number of days of allowed admin inactivity"),
172
                'help' => sprintf(_("You can override the default value of %d days after which an inactivity warning will be displayed on the NRO admin page."), \config\ConfAssistant::ADMIN_LOGINS['allowed_inactivity_days']),
173
            ],
174
            "fed:hide-admin-warnings" => [
175
                'display' => _("Do not show any warnings about missing/inactive admins"),
176
                'help' => _("You can block any warnings on missing/inactive admins. The default is to show them.")
177
            ],
178
            "media:SSID" => ['display' => $ssidText, 'help' => ""],
179
            "media:consortium_OI" => ['display' => $passpointOiText, 'help' => ""],
180
            "managedsp:guest_vlan" => ['display' => _("VLAN for guests"), 'help' => ""],
181
            "managedsp:vlan" => ['display' => _("VLAN for own users"), 'help' => ""],
182
            "managedsp:realmforvlan" => ['display' => _("Realm to be considered own users"), 'help' => ""],
183
            "managedsp:operatorname" => ['display' => _("Custom Operator-Name attribute"), 'help' => ""],
184
        ];
185
186
        if (!isset($displayNames[$input])) { // this is an error! throw an Exception
187
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
188
        }
189
        \core\common\Entity::outOfThePotatoes();
190
        // none of the strings have HTML in them, only translators can provide own text for it -> no threat, but complained about by the security review
191
        if ($fullDisplay) {
192
            return ['display' => htmlspecialchars($displayNames[$input]['display']), 'help' => $displayNames[$input]['help']];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('display' =...yNames[$input]['help']) returns the type array<string,string> which is incompatible with the documented return type string.
Loading history...
193
        } else {
194
            return htmlspecialchars($displayNames[$input]['display']);
195
        }
196
    }
197
198
    /**
199
     * creates an HTML information block with a list of options from a given category and level
200
     * @param array  $optionlist list of options
201
     * @param string $class      option class of interest
202
     * @param string $level      option level of interest
203
     * @return string HTML code
204
     */
205
    public function infoblock(array $optionlist, string $class, string $level) {
206
        \core\common\Entity::intoThePotatoes();
207
        $locationMarkers = [];
208
        $retval = "";
209
        $optioninfo = \core\Options::instance();
210
211
        foreach ($optionlist as $option) {
212
            $type = $optioninfo->optionType($option['name']);
213
            if (preg_match('/^' . $class . '/', $option['name']) && $option['level'] == "$level") {
214
                // all non-multilang attribs get this assignment ...
215
                $language = "";
216
                $content = $option['value'];
217
                // ... override them with multilang tags if needed
218
                if ($type["flag"] == "ML") {
219
                    $language = _("default/other languages");
220
                    if ($option['lang'] != 'C') {
221
                        $language = \config\Master::LANGUAGES[$option['lang']]['display'] ?? "(unsupported language)";
222
                    }
223
                }
224
225
                switch ($type["type"]) {
226
                    case "coordinates":
227
                        $coords = json_decode($option['value'], true);
228
                        $locationMarkers[] = $coords;
229
                        break;
230
                    case "file":
231
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td>";
232
                        switch ($option['name']) {
233
                            case "general:logo_file":
234
                            case "fed:logo_file":
235
                                $retval .= $this->previewImageinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']);
236
                                break;
237
                            case "eap:ca_file":
238
                            // fall-through intended: display both the same way
239
                            case "fed:minted_ca_file":
240
                                $retval .= $this->previewCAinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']);
241
                                break;
242
                            case "support:info_file":
243
                                $retval .= $this->previewInfoFileinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']);
244
                                break;
245
                            default:
246
                        }
247
                        break;
248
                    case "boolean":
249
                        if ($option['name'] == "fed:silverbullet" && \core\CAT::hostedServicesEnabled() && !\core\CAT::radiusProfilesEnabled()) {
250
                            // do not display the option at all; it gets auto-set by the ProfileSilverbullet constructor and doesn't have to be seen
251
                            break;
252
                        }
253
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>" . ($content == "on" ? _("on") : _("off") ) . "</strong></td></tr>";
254
                        break;
255
                    default:
256
                        $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>$content</strong></td></tr>";
257
                }
258
            }
259
        }
260
        if (count($locationMarkers)) {
261
            $marker = '<markers>';
262
            $locationCount = 0;
263
            foreach ($locationMarkers as $g) {
264
                $locationCount++;
265
                $marker .= '<marker name="' . $locationCount . '" lat="' . $g['lat'] . '" lng="' . $g['lon'] . '" />';
266
            }
267
            $marker .= '<\/markers>'; // some validator says this should be escaped
268
            $jMarker = json_encode($locationMarkers);
269
            $retval .= '<tr><td><script>markers=\'' . $marker . '\'; jmarkers = \'' . $jMarker . '\';</script></td><td></td><td></td></tr>';
270
        }
271
        \core\common\Entity::outOfThePotatoes();
272
        return $retval;
273
    }
274
275
    /**
276
     * creates HTML code to display all information boxes for an IdP
277
     * 
278
     * @param \core\IdP $myInst the IdP in question
279
     * @return string HTML code
280
     */
281
    public function instLevelInfoBoxes(\core\IdP $myInst) {
282
        \core\common\Entity::intoThePotatoes();
283
        $idpoptions = $myInst->getAttributes();
284
        $retval = "<div class='infobox'>
285
        <h2>" . sprintf(_("General %s details"), $this->nomenclatureParticipant) . "</h2>
286
        <table>
287
            <tr>
288
                <td>
289
                    " . _("Country:") . "
290
                </td>
291
                <td>
292
                </td>
293
                <td>
294
                    <strong>";
295
        $myFed = new \core\Federation($myInst->federation);
296
        $retval .= $myFed->name;
297
        $retval .= "</strong>
298
                </td>
299
            </tr>" . $this->infoblock($idpoptions, "general", "IdP") . "
300
        </table>
301
    </div>";
302
        $blocks = [["support", _("Global Helpdesk Details")]];        
303
        if ($myInst->type != "SP") {
304
            $blocks [] = ["media", _("Media Properties")];
305
        }
306
        foreach ($blocks as $block) {
307
            $retval .= "<div class='infobox'>
308
            <h2>" . $block[1] . "</h2>
309
            <table>" .
310
                    $this->infoblock($idpoptions, $block[0], "IdP") .
311
                    "</table>
312
        </div>";
313
        }
314
        \core\common\Entity::outOfThePotatoes();
315
        return $retval;
316
    }
317
318
    /**
319
     * pretty-prints a file size number in SI "bi" units
320
     * @param int $number the size of the file
321
     * @return string the pretty-print representation of the file size
322
     */
323
    private function displaySize(int $number) {
324
        if ($number > 1024 * 1024) {
325
            return round($number / 1024 / 1024, 2) . " MiB";
326
        }
327
        if ($number > 1024) {
328
            return round($number / 1024, 2) . " KiB";
329
        }
330
        return $number . " B";
331
    }
332
333
    /**
334
     * 
335
     * @param string  $table       the database table
336
     * @param integer $rowindex    the database row_id
337
     * @param boolean $checkpublic should we check if the requested piece of data is public?
338
     * @return string|boolean the requested data, or FALSE if something went wrong
339
     */
340
    public static function getBlobFromDB($table, $rowindex, $checkpublic) {
341
        // the data is either public (just give it away) or not; in this case, only
342
        // release if the data belongs to admin himself
343
        if ($checkpublic) {
344
345
            $owners = \core\EntityWithDBProperties::isDataRestricted($table, $rowindex);
346
347
            $ownersCondensed = [];
348
349
            if ($owners !== FALSE) { // restricted data, see if we're authenticated and owners of the data
350
                $auth = new \web\lib\admin\Authentication();
351
                if (!$auth->isAuthenticated()) {
352
                    return FALSE; // admin-only, but we are not an admin
353
                }
354
                // we might be called without session context (filepreview) so get the
355
                // context if needed
356
                \core\CAT::sessionStart();
357
358
                foreach ($owners as $oneowner) {
359
                    $ownersCondensed[] = $oneowner['ID'];
360
                }
361
                if (array_search($_SESSION['user'], $ownersCondensed) === FALSE) {
362
                    return FALSE; // wrong guy
363
                }
364
                // carry on and get the data
365
            }
366
        }
367
368
        $blob = \core\EntityWithDBProperties::fetchRawDataByIndex($table, $rowindex);
369
        return $blob; // this means we might return FALSE here if something was wrong with the original requested reference
370
    }
371
372
    /**
373
     * creates HTML code to display a nice UI representation of a CA
374
     * 
375
     * @param string $cAReference ROWID pointer to the CA to display
376
     * @return string HTML code
377
     */
378
    public function previewCAinHTML($cAReference) {
379
        \core\common\Entity::intoThePotatoes();
380
        $validator = new \web\lib\common\InputValidation();
381
        $ref = $validator->databaseReference($cAReference);
382
        $caExpiryTrashhold = \config\ConfAssistant::CERT_WARNINGS['expiry_warning'];
383
        $rawResult = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE);
384
        if (is_bool($rawResult)) { // we didn't actually get a CA!
385
            $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the certificate from the database!") . "</div>";
386
            \core\common\Entity::outOfThePotatoes();
387
            return $retval;
388
        }
389
        $cAblob = base64_decode($rawResult);
390
391
        $func = new \core\common\X509;
392
        $details = $func->processCertificate($cAblob);
393
        if ($details === FALSE) {
394
            $retval = _("There was an error processing the certificate!");
395
            \core\common\Entity::outOfThePotatoes();
396
            return $retval;
397
        }
398
        \core\common\Logging::debug_s(3, $details, "CERT DETAILS\n", "\n");
399
        $details['name'] = preg_replace('/(.)\/(.)/', "$1<br/>$2", $details['name']);
400
        $details['name'] = preg_replace('/\//', "", $details['name']);
401
        $certstatus = ( $details['root'] == 1 ? "R" : "I");
402
        $certTooltip = ( $details['root'] == 1 ? _("Root CA") : _("Intermediate CA"));
403
        $innerbgColor = "#0000ff";
404
        $leftBorderColor = "#00ff00";
405
        $message = "";
406
        if ($details['ca'] == 0 && $details['root'] != 1) {
407
            $leftBorderColor = "red";
408
            $message = _("This is a <strong>SERVER</strong> certificate!");
409
            if (\config\ConfAssistant::CERT_GUIDELINES !== '') {
410
                $message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>";
411
            }
412
            $message .= "<br/>";
413
            $retval = "<div class='ca-summary' style='border-left-color: $leftBorderColor'><div style='position:absolute; right: -15px; width:20px; height:20px; background-color:$innerbgColor; border-radius:10px; text-align: center;'><div style='padding-top:3px; font-weight:bold; color:#ffffff;'>S</div></div>" . $message . $details['name'] . "</div>";
414
            \core\common\Entity::outOfThePotatoes();
415
            return $retval;
416
        }
417
        $now = time();
418
        if ($now + \config\ConfAssistant::CERT_WARNINGS['expiry_critical'] > $details['full_details']['validTo_time_t']) {
419
            $leftBorderColor = "red";
420
            $message = _("Certificate expired!") . "<br>";
421
        } elseif($now + \config\ConfAssistant::CERT_WARNINGS['expiry_warning']  > $details['full_details']['validTo_time_t'] - $caExpiryTrashhold) {
422
            if ($leftBorderColor == "#00ff00") {
423
                $leftBorderColor = "yellow";
424
            }
425
            $message = _("Certificate close to expiry!") . "<br/>";            
426
        }
427
   
428
        if ($details['root'] == 1 && $details['basicconstraints_set'] == 0) {
429
            if ($leftBorderColor == "#00ff00") {
430
                $leftBorderColor = "yellow";
431
            }
432
            $message .= "<div style='max-width: 25em'><strong>" . _("Improper root certificate, required critical CA extension missing, will not reliably install!") . "</strong>";
433
            if (\config\ConfAssistant::CERT_GUIDELINES !== '') {
434
                $message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>";
435
            }
436
            $message .= "</div><br/>";
437
        }
438
        if (strlen($details['full_details']['serialNumberHex']) < 8) {
439
            $serial = $details['full_details']['serialNumberHex']." (0x".$details['full_details']['serialNumberHex'].")";
440
        } else {
441
            $ct = strlen($details['full_details']['serialNumberHex'])/2 - 1;
442
            $serial = preg_replace('/(..)/','$1:', $details['full_details']['serialNumberHex'], $ct);
443
        }
444
        $retval =  "<div class='ca-summary' style='border-left-color: $leftBorderColor'><div style='position:absolute; right: -15px; width:20px; height:20px; background-color:$innerbgColor; border-radius:10px; text-align: center;'><div title='$certTooltip' style='padding-top:3px; font-weight:bold; color:#ffffff;'>$certstatus</div></div>" . 
445
            $message.$details['name']."<br>". 
446
            _("Serial number:")." ".$serial."<br/>".
447
            $this->displayName('eap:ca_vaildfrom')." ".gmdate('Y-m-d H:i:s', $details['full_details']['validFrom_time_t']) . " UTC<br/>".    
448
            $this->displayName('eap:ca_vailduntil')." ".gmdate('Y-m-d H:i:s', $details['full_details']['validTo_time_t']) . " UTC</div>";
449
        \core\common\Entity::outOfThePotatoes();
450
        return $retval;
451
    }
452
453
    /**
454
     * creates HTML code to display a nice UI representation of an image
455
     * 
456
     * @param string $imageReference ROWID pointer to the image to display
457
     * @return string HTML code
458
     */
459
    public function previewImageinHTML($imageReference) {
460
        \core\common\Entity::intoThePotatoes();
461
        $retval = "<img style='max-width:150px' src='inc/filepreview.php?id=" . $imageReference . "' alt='" . _("Preview of logo file") . "'/>";
462
        \core\common\Entity::outOfThePotatoes();
463
        return $retval;
464
    }
465
466
    /**
467
     * creates HTML code to display a nice UI representation of a TermsOfUse file
468
     * 
469
     * @param string $fileReference ROWID pointer to the file to display
470
     * @return string HTML code
471
     */
472
    public function previewInfoFileinHTML($fileReference) {
473
        \core\common\Entity::intoThePotatoes();
474
        $validator = new \web\lib\common\InputValidation();
475
        $ref = $validator->databaseReference($fileReference);
476
        $fileBlob = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE);
477
        if (is_bool($fileBlob)) { // we didn't actually get a file!
478
            $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the file from the database!") . "</div>";
479
            \core\common\Entity::outOfThePotatoes();
480
            return $retval;
481
        }
482
        $decodedFileBlob = base64_decode($fileBlob);
483
        $fileinfo = new \finfo();
484
        $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>";
485
        \core\common\Entity::outOfThePotatoes();
486
        return $retval;
487
    }
488
489
    /**
490
     * creates HTML code for a UI element which informs the user about something.
491
     * 
492
     * @param int    $level         what kind of information is to be displayed?
493
     * @param string $text          the text to display
494
     * @param string $caption       the caption to display
495
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
496
     * @return string
497
     */
498
    public function boxFlexible(int $level, string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
499
        \core\common\Entity::intoThePotatoes();
500
        $uiMessages = [
501
            \core\common\Entity::L_OK => ['img' => 'Tabler/square-rounded-check-filled-green.svg', 'text' => _("OK")],
502
            \core\common\Entity::L_REMARK => ['img' => 'Tabler/info-square-rounded-filled-blue.svg', 'text' => _("Remark")],
503
            \core\common\Entity::L_WARN => ['img' => 'Tabler/alert-square-rounded-filled-yellow.svg', 'text' => _("Warning!")],
504
            \core\common\Entity::L_ERROR => ['img' => 'Tabler/square-rounded-x-filled-red.svg', 'text' => _("Error!")],
505
            \core\common\Entity::L_CERT_OK => ['img' => 'Tabler/certificate-green.svg', 'text' => _("OK")],
506
            \core\common\Entity::L_CERT_WARN => ['img' => 'Tabler/certificate-red.svg', 'text' => _("Warning!")],
507
            \core\common\Entity::L_CERT_ERROR => ['img' => 'Tabler/certificate-off.svg', 'text' => _("Warning!")],
508
            ];
509
        
510
        $retval = "";
511
        if (!$omittabletags) {
512
            $retval .= "<tr><td>";
513
        }
514
//        $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']);
515
//        $retval .= "<img class='icon cat-icon' src='" . $uiMessages[$level]['icon'] . "' alt='" . $finalCaption . "' title='" . $finalCaption . "'/>";
516
        $iconData = $uiMessages[$level];
517
        if ($caption !== NULL) {
518
            $iconData['text'] = $caption;
519
        }
520
521
522
        $retval .= $this->catIcon($iconData);
523
524
        if (!$omittabletags) {
525
            $retval .= "</td><td>";
526
        }
527
        if ($text !== NULL) {
528
            $retval .= $text;
529
        }
530
        if (!$omittabletags) {
531
            $retval .= "</td></tr>";
532
        }
533
        \core\common\Entity::outOfThePotatoes();
534
        return $retval;
535
    }
536
537
    /**
538
     * creates HTML code to display an "all is okay" message
539
     * 
540
     * @param string $text          the text to display
541
     * @param string $caption       the caption to display
542
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
543
     * @return string HTML: the box
544
     */
545
    public function boxOkay(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
546
        return $this->boxFlexible(\core\common\Entity::L_OK, $text, $caption, $omittabletags);
547
    }
548
549
    /**
550
     * creates HTML code to display a "smartass comment" message
551
     * 
552
     * @param string $text          the text to display
553
     * @param string $caption       the caption to display
554
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
555
     * @return string HTML: the box
556
     */
557
    public function boxRemark(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
558
        return $this->boxFlexible(\core\common\Entity::L_REMARK, $text, $caption, $omittabletags);
559
    }
560
561
    /**
562
     * creates HTML code to display a "something's a bit wrong" message
563
     * 
564
     * @param string $text          the text to display
565
     * @param string $caption       the caption to display
566
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
567
     * @return string HTML: the box
568
     */
569
    public function boxWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
570
        return $this->boxFlexible(\core\common\Entity::L_WARN, $text, $caption, $omittabletags);
571
    }
572
573
    /**
574
     * creates HTML code to display a "Whoa! Danger, Will Robinson!" message
575
     * 
576
     * @param string $text          the text to display
577
     * @param string $caption       the caption to display
578
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
579
     * @return string HTML: the box
580
     */
581
    public function boxError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
582
        return $this->boxFlexible(\core\common\Entity::L_ERROR, $text, $caption, $omittabletags);
583
    }
584
585
    /**
586
     * creates HTML code to display a "All fine" message
587
     * 
588
     * @param string $text          the text to display
589
     * @param string $caption       the caption to display
590
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
591
     * @return string HTML: the box 
592
     */
593
    public function boxCertOK(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
594
        return $this->boxFlexible(\core\common\Entity::L_CERT_OK, $text, $caption, $omittabletags);
595
    }
596
    
597
    /**
598
     * creates HTML code to display a "A certificate close to expiry" message
599
     * 
600
     * @param string $text          the text to display
601
     * @param string $caption       the caption to display
602
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
603
     * @return string HTML: the box
604
     */
605
    public function boxCertWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
606
        return $this->boxFlexible(\core\common\Entity::L_CERT_WARN, $text, $caption, $omittabletags);
607
    }
608
    /**
609
     * creates HTML code to display a "A certificate expired or dangerously close to expiry" message
610
     * 
611
     * @param string $text          the text to display
612
     * @param string $caption       the caption to display
613
     * @param bool   $omittabletags the output usually has tr/td table tags, this option suppresses them
614
     * @return string HTML: the box
615
     */
616
    public function boxCertError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) {
617
        return $this->boxFlexible(\core\common\Entity::L_CERT_ERROR, $text, $caption, $omittabletags);
618
    }    
619
    
620
    
621
    const QRCODE_PIXELS_PER_SYMBOL = 12;
622
623
    /**
624
     * Injects the consortium logo in the middle of a given PNG.
625
     * 
626
     * Usually used on QR code PNGs - the parameters inform about the structure of
627
     * the QR code so that the logo does not prevent parsing of the QR code.
628
     * 
629
     * @param string $inputpngstring the PNG to edit
630
     * @param int    $symbolsize     size in pixels of one QR "pixel"
631
     * @param int    $marginsymbols  size in pixels of border around the actual QR
632
     * @return string the image with logo centered in the middle
633
     */
634
    public function pngInjectConsortiumLogo(string $inputpngstring, int $symbolsize, int $marginsymbols = 4) {
635
        $loggerInstance = new \core\common\Logging();
636
        $inputgd = imagecreatefromstring($inputpngstring);
637
        if ($inputgd === FALSE) { // source image is bogus; don't do anything
638
            return "";
639
        }
640
641
        $loggerInstance->debug(4, "Consortium logo is at: " . ROOT . "/web/resources/images/consortium_logo_large.png");
642
        $logogd = imagecreatefrompng(ROOT . "/web/resources/images/consortium_logo_large.png");
643
        if ($logogd === FALSE) { // consortium logo is bogus; don't do anything
644
            return "";
645
        }
646
        $sizeinput = [imagesx($inputgd), imagesy($inputgd)];
647
        $sizelogo = [imagesx($logogd), imagesy($logogd)];
648
        // Q level QR-codes can sustain 25% "damage"
649
        // make our logo cover approx 15% of area to be sure; mind that there's a $symbolsize * $marginsymbols pixel white border around each edge
650
        $totalpixels = ($sizeinput[0] - $symbolsize * $marginsymbols) * ($sizeinput[1] - $symbolsize * $marginsymbols);
651
        $totallogopixels = ($sizelogo[0]) * ($sizelogo[1]);
652
        $maxoccupy = $totalpixels * 0.04;
653
        // find out how much we have to scale down logo to reach 10% QR estate
654
        $scale = sqrt($maxoccupy / $totallogopixels);
655
        $loggerInstance->debug(4, "Scaling info: $scale, $maxoccupy, $totallogopixels\n");
656
        // determine final pixel size - round to multitude of $symbolsize to match exact symbol boundary
657
        $targetwidth = (int) ($symbolsize * round($sizelogo[0] * $scale / $symbolsize));
658
        $targetheight = (int) ($symbolsize * round($sizelogo[1] * $scale / $symbolsize));
659
        // paint white below the logo, in case it has transparencies (looks bad)
660
        // have one symbol in each direction extra white space
661
        $whiteimage = imagecreate($targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize);
662
        if ($whiteimage === FALSE) { // we can't create an empty canvas. Weird. Stop processing.
663
            return "";
664
        }
665
        imagecolorallocate($whiteimage, 255, 255, 255);
666
        // also make sure the initial placement is a multitude of 12; otherwise "two half" symbols might be affected
667
        $targetplacementx = (int) ($symbolsize * round(($sizeinput[0] / 2 - ($targetwidth - $symbolsize + 1) / 2) / $symbolsize));
668
        $targetplacementy = (int) ($symbolsize * round(($sizeinput[1] / 2 - ($targetheight - $symbolsize + 1 ) / 2) / $symbolsize));
669
        imagecopyresized($inputgd, $whiteimage, $targetplacementx - $symbolsize, $targetplacementy - $symbolsize, 0, 0, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize);
670
        imagecopyresized($inputgd, $logogd, $targetplacementx, $targetplacementy, 0, 0, $targetwidth, $targetheight, $sizelogo[0], $sizelogo[1]);
671
        ob_start();
672
        imagepng($inputgd);
673
        return ob_get_clean();
674
    }
675
676
    /**
677
     * Something went wrong. We display the error cause and then throw an Exception.
678
     * 
679
     * @param string $headerDisplay error to put in the page header
680
     * @param string $uiDisplay     error string to display
681
     * @return void direct output
682
     * @throws Exception
683
     */
684
    public function errorPage($headerDisplay, $uiDisplay) {
685
        $decoObject = new PageDecoration();
686
        echo $decoObject->pageheader($headerDisplay, "ADMIN-IDP");
687
        echo "<h1>$uiDisplay</h1>";
688
        echo $decoObject->footer();
689
        throw new Exception("Error page raised: $headerDisplay - $uiDisplay.");
690
    }
691
692
    /**
693
     * creates the HTML code displaying the result of a test that was run previously
694
     * 
695
     * @param \core\SanityTests $test the test that was run
696
     * @return string
697
     * @throws Exception
698
     */
699
    public function sanityTestResultHTML($test) {
700
        $out = '';
701
        switch ($test->test_result['global']) {
702
            case \core\common\Entity::L_OK:
703
                $message = "Your configuration appears to be fine.";
704
                break;
705
            case \core\common\Entity::L_WARN:
706
                $message = "There were some warnings, but your configuration should work.";
707
                break;
708
            case \core\common\Entity::L_ERROR:
709
                $message = "Your configuration appears to be broken, please fix the errors.";
710
                if ($test->fatalError) {
711
                    $message .= "<br>Some of the errors prevented running additional tests so rerun after fixing.";
712
                }
713
                break;
714
            case \core\common\Entity::L_REMARK:
715
                $message = "Your configuration appears to be fine.";
716
                break;
717
            default:
718
                throw new Exception("The result code level " . $test->test_result['global'] . " is not defined!");
719
        }
720
        $out .= $this->boxFlexible($test->test_result['global'], "<br><strong>Test Summary</strong><br>" . $message . "<br>See below for details<br><hr>");
721
        foreach ($test->out as $testValue) {
722
            foreach ($testValue as $o) {
723
                $out .= $this->boxFlexible($o['level'], $o['message']);
724
            }
725
        }
726
        return($out);
727
    }
728
    /**
729
     * prepares data for icons
730
     * 
731
     * @param string $index
732
     * @return array
733
     */
734
    public function iconData($index) {
735
        \core\common\Entity::intoThePotatoes();
736
        $icons = [
737
            'CERT_STATUS_OK' => ['img' => 'Tabler/certificate-green.svg', 'text' => _("All certificates are valid long enough")],
738
            'CERT_STATUS_WARN' => ['img' => 'Tabler/certificate-red.svg', 'text' => _("At least one certificate is close to expiry")],
739
            'CERT_STATUS_ERROR' => ['img' => 'Tabler/certificate-off.svg', 'text' => _("At least one certificate either has expired or is very close to expiry")],
740
            'OVERALL_OPENROAMING_LEVEL_GOOD' => ['img' => 'Tabler/square-rounded-check-green.svg', 'text' => _("OpenRoaming appears to be configured properly")],
741
            'OVERALL_OPENROAMING_LEVEL_NOTE' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some minor OpenRoaming configuration issues")],
742
            'OVERALL_OPENROAMING_LEVEL_WARN' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some average level OpenRoaming configuration issues")],
743
            'OVERALL_OPENROAMING_LEVEL_ERROR' => ['img' => 'Tabler/alert-square-rounded-red.svg', 'text' => _("There are some critical OpenRoaming configuration issues")],            
744
            'PROFILES_SHOWTIME' => ['img' => 'Tabler/checks-green.svg', 'text' => _("At least one profile is fully configured and visible in the user interface")],
745
            'PROFILES_CONFIGURED' => ['img' => 'Tabler/check-green.svg', 'text' => _("At least one profile is fully configured but none are set as production-ready therefore the institution is not visible in the user interface")],
746
            'PROFILES_INCOMPLETE' => ['img' => 'Tabler/access-point-off-red.svg', 'text' => _("No configured profiles")],
747
            'PROFILES_REDIRECTED' => ['img' => 'Tabler/external-link.svg', 'text' => _("All active profiles redirected")],
748
            'IDP_LINKED' => ['img' => 'Tabler/database-green.svg', 'text' => _("Linked")],
749
            'IDP_NOT_LINKED' => ['img' => 'Tabler/database-off-red.svg', 'text' => _("NOT linked")],
750
            'CERTS_NOT_SHOWN' => ['img' => 'Tabler/question-mark-blue.svg', 'text' => _("Not showing cert info if no profiles are visible")],
751
            'ADMINS_INACTIVE' => ['img' => 'Tabler/alert-square-rounded-filled-red-small.svg', 'text' => _("Some of the admins have not been seen for a long time")],
752
            'ADMINS_OK' => ['img' => 'Tabler/check-green-small.svg', 'text' => _("All admins are active")],
753
            'ADMINS_MISSING' => ['img' => 'Tabler/alert-square-rounded-filled-yellow-small.svg', 'text' => _("No admins registered")],
754
            ];
755
            \core\common\Entity::outOfThePotatoes();
756
        return($icons[$index]);
757
    }
758
    
759
/**
760
 * the HTML img element produced 0n the basis of a simple [src,title] array
761
 * @param type array
762
 * @return string the img element
763
 */
764
    public function catIcon($data) {
765
        return "<img src='../resources/images/icons/".$data['img']."' alt='".$data['text']."' title = '".$data['text']."' class='cat-icon'>";                  
766
    }
767
}
768