| @@ -2,57 +2,82 @@ | ||
| 2 | 2 | |
| 3 | 3 | namespace FluentForm\App\Modules\Form; |
| 4 | 4 | |
| 5 | 5 | use FluentForm\Framework\Helpers\ArrayHelper; |
| 6 | -use WpFluent\Exception; | |
| 7 | 6 | |
| 8 | 7 | class FormDataParser |
| 9 | 8 | { |
| 10 | - protected static $data = null; | |
| 9 | + protected static $data = null; | |
| 10 | + protected static $submissionId = null; | |
| 11 | 11 | |
| 12 | - public static function parseFormEntries($entries, $form, $fields = null) | |
| 13 | - { | |
| 14 | - $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form); | |
| 12 | + public static function parseFormEntries($entries, $form, $fields = null) | |
| 13 | + { | |
| 14 | + $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form); | |
| 15 | 15 | |
| 16 | - foreach ($entries as $entry) { | |
| 17 | - static::parseFormEntry($entry, $form, $fields); | |
| 16 | + foreach ($entries as $entry) { | |
| 17 | + static::parseFormEntry($entry, $form, $fields); | |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | return $entries; |
| 21 | - } | |
| 21 | + } | |
| 22 | 22 | |
| 23 | - public static function parseFormEntry($entry, $form, $fields = null, $isHtml = false) | |
| 24 | - { | |
| 25 | - $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form); | |
| 23 | + public static function parseFormEntry($entry, $form, $fields = null, $isHtml = false) | |
| 24 | + { | |
| 25 | + $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form); | |
| 26 | 26 | |
| 27 | - $entry->user_inputs = static::parseData( | |
| 28 | - json_decode($entry->response), $fields, $form->id, $isHtml | |
| 29 | - ); | |
| 27 | + $entry->user_inputs = static::parseData( | |
| 28 | + json_decode($entry->response), | |
| 29 | + $fields, | |
| 30 | + $form->id, | |
| 31 | + $isHtml | |
| 32 | + ); | |
| 30 | 33 | |
| 31 | 34 | return $entry; |
| 32 | - } | |
| 35 | + } | |
| 33 | 36 | |
| 34 | - public static function parseFormSubmission($submission, $form, $fields, $isHtml = false) | |
| 35 | - { | |
| 36 | - if (is_null(static::$data)) { | |
| 37 | - static::$data = static::parseData( | |
| 38 | - json_decode($submission->response), $fields, $form->id, $isHtml | |
| 39 | - ); | |
| 40 | - } | |
| 41 | - | |
| 42 | - $submission->user_inputs = static::$data; | |
| 37 | + public static function parseFormSubmission($submission, $form, $fields, $isHtml = false) | |
| 38 | + { | |
| 39 | + // Sometimes submission will change inside loop. So we need to parse submission data for new one | |
| 40 | + $newSubmission = $submission->id != static::$submissionId; | |
| 43 | 41 | |
| 42 | + if (is_null(static::$data) || $newSubmission) { | |
| 43 | + static::$data = static::parseData( | |
| 44 | + json_decode($submission->response), | |
| 45 | + $fields, | |
| 46 | + $form->id, | |
| 47 | + $isHtml | |
| 48 | + ); | |
| 49 | + static::$submissionId = $submission->id; | |
| 50 | + } | |
| 51 | + | |
| 52 | + $submission->user_inputs = static::$data; | |
| 53 | + | |
| 44 | 54 | return $submission; |
| 45 | - } | |
| 55 | + } | |
| 46 | 56 | |
| 47 | - public static function parseData( $response, $fields, $formId, $isHtml = false ) | |
| 48 | - { | |
| 49 | - $trans = []; | |
| 57 | + public static function parseData($response, $fields, $formId, $isHtml = false) | |
| 58 | + { | |
| 59 | + $trans = []; | |
| 50 | 60 | foreach ($fields as $field_key => $field) { |
| 51 | 61 | if (isset($response->{$field_key})) { |
| 62 | + $value = $response->{$field_key}; | |
| 63 | + | |
| 64 | + $value = apply_filters_deprecated( | |
| 65 | + 'fluentform_response_render_' . $field['element'], | |
| 66 | + [ | |
| 67 | + $value, | |
| 68 | + $field, | |
| 69 | + $formId, | |
| 70 | + $isHtml | |
| 71 | + ], | |
| 72 | + FLUENTFORM_FRAMEWORK_UPGRADE, | |
| 73 | + 'fluentform/response_render_' . $field['element'], | |
| 74 | + 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element'] | |
| 75 | + ); | |
| 76 | + | |
| 52 | 77 | $value = apply_filters( |
| 53 | - 'fluentform_response_render_'.$field['element'], | |
| 54 | - $response->{$field_key}, | |
| 78 | + 'fluentform/response_render_' . $field['element'], | |
| 79 | + $value, | |
| 55 | 80 | $field, |
| 56 | 81 | $formId, |
| 57 | 82 | $isHtml |
| 58 | 83 | ); |
| @@ -62,39 +87,46 @@ | ||
| 62 | 87 | } |
| 63 | 88 | } |
| 64 | 89 | |
| 65 | 90 | return $trans; |
| 66 | - } | |
| 91 | + } | |
| 67 | 92 | |
| 68 | - public static function formatValue($value) | |
| 69 | - { | |
| 70 | - if (is_array($value) || is_object($value)) { | |
| 93 | + public static function formatValue($value) | |
| 94 | + { | |
| 95 | + if (is_array($value) || is_object($value)) { | |
| 71 | 96 | return fluentImplodeRecursive(', ', array_filter(array_values((array) $value))); |
| 72 | 97 | } |
| 73 | 98 | |
| 74 | 99 | return $value; |
| 75 | - } | |
| 100 | + } | |
| 76 | 101 | |
| 77 | - public static function formatFileValues($values, $isHtml) | |
| 102 | + public static function formatFileValues($values, $isHtml, $form_id = null) | |
| 78 | 103 | { |
| 79 | - if(!$values) { | |
| 104 | + if (!$values) { | |
| 80 | 105 | return $values; |
| 81 | 106 | } |
| 82 | 107 | |
| 83 | - if(is_string($values)) { | |
| 108 | + if (is_string($values)) { | |
| 84 | 109 | return $values; |
| 85 | 110 | } |
| 86 | 111 | |
| 87 | - if(!$isHtml) { | |
| 112 | + if (!$isHtml) { | |
| 88 | 113 | return fluentImplodeRecursive(', ', array_filter(array_values((array) $values))); |
| 89 | 114 | } |
| 115 | + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) { | |
| 116 | + return ''; | |
| 117 | + } | |
| 90 | 118 | |
| 91 | 119 | $html = '<ul class="ff_entry_list">'; |
| 92 | 120 | foreach ($values as $value) { |
| 93 | - if(!$value) { | |
| 121 | + if (!$value) { | |
| 94 | 122 | continue; |
| 95 | 123 | } |
| 96 | - $html .= '<li><a href="'.$value.'" target="_blank">'.basename($value).'</a></li>'; | |
| 124 | + // SECURITY (FINDING-23): escape the submitted upload value. It reaches this HTML sink | |
| 125 | + // via an unauthenticated submission and is only sanitize_text_field'd (keeps " and :), | |
| 126 | + // so a javascript: URL or an " onmouseover=" attribute breakout would otherwise render | |
| 127 | + // in the admin entry view, notification email and PDF. esc_url enforces a safe scheme. | |
| 128 | + $html .= '<li><a href="' . esc_url($value) . '" target="_blank">' . esc_html(basename($value)) . '</a></li>'; | |
| 97 | 129 | } |
| 98 | 130 | |
| 99 | 131 | $html .= '</ul>'; |
| 100 | 132 | return $html; |
| @@ -99,36 +131,43 @@ | ||
| 99 | 131 | $html .= '</ul>'; |
| 100 | 132 | return $html; |
| 101 | 133 | } |
| 102 | 134 | |
| 103 | - public static function formatImageValues($values, $isHtml) | |
| 135 | + public static function formatImageValues($values, $isHtml, $form_id = null) | |
| 104 | 136 | { |
| 105 | - if(!$values) { | |
| 137 | + if (!$values) { | |
| 106 | 138 | return $values; |
| 107 | 139 | } |
| 108 | 140 | |
| 109 | - if(is_string($values)) { | |
| 141 | + if (is_string($values)) { | |
| 110 | 142 | return $values; |
| 111 | 143 | } |
| 144 | + | |
| 145 | + $isHtml = apply_filters('fluentform/render_field_as_html', $isHtml, $values, $form_id); | |
| 112 | 146 | |
| 113 | - if(!$isHtml) { | |
| 147 | + | |
| 148 | + if (!$isHtml) { | |
| 114 | 149 | return fluentImplodeRecursive(', ', array_filter(array_values((array) $values))); |
| 115 | 150 | } |
| 116 | - | |
| 117 | - if(count($values) == 1) { | |
| 151 | + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) { | |
| 152 | + return ''; | |
| 153 | + } | |
| 154 | + if (1 == count($values)) { | |
| 118 | 155 | $value = $values[0]; |
| 119 | - if(!$value) { | |
| 156 | + if (!$value) { | |
| 120 | 157 | return ''; |
| 121 | 158 | } |
| 122 | - return '<a href="'.$value.'" target="_blank"><img style="max-width:180px" src="'.$value.'" /></a>'; | |
| 159 | + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues). | |
| 160 | + return '<a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a>'; | |
| 123 | 161 | } |
| 124 | 162 | |
| 125 | 163 | $html = '<ul class="ff_entry_list ff_entry_images">'; |
| 126 | 164 | foreach ($values as $value) { |
| 127 | - if(!$value) { | |
| 165 | + if (!$value) { | |
| 128 | 166 | continue; |
| 129 | 167 | } |
| 130 | - $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="'.$value.'" target="_blank"><img style="max-width:180px" src="'.$value.'" /></a></li>'; | |
| 168 | + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues). | |
| 169 | + $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a></li>'; | |
| 131 | 170 | } |
| 132 | 171 | |
| 133 | 172 | $html .= '</ul>'; |
| 134 | 173 | return $html; |
| @@ -133,115 +172,114 @@ | ||
| 133 | 172 | $html .= '</ul>'; |
| 134 | 173 | return $html; |
| 135 | 174 | } |
| 136 | 175 | |
| 137 | - public static function formatRepeatFieldValue($value, $field, $form_id) { | |
| 176 | + public static function formatRepeatFieldValue($value, $field, $form_id) | |
| 177 | + { | |
| 138 | 178 | |
| 139 | - if(defined('FLUENTFORM_RENDERING_ENTRIES')) { | |
| 140 | - return __('....', 'fluentform'); | |
| 141 | - } | |
| 179 | + if (defined('FLUENTFORM_RENDERING_ENTRIES')) { | |
| 180 | + return __('....', 'fluentform'); | |
| 181 | + } | |
| 142 | 182 | |
| 143 | - if(is_string($value)) { | |
| 144 | - return $value; | |
| 145 | - } | |
| 183 | + if (is_string($value)) { | |
| 184 | + return $value; | |
| 185 | + } | |
| 146 | 186 | |
| 147 | - try { | |
| 148 | - $repeatColumns = ArrayHelper::get($field, 'raw.fields'); | |
| 149 | - $rows = count($value[0]); | |
| 150 | - $columns = count($value); | |
| 187 | + try { | |
| 188 | + $repeatColumns = ArrayHelper::get($field, 'raw.fields'); | |
| 189 | + $rows = count($value[0]); | |
| 190 | + $columns = count($value); | |
| 151 | 191 | |
| 152 | - ob_start(); | |
| 153 | - if($repeatColumns) { | |
| 192 | + ob_start(); | |
| 193 | + if ($repeatColumns) { | |
| 154 | 194 | ?> |
| 155 | 195 | <div class="ff_entry_table_wrapper"> |
| 156 | 196 | <table class="ff_entry_table_field ff-table"> |
| 157 | 197 | <thead> |
| 158 | - <tr> | |
| 159 | - <?php foreach ($repeatColumns as $repeatColumn) : ?> | |
| 160 | - <th><?php echo ArrayHelper::get($repeatColumn, 'settings.label'); ?></th> | |
| 161 | - <?php endforeach; ?> | |
| 162 | - </tr> | |
| 198 | + <tr> | |
| 199 | + <?php foreach ($repeatColumns as $repeatColumn) : ?> | |
| 200 | + <th><?php echo fluentform_sanitize_html(ArrayHelper::get($repeatColumn, 'settings.label')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?> | |
| 201 | + </th> | |
| 202 | + <?php endforeach; ?> | |
| 203 | + </tr> | |
| 163 | 204 | </thead> |
| 164 | 205 | |
| 165 | 206 | <tbody> |
| 166 | - <?php for ($i = 0; $i < $rows; $i++) : ?> | |
| 207 | + <?php for ($i = 0; $i < $rows; $i++) : ?> | |
| 167 | 208 | <tr> |
| 168 | 209 | <?php for ($j = 0; $j < $columns; $j++) : ?> |
| 169 | - <td> | |
| 170 | - <?php echo $value[$j][$i] ?> | |
| 171 | - </td> | |
| 210 | + <td> | |
| 211 | + <?php echo fluentform_sanitize_html($value[$j][$i]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?> | |
| 212 | + </td> | |
| 172 | 213 | <?php endfor; ?> |
| 173 | 214 | </tr> |
| 174 | - <?php endfor; ?> | |
| 215 | + <?php endfor; ?> | |
| 175 | 216 | </tbody> |
| 176 | 217 | </table> |
| 177 | 218 | </div> |
| 178 | 219 | <?php |
| 179 | 220 | } |
| 180 | - return ob_get_clean(); | |
| 181 | - } catch (Exception $e) { | |
| 221 | + return ob_get_clean(); | |
| 222 | + } catch (\Exception $e) { | |
| 223 | + } | |
| 182 | 224 | |
| 183 | - } | |
| 225 | + return $value; | |
| 226 | + } | |
| 184 | 227 | |
| 185 | - return $value; | |
| 186 | - } | |
| 228 | + public static function formatTabularGridFieldValue($value, $field, $form_id, $isHtml = false) | |
| 229 | + { | |
| 230 | + if (defined('FLUENTFORM_RENDERING_ENTRIES')) { | |
| 231 | + return __('....', 'fluentform'); | |
| 232 | + } | |
| 187 | 233 | |
| 188 | - public static function formatTabularGridFieldValue($value, $field, $form_id, $isHtml = false) | |
| 189 | - { | |
| 190 | - if(defined('FLUENTFORM_RENDERING_ENTRIES')) { | |
| 191 | - return __('....', 'fluentform'); | |
| 192 | - } | |
| 234 | + if (is_string($value)) { | |
| 235 | + return $value; | |
| 236 | + } | |
| 193 | 237 | |
| 194 | - if(is_string($value)) { | |
| 195 | - return $value; | |
| 196 | - } | |
| 197 | - | |
| 198 | - if(is_array($value)) { | |
| 238 | + if (is_array($value)) { | |
| 199 | 239 | $value = (object) $value; |
| 200 | 240 | } |
| 201 | - try { | |
| 202 | - if(empty($field['raw'])) { | |
| 203 | - return $value; | |
| 241 | + try { | |
| 242 | + if (empty($field['raw'])) { | |
| 243 | + return $value; | |
| 204 | 244 | } |
| 205 | - $columnLabels = $field['raw']['settings']['grid_columns']; | |
| 206 | - $fieldType = $field['raw']['settings']['tabular_field_type']; | |
| 207 | - $columnHeaders = implode('</th><th>', array_values($columnLabels)); | |
| 245 | + $columnLabels = $field['raw']['settings']['grid_columns']; | |
| 246 | + $fieldType = $field['raw']['settings']['tabular_field_type']; | |
| 247 | + $columnHeaders = implode('</th><th style="text-align: center;">', array_values($columnLabels)); | |
| 208 | 248 | |
| 209 | - $elMarkup = "<table class='ff-table'><thead><tr><th></th><th>{$columnHeaders}</th></tr></thead><tbody>"; | |
| 249 | + $elMarkup = "<table class='ff-table'><thead><tr><th></th><th style='text-align: center;'>{$columnHeaders}</th></tr></thead><tbody>"; | |
| 210 | 250 | |
| 211 | - foreach (static::makeTabularData($field['raw']) as $row) { | |
| 212 | - | |
| 213 | - $elMarkup .= "<tr>"; | |
| 214 | - $elMarkup .= "<td>{$row['label']}</td>"; | |
| 215 | - foreach ($row['columns'] as $column) { | |
| 251 | + foreach (static::makeTabularData($field['raw']) as $row) { | |
| 252 | + $elMarkup .= '<tr>'; | |
| 253 | + $elMarkup .= "<td>{$row['label']}</td>"; | |
| 254 | + foreach ($row['columns'] as $column) { | |
| 216 | 255 | $isChecked = ''; |
| 217 | - if ($fieldType == 'radio') { | |
| 218 | - if(isset($value->{$row['name']})) { | |
| 256 | + if ('radio' == $fieldType) { | |
| 257 | + if (isset($value->{$row['name']})) { | |
| 219 | 258 | $isChecked = $value->{$row['name']} == $column['name'] ? 'checked' : ''; |
| 220 | 259 | } |
| 221 | 260 | } else { |
| 222 | - if(isset($value->{$row['name']})) { | |
| 261 | + if (isset($value->{$row['name']})) { | |
| 223 | 262 | $isChecked = in_array($column['name'], $value->{$row['name']}) ? 'checked' : ''; |
| 224 | 263 | } |
| 225 | 264 | } |
| 226 | 265 | $icon = "<input disabled type='{$fieldType}' {$isChecked}>"; |
| 227 | - if($isChecked) { | |
| 266 | + if ($isChecked) { | |
| 228 | 267 | $icon = '✔'; |
| 229 | 268 | } |
| 230 | - $elMarkup .= "<td>".$icon."</td>"; | |
| 231 | - } | |
| 232 | - $elMarkup .= "</tr>"; | |
| 233 | - } | |
| 269 | + $elMarkup .= "<td style='text-align: center;'>" . $icon . '</td>'; | |
| 270 | + } | |
| 271 | + $elMarkup .= '</tr>'; | |
| 272 | + } | |
| 234 | 273 | |
| 235 | - $elMarkup .= "</tbody></table>"; | |
| 274 | + $elMarkup .= '</tbody></table>'; | |
| 236 | 275 | |
| 237 | - return $elMarkup; | |
| 238 | - } catch (Exception $e) { | |
| 276 | + return $elMarkup; | |
| 277 | + } catch (\Exception $e) { | |
| 278 | + } | |
| 279 | + return ''; | |
| 280 | + } | |
| 239 | 281 | |
| 240 | - } | |
| 241 | - return ''; | |
| 242 | - } | |
| 243 | - | |
| 244 | 282 | public static function makeTabularData($data) |
| 245 | 283 | { |
| 246 | 284 | $table = []; |
| 247 | 285 | $rows = $data['settings']['grid_rows']; |
| @@ -247,37 +285,93 @@ | ||
| 247 | 285 | $rows = $data['settings']['grid_rows']; |
| 248 | 286 | $columns = $data['settings']['grid_columns']; |
| 249 | 287 | |
| 250 | 288 | foreach ($rows as $rowKey => $rowValue) { |
| 289 | + $rowKey = trim(sanitize_text_field($rowKey)); | |
| 251 | 290 | $table[$rowKey] = [ |
| 252 | - 'name' => $rowKey, | |
| 253 | - 'label' => $rowValue, | |
| 254 | - 'columns' => [] | |
| 291 | + 'name' => $rowKey, | |
| 292 | + 'label' => $rowValue, | |
| 293 | + 'columns' => [], | |
| 255 | 294 | ]; |
| 256 | 295 | |
| 257 | 296 | foreach ($columns as $columnKey => $columnValue) { |
| 258 | 297 | $table[$rowKey]['columns'][] = [ |
| 259 | - 'name' => $columnKey, | |
| 260 | - 'label' => $columnValue | |
| 298 | + 'name' => trim(sanitize_text_field($columnKey)), | |
| 299 | + 'label' => $columnValue, | |
| 261 | 300 | ]; |
| 262 | 301 | } |
| 263 | 302 | } |
| 303 | + return $table; | |
| 304 | + } | |
| 264 | 305 | |
| 265 | - return $table; | |
| 306 | + /** | |
| 307 | + * Format input_name field value by concatenating all name fields. | |
| 308 | + * | |
| 309 | + * @param array|object $value | |
| 310 | + * | |
| 311 | + * @return string $value | |
| 312 | + */ | |
| 313 | + public static function formatName($value) | |
| 314 | + { | |
| 315 | + if (is_array($value) || is_object($value)) { | |
| 316 | + $value = (array) $value; | |
| 317 | + $order = ['first_name', 'middle_name', 'last_name']; | |
| 318 | + uksort($value, function($a, $b) use ($order) { | |
| 319 | + $posA = array_search($a, $order); | |
| 320 | + $posB = array_search($b, $order); | |
| 321 | + return $posA - $posB; | |
| 322 | + }); | |
| 323 | + return fluentImplodeRecursive(' ', array_filter(array_values($value))); | |
| 324 | + } | |
| 325 | + | |
| 326 | + return $value; | |
| 266 | 327 | } |
| 267 | 328 | |
| 268 | - /** | |
| 269 | - * Format input_name field value by concatenating all name fields. | |
| 270 | - * | |
| 271 | - * @param array|object $value | |
| 272 | - * | |
| 273 | - * @return string $value | |
| 274 | - */ | |
| 275 | - public static function formatName($value) | |
| 276 | - { | |
| 277 | - if (is_array($value) || is_object($value)) { | |
| 278 | - return fluentImplodeRecursive(' ', array_filter(array_values((array) $value))); | |
| 279 | - } | |
| 329 | + public static function formatCheckBoxValues($values, $field, $isHtml = false) | |
| 330 | + { | |
| 331 | + if (!$isHtml) { | |
| 332 | + if ( | |
| 333 | + defined('FLUENTFORM_RENDERING_ENTRIES') && | |
| 334 | + $values && is_array($values) && | |
| 335 | + $options = ArrayHelper::get($field, 'raw.settings.advanced_options', []) | |
| 336 | + ) { | |
| 337 | + $options = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap($options); | |
| 338 | + foreach ($values as &$value) { | |
| 339 | + if ($label = ArrayHelper::get($options, $value)) { | |
| 340 | + $value = $label; | |
| 341 | + } | |
| 342 | + } | |
| 343 | + } | |
| 344 | + return self::formatValue($values); | |
| 345 | + } | |
| 280 | 346 | |
| 281 | - return $value; | |
| 282 | - } | |
| 347 | + if (!is_array($values)) { | |
| 348 | + return $values; | |
| 349 | + } | |
| 350 | + | |
| 351 | + if (empty($values)) { | |
| 352 | + return ''; | |
| 353 | + } | |
| 354 | + | |
| 355 | + if (!isset($field['options'])) { | |
| 356 | + $field['options'] = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap( | |
| 357 | + ArrayHelper::get($field, 'raw.settings.advanced_options', []) | |
| 358 | + ); | |
| 359 | + } | |
| 360 | + | |
| 361 | + $html = '<ul style="white-space: normal;">'; | |
| 362 | + foreach ($values as $value) { | |
| 363 | + $item = $value; | |
| 364 | + if ($itemLabel = ArrayHelper::get($field, 'options.' . $item)) { | |
| 365 | + $item = $itemLabel; | |
| 366 | + } | |
| 367 | + $html .= '<li>' . $item . '</li>'; | |
| 368 | + } | |
| 369 | + | |
| 370 | + return $html . '</ul>'; | |
| 371 | + } | |
| 372 | + | |
| 373 | + public static function resetData() | |
| 374 | + { | |
| 375 | + static::$data = null; | |
| 376 | + } | |
| 283 | 377 | } |