PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.6
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.6
3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 All 199 releases
← All changes | includes/class-convertkit-form-entries.php +74 -34 3.3.4 → 3.4.6 View file →
@@ -24,8 +24,28 @@
24 24 */
25 25 private $table = 'kit_form_entries';
26 26
27 27 /**
28 + * Holds the columns that entries can be ordered by.
29 + *
30 + * @since 3.4.6
31 + *
32 + * @var array
33 + */
34 + private $order_by_columns = array(
35 + 'id',
36 + 'post_id',
37 + 'first_name',
38 + 'email',
39 + 'form_id',
40 + 'tag_id',
41 + 'sequence_id',
42 + 'created_at',
43 + 'updated_at',
44 + 'api_result',
45 + );
46 +
47 + /**
28 48 * Create database table.
29 49 *
30 50 * @since 3.0.0
31 51 *
@@ -35,10 +55,10 @@
35 55
36 56 global $wpdb;
37 57
38 58 // Create database table.
39 - $query = $wpdb->prepare(
40 - "CREATE TABLE IF NOT EXISTS %i (
59 + // The table name isn't prepared, as the %i placeholder requires WordPress 6.2+.
60 + $query = "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}{$this->table}` (
41 61 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
42 62 `post_id` int(11) NOT NULL,
43 63 `first_name` varchar(191) NOT NULL DEFAULT '',
44 64 `email` varchar(191) NOT NULL DEFAULT '',
@@ -57,11 +77,9 @@
57 77 KEY `form_id` (`form_id`),
58 78 KEY `tag_id` (`tag_id`),
59 79 KEY `sequence_id` (`sequence_id`),
60 80 KEY `api_result` (`api_result`)
61 - )",
62 - $wpdb->prefix . $this->table
63 - );
81 + )";
64 82 $query .= ' ' . $wpdb->get_charset_collate() . ' AUTO_INCREMENT=1';
65 83 $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
66 84
67 85 }
@@ -157,9 +175,9 @@
157 175 array( 'id' => $id )
158 176 );
159 177
160 178 // Return the entry ID.
161 - return $wpdb->insert_id;
179 + return (int) $id;
162 180
163 181 }
164 182
165 183 /**
@@ -197,10 +215,9 @@
197 215
198 216 // Check if an entry already exists for the given Post ID and Email.
199 217 $id = $wpdb->get_var(
200 218 $wpdb->prepare(
201 - 'SELECT id FROM %i WHERE post_id = %d AND email = %s',
202 - $wpdb->prefix . $this->table,
219 + "SELECT id FROM `{$wpdb->prefix}{$this->table}` WHERE post_id = %d AND email = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
203 220 $entry['post_id'],
204 221 $entry['email']
205 222 )
206 223 );
@@ -263,12 +280,12 @@
263 280 return '';
264 281 }
265 282
266 283 $csv = array(
267 - '"' . implode( '","', array_keys( $entries[0] ) ) . '"',
284 + $this->get_csv_row( array_keys( $entries[0] ) ),
268 285 );
269 286 foreach ( $entries as $entry ) {
270 - $csv[] = '"' . implode( '","', $entry ) . '"';
287 + $csv[] = $this->get_csv_row( $entry );
271 288 }
272 289
273 290 return implode( "\n", $csv );
274 291
@@ -274,8 +291,35 @@
274 291
275 292 }
276 293
277 294 /**
295 + * Returns a CSV row for the given values, escaping double quotes and
296 + * values that spreadsheet applications would run as a formula.
297 + *
298 + * @since 3.4.6
299 + *
300 + * @param array $values Values.
301 + * @return string
302 + */
303 + private function get_csv_row( $values ) {
304 +
305 + $row = array();
306 + foreach ( $values as $value ) {
307 + $value = (string) $value;
308 +
309 + // Prefix values starting with a formula character, so they're treated as text.
310 + if ( $value !== '' && in_array( $value[0], array( '=', '+', '-', '@', "\t", "\r" ), true ) ) {
311 + $value = "'" . $value;
312 + }
313 +
314 + $row[] = '"' . str_replace( '"', '""', $value ) . '"';
315 + }
316 +
317 + return implode( ',', $row );
318 +
319 + }
320 +
321 + /**
278 322 * Searches entries by the given key/value pairs
279 323 *
280 324 * @since 3.0.0
281 325 *
@@ -291,12 +335,9 @@
291 335
292 336 global $wpdb;
293 337
294 338 // Prepare query.
295 - $query = $wpdb->prepare(
296 - 'SELECT * FROM %i',
297 - $wpdb->prefix . $this->table
298 - );
339 + $query = "SELECT * FROM `{$wpdb->prefix}{$this->table}`";
299 340
300 341 // Build where clauses.
301 342 $where_clauses = $this->build_where_clauses( $search, $api_result );
302 343
@@ -304,15 +345,15 @@
304 345 if ( count( $where_clauses ) ) {
305 346 $query .= ' WHERE ' . implode( ' AND ', $where_clauses );
306 347 }
307 348
349 + // Fallback to ordering by created_at if the order by column is invalid.
350 + if ( ! in_array( $order_by, $this->order_by_columns, true ) ) {
351 + $order_by = 'created_at';
352 + }
353 +
308 354 // Order.
309 - $query .= $wpdb->prepare(
310 - ' ORDER BY %i.%i',
311 - $wpdb->prefix . $this->table,
312 - $order_by
313 - );
314 - $query .= ' ' . ( strtolower( $order ) === 'asc' ? 'ASC' : 'DESC' );
355 + $query .= " ORDER BY `{$order_by}` " . ( strtolower( $order ) === 'asc' ? 'ASC' : 'DESC' );
315 356
316 357 // Limit.
317 358 if ( $page > 0 && $per_page > 0 ) {
318 359 $query .= $wpdb->prepare( ' LIMIT %d, %d', ( ( $page - 1 ) * $per_page ), $per_page );
@@ -336,13 +377,9 @@
336 377
337 378 global $wpdb;
338 379
339 380 // Prepare query.
340 - $query = $wpdb->prepare(
341 - 'SELECT COUNT(%i.id) FROM %i',
342 - $wpdb->prefix . $this->table,
343 - $wpdb->prefix . $this->table
344 - );
381 + $query = "SELECT COUNT(id) FROM `{$wpdb->prefix}{$this->table}`";
345 382
346 383 // Build where clauses.
347 384 $where_clauses = $this->build_where_clauses( $search, $api_result );
348 385
@@ -374,10 +411,10 @@
374 411 // Add search clause.
375 412 if ( $search ) {
376 413 $where_clauses[] = $wpdb->prepare(
377 414 '(first_name LIKE %s OR email LIKE %s)',
378 - '%' . $search . '%',
379 - '%' . $search . '%'
415 + '%' . $wpdb->esc_like( $search ) . '%',
416 + '%' . $wpdb->esc_like( $search ) . '%'
380 417 );
381 418 }
382 419
383 420 // Add API result clause.
@@ -418,14 +455,22 @@
418 455 *
419 456 * @since 3.0.0
420 457 *
421 458 * @param array $ids Entry IDs.
422 - * @return bool Success
459 + * @return int|bool Number of entries deleted, or false on error
423 460 */
424 461 public function delete_by_ids( $ids ) {
425 462
426 463 global $wpdb;
427 464
465 + // Map IDs as integers.
466 + $ids = array_values( array_filter( array_map( 'absint', $ids ) ) );
467 +
468 + // Bail if no IDs are provided.
469 + if ( empty( $ids ) ) {
470 + return 0;
471 + }
472 +
428 473 return $wpdb->query(
429 474 $wpdb->prepare(
430 475 sprintf(
431 476 'DELETE FROM %s WHERE id IN (%s)',
@@ -448,14 +493,9 @@
448 493 public function delete_all() {
449 494
450 495 global $wpdb;
451 496
452 - return $wpdb->query(
453 - $wpdb->prepare(
454 - 'TRUNCATE TABLE %i',
455 - $wpdb->prefix . $this->table
456 - )
457 - );
497 + return $wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}{$this->table}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
458 498
459 499 }
460 500
461 501 }