PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Core/Database/FormEntryMetaModel.php +255 -161 2.10.13.3.1 View file →
@@ -5,10 +5,12 @@
5 5 */
6 6
7 7 namespace BitCode\BitForm\Core\Database;
8 8
9 +use BitCode\BitForm\Core\Util\FileHandler;
10 +
9 11 /**
10 - * Undocumented class
12 + * Manages entry meta (per-field values) for each form submission.
11 13 */
12 14
13 15 class FormEntryMetaModel extends Model
14 16 {
@@ -18,10 +20,10 @@
18 20 {
19 21 $values[] = $data['duplicateID'];
20 22 $values[] = $data['entryID'];
21 23 $sql = "INSERT INTO $this->table_name (bitforms_form_entry_id,meta_key,meta_value)"
22 - . ' SELECT %d as bitforms_form_entry_id,meta_key,meta_value'
23 - . " FROM `$this->table_name` WHERE bitforms_form_entry_id = %d";
24 + . ' SELECT %d as bitforms_form_entry_id,meta_key,meta_value'
25 + . " FROM `$this->table_name` WHERE bitforms_form_entry_id = %d";
24 26 return $this->execute($sql, $values)->getResult();
25 27 }
26 28
27 29 public function update(array $data, array $condition)
@@ -29,8 +31,9 @@
29 31 $entryID = $condition['bitforms_form_entry_id'];
30 32 if (empty($entryID)) {
31 33 return false;
32 34 }
35 + // Form entry meta lookup; meta_key/meta_value query required to map dynamic field keys per entry.
33 36 $formEntryMeta = $this->get(
34 37 [
35 38 'meta_key',
36 39 'meta_value',
@@ -46,11 +49,11 @@
46 49 $updatedData = [];
47 50 $oldEntriesKey = array_keys($oldEntries);
48 51 foreach ($data as $upKey => $upValue) {
49 52 $updatedData[$upKey] = is_string($upValue) ?
50 - $upValue :
51 - wp_json_encode($upValue);
52 - if (!\in_array($upKey, $oldEntriesKey)) {
53 + $upValue :
54 + wp_json_encode($upValue);
55 + if (!in_array($upKey, $oldEntriesKey, true)) {
53 56 $this->insert(
54 57 [
55 58 'bitforms_form_entry_id' => $entryID,
56 59 'meta_key' => $upKey,
@@ -72,9 +75,9 @@
72 75 $condition['meta_key'] = array_keys($data);
73 76 foreach ($data as $key => $value) {
74 77 $value = is_string($value) ? $value : wp_json_encode($value);
75 78 $case_part .= "
76 - WHEN '$key' THEN " . $this->getFieldFormat($value);
79 + WHEN '" . esc_sql($key) . "' THEN " . $this->getFieldFormat($value);
77 80 $all_values[] = $value;
78 81 }
79 82 $formattedCondition = $this->getFormatedCondition($condition);
80 83 if ($formattedCondition) {
@@ -116,11 +119,17 @@
116 119
117 120 $dateQuery = $dbHelper->dateQueryList();
118 121
119 122 $validCondtions = $this->validQueryCondition($conditions);
123 + $safeOperators = ['=', '!=', '>', '<', '>=', '<=', 'LIKE', 'NOT LIKE'];
120 124
121 125 foreach ($validCondtions as $condition) {
122 126 if (is_object($condition)) {
127 + $field = sanitize_key($condition->field);
128 + if (empty($field)) {
129 + continue;
130 + }
131 +
123 132 if (is_array($condition->val)) {
124 133 $value = $dbHelper->arrValueModifyByLogic($condition->logic, $condition->val);
125 134 } else {
126 135 $value = $dbHelper->strValueModifyByLogic($condition->logic, $condition->val);
@@ -126,20 +135,23 @@
126 135 $value = $dbHelper->strValueModifyByLogic($condition->logic, $condition->val);
127 136 }
128 137
129 138 $operator = $dbHelper->convertToSqlOperator($condition->logic);
139 + if (!in_array($operator, $safeOperators, true)) {
140 + continue;
141 + }
130 142
131 - if (isset($dateQuery[$condition->val])) {
132 - $sql .= $dbHelper->fieldQueryByDate($condition->field, $operator, $value, $condition->logic);
143 + if (!is_array($condition->val) && isset($dateQuery[$condition->val])) {
144 + $sql .= $dbHelper->fieldQueryByDate($field, $operator, $value, $condition->logic);
133 145 } else {
134 146 if (!is_int($value)) {
135 - $value = "'" . $value . "'";
147 + $value = "'" . esc_sql($value) . "'";
136 148 }
137 149
138 - $sql .= "`$condition->field` $operator $value";
150 + $sql .= "`{$field}` $operator $value";
139 151 }
140 - } else {
141 - $sql .= ' ' . $condition;
152 + } elseif (in_array(strtoupper(trim((string) $condition)), ['AND', 'OR'], true)) {
153 + $sql .= ' ' . strtoupper(trim((string) $condition));
142 154 }
143 155 }
144 156
145 157 return trim($sql);
@@ -156,12 +168,14 @@
156 168 $countResult = $this->execute($sql, $all_values)->getResult();
157 169 return $countResult[0]->count;
158 170 }
159 171
160 - public function selectedEntryMeta($formFields, $fieldCount)
172 + public function selectedEntryMeta($formFields, $fieldCount, $filter = null)
161 173 {
162 174 $all_values = [];
163 175 $formFieldsNames = [];
176 + $globalFilterString = '';
177 + $globalFilterValues = [];
164 178 $metaChecker = 0;
165 179 $selectedMeta = '`bitforms_form_entry_id` as entry_id,';
166 180 $selectedMeta .= "e.user_id as '__user_id',";
167 181 $selectedMeta .= "e.user_ip as '__user_ip',";
@@ -176,9 +190,10 @@
176 190 $selectedMeta .= ',';
177 191 }
178 192
179 193 foreach ($formFields as $fieldDetails) {
180 - $fieldFormat = $this->getFieldFormat($fieldDetails['key']);
194 + $safeFieldKey = sanitize_key($fieldDetails['key']);
195 + $fieldFormat = $this->getFieldFormat($safeFieldKey);
181 196 $selectedMeta .= "GROUP_CONCAT(
182 197 CASE
183 198 `meta_key`
184 199 WHEN '$fieldFormat' THEN `meta_value`
@@ -184,32 +199,33 @@
184 199 WHEN '$fieldFormat' THEN `meta_value`
185 200 END
186 201 ) AS '$fieldFormat'";
187 202 $metaChecker += 1;
188 - $all_values[] = $fieldDetails['key'];
189 - $all_values[] = $fieldDetails['key'];
190 - $formFieldsNames[] = $fieldDetails['key'];
203 + $all_values[] = $safeFieldKey;
204 + $all_values[] = $safeFieldKey;
205 + $formFieldsNames[] = $safeFieldKey;
191 206 if ($metaChecker < $fieldCount) {
192 207 $selectedMeta .= ',';
193 208 }
194 - //#unused code commented by me##
195 - // if ( !empty( $filter['global'] ) ) {
196 - // $globalFilterString .= " `" . $fieldDetails['key'] . "` LIKE '%%" . $this->getFieldFormat( $filter['global'] ) . "%%' ";
197 - // if ( $metaChecker < $fieldCount ) {
198 - // $globalFilterString .= " OR ";
199 - // }
200 - // $globalFilterValues[] = $filter['global'];
201 - // }
209 + if (!empty($filter['global'])) {
210 + $globalFilterString .= ' `' . $safeFieldKey . '` LIKE %s ';
211 + if ($metaChecker < $fieldCount) {
212 + $globalFilterString .= ' OR ';
213 + }
214 + $globalFilterValues[] = '%' . $this->app_db->esc_like($filter['global']) . '%';
215 + }
202 216 }
203 217
204 218 return [
205 - 'selected_meta' => $selectedMeta,
206 - 'form_fields_names' => $formFieldsNames,
207 - 'all_values' => $all_values,
219 + 'selected_meta' => $selectedMeta,
220 + 'form_fields_names' => $formFieldsNames,
221 + 'all_values' => $all_values,
222 + 'global_filter_string' => $globalFilterString,
223 + 'global_filter_values' => $globalFilterValues,
208 224 ];
209 225 }
210 226
211 - public function groupedCondition($condition, $all_values, $fieldConditions)
227 + public function groupedCondition($condition, $all_values, $fieldConditions, $filter = null, $globalFilterString = '', $globalFilterValues = [])
212 228 {
213 229 $isFldCondition = false;
214 230 $formattedCondition = $this->getFormatedCondition($condition);
215 231 if ($formattedCondition) {
@@ -218,51 +234,23 @@
218 234 $all_values = array_merge($all_values, $formattedCondition['values']);
219 235 } else {
220 236 $groupedCondition = null;
221 237 }
222 - //#unused code commented by me##
223 - //$isRecount = false;
238 + if (!empty($filter['global']) && !empty($globalFilterString) && !empty($globalFilterValues)) {
239 + $isFldCondition = true;
240 + if ($groupedCondition && false !== strpos($groupedCondition, 'HAVING')) {
241 + $groupedCondition .= ' AND (' . $globalFilterString . ') ';
242 + } else {
243 + $groupedCondition .= ' HAVING (' . $globalFilterString . ') ';
244 + }
245 + $all_values = array_merge($all_values, $globalFilterValues);
246 + }
224 247
225 - // if ( !empty( $filter['field'] ) ) {
226 - // $isRecount = true;
227 - // $filterFieldCount = count( $filter['field'] );
228 - // $filterFieldChecker = 0;
229 - // if ( $filterFieldCount > 0 ) {
230 - // $groupedCondition .= " HAVING ";
231 - // }
232 - // foreach ( $filter['field'] as $filterFieldKey => $filterFieldDetails ) {
233 - // $groupedCondition .= " `$filterFieldDetails->id` ='%%" . $this->getFieldFormat( $filterFieldDetails->value ) . "%%'";
234 - // $all_values[] = $filterFieldDetails->value;
235 - // if ( $filterFieldChecker < $filterFieldCount ) {
236 - // $groupedCondition .= " AND ";
237 - // }
238 - // }
239 - // }
240 -
241 - // if ( !empty( $filter['global'] ) && !empty( $globalFilterString ) && !empty( $globalFilterValues ) ) {
242 - // $isRecount = true;
243 - // if ( !empty( $filter['field'] ) ) {
244 - // $groupedCondition .= " AND (" . $globalFilterString . ") ";
245 - // } else {
246 - // $groupedCondition .= " HAVING $globalFilterString ";
247 - // }
248 - // $offset = 0;
249 - // $all_values = array_merge( $all_values, $globalFilterValues );
250 - // }
251 -
252 - // if ( !empty( $dateBetweenFilter ) && !empty( $dateBetweenFilter->start_date ) && !empty( $dateBetweenFilter->end_date ) ) {
253 - // if ( strpos( $groupedCondition, 'HAVING' ) !== false ) {
254 - // $groupedCondition .= " AND `__created_at` BETWEEN '" . $dateBetweenFilter->start_date . "' AND '" . $dateBetweenFilter->end_date . "' ";
255 - // } else {
256 - // $groupedCondition .= " HAVING `__created_at` BETWEEN '" . $dateBetweenFilter->start_date . "' AND '" . $dateBetweenFilter->end_date . "' ";
257 - // }
258 - // }
259 -
260 248 $sqlQryByFldCondtion = $this->sqlQryGenerateByFldCondition($fieldConditions);
261 249
262 250 if (!empty($sqlQryByFldCondtion)) {
263 251 $isFldCondition = true;
264 - if (false !== strpos($groupedCondition, 'HAVING')) {
252 + if ($groupedCondition && false !== strpos($groupedCondition, 'HAVING')) {
265 253 $groupedCondition .= ' AND (' . $sqlQryByFldCondtion . ') ';
266 254 } else {
267 255 $groupedCondition .= ' HAVING (' . $sqlQryByFldCondtion . ') ';
268 256 }
@@ -284,12 +272,12 @@
284 272 $orderCondition .= ' ORDER BY ';
285 273 }
286 274 $orderList = '';
287 275 foreach ($sortBy as $sortableFieldKey => $sortableFieldDetails) {
288 - // $orderCondition .=" ".$this->getFieldFormat($sortableFieldDetails->id);
289 276 $sortableFieldChecker += 1;
290 - if (in_array($sortableFieldDetails->id, $formFieldsNames)) {
291 - $orderList .= " `$sortableFieldDetails->id` ";
277 + if (in_array($sortableFieldDetails->id, $formFieldsNames, true)) {
278 + $safeId = sanitize_key($sortableFieldDetails->id);
279 + $orderList .= " `$safeId` ";
292 280 $orderFollow = $sortableFieldDetails->desc ? ' DESC ' : ' ASC ';
293 281 $orderList .= ' ' . $orderFollow;
294 282 if ($sortableFieldChecker < $sortableFieldCount) {
295 283 $orderList .= ', ';
@@ -324,20 +312,18 @@
324 312 public function getEntryMeta($formFields, $entries, $limit = null, $offset = null, $filter = null, $sortBy = null, $fieldConditions = null, $dateBetweenFilter = null)
325 313 {
326 314 $entry_table = $this->app_db->prefix . 'bitforms_form_entries';
327 315 $fieldCount = count($formFields);
328 - $getSelectedMetaFldValue = $this->selectedEntryMeta($formFields, $fieldCount);
329 -
316 + $getSelectedMetaFldValue = $this->selectedEntryMeta($formFields, $fieldCount, $filter);
330 317 $selectedMeta = $getSelectedMetaFldValue['selected_meta'];
331 318 $formFieldsNames = $getSelectedMetaFldValue['form_fields_names'];
332 319 $all_values = $getSelectedMetaFldValue['all_values'];
320 + $globalFilterString = $getSelectedMetaFldValue['global_filter_string'];
321 + $globalFilterValues = $getSelectedMetaFldValue['global_filter_values'];
333 322 $entryIDs = [];
334 323 $entryCount = count($entries);
335 - // $paginateEntry = empty($sortBy) && empty($filter['field']) && empty($filter['global']);
336 - // $entries = $paginateEntry ? array_slice($entries, $offset, $limit) : $entries;
337 - $paginateEntry = false;
338 324 foreach ($entries as $entryDetail) {
339 - $entryIDs[] = $entryDetail->id;
325 + $entryIDs[] = $entryDetail->id ?? $entryDetail;
340 326 }
341 327 if (empty($entryIDs)) {
342 328 return [
343 329 'count' => 0,
@@ -344,34 +330,36 @@
344 330 'entries' => [],
345 331 ];
346 332 }
347 333 $condition['bitforms_form_entry_id'] = $entryIDs;
348 - $group = $this->groupedCondition($condition, $all_values, $fieldConditions);
334 + $group = $this->groupedCondition($condition, $all_values, $fieldConditions, $filter, $globalFilterString, $globalFilterValues);
349 335 $groupedCondition = $group['groupedCondition'];
350 336 $all_values = $group['all_values'];
351 337 $isFldCondition = $group['isFldCondition'];
352 - $orderCondition = $this->orderCondition($formFieldsNames, $sortBy);
338 + $orderCondition = $this->orderCondition($formFieldsNames, (array) $sortBy);
353 339
354 340 $paginate = null;
355 - if (!\is_null($limit)) {
356 - $limit = \intval($limit);
341 + if (!is_null($limit)) {
342 + $limit = intval($limit);
357 343 $paginate .= " LIMIT $limit ";
358 344 }
359 - if (!\is_null($offset)) {
360 - $offset = \intval($offset);
361 - $paginate .= " OFFSET $offset ";
345 + if (!is_null($offset)) {
346 + $offset = intval($offset);
347 + $paginate .= " OFFSET $offset ";
362 348 }
349 +
363 350 $sql = "SELECT $selectedMeta FROM `$this->table_name` em";
364 351 $sql .= " INNER JOIN $entry_table e on e.id = em.bitforms_form_entry_id ";
365 352 if ($dateBetweenFilter) {
366 - $startDate = $dateBetweenFilter->start_date;
367 - $endDate = $dateBetweenFilter->end_date;
353 + $startDate = sanitize_text_field($dateBetweenFilter->start_date ?? '');
354 + $endDate = sanitize_text_field($dateBetweenFilter->end_date ?? '');
355 +
368 356 if ($startDate && $endDate) {
369 - $sql .= " AND DATE(e.created_at) BETWEEN '$startDate' AND '$endDate' ";
357 + $sql .= $this->app_db->prepare(' AND e.created_at BETWEEN %s AND %s', $startDate . ' 00:00:00', $endDate . ' 23:59:59');
370 358 } elseif ($startDate) {
371 - $sql .= " AND DATE(e.created_at) >= '$startDate' ";
359 + $sql .= $this->app_db->prepare(' AND e.created_at >= %s', $startDate . ' 00:00:00');
372 360 } elseif ($endDate) {
373 - $sql .= " AND DATE(e.created_at) <= '$endDate' ";
361 + $sql .= $this->app_db->prepare(' AND e.created_at <= %s', $endDate . ' 23:59:59');
374 362 }
375 363 }
376 364 $sql .= $groupedCondition . $orderCondition . $paginate;
377 365 $result = $this->execute($sql, $all_values)->getResult();
@@ -376,18 +364,15 @@
376 364 $sql .= $groupedCondition . $orderCondition . $paginate;
377 365 $result = $this->execute($sql, $all_values)->getResult();
378 366 if (is_wp_error($result)) {
379 367 return [
380 - 'count' => $paginateEntry ? $entryCount : 0,
368 + 'count' => 0,
381 369 'entries' => [],
382 370 'error' => $result->get_error_message()
383 371 ];
384 372 }
385 373 if ($isFldCondition) {
386 - $condition['bitforms_form_entry_id'] = $entryIDs;
387 - $group = $this->groupedCondition($condition, $all_values, $fieldConditions);
388 - $all_values = $group['all_values'];
389 - $entryCount = $this->queryRecount($selectedMeta, $group['groupedCondition'], $orderCondition, $all_values);
374 + $entryCount = $this->queryRecount($selectedMeta, $groupedCondition, $orderCondition, $all_values);
390 375 }
391 376 $resultedEntries = [
392 377 'count' => $entryCount,
393 378 'entries' => $result,
@@ -394,13 +379,37 @@
394 379 ];
395 380 return $resultedEntries;
396 381 }
397 382
398 - private function csvInjectionPrevent($value)
383 + public function getSingleEntryMeta($formFields, $entryId)
399 384 {
385 + $entry_table = $this->app_db->prefix . 'bitforms_form_entries';
386 + $fieldCount = count($formFields);
387 + $getSelectedMetaFldValue = $this->selectedEntryMeta($formFields, $fieldCount);
388 + $selectedMeta = $getSelectedMetaFldValue['selected_meta'];
389 + $formFieldsNames = $getSelectedMetaFldValue['form_fields_names'];
390 + $all_values = $getSelectedMetaFldValue['all_values'];
391 + $condition['bitforms_form_entry_id'] = [$entryId];
392 + $group = $this->groupedCondition($condition, $all_values, []);
393 + $groupedCondition = $group['groupedCondition'];
394 + $all_values = $group['all_values'];
395 + $orderCondition = $this->orderCondition($formFieldsNames, null);
396 + $sql = "SELECT $selectedMeta FROM `$this->table_name` em";
397 + $sql .= " INNER JOIN $entry_table e on e.id = em.bitforms_form_entry_id ";
398 + $sql .= $groupedCondition . $orderCondition;
399 + $result = $this->execute($sql, $all_values)->getResult();
400 +
401 + if (is_wp_error($result)) {
402 + return [];
403 + }
404 + return $result;
405 + }
406 +
407 + private static function csvInjectionPrevent($value)
408 + {
400 409 $formula = ['=', '-', '+', '@', "\t", "\r"];
401 410 $valueFilter = preg_replace('/[\]["]/i', '', $value);
402 - if (\in_array(substr($value, 0, 1), $formula, true)) {
411 + if (in_array(substr($valueFilter, 0, 1), $formula, true)) {
403 412 $valueFilter = "'" . trim($valueFilter);
404 413 }
405 414
406 415 return $valueFilter;
@@ -405,37 +414,83 @@
405 414
406 415 return $valueFilter;
407 416 }
408 417
409 - public function getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit = null, $sortBy = null, $sortByField = null)
418 + private static function unescapeString($str)
410 419 {
420 + if (is_string($str) && '' !== $str) {
421 + $decoded = json_decode('"' . str_replace('"', '\\"', $str) . '"');
422 + return (null !== $decoded) ? $decoded : $str;
423 + }
424 + return $str;
425 + }
426 +
427 + private static function formatRepeaterValue($rawValue, $fieldMap)
428 + {
429 + if (empty($rawValue)) {
430 + return '';
431 + }
432 + $rows = [];
433 + preg_match_all('/\{([^}]+)\}/', $rawValue, $matches);
434 +
435 + foreach ($matches[1] as $row) {
436 + $pairs = explode(',', $row);
437 + $formattedPairs = [];
438 +
439 + foreach ($pairs as $pair) {
440 + if (false === strpos($pair, ':')) {
441 + continue;
442 + }
443 + [$childKey, $value] = explode(':', $pair, 2);
444 + $childKey = trim($childKey);
445 + $value = trim($value);
446 +
447 + // Get label from fieldMap or use key
448 + $label = $fieldMap[$childKey]['adminLbl'] ?? $childKey;
449 + $formattedPairs[] = "$label: " . self::csvInjectionPrevent(self::unescapeString($value));
450 + }
451 +
452 + $rows[] = implode(', ', $formattedPairs);
453 + }
454 +
455 + return implode('; ', $rows);
456 + }
457 +
458 + public function getExportEntry($formFields, $entries, $formId, $fieldLabels, $limit = null, $sortBy = null, $sortByField = null, $offset = null, $entryConditions = null)
459 + {
411 460 $entry_table = $this->app_db->prefix . 'bitforms_form_entries';
412 461 $selectedEntryMeta = '`bitforms_form_entry_id` as entry_id,';
413 - $selectedEntryMeta .= "e.user_id as '__user_id',";
414 - $selectedEntryMeta .= "e.status as '__entry_status',";
415 - $selectedEntryMeta .= "e.user_ip as '__user_ip',";
416 - $selectedEntryMeta .= "e.user_location as '__user_location',";
417 - $selectedEntryMeta .= "e.user_device as '__user_device',";
418 - $selectedEntryMeta .= "e.referer as '__referer',";
419 - $selectedEntryMeta .= "e.created_at as '__created_at',";
420 - $selectedEntryMeta .= "e.updated_at as '__updated_at',";
462 + $selectedEntryMeta .= 'e.user_id as `__user_id`,';
463 + $selectedEntryMeta .= 'e.status as `__entry_status`,';
464 + $selectedEntryMeta .= 'e.user_ip as `__user_ip`,';
465 + $selectedEntryMeta .= 'e.user_location as `__user_location`,';
466 + $selectedEntryMeta .= 'e.user_device as `__user_device`,';
467 + $selectedEntryMeta .= 'e.referer as `__referer`,';
468 + $selectedEntryMeta .= 'e.created_at as `__created_at`,';
469 + $selectedEntryMeta .= 'e.updated_at as `__updated_at`,';
421 470 $metaChecker = 0;
422 471
423 - $entryInfo = ['__user_id', '__user_ip', /* '__user_location', */'__user_device',
424 - '__referer', '__created_at', '__updated_at'];
472 + $entryInfo = [
473 + '__user_id',
474 + '__user_ip', /* '__user_location', */
475 + '__user_device',
476 + '__entry_status',
477 + '__referer',
478 + '__created_at',
479 + '__updated_at'
480 + ];
425 481 $all_values = [];
426 482 if ([] === $formFields) {
427 - $data = [
483 + return [
428 484 'count' => 0,
429 485 'entries' => [],
430 486 ];
431 - wp_send_json_success($data, 200);
432 487 }
433 488 $fieldCount = count($formFields) - count(array_intersect($formFields, $entryInfo));
434 489 $formFieldsNames = [];
435 490 foreach ($formFields as $fldKey) {
436 491 $formFieldsNames[] = $fldKey;
437 - if (in_array($fldKey, $entryInfo)) {
492 + if (in_array($fldKey, $entryInfo, true)) {
438 493 continue;
439 494 }
440 495 $fieldFormat = $this->getFieldFormat($fldKey);
441 496 $selectedEntryMeta .= "GROUP_CONCAT(
@@ -461,42 +516,66 @@
461 516 'entries' => [],
462 517 ];
463 518 }
464 519 $condition['bitforms_form_entry_id'] = $entryIDs;
465 - $formattedCondition = $this->getFormatedCondition($condition);
466 - $groupedCondition = null;
467 - if ($formattedCondition) {
468 - $groupedCondition = $formattedCondition['conditions'] . ' GROUP BY
469 - `bitforms_form_entry_id` ';
470 - $all_values = array_merge($all_values, $formattedCondition['values']);
471 - }
472 - $order = \is_null($sortBy) ? 'DESC ' : "$sortBy";
473 - $orderField = \is_null($sortByField) ? 'bitforms_form_entry_id' : "`$sortByField`";
520 + $grpCon = $this->groupedCondition($condition, $all_values, $entryConditions);
521 + $groupedCondition = $grpCon['groupedCondition'];
522 + $all_values = $grpCon['all_values'];
474 523
524 + $order = 'DESC' === $sortBy ? 'DESC ' : 'ASC ';
525 + $validSortFields = array_column($fieldLabels, 'key');
526 + $orderField = (!is_null($sortByField) && in_array($sortByField, $validSortFields, true))
527 + ? '`' . sanitize_key($sortByField) . '`'
528 + : '`bitforms_form_entry_id`';
529 +
475 530 $orderCondition = "ORDER BY $orderField $order ";
476 - if (!\is_null($limit)) {
477 - $limitInt = \intval($limit);
478 - $limit = " LIMIT $limitInt ";
531 + $limitClause = '';
532 + if (!is_null($limit)) {
533 + $limitInt = intval($limit);
534 + $limitClause = " LIMIT $limitInt ";
535 + if (!is_null($offset)) {
536 + $offsetInt = intval($offset);
537 + $limitClause .= " OFFSET $offsetInt ";
538 + }
479 539 }
540 +
541 + $this->app_db->query('SET SESSION group_concat_max_len = 10000');
480 542 $sql = "SELECT $selectedEntryMeta FROM `$this->table_name` em";
481 543 $sql .= " INNER JOIN $entry_table e on e.id = em.bitforms_form_entry_id ";
482 - $sql .= $groupedCondition . $orderCondition . $limit;
544 + $sql .= $groupedCondition . $orderCondition . $limitClause;
545 + $result = $this->execute($sql, $all_values)->getResult();
546 + if (is_wp_error($result)) {
547 + return new \WP_Error('db_error', 'Internal server error');
548 + }
483 549
484 - $result = $this->execute($sql, $all_values)->getResult();
485 550 $allData = [];
486 - $entry_id = 'entry_id';
487 - $users = get_users(['fields' => ['ID', 'display_name']]);
551 + $entryStatus = [
552 + '0' => 'Read',
553 + '1' => 'Unread',
554 + '2' => 'Unconfirmed',
555 + '3' => 'Confirmed',
556 + '9' => 'Draft',
557 + ];
558 + $userIds = array_unique(array_filter(
559 + array_map(static fn ($row) => (int) $row->__user_id, (array) $result),
560 + static fn ($id) => $id > 0
561 + ));
488 562 $userNames = [];
489 - foreach ($users as $key => $value) {
490 - $userNames[$value->ID] = $value->display_name;
563 + if (!empty($userIds)) {
564 + $users = get_users(['include' => $userIds, 'fields' => ['ID', 'display_name']]);
565 + foreach ($users as $user) {
566 + $userNames[$user->ID] = $user->display_name;
567 + }
491 568 }
492 569 foreach ($result as $key => $value) {
493 570 foreach ($formFieldsNames as $formFieldName) {
494 - $allData[$key]['entry_id'] = preg_replace('/[\]["]/i', '', $value->$entry_id);
571 + $allData[$key]['entry_id'] = preg_replace('/[\]["]/i', '', $value->entry_id);
495 572 if ('__user_id' === $formFieldName && intval($value->$formFieldName) > 0) {
496 - $allData[$key][$formFieldName] = $userNames[$value->$formFieldName];
573 + $allData[$key][$formFieldName] = $userNames[$value->$formFieldName] ?? '';
497 574 } elseif ('__user_ip' === $formFieldName) {
498 - $allData[$key][$formFieldName] = long2ip($value->$formFieldName);
575 + $allData[$key][$formFieldName] = long2ip((int) $value->$formFieldName);
576 + } elseif ('__entry_status' === $formFieldName) {
577 + $allData[$key][$formFieldName] = $entryStatus[$value->{$formFieldName}] ?? '';
499 578 } else {
500 579 $allData[$key][$formFieldName] = preg_replace('/[\]["]/i', '', $value->$formFieldName);
501 580 }
502 581 }
@@ -501,40 +580,55 @@
501 580 }
502 581 }
503 582 }
504 583
505 - if (is_wp_error($result)) {
506 - wp_send_json_error('Internal server error', 500);
507 - } else {
508 - $downloadableFieldType = ['file-up', 'signature', 'advanced-file-up'];
509 - foreach ($fieldLabels as $field) {
510 - foreach ($allData as $index => $entry) {
511 - if (array_key_exists($field['key'], $entry) && in_array($field['type'], $downloadableFieldType, true)) {
512 - $key = $field['key'];
513 - if (empty($entry[$key])) {
514 - continue;
515 - }
516 - $_upload_dir = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formId . DIRECTORY_SEPARATOR . $entry['entry_id'];
517 - $imageArray = explode(',', $entry[$key]);
518 - if (is_array($imageArray)) {
519 - $fileData = [];
520 - foreach ($imageArray as $file) {
521 - $path = "bitforms/bitforms-file-$formId/?formID=$formId&entryID=" . $entry['entry_id'] . "&fileID=$file";
522 - if (file_exists($_upload_dir . DIRECTORY_SEPARATOR . $file)) {
523 - $fileData[] = site_url($path, null);
524 - }
525 - }
526 - $allData[$index][$key] = implode(',', $fileData);
527 - } else {
528 - $uploadedFile = explode('_', $entry[$key]);
529 - $path = "bitforms/bitforms-file-$formId/?formID=$formId&entryID=" . $entry['entry_id'] . '&fileID=' . $uploadedFile[0];
530 - if (file_exists($_upload_dir . DIRECTORY_SEPARATOR . $uploadedFile[0])) {
531 - $allData[$index][$key] = site_url($path, null);
532 - }
533 - }
584 + $fieldMap = [];
585 + $repeaterFields = [];
586 + $fileFields = [];
587 + $downloadableFieldType = ['file-up', 'signature', 'advanced-file-up'];
588 +
589 + foreach ($fieldLabels as $field) {
590 + $key = $field['key'];
591 + $fieldMap[$key] = $field;
592 + if ('repeater' === $field['type']) {
593 + $repeaterFields[] = $key;
594 + } elseif (in_array($field['type'], $downloadableFieldType, true)) {
595 + $fileFields[] = $key;
596 + }
597 + }
598 +
599 + foreach ($allData as &$entry) {
600 + foreach ($entry as $key => &$value) {
601 + if (is_string($value)) {
602 + $value = self::csvInjectionPrevent(self::unescapeString($value));
603 + }
604 + if (in_array($key, $repeaterFields, true)) {
605 + $value = self::formatRepeaterValue($value, $fieldMap);
606 + }
607 + }
608 + unset($value);
609 + }
610 + unset($entry, $value);
611 +
612 + foreach ($allData as &$entry) {
613 + $entryId = $entry['entry_id'];
614 + $_upload_dir = FileHandler::getEntriesFileUploadDir($formId, $entryId);
615 + foreach ($fileFields as $fileKey) {
616 + if (empty($entry[$fileKey])) {
617 + continue;
618 + }
619 + $fileIds = explode(',', $entry[$fileKey]);
620 + $urls = [];
621 + foreach ($fileIds as $fileId) {
622 + $path = "bitforms/bitforms-file/?formID=$formId&entryID=$entryId&fileID=$fileId";
623 + if (file_exists($_upload_dir . DIRECTORY_SEPARATOR . $fileId)) {
624 + $urls[] = site_url($path);
534 625 }
535 626 }
627 + $entry[$fileKey] = implode(',', $urls);
536 628 }
537 - wp_send_json_success($allData, 200);
538 629 }
630 + unset($entry);
631 +
632 + return $allData;
539 633 }
540 634 }