PluginProbe
Repeater Fields for Gravity Forms / 3.2.0
Repeater Fields for Gravity Forms v3.2.0
3.2.3 3.2.2 3.2.1 3.2.0 3.1.1 3.1.0 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 2.5.1 2.5.0 2.4.6 trunk 2.0.5 2.0.9 2.1.0 2.3.2 2.3.7 2.4.1 2.4.3 2.4.4 2.4.5
← All changes | fields/repeater_field.php +0 -1046 3.2.2 → 3.2.0 View file →
@@ -1,1046 +1,0 @@
1 -<?php
2 -// If Gravity Forms isn't loaded, bail.
3 -if (!class_exists('GFForms')) {
4 - die();
5 -}
6 -/**
7 - * Class GF_Field_Phone
8 - *
9 - * Handles the behavior of Phone fields.
10 - *
11 - * @since Unknown
12 - */
13 -class Superaddons_GFRepeater_Field extends GF_Field
14 -{
15 - private $lead_id = null;
16 - /**
17 - * Defines the field type.
18 - *
19 - * @since Unknown
20 - * @access public
21 - *
22 - * @var string The field type.
23 - */
24 - public $type = 'repeater_end';
25 - public $list_fields_validate = array();
26 - /**
27 - * Defines the field title to be used in the form editor.
28 - *
29 - * @since Unknown
30 - * @access public
31 - *
32 - * @used-by GFCommon::get_field_type_title()
33 - *
34 - * @return string The field title. Translatable and escaped.
35 - */
36 - public function get_form_editor_field_title()
37 - {
38 - return esc_attr__('Repeater end', 'repeater-for-gravity-forms');
39 - }
40 - public function get_form_editor_button()
41 - {
42 - return array(
43 - 'group' => 'advanced_fields',
44 - 'text' => $this->get_form_editor_field_title()
45 - );
46 - }
47 - /**
48 - * Defines the field settings available within the field editor.
49 - *
50 - * @since Unknown
51 - * @access public
52 - *
53 - * @return array The field settings available for the field.
54 - */
55 - function get_form_editor_field_settings()
56 - {
57 - return array(
58 - 'conditional_logic_field_setting',
59 - 'prepopulate_field_setting',
60 - 'error_message_setting',
61 - 'label_setting',
62 - 'field_field_repeater_end_text_setting',
63 - 'field_field_repeater_initial_rows_setting',
64 - 'field_field_repeater_max_setting',
65 - 'label_placement_setting',
66 - 'admin_label_setting',
67 - 'size_setting',
68 - 'rules_setting',
69 - 'visibility_setting',
70 - 'duplicate_setting',
71 - 'placeholder_setting',
72 - 'description_setting',
73 - 'css_class_setting',
74 - );
75 - }
76 - /**
77 - * Defines if conditional logic is supported in this field type.
78 - *
79 - * @since Unknown
80 - * @access public
81 - *
82 - * @used-by GFFormDetail::inline_scripts()
83 - * @used-by GFFormSettings::output_field_scripts()
84 - *
85 - * @return bool true
86 - */
87 - public function is_conditional_logic_supported()
88 - {
89 - return false;
90 - }
91 - /**
92 - * Returns the field input.
93 - *
94 - * @since Unknown
95 - * @access public
96 - *
97 - * @used-by GFCommon::get_field_input()
98 - * @uses GF_Field::is_entry_detail()
99 - * @uses GF_Field::is_form_editor()
100 - * @uses GF_Field_Phone::$failed_validation
101 - * @uses GF_Field_Phone::get_phone_format()
102 - * @uses GFFormsModel::is_html5_enabled()
103 - * @uses GF_Field::get_field_placeholder_attribute()
104 - * @uses GF_Field_Phone::$isRequired
105 - * @uses GF_Field::get_tabindex()
106 - *
107 - * @param array $form The Form Object.
108 - * @param string $value The value of the input. Defaults to empty string.
109 - * @param null|array $entry The Entry Object. Defaults to null.
110 - *
111 - * @return string The HTML markup for the field.
112 - */
113 - public function get_field_input($form, $value = '', $entry = null)
114 - {
115 - if (is_array($value)) {
116 - $value = '';
117 - }
118 - $id = intval($this->id);
119 -
120 - // If resuming draft submission via Save & Continue and $value doesn't have saved_values,
121 - // attempt to restore from draft $entry if available
122 - if (empty($value) || (is_string($value) && strpos($value, 'saved_values') === false)) {
123 - if (!empty($entry) && is_array($entry) && !empty($entry[$id])) {
124 - $entry_data = maybe_unserialize($entry[$id]);
125 - if (is_array($entry_data) && !empty($entry_data)) {
126 - $decoded_value = !empty($value) ? json_decode($value, true) : array();
127 - if (!is_array($decoded_value)) {
128 - $decoded_value = array();
129 - }
130 - $row_ids = array_keys($entry_data);
131 - $decoded_value['count'] = count($row_ids);
132 - $decoded_value['id'] = $row_ids;
133 - $saved_rows = array();
134 - foreach ($entry_data as $row_id => $row_inputs) {
135 - if (is_array($row_inputs)) {
136 - foreach ($row_inputs as $in_name => $in_val) {
137 - if (is_array($in_val)) {
138 - foreach ($in_val as $sub_k => $sub_v) {
139 - $saved_rows[$row_id]['input_' . str_replace('.', '_', $sub_k) . '__' . $row_id] = $sub_v;
140 - }
141 - } else {
142 - $saved_rows[$row_id][$in_name] = $in_val;
143 - }
144 - }
145 - }
146 - }
147 - $decoded_value['saved_values'] = $saved_rows;
148 - $value = wp_json_encode($decoded_value);
149 - }
150 - }
151 - }
152 -
153 - $is_entry_detail = $this->is_entry_detail();
154 - $is_form_editor = $this->is_form_editor();
155 - $form_id = $form['id'];
156 - $field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
157 - $size = $this->size;
158 - $disabled_text = $is_form_editor ? "disabled='disabled'" : '';
159 - $class_suffix = $is_entry_detail ? '_admin' : '';
160 - $class = $size . $class_suffix . " gf-field-repeater-data hidden";
161 - $class = esc_attr($class);
162 - $instruction_div = '';
163 - $html_input_type = 'hidden';
164 - $placeholder_attribute = $this->get_field_placeholder_attribute();
165 - $required_attribute = $this->isRequired ? 'aria-required="true"' : '';
166 - $invalid_attribute = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
167 - $aria_describedby = $this->get_aria_describedby();
168 - $tabindex = $this->get_tabindex();
169 - $repeater_add_button = $this->field_repeater_end_text;
170 - if ($repeater_add_button == "") {
171 - $repeater_add_button = esc_attr__("Add more", "gravityforms-repeater");
172 - }
173 - $initial_rows = $this->repeater_initial_rows;
174 - if ($initial_rows == "") {
175 - $initial_rows = 1;
176 - }
177 - $limit = $this->repeater_max;
178 - if ($limit == "" or $limit < 1) {
179 - $limit = 9999;
180 - }
181 - $limit = apply_filters("yeeaddons_gf_repeater_limit", 5, $limit);
182 - $initial_rows = apply_filters("yeeaddons_gf_repeater_initial_rows", 1, $initial_rows);
183 - $input = "<input data-initial_rows_map_check='" . esc_attr($this->repeater_initial_rows_map) . "' data-initial_rows_map='input_{$form_id}_" . esc_attr($this->repeater_initial_rows_map) . "' data-map_id='field_" . $form_id . "_" . $id . "' name='input_{$id}' id='{$field_id}' type='{$html_input_type}' value='" . esc_attr($value) . "' class='{$class}' {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>";
184 - $html = '<div data-initial_rows_map_check="' . esc_attr($this->repeater_initial_rows_map) . '" class="repeater-field-warp-item-data" data-initial_rows="' . $initial_rows . '" data-limit="' . $limit . '" data-initial_rows_map="input_' . $form_id . '_' . esc_attr($this->repeater_initial_rows_map) . '" data-map_id="field_' . $form_id . '_' . $id . '">
185 - <div class="repeater-field-warp-item">
186 - </div>
187 - <div class="repeater-field-footer"><a href="#"" class="gf-repeater-field-button-add" >' . $repeater_add_button . '</a></div>
188 - ' . $input . '
189 - <input type="hidden" class="gf-field-repeater-data-html hidden" />
190 - </div>';
191 - if (is_admin()) {
192 - $html = '<hr>End Repeater<hr>';
193 - return sprintf("<div class='ginput_container1'>%s</div>", $html);
194 - } else {
195 - return sprintf("<div class='ginput_container'>%s</div>", $html);
196 - }
197 - }
198 - public static function save_draft_submission_values($submitted_values, $form)
199 - {
200 - if (empty($form['fields']) || !is_array($form['fields'])) {
201 - return $submitted_values;
202 - }
203 -
204 - foreach ($form['fields'] as $field) {
205 - if ($field->type !== 'repeater_end') {
206 - continue;
207 - }
208 -
209 - $raw_value = rgar($submitted_values, $field->id);
210 - if (empty($raw_value)) {
211 - $raw_value = rgpost('input_' . $field->id);
212 - }
213 -
214 - if (empty($raw_value)) {
215 - continue;
216 - }
217 -
218 - $repeater_data = is_array($raw_value) ? $raw_value : json_decode($raw_value, true);
219 - if (!is_array($repeater_data) || empty($repeater_data['id']) || !is_array($repeater_data['id'])) {
220 - continue;
221 - }
222 -
223 - // Capture all $_POST values for each repeater row
224 - $saved_rows = array();
225 - foreach ($repeater_data['id'] as $id_rand) {
226 - $row_data = array();
227 - foreach ($_POST as $post_key => $post_val) {
228 - if (strpos($post_key, '__' . $id_rand) !== false) {
229 - $row_data[$post_key] = wp_unslash($post_val);
230 - }
231 - }
232 -
233 - // Also check if any file was uploaded in this row
234 - $transient_keys = array();
235 - $unique_id = rgpost('gform_unique_id');
236 - if (!empty($unique_id)) {
237 - $transient_keys[] = 'gf_repeater_files_' . $unique_id;
238 - }
239 - $transient_keys[] = 'gf_repeater_files_' . $form['id'];
240 - $transient_keys[] = 'gf_repeater_files_1';
241 -
242 - foreach ($transient_keys as $t_key) {
243 - $transient_files = get_transient($t_key);
244 - if (is_array($transient_files)) {
245 - foreach ($transient_files as $tk => $tv) {
246 - if (strpos($tk, '__' . $id_rand) !== false && !empty($tv)) {
247 - $row_data[$tk] = $tv;
248 - }
249 - }
250 - }
251 - }
252 -
253 - $saved_rows[$id_rand] = $row_data;
254 - }
255 -
256 - $repeater_data['saved_values'] = $saved_rows;
257 - $submitted_values[$field->id] = wp_json_encode($repeater_data);
258 - }
259 -
260 - return $submitted_values;
261 - }
262 - public static function remove_validation($form)
263 - {
264 - $zo = false;
265 - $zo_datas = array();
266 - foreach ($form["fields"] as $field) {
267 - $type = $field->type;
268 - if ($type == "repeater_start") {
269 - $zo = true;
270 - continue;
271 - }
272 - if ($type == "repeater_end") {
273 - $zo = false;
274 - continue;
275 - }
276 - if ($zo) {
277 - if (!isset($field->repeater_validate)) {
278 - $field->repeater_validate = array("isRequired" => $field->isRequired);
279 - }
280 - $field->isRequired = false;
281 - $field->validateState = false;
282 - }
283 - }
284 - return $form;
285 - }
286 - function validate($value, $form)
287 - {
288 - $datas = json_decode($value, true);
289 - $list_fields_validate = array();
290 - $failedValidation = false;
291 - foreach ($form["fields"] as $field) {
292 - if (is_array($field->repeater_validate)) {
293 - foreach ($field->repeater_validate as $k => $v) {
294 - if ($k == "isRequired" && $v == true) {
295 - $list_fields_validate["input_" . $field->id] = $field->type;
296 - }
297 - }
298 - }
299 - }
300 - if (isset($datas["id"]) && is_array($datas["id"])) {
301 - // Check if file upload is attempted without the Pro version activated
302 - if (!class_exists('Yeeaddons_Upgrade_GF_Repeater_Plugin')) {
303 - $is_fileupload_attempt = false;
304 - foreach ($datas["id"] as $id) {
305 - foreach ($datas["fields"] as $field) {
306 - if ($field == "") {
307 - continue;
308 - }
309 - $field = str_replace('[]', '', $field);
310 - $input_name = $field . "__" . $id;
311 - // Standard file upload
312 - if (isset($_FILES[$input_name]) && !empty($_FILES[$input_name]['name']) && (is_array($_FILES[$input_name]['name']) ? !empty(array_filter($_FILES[$input_name]['name'])) : true)) {
313 - $is_fileupload_attempt = true;
314 - break 2;
315 - }
316 - // Multi-file upload (Ajax/plupload)
317 - if (strpos($field, 'gform_multifile_upload_') === 0 && !empty($_POST[$input_name])) {
318 - $is_fileupload_attempt = true;
319 - break 2;
320 - }
321 - }
322 - }
323 - if ($is_fileupload_attempt) {
324 - $this->failed_validation = true;
325 - $this->validation_message = esc_html__('File upload in repeater is only supported in the Pro version.', 'repeater-for-gravity-forms');
326 - return;
327 - }
328 - }
329 -
330 - foreach ($datas["id"] as $id) {
331 - foreach ($datas["fields"] as $field) {
332 - if ($field == "") {
333 - continue;
334 - }
335 - $field = str_replace('[]', '', $field);
336 - if (isset($list_fields_validate[$field])) {
337 - $getInputData = rgpost($field . "__" . $id);
338 - switch ($list_fields_validate[$field]) {
339 - case "name":
340 - $first_name = rgpost($field . "_3__" . $id);
341 - $last_name = rgpost($field . "_6__" . $id);
342 - if (empty($first_name) || empty($last_name)) {
343 - $failedValidation = true;
344 - }
345 - break;
346 - case 'address':
347 - $address = rgpost($field . "_1__" . $id);
348 - if (empty($address)) {
349 - $failedValidation = true;
350 - }
351 - break;
352 - case 'fileupload':
353 - if (isset($_FILES[$field . "__" . $id]) && $_FILES[$field . "__" . $id]['name'][0]) {
354 - //file uploaded
355 - } else {
356 - $failedValidation = true;
357 - }
358 - break;
359 - case 'radio':
360 - if (rgblank($getInputData)) {
361 - $failedValidation = true;
362 - } elseif ($getInputData === 'gf_other_choice') {
363 - $other_val = rgpost($field . "_other__" . $id);
364 - if (rgblank($other_val)) {
365 - $failedValidation = true;
366 - }
367 - }
368 - break;
369 - default:
370 - if (is_array($getInputData)) {
371 - foreach ($getInputData as $vl) {
372 - if (rgblank($vl)) {
373 - $failedValidation = true;
374 - }
375 - }
376 - } else {
377 - if (rgblank($getInputData)) {
378 - $failedValidation = true;
379 - }
380 - }
381 - break;
382 - }
383 - }
384 - }
385 - }
386 - if ($failedValidation) {
387 - $this->failed_validation = true;
388 - if ($this->errorMessage) {
389 - $this->validation_message = $this->errorMessage;
390 - } else {
391 - $this->validation_message = esc_html__('This field is required.', 'gravityforms');
392 - }
393 - return;
394 - } else {
395 - $this->failed_validation = false;
396 - }
397 - }
398 - }
399 - function custom_validation($form)
400 - {
401 - $dataRepeater = array();
402 - $zo = false;
403 - $zo_datas = array();
404 - foreach ($form["fields"] as $field) {
405 - $type = $field->type;
406 - if ($type == "repeater_start") {
407 - $zo = true;
408 - continue;
409 - }
410 - if ($zo) {
411 - $zo_datas[$field->id] = array("isRequired" => $field->isRequired);
412 - $field->isRequired = false;
413 - }
414 - if ($type == "repeater_end") {
415 - $zo = false;
416 - $zo_datas = array();
417 - continue;
418 - }
419 - }
420 - return $form;
421 - }
422 - /**
423 - * Gets the value of the submitted field.
424 - *
425 - * @since Unknown
426 - * @access public
427 - *
428 - * @used-by GFFormsModel::get_field_value()
429 - * @uses GF_Field::get_value_submission()
430 - * @uses GF_Field_Phone::sanitize_entry_value()
431 - *
432 - * @param array $field_values The dynamic population parameter names with their corresponding values to be populated.
433 - * @param bool $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values. Defaults to true.
434 - *
435 - * @return array|string
436 - */
437 - public function get_value_submission($field_values, $get_from_post_global_var = true)
438 - {
439 - $value = parent::get_value_submission($field_values, $get_from_post_global_var);
440 - $value = $this->sanitize_entry_value($value, $this->formId);
441 - return $value;
442 - }
443 - /**
444 - * Sanitizes the entry value.
445 - *
446 - * @since Unknown
447 - * @access public
448 - *
449 - * @used-by GF_Field_Phone::get_value_save_entry()
450 - * @used-by GF_Field_Phone::get_value_submission()
451 - *
452 - * @param string $value The value to be sanitized.
453 - * @param int $form_id The form ID of the submitted item.
454 - *
455 - * @return string The sanitized value.
456 - */
457 - public function sanitize_entry_value($value, $form_id)
458 - {
459 - $value = is_array($value) ? array_map('sanitize_text_field', $value) : sanitize_text_field($value);
460 - return $value;
461 - }
462 - /**
463 - * Gets the field value when an entry is being saved.
464 - *
465 - * @since Unknown
466 - * @access public
467 - *
468 - * @used-by GFFormsModel::prepare_value()
469 - * @uses GF_Field_Phone::sanitize_entry_value()
470 - * @uses GF_Field_Phone::$phoneFormat
471 - *
472 - * @param string $value The input value.
473 - * @param array $form The Form Object.
474 - * @param string $input_name The input name.
475 - * @param int $lead_id The Entry ID.
476 - * @param array $lead The Entry Object.
477 - *
478 - * @return string The field value.
479 - */
480 - public function get_value_save_entry($value, $form, $input_name, $lead_id, $lead)
481 - {
482 - $this->lead_id = $lead_id;
483 - $datas = json_decode($value, true);
484 - if (!is_array($datas) || !isset($datas["id"]) || !is_array($datas["id"])) {
485 - return maybe_serialize(array());
486 - }
487 -
488 - $values = array();
489 - $form_id = $this->formId;
490 - $get_form = GFFormsModel::get_form_meta_by_id($form_id);
491 - $form = $get_form[0];
492 - $fields = array();
493 - if (isset($datas["fields"]) && is_array($datas["fields"])) {
494 - foreach ($datas["fields"] as $f) {
495 - if (!is_string($f) || empty($f)) {
496 - continue;
497 - }
498 - $datas_f = explode(".", $f);
499 - $fields[] = $datas_f[0];
500 - }
501 - }
502 - $fields = array_unique($fields);
503 - foreach ($datas["id"] as $id_rand) {
504 - $datas_step = array();
505 - foreach ($fields as $field) {
506 - $field = str_replace("[]", "", $field);
507 - $inputs = $this->get_inputs($form, $field);
508 - if (is_array($inputs)) {
509 - $getInputData = array();
510 - foreach ($inputs as $child_input) {
511 - $getInputName = "input_" . $child_input["id"] . "__" . $id_rand;
512 - $vl = rgpost(str_replace('.', '_', strval($getInputName)));
513 - if ($vl !== "" && $vl !== false && $vl !== null) {
514 - $child_field_type = $this->get_type($form, "input_" . $child_input["id"], $form_id);
515 - if (is_string($vl)) {
516 - if ($child_field_type === 'textarea') {
517 - $vl = sanitize_textarea_field($vl);
518 - } else {
519 - $vl = sanitize_text_field($vl);
520 - }
521 - }
522 - $getInputData[$child_input["id"]] = $vl;
523 - }
524 - }
525 - $datas_step[$getInputName] = $getInputData;
526 - } else {
527 - $getInputName = $field . "__" . $id_rand;
528 - $saved_url = '';
529 -
530 - // 1. Check transient saved across multi-step pages by gform_unique_id or form_id
531 - $unique_id = rgpost('gform_unique_id');
532 - $transient_keys = array();
533 - if (!empty($unique_id)) {
534 - $transient_keys[] = 'gf_repeater_files_' . $unique_id;
535 - }
536 - $transient_keys[] = 'gf_repeater_files_' . $form_id;
537 - $transient_keys[] = 'gf_repeater_files_1';
538 -
539 - foreach ($transient_keys as $t_key) {
540 - $transient_files = get_transient($t_key);
541 - if (is_array($transient_files)) {
542 - if (!empty($transient_files[$getInputName])) {
543 - $saved_url = $transient_files[$getInputName];
544 - break;
545 - }
546 - // Also check alternative key name format
547 - foreach ($transient_files as $tk => $tv) {
548 - if (strpos($tk, '__' . $id_rand) !== false && !empty($tv)) {
549 - $clean_f = str_replace('gform_multifile_upload_' . $form_id . '_', '', $field);
550 - $clean_f = str_replace('input_', '', $clean_f);
551 - if (strpos($tk, '_' . $clean_f . '__') !== false || strpos($tk, 'input_' . $clean_f . '__') !== false) {
552 - $saved_url = $tv;
553 - break 2;
554 - }
555 - }
556 - }
557 - }
558 - }
559 -
560 -
561 - // 2. Check if $_POST contains a direct URL
562 - if (empty($saved_url)) {
563 - $post_val = rgpost(str_replace('.', '_', strval($getInputName)));
564 - if (!empty($post_val) && is_string($post_val) && strpos($post_val, 'http') === 0) {
565 - $saved_url = $post_val;
566 - }
567 - }
568 -
569 - // 3. Check gform_uploaded_files (GF native temp file store)
570 - if (empty($saved_url)) {
571 - $uploaded_files_json = rgpost('gform_uploaded_files');
572 - if (!empty($uploaded_files_json)) {
573 - $uploaded_files = json_decode(stripslashes($uploaded_files_json), true);
574 - if (is_array($uploaded_files) && isset($uploaded_files[$getInputName])) {
575 - $file_info = $uploaded_files[$getInputName];
576 - if (is_string($file_info) && strpos($file_info, 'http') === 0) {
577 - $saved_url = $file_info;
578 - }
579 - }
580 - }
581 - }
582 -
583 - // 4. Check $_FILES if uploaded on the current submit step
584 - if (empty($saved_url) && isset($_FILES[$getInputName]) && !empty($_FILES[$getInputName]['name']) && (is_array($_FILES[$getInputName]['name']) ? !empty(array_filter($_FILES[$getInputName]['name'])) : ($_FILES[$getInputName]['error'] === UPLOAD_ERR_OK))) {
585 - $res = apply_filters("yeeaddons_gf_repeater_file", "", $form_id, $_FILES[$getInputName]);
586 - if ($res && $res !== "Upgrade to pro version") {
587 - $saved_url = $res;
588 - }
589 - }
590 -
591 - // 5. Fallback to $_POST value if available
592 - if (empty($saved_url)) {
593 - $saved_url = rgpost(str_replace('.', '_', strval($getInputName)));
594 - }
595 -
596 - // Sanitize theo kiểu trường trước khi lưu vào DB
597 - $field_type = $this->get_type($form, $field, $form_id);
598 - if (is_string($saved_url)) {
599 - if ($field_type === 'textarea') {
600 - $saved_url = sanitize_textarea_field($saved_url);
601 - } elseif ($field_type === 'fileupload' || filter_var($saved_url, FILTER_VALIDATE_URL) !== false) {
602 - $saved_url = esc_url_raw($saved_url);
603 - } else {
604 - $saved_url = sanitize_text_field($saved_url);
605 - }
606 - }
607 -
608 - $datas_step[$getInputName] = $saved_url;
609 - }
610 - }
611 - $values[$id_rand] = $datas_step;
612 - }
613 - return maybe_serialize($values);
614 - }
615 - public function upload_file($form_id, $file)
616 - {
617 - $target = GFFormsModel::get_file_upload_path($form_id, $file['name']);
618 - if (!$target) {
619 - return 'FAILED (Upload folder could not be created.)';
620 - }
621 - if (move_uploaded_file($file['tmp_name'], $target['path'])) {
622 - $this->set_permissions($target['path']);
623 - return $target['url'];
624 - } else {
625 - return 'FAILED (Temporary file could not be copied.)';
626 - }
627 - }
628 - function set_permissions($path)
629 - {
630 - GFFormsModel::set_permissions($path);
631 - }
632 - public function get_value_entry_list($value, $entry, $field_id, $columns, $form)
633 - {
634 - if (empty($value)) {
635 - return '';
636 - } else {
637 - $dataArray = GFFormsModel::unserialize($value);
638 - $arrayCount = count($dataArray);
639 - if ($arrayCount > 1) {
640 - $returnText = $arrayCount . ' entries';
641 - } else {
642 - $returnText = $arrayCount . ' entry';
643 - }
644 - return $returnText;
645 - }
646 - }
647 - function get_datas_field($format = "html", $value = '', $form_id = "")
648 - {
649 - global $wpdb;
650 - $dataArray = GFFormsModel::unserialize($value);
651 - $get_form = GFFormsModel::get_form_meta_by_id($form_id);
652 - $form = $get_form[0];
653 - if (isset($_GET['lid'])) {
654 - $result = GFAPI::get_entry(absint($_GET['lid']));
655 - } else {
656 - $table = $wpdb->prefix . "gf_entry";
657 - $mylink = $wpdb->get_row("SELECT id FROM $table ORDER BY id DESC");
658 - $result = ($mylink && isset($mylink->id)) ? GFAPI::get_entry($mylink->id) : array();
659 - }
660 - if (!is_array($result) || is_wp_error($result)) {
661 - $result = array();
662 - }
663 - if (!is_array($dataArray) || empty($dataArray)) {
664 - return '';
665 - }
666 - if ($format === 'html') {
667 - $html = '<ol>';
668 - foreach ($dataArray as $step_datas) {
669 - $html .= '<li><ul>';
670 - foreach ($step_datas as $name => $vl) {
671 - $type = $this->get_type($form, $name, $form_id);
672 - $lb = $this->get_custom_field_label($form, $name, $type, false, $form_id);
673 - if (is_array($vl)) {
674 - switch ($type) {
675 - case "address":
676 - $vl_data = "";
677 - foreach ($vl as $k => $v) {
678 - $child_lb = $this->get_custom_field_label($form, "input_" . $k, $type, true);
679 - $vl_data .= esc_html($child_lb) . ": " . esc_html($v) . "<br>";
680 - }
681 - $html .= '<li>' . esc_html($lb) . ": <br>" . $vl_data . "</li>";
682 - break;
683 - default:
684 - $vl_data = implode(", ", array_map('esc_html', $vl));
685 - $html .= '<li>' . esc_html($lb) . ": " . $vl_data . "</li>";
686 - break;
687 - }
688 - } else {
689 - $vl_data = $vl;
690 -
691 - // Helper functions kiểm tra và render file/hình ảnh
692 - $encode_url = function ($url) {
693 - return preg_replace_callback('/[^\x20-\x7E]/u', function ($match) {
694 - return rawurlencode($match[0]);
695 - }, trim($url));
696 - };
697 -
698 - $is_valid_url = function ($url) use ($encode_url) {
699 - if (empty($url))
700 - return false;
701 - $encoded = $encode_url($url);
702 - return filter_var($encoded, FILTER_VALIDATE_URL) !== false;
703 - };
704 -
705 - $is_image_url = function ($url) {
706 - if (!is_string($url) || empty($url))
707 - return false;
708 - $clean_path = parse_url($url, PHP_URL_PATH);
709 - if (!$clean_path) {
710 - $clean_path = $url;
711 - }
712 - $ext = strtolower(pathinfo($clean_path, PATHINFO_EXTENSION));
713 - return in_array($ext, array('jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif', 'heic'), true);
714 - };
715 -
716 - $render_file_or_image = function ($file_url, $display_name = '') use ($is_image_url) {
717 - $file_url = trim($file_url);
718 - if (empty($file_url))
719 - return '';
720 - $filename = !empty($display_name) ? $display_name : basename($file_url);
721 -
722 - if ($is_image_url($file_url)) {
723 - return '<a href="' . esc_url($file_url) . '" target="_blank" class="repeater-image-preview" style="display:inline-block; margin: 4px 6px 4px 0;">'
724 - . '<img src="' . esc_url($file_url) . '" alt="' . esc_attr($filename) . '" style="max-width: 150px; max-height: 150px; object-fit: contain; border-radius: 4px; border: 1px solid #e2e8f0; display: inline-block; vertical-align: middle;" />'
725 - . '</a>';
726 - } else {
727 - return '<a href="' . esc_url($file_url) . '" download>' . esc_html($filename) . '</a>';
728 - }
729 - };
730 -
731 - switch ($type) {
732 - case "fileupload":
733 - if ($vl_data === "Upgrade to pro version") {
734 - $html .= '<li>' . esc_html($lb) . ": " . esc_html($vl_data) . "</li>";
735 - break;
736 - }
737 -
738 - // Tách chuỗi theo dấu phẩy phòng trường hợp có nhiều file
739 - $parts = array_filter(array_map('trim', explode(",", $vl_data)));
740 - $content = array();
741 -
742 - // TRƯỜNG HỢP 1: Tất cả các phần tử đều là URL hợp lệ
743 - $all_are_urls = !empty($parts);
744 - foreach ($parts as $part) {
745 - if (!$is_valid_url($part)) {
746 - $all_are_urls = false;
747 - break;
748 - }
749 - }
750 -
751 - if ($all_are_urls) {
752 - foreach ($parts as $part) {
753 - $filename = basename($part);
754 - $content[] = $render_file_or_image($part, $filename);
755 - }
756 - $html .= '<li>' . esc_html($lb) . ': ' . implode(" ", $content) . "</li>";
757 - break;
758 - }
759 -
760 - // TRƯỜNG HỢP 2: Chuỗi chứa tên file hoặc URL không hợp lệ -> Tìm trong $result
761 - $main_name = explode("__", $name);
762 - $main_name = explode("_", $main_name[0]);
763 - $main_name_id = isset($main_name[4]) ? $main_name[4] : (isset($main_name[1]) ? $main_name[1] : null);
764 -
765 - if ($main_name_id && isset($result[$main_name_id])) {
766 - $raw_uploads = $result[$main_name_id];
767 - $data_uploads = is_array($raw_uploads) ? $raw_uploads : json_decode($raw_uploads, true);
768 -
769 - if (!is_array($data_uploads)) {
770 - $data_uploads = array_filter(array_map('trim', explode(",", (string) $raw_uploads)));
771 - }
772 -
773 - foreach ($parts as $n) {
774 - $clean_n = sanitize_file_name($n);
775 - $name_s = preg_quote(pathinfo($clean_n, PATHINFO_FILENAME), '/');
776 -
777 - foreach ($data_uploads as $upload_file) {
778 - // Regex khớp chính xác tên file gốc hoặc tên file có đánh số thứ tự
779 - if (preg_match('/' . $name_s . '(\d+)?\./i', $upload_file)) {
780 - $content[] = $render_file_or_image($upload_file, $clean_n);
781 - break;
782 - }
783 - }
784 - }
785 - }
786 -
787 - $html .= '<li>' . esc_html($lb) . ': ' . (!empty($content) ? implode(" ", $content) : esc_html($vl_data)) . "</li>";
788 - break;
789 -
790 - default:
791 - if (is_string($vl_data) && $is_valid_url($vl_data) && $is_image_url($vl_data)) {
792 - $html .= '<li>' . esc_html($lb) . ': ' . $render_file_or_image($vl_data) . "</li>";
793 - } else {
794 - $html .= '<li>' . esc_html($lb) . ": " . esc_html($vl_data) . "</li>";
795 - }
796 - break;
797 - }
798 - }
799 - }
800 - $html .= '</ul></li>';
801 - }
802 - $html .= '</ol>';
803 - $html = apply_filters("yeeadons_gravity_forms_repeater_html", $html, $dataArray, $form);
804 - return $html;
805 - } else {
806 - $text = '';
807 - foreach ($dataArray as $step_datas) {
808 - if (!is_array($step_datas)) {
809 - continue;
810 - }
811 - foreach ($step_datas as $name => $vl) {
812 - $type = $this->get_type($form, $name, $form_id);
813 - $lb = $this->get_custom_field_label($form, $name, $type, false, $form_id);
814 - $lb = esc_html($lb);
815 -
816 - if (is_array($vl)) {
817 - switch ($type) {
818 - case "address":
819 - $vl_data = "";
820 - foreach ($vl as $k => $v) {
821 - $child_lb = $this->get_custom_field_label($form, "input_" . $k, $type, true);
822 - $vl_data .= esc_html($child_lb) . ": " . esc_html($v) . "\n";
823 - }
824 - $text .= $lb . " : \n" . $vl_data;
825 - break;
826 - default:
827 - $vl_escaped = array_map('esc_html', $vl);
828 - $text .= $lb . ": " . implode(", ", $vl_escaped) . "\n";
829 - break;
830 - }
831 - } else {
832 - $vl_data = $vl;
833 - switch ($type) {
834 - case "fileupload":
835 - if ($vl_data === "Upgrade to pro version") {
836 - $text .= $lb . ": " . esc_html($vl_data) . "\n";
837 - break;
838 - }
839 - $parts = array_filter(array_map('trim', explode(",", $vl_data)));
840 - $content = array();
841 -
842 - $all_are_urls = !empty($parts);
843 - foreach ($parts as $part) {
844 - if (filter_var($part, FILTER_VALIDATE_URL) === false) {
845 - $all_are_urls = false;
846 - break;
847 - }
848 - }
849 -
850 - if ($all_are_urls) {
851 - foreach ($parts as $part) {
852 - $filename = basename($part);
853 - $content[] = esc_html($filename) . ' (' . esc_url($part) . ')';
854 - }
855 - $text .= $lb . ': ' . implode(", ", $content) . "\n";
856 - } else {
857 - $main_name = explode("__", $name);
858 - $main_name = explode("_", $main_name[0]);
859 - $main_name_id = isset($main_name[4]) ? $main_name[4] : (isset($main_name[1]) ? $main_name[1] : null);
860 -
861 - if ($main_name_id && isset($result[$main_name_id])) {
862 - $raw_uploads = $result[$main_name_id];
863 - $data_uploads = is_array($raw_uploads) ? $raw_uploads : json_decode($raw_uploads, true);
864 - if (!is_array($data_uploads)) {
865 - $data_uploads = array_filter(array_map('trim', explode(",", (string)$raw_uploads)));
866 - }
867 -
868 - foreach ($parts as $n) {
869 - $clean_n = sanitize_file_name($n);
870 - $name_s = preg_quote(pathinfo($clean_n, PATHINFO_FILENAME), '/');
871 - foreach ($data_uploads as $upload_file) {
872 - if (preg_match('/' . $name_s . '(\d+)?\./i', $upload_file)) {
873 - $content[] = esc_html($clean_n) . ' (' . esc_url($upload_file) . ')';
874 - break;
875 - }
876 - }
877 - }
878 - }
879 - $text .= $lb . ': ' . (!empty($content) ? implode(", ", $content) : esc_html($vl_data)) . "\n";
880 - }
881 - break;
882 -
883 - default:
884 - $text .= $lb . ": " . esc_html($vl_data) . "\n";
885 - break;
886 - }
887 - }
888 - }
889 - $text .= "\n";
890 - }
891 - $text = apply_filters("yeeadons_gravity_forms_repeater_text", $text, $dataArray, $form);
892 - return $text;
893 - }
894 - }
895 - public function get_value_export($entry, $input_id = '', $use_text = false, $is_csv = false)
896 - {
897 - //Export doesn’t require encoding, but field data may require some manipulation or formatting before it is exported
898 - $form_id = $entry["form_id"];
899 - $return = $this->get_datas_field("text", rgar($entry, $input_id), $form_id);
900 - return $return;
901 - }
902 - public function get_value_merge_tag($value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br)
903 - {
904 - if (empty($value)) {
905 - return '';
906 - }
907 - $form_id = isset($form['id']) ? absint($form['id']) : null;
908 - $return = $this->get_datas_field($format, $value, $form_id);
909 - return $return;
910 - }
911 - public function get_value_entry_detail($value, $currency = '', $use_text = false, $format = 'html', $media = 'screen')
912 - {
913 - global $wpdb;
914 - if (empty($value)) {
915 - return '';
916 - } else {
917 - $form_id = $this->formId;
918 - $return = $this->get_datas_field($format, $value, $form_id);
919 - return $return;
920 - }
921 - }
922 - function get_custom_field_label($form, $name = '', $type = '', $child = false, $form_id = '')
923 - {
924 - if (is_array($form)) {
925 - if (!array_key_exists('fields', $form)) {
926 - return false;
927 - }
928 - } else {
929 - return false;
930 - }
931 - $name = str_replace("gform_multifile_upload_" . $form_id, "input", $name);
932 - $names = explode("__", $name);
933 - $names = explode("_", $names[0]);
934 - $name = isset($names[1]) ? $names[1] : $names[0];
935 - foreach ($form['fields'] as $field_key => $field_value) {
936 - if (is_array($field_value->inputs)) {
937 - foreach ($field_value->inputs as $children_id) {
938 - if ($children_id["id"] == $name) {
939 - if ($child) {
940 - return $children_id["label"];
941 - } else {
942 - return $field_value->label;
943 - }
944 - }
945 - }
946 - } else {
947 - if ($field_value->id == $name) {
948 - return $field_value->label;
949 - }
950 - }
951 - }
952 - return "";
953 - }
954 - function get_inputs($form, $name = '')
955 - {
956 - if (is_array($form)) {
957 - if (!array_key_exists('fields', $form)) {
958 - return false;
959 - }
960 - } else {
961 - return false;
962 - }
963 - $names = explode("__", $name);
964 - $names = explode("_", $names[0]);
965 - $name = isset($names[1]) ? $names[1] : $names[0];
966 - foreach ($form['fields'] as $field_key => $field_value) {
967 - if ($field_value->id == $name) {
968 - if ($field_value->type == "date") {
969 - return $name;
970 - }
971 - if (is_array($field_value->inputs)) {
972 - return $field_value->inputs;
973 - }
974 - }
975 - }
976 - return false;
977 - }
978 - function get_type($form, $name = '', $form_id = "")
979 - {
980 - if (is_array($form)) {
981 - if (!array_key_exists('fields', $form)) {
982 - return false;
983 - }
984 - } else {
985 - return false;
986 - }
987 - $name = str_replace("gform_multifile_upload_" . $form_id, "input", $name);
988 - $names = explode("__", $name);
989 - $names = explode("_", $names[0]);
990 - $name = isset($names[1]) ? $names[1] : $names[0];
991 - foreach ($form['fields'] as $field_key => $field_value) {
992 - if (is_array($field_value->inputs)) {
993 - foreach ($field_value->inputs as $children_id) {
994 - if ($children_id["id"] == $name) {
995 - return $field_value->type;
996 - }
997 - }
998 - } else {
999 - if ($field_value->id == $name) {
1000 - return $field_value->type;
1001 - }
1002 - }
1003 - }
1004 - return "";
1005 - }
1006 -
1007 - public static function remove_child_fields($form)
1008 - {
1009 - if (!is_admin()) {
1010 - return $form;
1011 - }
1012 -
1013 - $page = isset($_GET['page']) ? $_GET['page'] : '';
1014 - $gf_page = isset($_GET['gf_page']) ? $_GET['gf_page'] : '';
1015 -
1016 - $target_pages = array('gf_entries', 'gf_export');
1017 - $target_gf_pages = array('select_columns', 'print-entry');
1018 -
1019 - if (in_array($page, $target_pages) || in_array($gf_page, $target_gf_pages)) {
1020 - $fields = array();
1021 - $zo = false;
1022 - foreach ($form["fields"] as $field) {
1023 - $type = $field->type;
1024 - if ($type == "repeater_start") {
1025 - $zo = true;
1026 - $fields[] = $field;
1027 - continue;
1028 - }
1029 - if ($type == "repeater_end") {
1030 - $zo = false;
1031 - $fields[] = $field;
1032 - continue;
1033 - }
1034 - if ($zo) {
1035 - continue;
1036 - }
1037 - $fields[] = $field;
1038 - }
1039 - $form["fields"] = $fields;
1040 - }
1041 -
1042 - return $form;
1043 - }
1044 -}
1045 -// Register the phone field with the field framework.
1046 -GF_Fields::register(new Superaddons_GFRepeater_Field());