Passed
Push — master ( f82f16...cca347 )
by Stefan
04:55
created

OptionDisplay::selectLanguage()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 4
nop 2
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
namespace web\lib\admin;
13
14
use Exception;
15
16
require_once(dirname(dirname(dirname(dirname(__FILE__)))) . "/config/_config.php");
17
18
/**
19
 * We need to display previously set options in various forms. This class covers
20
 * the ways to do that; the generated page content can then be parsed with 
21
 * OptionParser.
22
 * 
23
 * @author Stefan Winter <[email protected]>
24
 */
25
class OptionDisplay {
26
27
    /**
28
     * stores all the options we are caring about
29
     * 
30
     * @var array
31
     */
32
    private $listOfOptions;
33
34
    /**
35
     * on which level are we operating?
36
     * 
37
     * @var string
38
     */
39
    private $level;
40
41
    /**
42
     * a counter storing how many locations are to be displayed
43
     * 
44
     * @var int
45
     */
46
    private $allLocationCount;
47
48
    /**
49
     * When "fresh" options are displayed (HTML select/otion fields, optionally
50
     * with language, and of varying data types) we want to give each option
51
     * the same prominence and iterate over all options in the list. This
52
     * variable keeps track how many option HTML code we've already sent, so
53
     * that we can iterate correctly.
54
     * 
55
     * Only used inside noPrefillText variant of the optiontext() call
56
     * 
57
     * @var int
58
     */
59
    private $optionIterator;
60
61
    /**
62
     * Which attributes are we talking about?
63
     * @param array $options the options of interest
64
     * @param string $level the level on which these options were defined by the user
65
     */
66
    public function __construct(array $options, string $level) {
67
        $this->listOfOptions = $options;
68
        $this->level = $level;
69
        $this->allLocationCount = 0;
70
    }
71
72
    /**
73
     * creates a table with all the set options prefilled. Only displays options
74
     * of the category indicated.
75
     * @param string $attributePrefix category of option to display
76
     * @return string HTML code <table>
77
     */
78
    public function prefilledOptionTable(string $attributePrefix) {
79
        $retval = "<table id='expandable_$attributePrefix" . "_options'>";
80
81
        $prepopulate = [];
82
        foreach ($this->listOfOptions as $existingAttribute) {
83
            if ($existingAttribute['level'] == $this->level) {
84
                $prepopulate[] = $existingAttribute;
85
            }
86
        }
87
        $retval .= $this->addOption($attributePrefix, $prepopulate);
88
        $retval .= "</table>";
89
        return $retval;
90
    }
91
92
    /**
93
     * Displays options for a given option class.
94
     * 
95
     * @param string $class the class of options that is to be displayed
96
     * @param array $prepopulate should an empty set of fillable options be displayed, or do we have existing data to prefill with
97
     */
98
    private function addOption(string $class, array $prepopulate = []) { // no GET class ? we've been called directly:
99
        // this can mean either a new object (list all options with empty values)
100
        // or that an object is to be edited. In that case, $prepopulated has to
101
        // contain the array of existing variables
102
        // we expect the variable $class to contain the class of options
103
        $retval = "";
104
105
        $optioninfo = \core\Options::instance();
106
107
        if (is_array($prepopulate) && ( count($prepopulate) > 1 || $class == "device-specific" || $class == "eap-specific")) { // editing... fill with values
108
            foreach ($prepopulate as $option) {
109
                if (preg_match("/$class:/", $option['name']) && !preg_match("/(user:fedadmin)/", $option['name'])) {
110
                    $optiontypearray = $optioninfo->optionType($option['name']);
111
                    $loggerInstance = new \core\common\Logging();
112
                    $loggerInstance->debug(5, "About to execute optiontext with PREFILL!\n");
113
                    $retval .= $this->optiontext([$option['name']], ($optiontypearray["type"] == "file" ? 'ROWID-' . $option['level'] . '-' . $option['row'] : $option['value']), $option['lang']);
114
                }
115
            }
116
        } else { // not editing exist, this in new: add empty list
117
            $list = $optioninfo->availableOptions($class);
118
            switch ($class) {
119
                case "general":
120
                    $blacklistItem = array_search("general:geo_coordinates", $list);
121
                    break;
122
                case "profile":
123
                    $blacklistItem = array_search("profile:QR-user", $list);
124
                    break;
125
                case "user":
126
                    $blacklistItem = array_search("user:fedadmin", $list);
127
                    break;
128
                default:
129
                    $blacklistItem = FALSE;
130
            }
131
            if ($blacklistItem !== FALSE) {
132
                unset($list[$blacklistItem]);
133
                $list = array_values($list);
134
            }
135
136
            // add as many options as there are different option types
137
            $numberOfOptions = count($list);
138
            for ($this->optionIterator = 0; $this->optionIterator < $numberOfOptions; $this->optionIterator++) {
139
                $retval .= $this->optiontext($list);
140
            }
141
        }
142
        return $retval;
143
    }
144
145
    private function selectElement($rowid, $list, $jsmagic) {
146
        $optioninfo = \core\Options::instance();
147
        $retval = "<select id='option-S$rowid-select' name='option[S$rowid]' $jsmagic>";
148
        $iterator = 0;
149
        $uiElements = new UIElements();
150
        $activelisttype = [];
151
        foreach ($list as $value) {
152
            $listtype = $optioninfo->optionType($value);
153
            $retval .= "<option id='option-S$rowid-v-$value' value='$value#" . $listtype["type"] . "#" . $listtype["flag"] . "#' ";
154
            if ($iterator == $this->optionIterator) {
155
                $retval .= "selected='selected'";
156
                $activelisttype = $listtype;
157
            }
158
            $retval .= ">" . $uiElements->displayName($value) . "</option>";
159
            $iterator++;
160
        }
161
        if (count($activelisttype) == 0) {
162
            throw new \Exception("We should have found the active list type by now!");
163
        }
164
        $retval .= "</select>";
165
        return ["TEXT" => $retval, "ACTIVE" => $activelisttype];
166
    }
167
168
    private function selectLanguage($rowid, $makeVisible) {
169
        $retval = "<select style='display:" . ($makeVisible ? "block" : "none") . "' name='value[S$rowid-lang]' id='S" . $rowid . "-input-langselect'>
170
            <option value='' name='select_language' selected>" . _("select language") . "</option>
171
            <option value='C' name='all_languages'>" . _("default/other languages") . "</option>";
172
        foreach (CONFIG['LANGUAGES'] as $langindex => $possibleLang) {
173
            $thislang = $possibleLang['display'];
174
            $retval .= "<option value='$langindex' name='$langindex'>$thislang</option>";
175
        }
176
        $retval .= "</select>";
177
        return $retval;
178
    }
179
    
180
    /**
181
     * HTML code to display a "fresh" option (including type selector and JavaScript to show/hide relevant input fields)
182
     * @param int $rowid the HTML field base name of the option to be displayed
183
     * @param array $list the list of option names to include in the type selector
184
     * @return string HTML code
185
     * @throws Exception
186
     */
187
    private function noPrefillText(int $rowid, array $list) {
188
        $jsmagic = "onchange='
189
                               if (/#ML#/.test(document.getElementById(\"option-S" . $rowid . "-select\").value)) {
190
                                   document.getElementById(\"S$rowid-input-langselect\").style.display = \"block\";
191
                                   } else {
192
                                   document.getElementById(\"S$rowid-input-langselect\").style.display = \"none\";
193
                                   }";
194
        $dataTypes = ["file", "text", "string", "boolean", "integer"];
195
        foreach ($dataTypes as $oneDataType) {
196
            // TODO make this a $jsmagic .= after the update of cat-pilot
197
            $jsmagic .= "if (/#$oneDataType#/.test(document.getElementById(\"option-S" . $rowid . "-select\").value)) {
198
                                  document.getElementById(\"S$rowid-input-file\").style.display = \"" . ($oneDataType == "file" ? "block" : "none") . "\";
199
                                  document.getElementById(\"S$rowid-input-text\").style.display = \"" . ($oneDataType == "text" ? "block" : "none") . "\";
200
                                  document.getElementById(\"S$rowid-input-string\").style.display = \"" . ($oneDataType == "string" ? "block" : "none") . "\";
201
                                  document.getElementById(\"S$rowid-input-boolean\").style.display = \"" . ($oneDataType == "boolean" ? "block" : "none") . "\";
202
                                  document.getElementById(\"S$rowid-input-integer\").style.display = \"" . ($oneDataType == "integer" ? "block" : "none") . "\";
203
                             }
204
                             ";
205
        }
206
        $jsmagic .= "'";
207
        // first column: the <select> element with the names of options and their field-toggling JS magic
208
        $selectorInfo = $this->selectElement($rowid, $list, $jsmagic);
209
        $retval = "<td>" . $selectorInfo["TEXT"] . "</td>";
210
        // second column: the <select> element for language selection - only visible if the active option is multi-lang
211
        $retval .= "<td>" . $this->selectLanguage($rowid, $selectorInfo['ACTIVE']['flag'] == "ML") . "</td>";
212
        // third column: the actual input fields; the data type of the active option is visible, all others hidden
213
        $retval .= "<td>
214
            <input type='text'     style='display:" . ($selectorInfo['ACTIVE']['type'] == "string" ? "block" : "none") . "' name='value[S$rowid-0]'  id='S" . $rowid . "-input-string'>
215
            <textarea cols='30' rows='3'     style='display:" . ($selectorInfo['ACTIVE']["type"] == "text" ? "block" : "none") . "' name='value[S$rowid-1]'  id='S" . $rowid . "-input-text'></textarea>
216
            <input type='file'     style='display:" . ($selectorInfo['ACTIVE']['type'] == "file" ? "block" : "none") . "' name='value[S$rowid-2]'  id='S" . $rowid . "-input-file' size='10'>
217
            <input type='checkbox' style='display:" . ($selectorInfo['ACTIVE']['type'] == "boolean" ? "block" : "none") . "' name='value[S$rowid-3]'  id='S" . $rowid . "-input-boolean'>
218
            <input type='number' style='display:" . ($selectorInfo['ACTIVE']['type'] == "integer" ? "block" : "none") . "' name='value[S$rowid-4]'  id='S" . $rowid . "-input-integer'>";
219
        $retval .= "</td>";
220
221
        return $retval;
222
    }
223
224
    const TYPECODE_STRING = 0;
225
    const TYPECODE_INTEGER = 4;
226
    const TYPECODE_TEXT = 1;
227
    const TYPECODE_BOOLEAN = 3;
228
229
    /**
230
     * generates HTML code that displays an already set option.
231
     * 
232
     * @param int $rowid the HTML field base name of the option to be displayed
233
     * @param string $optionName the name of the option to display
234
     * @param string $optionValue the value of the option to display
235
     * @param mixed $optionLang the language of the option to display
236
     * @return string HTML code
237
     * @throws Exception
238
     */
239
    private function prefillText(int $rowid, string $optionName, string $optionValue, $optionLang) {
240
        $retval = "";
241
        $optioninfo = \core\Options::instance();
242
        $loggerInstance = new \core\common\Logging();
243
        $loggerInstance->debug(5, "Executed with PREFILL $optionValue!\n");
244
        $retval .= "<td>";
245
        $uiElements = new UIElements();
246
        $listtype = $optioninfo->optionType($optionName);
247
        $retval .= $uiElements->displayName($optionName);
248
        $retval .= $uiElements->tooltip($optionName);
249
        $retval .= "<input type='hidden' id='option-S$rowid-select' name='option[S$rowid]' value='$optionName#" . $listtype["type"] . "#" . $listtype["flag"] . "#' ></td>";
250
251
        // language tag if any
252
        $retval .= "<td>";
253
        if ($listtype["flag"] == "ML") {
254
255
            $language = "(" . strtoupper($optionLang) . ")";
256
            if ($optionLang == 'C') {
257
                $language = _("(default/other languages)");
258
            }
259
            $retval .= $language;
260
            $retval .= "<input type='hidden' name='value[S$rowid-lang]' id='S" . $rowid . "-input-langselect' value='" . $optionLang . "' style='display:block'>";
261
        }
262
        $retval .= "</td>";
263
// attribute content
264
        $retval .= "<td>";
265
        $intCode = -1;
266
        $displayedVariant = "";
267
        switch ($listtype["type"]) {
268
            case "coordinates":
269
                $this->allLocationCount = $this->allLocationCount + 1;
270
                $link = "<button id='location_b_" . $this->allLocationCount . "' class='location_button'>" . _("Click to see location") . " $this->allLocationCount</button>";
271
                $retval .= "<input readonly style='display:none' type='text' name='value[S$rowid-" . self::TYPECODE_TEXT . "]' id='S$rowid-input-text' value='$optionValue'>$link";
272
                break;
273
            case "file":
274
                $retval .= "<input readonly type='text' name='value[S$rowid-1]' id='S" . $rowid . "-input-string' style='display:none' value='" . urlencode($optionValue) . "'>";
275
                $uiElements = new UIElements();
276
                switch ($optionName) {
277
                    case "eap:ca_file":
278
                        $retval .= $uiElements->previewCAinHTML($optionValue);
279
                        break;
280
                    case "general:logo_file":
281
                    case "fed:logo_file":
282
                        $retval .= $uiElements->previewImageinHTML($optionValue);
283
                        break;
284
                    case "support:info_file":
285
                        $retval .= $uiElements->previewInfoFileinHTML($optionValue);
286
                        break;
287
                    default:
288
                        $retval .= _("file content");
289
                }
290
                break;
291
            case "string":
292
                if ($intCode == -1) {
293
                    $intCode = self::TYPECODE_STRING;
294
                }
295
            // fall-thorugh is intentional; mostly identical HTML code for the three types
296
            case "integer":
297
                if ($intCode == -1) {
298
                    $intCode = self::TYPECODE_INTEGER;
299
                }
300
            // fall-thorugh is intentional; mostly identical HTML code for the three types
301
            case "text":
302
                if ($intCode == -1) {
303
                    $intCode = self::TYPECODE_TEXT;
304
                }
305
                $displayedVariant = $optionValue; // for all three types, value tag and actual display are identical
306
            case "boolean":
307
                if ($intCode == -1) {
308
                    $intCode = self::TYPECODE_BOOLEAN;
309
                }
310
                if ($displayedVariant == "") { // a fall-through has set this before
311
                    $displayedVariant = _("off");
312
                    if ($optionValue == "on") {
313
                        /// Device assessment is "on"
314
                        $displayedVariant = _("on");
315
                    }
316
                }
317
                $retval .= "<strong>$displayedVariant</strong><input type='hidden' name='value[S$rowid-$intCode]' id='S" . $rowid . "-input-" . $listtype["type"] . "' value=\"" . htmlspecialchars($optionValue) . "\" style='display:block'>";
318
                break;
319
            default:
320
                // this should never happen!
321
                throw new Exception("Internal Error: unknown attribute type $listtype!");
322
        }
323
        $retval .= "</td>";
324
        return $retval;
325
    }
326
327
    /**
328
     * Displays a container for options. Either with prefilled data or empty; if
329
     * empty then has HTML <input> tags with clever javaScript to allow selection
330
     * of different option names and types
331
     * @param array $list options which should be displayed; can be only exactly one if existing option, or multiple if new option type
332
     * @param string $prefillValue for an existing option, it's value to be displayed
333
     * @param string $prefillLang for an existing option, the language of the value to be displayed
334
     * @return string HTML code <tr>
335
     */
336
    public function optiontext(array $list, string $prefillValue = NULL, string $prefillLang = NULL) {
337
        $rowid = mt_rand();
338
339
        $retval = "<tr id='option-S$rowid' style='vertical-align:top'>";
340
341
        $item = "MULTIPLE";
342
        if ($prefillValue === NULL) {
343
            $retval .= $this->noPrefillText($rowid, $list);
344
        }
345
346
        if ($prefillValue !== NULL) {
347
            // prefill is always only called with a list with exactly one element.
348
            // if we see anything else here, get excited.
349
            if (count($list) != 1) {
350
                throw new Exception("Optiontext prefilled display only can work with exactly one option!");
351
            }
352
            $item = array_pop($list);
353
            $retval .= $this->prefillText($rowid, $item, $prefillValue, $prefillLang);
354
        }
355
        $retval .= "
356
357
       <td>
358
          <button type='button' class='delete' onclick='deleteOption(" . ( $prefillValue !== NULL && $item == "general:geo_coordinates" ? $this->allLocationCount : 0 ) . ",\"option-S" . $rowid . "\")'>-</button>
359
       </td>
360
    </tr>";
361
        return $retval;
362
    }
363
364
}
365