Issues (173)

Security Analysis    13 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting (3)
Response Splitting can be used to send arbitrary responses.
  File Manipulation (6)
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (1)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

web/lib/admin/OptionDisplay.php (1 issue)

Labels
Severity
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
 * We need to display previously set options in various forms. This class covers
29
 * the ways to do that; the generated page content can then be parsed with 
30
 * OptionParser.
31
 * 
32
 * @author Stefan Winter <[email protected]>
33
 */
34
class OptionDisplay extends \core\common\Entity
35
{
36
37
    /**
38
     * stores all the options we are caring about
39
     * 
40
     * @var array
41
     */
42
    private $listOfOptions;
43
44
    /**
45
     * on which level are we operating?
46
     * 
47
     * @var string
48
     */
49
    private $level;
50
51
    /**
52
     * a counter storing how many locations are to be displayed
53
     * 
54
     * @var integer
55
     */
56
    private $allLocationCount;
57
58
    /**
59
     * When "fresh" options are displayed (HTML select/option fields, optionally
60
     * with language, and of varying data types) we want to give each option
61
     * the same prominence and iterate over all options in the list. This
62
     * variable keeps track how many option HTML code we've already sent, so
63
     * that we can iterate correctly.
64
     * 
65
     * Only used inside noPrefillText variant of the optiontext() call
66
     * 
67
     * @var integer
68
     */
69
    private $optionIterator;
70
71
    private $htmlDatatypeTexts;
72
    
73
    private $enumPrettyPrints;
74
    /**
75
     * Which attributes are we talking about?
76
     * @param array  $options the options of interest
77
     * @param string $level   the level on which these options were defined by the user (not applicable for XHR UI, then it is NULL)
78
     */
79
    public function __construct(array $options, string $level = NULL)
80
    {
81
        $this->listOfOptions = $options;
82
        $this->level = $level;
83
        $this->allLocationCount = 0;
84
85
        $this->enumPrettyPrints = [
86
            "ask" => _("Ask User"),
87
            "ask-preagreed" => _("Ask User; T&C Pre-Agreed"),
88
            "always" => _("Always"),
89
            "always-preagreed" => _("Always; T&C Pre-Agreed"),
90
        ];
91
        $openRoamingTail = "";
92
        foreach ($this->enumPrettyPrints as $optionName => $optionDisplay) {
93
            $openRoamingTail .= "<option value='$optionName'>$optionDisplay</option>";
94
        }
95
        
96
        $this->htmlDatatypeTexts = [
97
            \core\Options::TYPECODE_FILE => ["html" => "input type='file'", "tail" => ' size=\'10\''],
98
            \core\Options::TYPECODE_BOOLEAN => ["html" => "input type='checkbox' checked", "tail" => ''],
99
            \core\Options::TYPECODE_INTEGER => ["html" => "input type='number'", "tail" => ''],
100
            \core\Options::TYPECODE_STRING => ["html" => "input type='string'", "tail" => ''],
101
            \core\Options::TYPECODE_ENUM_OPENROAMING => ["html" => "select", "tail" => ">$openRoamingTail</select"],
102
            \core\Options::TYPECODE_TEXT => ["html" => "textarea cols='30' rows='3'", "tail" => '></textarea'],
103
        ];
104
    }
105
106
    /**
107
     * creates a table with all the set options prefilled. Only displays options
108
     * of the category indicated.
109
     * @param string $attributePrefix category of option to display
110
     * @param string $fed             the federation we are in
111
     * @return string HTML code <table>
112
     */
113
    public function prefilledOptionTable(string $attributePrefix, $fed)
114
    {
115
        $retval = "<table id='expandable_$attributePrefix" . "_options'>";
116
117
        $prepopulate = [];
118
        foreach ($this->listOfOptions as $existingAttribute) {
119
            if ($existingAttribute['level'] == $this->level) {
120
                $prepopulate[] = $existingAttribute;
121
            }
122
        }
123
        if (is_array($prepopulate) && ( count($prepopulate) > 0 || $attributePrefix == "device-specific" || $attributePrefix == "eap-specific" )) { // editing... fill with values
124
            $retval .= $this->addOptionEdit($attributePrefix, $prepopulate);
125
        } else {
126
            $retval .= $this->addOptionNew($attributePrefix, $fed);
127
        }
128
        $retval .= "</table>";
129
        return $retval;
130
    }
131
132
    /**
133
     * Displays options for a given option class, in Edit mode.
134
     * 
135
     * @param string $class       the class of options that is to be displayed
136
     * @param array  $prepopulate should an empty set of fillable options be displayed, or do we have existing data to prefill with
137
     * @return string
138
     */
139
    private function addOptionEdit(string $class, array $prepopulate = [])
140
    { // no GET class ? we've been called directly:
141
        // this can mean either a new object (list all options with empty values)
142
        // or that an object is to be edited. In that case, $prepopulated has to
143
        // contain the array of existing variables
144
        // we expect the variable $class to contain the class of options
145
        $retval = "";
146
        $optioninfo = \core\Options::instance();
147
        $loggerInstance = new \core\common\Logging();
148
        
149
        $blackListOnPrefill = "user:fedadmin|managedsp:vlan|managedsp:operatorname|managedsp:guest_vlan";
150
        if (\config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_SILVERBULLET'] == "LOCAL" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_RADIUS'] != "LOCAL") {
151
            $blackListOnPrefill .= "|fed:silverbullet";
152
        }
153
        foreach ($prepopulate as $option) {
154
            if (preg_match("/^$class:/", $option['name']) && !preg_match("/($blackListOnPrefill)/", $option['name'])) {
155
                $optiontypearray = $optioninfo->optionType($option['name']);
156
                $loggerInstance->debug(5, "About to execute optiontext with PREFILL!\n");
157
                $retval .= $this->optiontext([$option['name']], ($optiontypearray["type"] == "file" ? 'ROWID-' . $option['level'] . '-' . $option['row_id'] : $option['value']), $option['lang']);
158
            }
159
        }
160
        return $retval;
161
    }
162
163
    /**
164
     * Find which options to expose to UI and which to hide.
165
     * Not all options defined in the database are (always) displayed. Some have
166
     * custom UI not matching the usual dropdown display, some depend on context
167
     * (e.g. OpenRoaming or not, depending on whether the fed operator wants it
168
     * 
169
     * @param string $class the type of options requested
170
     * @param string $fed   the federation TLD, to determine fed ops preference context
171
     * @return array the list of options to display
172
     */
173
    public static function enumerateOptionsToDisplay($class, $fed, $device='')
174
    {
175
        $optioninfo = \core\Options::instance();
176
        $loggerInstance = new \core\common\Logging();
177
        $list = $optioninfo->availableOptions($class);
178
        // use federation context to delete more options, if the feds don't like
179
        // a particular one
180
        $fedInstance = new \core\Federation($fed);
181
        switch ($class) {
182
            case "general":
183
                unset($list[array_search("general:geo_coordinates", $list)]);
184
                break;
185
            case "user":
186
                unset($list[array_search("user:fedadmin", $list)]);
187
                break;
188
            case "managedsp":
189
                unset($list[array_search("managedsp:vlan", $list)]);
190
                unset($list[array_search("managedsp:guest_vlan", $list)]);
191
                unset($list[array_search("managedsp:operatorname", $list)]);
192
                break;
193
            case "fed":
194
                //normally, we have nothing to hide on that level
195
                // if we are a Managed IdP exclusive deployment, do not display or allow
196
                // to change the "Enable Managed IdP" boolean - it is simply always there
197
                if (\config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_SILVERBULLET'] == "LOCAL" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_RADIUS'] != "LOCAL") {
198
                    unset($list[array_search("fed:silverbullet", $list)]);
199
                }
200
                break;
201
            case "media":
202
                if ($fedInstance->getAttributes("fed:openroaming") == []) {
203
                    // no openroaming here
204
                    unset($list[array_search("media:openroaming", $list)]);
205
                }
206
                break;
207
            case "device-specific":
208
                if ($device != '') {
209
                    $factory = new \core\DeviceFactory($device);
210
                    $dev = $factory->device;
211
                    foreach ($list as $l) {
212
                        $optFlag = $optioninfo->optionType($l)['flag'];
213
                        if ($optFlag == "SPECIFIC") {
214
                            $opt = str_replace('device-specific:', '', $l);
215
                            if (!isset($dev->options['device_options']) || !in_array($opt, $dev->options['device_options'])) {
216
                                $loggerInstance->debug(5, $l, "removing option: ", "\n");
217
                                unset($list[array_search($l, $list)]);
218
                            }
219
                        }
220
                    }
221
                }
222
                 break;
223
            default:
224
                break;
225
        }
226
227
        return $list;
228
    }
229
230
    /**
231
     * Displays options for a given option class, in New mode.
232
     * 
233
     * @param string $class           the class of options that is to be displayed
234
     * @param string $fed             the federation we are in
235
     * @return string
236
     */
237
    private function addOptionNew(string $class, $fed)
238
    {
239
        $retval = "";
240
241
        $list2 = array_values(OptionDisplay::enumerateOptionsToDisplay($class, $fed));
242
243
        // add as many options as there are different option types
244
        $numberOfOptions = count($list2);
245
        for ($this->optionIterator = 0; $this->optionIterator < $numberOfOptions; $this->optionIterator++) {
246
            $retval .= $this->optiontext($list2);
247
        }
248
        return $retval;
249
    }
250
251
    /**
252
     * produce code for a option-specific tooltip
253
     * @param int     $rowid     the number (once during page build) of the option 
254
     *                           that should get the tooltip
255
     * @param string  $input     the option name. Tooltip for it will be displayed
256
     *                           if we have one available.
257
     * @param boolean $isVisible should the tooltip be visible with the option,
258
     *                           or are they both currently hidden?
259
     * @return string
260
     */
261
    private function tooltip($rowid, $input, $isVisible)
262
    {
263
        \core\common\Entity::intoThePotatoes();
264
        $descriptions = [];
265
        if (count(\config\ConfAssistant::CONSORTIUM['ssid']) > 0) {
266
            $descriptions["media:SSID"] = sprintf(_("This attribute can be set if you want to configure an additional SSID besides the default SSIDs for %s. It is almost always a bad idea not to use the default SSIDs. The only exception is if you have premises with an overlap of the radio signal with another %s hotspot. Typical misconceptions about additional SSIDs include: I want to have a local SSID for my own users. It is much better to use the default SSID and separate user groups with VLANs. That approach has two advantages: 1) your users will configure %s properly because it is their everyday SSID; 2) if you use a custom name and advertise this one as extra secure, your users might at some point roam to another place which happens to have the same SSID name. They might then be misled to believe that they are connecting to an extra secure network while they are not."), \config\ConfAssistant::CONSORTIUM['display_name'], \config\ConfAssistant::CONSORTIUM['display_name'], \config\ConfAssistant::CONSORTIUM['display_name']);
267
        }
268
        $descriptions["media:force_proxy"] = sprintf(_("The format of this option is: IPv4|IPv6|hostname:port . Forcing your users through a content filter of your own is a significant invasion of user self-determination. It also has technical issues. Please thoroughly read the discussion at %s before specifying a proxy with this option. This feature is currently experimental and only has an effect in Apple installers."), "https://github.com/GEANT/CAT/issues/96");
269
        $descriptions["managedsp:realmforvlan"] = sprintf(_("If you are also using %s, then your own realm is automatically tagged with the VLAN you choose, there is no need to add it here manually."), \core\ProfileSilverbullet::PRODUCTNAME);
270
        $descriptions["media:openroaming"] = sprintf(_("By opting in to OpenRoaming, you agree to be bound by the %s."), "eduroam Ecosystem Broker OpenRoaming Identity Provider Policy") .
271
                " " .
272
                sprintf(_("Note that your requirement to inform users about the OpenRoaming End User Terms and Conditions is fulfilled when directing your end users to the %s download portal for installer download. Any other means of providing the installers needs to present this information via its own channel."), \config\Master::APPEARANCE['productname']) .
273
                " " .
274
                _("You are also aware that for best technical interoperability, you need to add a DNS entry into the DNS zone of your RADIUS realm.") .
275
                " " .
276
                _("Read the instructions in the wiki.");
277
        \core\common\Entity::outOfThePotatoes();
278
        if (!isset($descriptions[$input])) {
279
            return "";
280
        }
281
        return "<span class='tooltip' id='S$rowid-tooltip-$input' style='display:" . ($isVisible ? "block" : "none") . "' onclick='alert(\"" . $descriptions[$input] . "\")'><img src='../resources/images/icons/question-mark-icon.png" . "'></span>";
282
    }
283
284
    /**
285
     * 
286
     * @param int   $rowid the number (once during page build) of the option 
287
     *                     that should get the tooltip
288
     * @param array $list  elements of the drop-down list
289
     * @return array HTML code and which option is active
290
     * @throws \Exception
291
     */
292
    private function selectElement($rowid, $list)
293
    {
294
        $jsmagic = "onchange='
295
                               if (/#ML#/.test(document.getElementById(\"option-S" . $rowid . "-select\").value)) {
296
                                   document.getElementById(\"S$rowid-input-langselect\").style.display = \"block\";
297
                                   } else {
298
                                   document.getElementById(\"S$rowid-input-langselect\").style.display = \"none\";
299
                                   }";
300
        foreach (array_keys($this->htmlDatatypeTexts) as $key) {
301
            $jsmagic .= "if (/#" . $key . "#/.test(document.getElementById(\"option-S" . $rowid . "-select\").value)) {
302
                                  document.getElementById(\"S$rowid-input-file\").style.display = \"" . ($key == \core\Options::TYPECODE_FILE ? "block" : "none") . "\";
303
                                  document.getElementById(\"S$rowid-input-text\").style.display = \"" . ($key == \core\Options::TYPECODE_TEXT ? "block" : "none") . "\";
304
                                  document.getElementById(\"S$rowid-input-string\").style.display = \"" . ($key == \core\Options::TYPECODE_STRING ? "block" : "none") . "\";
305
                                  document.getElementById(\"S$rowid-input-enum_openroaming\").style.display = \"" . ($key == \core\Options::TYPECODE_ENUM_OPENROAMING ? "block" : "none") . "\";
306
                                  document.getElementById(\"S$rowid-input-boolean\").style.display = \"" . ($key == \core\Options::TYPECODE_BOOLEAN ? "block" : "none") . "\";
307
                                  document.getElementById(\"S$rowid-input-integer\").style.display = \"" . ($key == \core\Options::TYPECODE_INTEGER ? "block" : "none") . "\";
308
                             }
309
                             ";
310
            // hide all tooltips (each is a <span>, and there are no other <span>s)
311
            $jsmagic .= <<< FOO
312
                    var ourtooltips = document.querySelectorAll(&#34;[id^=&#39;S$rowid-tooltip-&#39;]&#34;);
313
                    for (var i=0; i<ourtooltips.length; i++) {
314
                      ourtooltips[i].style.display = "none";
315
                    }
316
                    var optionnamefull = document.getElementById("option-S$rowid-select").value;
317
                    var firstdelimiter = optionnamefull.indexOf("#");
318
                    var optionname = optionnamefull.substring(0,firstdelimiter);
319
                    var tooltipifany = document.getElementById("S$rowid-tooltip-"+optionname);
320
                    if (tooltipifany != null) {
321
                      tooltipifany.style.display = "block";
322
                    }
323
FOO;
324
        }
325
        $jsmagic .= "'";
326
327
        $optioninfo = \core\Options::instance();
328
        $retval = "<span style='display:flex';>";
329
        $iterator = 0;
330
        $tooltips = "";
331
        $uiElements = new UIElements();
332
        $activelisttype = [];
333
        switch (count($list)) {
334
            case 1: // if there is only one option available, don't introduce an artificial drop-down for it
335
                $value = array_shift($list);
336
                $listtype = $optioninfo->optionType($value);
337
                $retval .= $uiElements->displayName($value);
338
                $retval .= "<input type='hidden' name='option[S$rowid]' value='$value#" . $listtype["type"] . "#" . $listtype["flag"] . "#'/>";
339
                $activelisttype = $listtype;
340
                $tooltips = $this->tooltip($rowid, $value, TRUE);
341
                break;
342
            default:
343
                $retval .= "<select id='option-S$rowid-select' name='option[S$rowid]' $jsmagic>";
344
                $sortArray = [];
345
                foreach ($list as $value) {
346
                    $sortArray[] = $uiElements->displayName($value);
347
                }
348
                array_multisort($sortArray, SORT_ASC, $list);
0 ignored issues
show
web\lib\admin\SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

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

348
                array_multisort($sortArray, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
349
                foreach ($list as $value) {
350
                    $listtype = $optioninfo->optionType($value);
351
                    $retval .= "<option id='option-S$rowid-v-$value' value='$value#" . $listtype["type"] . "#" . $listtype["flag"] . "#' ";
352
                    if ($iterator == $this->optionIterator) {
353
                        $retval .= "selected='selected'";
354
                        $activelisttype = $listtype;
355
                        $tooltips .= $this->tooltip($rowid, $value, TRUE);
356
                    } else {
357
                        $tooltips .= $this->tooltip($rowid, $value, FALSE);
358
                    }
359
                    $retval .= ">" . $uiElements->displayName($value) . "</option>";
360
                    $iterator++;
361
                }
362
363
                if (count($activelisttype) == 0) {
364
                    throw new \Exception("We should have found the active list type by now!");
365
                }
366
                $retval .= "</select>";
367
        }
368
        $retval .= $tooltips;
369
        $retval .= "</span>";
370
371
        return ["TEXT" => $retval, "ACTIVE" => $activelisttype];
372
    }
373
374
    /**
375
     * HTML code to display the language selector
376
     * 
377
     * @param int     $rowid       the number (once during page build) of the option 
378
     *                             that should get the tooltip
379
     * @param boolean $makeVisible is the language selector to be made visible?
380
     * @return string
381
     */
382
    private function selectLanguage($rowid, $makeVisible)
383
    {
384
        \core\common\Entity::intoThePotatoes();
385
        $retval = "<select style='display:" . ($makeVisible ? "block" : "none") . "' name='value[S$rowid-lang]' id='S" . $rowid . "-input-langselect'>
386
            <option value='' name='select_language' selected>" . _("select language") . "</option>
387
            <option value='C' name='all_languages'>" . _("default/other languages") . "</option>";
388
        foreach (\config\Master::LANGUAGES as $langindex => $possibleLang) {
389
            $thislang = $possibleLang['display'];
390
            $retval .= "<option value='$langindex' name='$langindex'>$thislang</option>";
391
        }
392
        $retval .= "</select>";
393
        \core\common\Entity::outOfThePotatoes();
394
        return $retval;
395
    }
396
397
    /**
398
     * HTML code for a given option. Marks the matching datatype as visible, all other datatypes hidden
399
     * @param int   $rowid      the number (once during page build) of the option 
400
     *                          that should get the tooltip
401
     * @param array $activetype the active datatype that is to be visible
402
     * @return string
403
     */
404
    private function inputFields($rowid, $activetype)
405
    {
406
        $retval = "";
407
        foreach ($this->htmlDatatypeTexts as $key => $type) {
408
            $retval .= "<" . $type['html'] . " style='display:" . ($activetype['type'] == $key ? "block" : "none") . "' name='value[S$rowid-$key]' id='S" . $rowid . "-input-" . $key . "'" . $type['tail'] . ">";
409
        }
410
        return $retval;
411
    }
412
413
    /**
414
     * HTML code to display a "fresh" option (including type selector and JavaScript to show/hide relevant input fields)
415
     * @param int   $rowid the HTML field base name of the option to be displayed
416
     * @param array $list  the list of option names to include in the type selector
417
     * @return string HTML code
418
     * @throws Exception
419
     */
420
    private function noPrefillText(int $rowid, array $list)
421
    {
422
        // first column: the <select> element with the names of options and their field-toggling JS magic
423
        $selectorInfo = $this->selectElement($rowid, $list);
424
        $retval = "<td>" . $selectorInfo["TEXT"] . "</td>";
425
        // second column: the <select> element for language selection - only visible if the active option is multi-lang
426
        $retval .= "<td>" . $this->selectLanguage($rowid, $selectorInfo['ACTIVE']['flag'] == "ML") . "</td>";
427
        // third column: the actual input fields; the data type of the active option is visible, all others hidden
428
        $retval .= "<td>" . $this->inputFields($rowid, $selectorInfo['ACTIVE']) . "</td>";
429
        return $retval;
430
    }
431
432
    /**
433
     * generates HTML code that displays an already set option.
434
     * 
435
     * @param int    $rowid       the HTML field base name of the option to be displayed
436
     * @param string $optionName  the name of the option to display
437
     * @param string $optionValue the value of the option to display
438
     * @param mixed  $optionLang  the language of the option to display
439
     * @return string HTML code
440
     * @throws Exception
441
     */
442
    private function prefillText(int $rowid, string $optionName, string $optionValue, $optionLang)
443
    {
444
        \core\common\Entity::intoThePotatoes();
445
        $retval = "";
446
        $optioninfo = \core\Options::instance();
447
        $loggerInstance = new \core\common\Logging();
448
        $loggerInstance->debug(5, "Executed with PREFILL $optionValue!\n");
449
        $retval .= "<td>";
450
        $uiElements = new UIElements();
451
        $listtype = $optioninfo->optionType($optionName);
452
        $retval .= "<span style='display:flex;'>" . $uiElements->displayName($optionName);
453
        $retval .= $this->tooltip($rowid, $optionName, TRUE) . "</span>";
454
        $retval .= "<input type='hidden' id='option-S$rowid-select' name='option[S$rowid]' value='$optionName#" . $listtype["type"] . "#" . $listtype["flag"] . "#' ></td>";
455
456
        // language tag if any
457
        $retval .= "<td>";
458
        if ($listtype["flag"] == "ML") {
459
460
            $language = "(" . strtoupper($optionLang) . ")";
461
            if ($optionLang == 'C') {
462
                $language = _("(default/other languages)");
463
            }
464
            $retval .= $language;
465
            $retval .= "<input type='hidden' name='value[S$rowid-lang]' id='S" . $rowid . "-input-langselect' value='" . $optionLang . "' style='display:block'>";
466
        }
467
        $retval .= "</td>";
468
// attribute content
469
        $retval .= "<td>";
470
        $displayedVariant = "";
471
        switch ($listtype["type"]) {
472
            case \core\Options::TYPECODE_COORDINATES:
473
                $this->allLocationCount = $this->allLocationCount + 1;
474
                // display of the locations varies by map provider
475
                $classname = "\web\lib\admin\Map" . \config\ConfAssistant::MAPPROVIDER['PROVIDER'];
476
                $link = $classname::optionListDisplayCode($optionValue, $this->allLocationCount);
477
                $retval .= "<input readonly style='display:none' type='text' name='value[S$rowid-" . \core\Options::TYPECODE_TEXT . "]' id='S$rowid-input-text' value='$optionValue'>$link";
478
                break;
479
            case \core\Options::TYPECODE_FILE:
480
                $retval .= "<input readonly type='text' name='value[S$rowid-" . \core\Options::TYPECODE_STRING . "]' id='S" . $rowid . "-input-string' style='display:none' value='" . urlencode($optionValue) . "'>";
481
                $uiElements = new UIElements();
482
                switch ($optionName) {
483
                    case "eap:ca_file":
484
                    // fall-through intentional: display both types the same way
485
                    case "fed:minted_ca_file":
486
                        $retval .= $uiElements->previewCAinHTML($optionValue);
487
                        break;
488
                    case "general:logo_file":
489
                    // fall-through intentional: display both types the same way
490
                    case "fed:logo_file":
491
                        $retval .= $uiElements->previewImageinHTML($optionValue);
492
                        break;
493
                    case "support:info_file":
494
                        $retval .= $uiElements->previewInfoFileinHTML($optionValue);
495
                        break;
496
                    default:
497
                        $retval .= _("file content");
498
                }
499
                break;
500
            case \core\Options::TYPECODE_ENUM_OPENROAMING: // is a string after all
501
                $displayedVariant = $this->enumPrettyPrints[$optionValue];
502
                $retval .= "<strong>$displayedVariant</strong><input type='hidden' name='value[S$rowid-" . $listtype['type'] . "]' id='S" . $rowid . "-input-" . $listtype["type"] . "' value=\"" . htmlspecialchars($optionValue) . "\" style='display:block'>";
503
                break;
504
            case \core\Options::TYPECODE_STRING:
505
            // fall-thorugh is intentional; mostly identical HTML code for the three types
506
            case \core\Options::TYPECODE_INTEGER:
507
            // fall-thorugh is intentional; mostly identical HTML code for the three types
508
            case \core\Options::TYPECODE_TEXT:
509
                $displayedVariant = $optionValue; // for all three types, value tag and actual display are identical
510
                $retval .= "<strong>$displayedVariant</strong><input type='hidden' name='value[S$rowid-" . $listtype['type'] . "]' id='S" . $rowid . "-input-" . $listtype["type"] . "' value=\"" . htmlspecialchars($optionValue) . "\" style='display:block'>";
511
                break;
512
            case \core\Options::TYPECODE_BOOLEAN:
513
                $displayedVariant = ($optionValue == "on" ? _("on") : _("off"));
514
                $retval .= "<strong>$displayedVariant</strong><input type='hidden' name='value[S$rowid-" . $listtype['type'] . "]' id='S" . $rowid . "-input-" . $listtype["type"] . "' value=\"" . htmlspecialchars($optionValue) . "\" style='display:block'>";
515
                break;
516
            default:
517
                // this should never happen!
518
                throw new Exception("Internal Error: unknown attribute type $listtype!");
519
        }
520
        $retval .= "</td>";
521
        \core\common\Entity::outOfThePotatoes();
522
        return $retval;
523
    }
524
525
    /**
526
     * Displays a container for options. Either with prefilled data or empty; if
527
     * empty then has HTML <input> tags with clever javaScript to allow selection
528
     * of different option names and types
529
     * @param array  $list         options which should be displayed; can be only exactly one if existing option, or multiple if new option type
530
     * @param string $prefillValue for an existing option, it's value to be displayed
531
     * @param string $prefillLang  for an existing option, the language of the value to be displayed
532
     * @return string HTML code <tr>
533
     * @throws Exception
534
     */
535
    public function optiontext(array $list, string $prefillValue = NULL, string $prefillLang = NULL)
536
    {
537
        $rowid = mt_rand();
538
539
        $retval = "<tr id='option-S$rowid' style='vertical-align:top'>";
540
541
        $item = "MULTIPLE";
542
        if ($prefillValue === NULL) {
543
            $retval .= $this->noPrefillText($rowid, $list);
544
        }
545
546
        if ($prefillValue !== NULL) {
547
            // prefill is always only called with a list with exactly one element.
548
            // if we see anything else here, get excited.
549
            if (count($list) != 1) {
550
                throw new Exception("Optiontext prefilled display only can work with exactly one option!");
551
            }
552
            $item = array_pop($list);
553
            $retval .= $this->prefillText($rowid, $item, $prefillValue, $prefillLang);
554
        }
555
        $retval .= "
556
557
       <td>
558
          <button type='button' class='delete' onclick='";
559
        if ($prefillValue !== NULL && $item == "general:geo_coordinates") {
560
            $funcname = "Map" . \config\ConfAssistant::MAPPROVIDER['PROVIDER'] . 'DeleteCoord';
561
            $retval .= 'if (typeof ' . $funcname . ' === "function") { ' . $funcname . '(' . $this->allLocationCount . '); } ';
562
        }
563
        $retval .= 'deleteOption("option-S' . $rowid . '")';
564
        $retval .= "'>-</button>
565
       </td>
566
    </tr>";
567
        return $retval;
568
    }
569
}
570