UIElements   F
last analyzed

Complexity

Total Complexity 84

Size/Duplication

Total Lines 723
Duplicated Lines 0 %

Importance

Changes 12
Bugs 0 Features 0
Metric Value
wmc 84
eloc 376
c 12
b 0
f 0
dl 0
loc 723
rs 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A previewInfoFileinHTML() 0 15 2
A boxError() 0 2 1
A boxRemark() 0 2 1
A catIcon() 0 2 1
A boxCertOK() 0 2 1
B boxFlexible() 0 37 6
D infoblock() 0 68 20
A pngInjectConsortiumLogo() 0 40 4
C previewCAinHTML() 0 63 15
A displaySize() 0 8 3
A instLevelInfoBoxes() 0 35 3
A getBlobFromDB() 0 30 6
A boxOkay() 0 2 1
A errorPage() 0 6 1
A boxCertError() 0 2 1
A boxCertWarning() 0 2 1
A previewImageinHTML() 0 5 1
B sanityTestResultHTML() 0 28 8
A boxWarning() 0 2 1
B displayName() 0 110 5
A iconData() 0 23 1

How to fix   Complexity   

Complex Class

Complex classes like UIElements often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UIElements, and based on these observations, apply Extract Interface, too.

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