PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / core / Category.class.php

Category.class.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at core/Category.class.php

557 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn {
4
5 use Forge12\Shared\Logger;
6 use Forge12\Shared\LoggerInterface;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Class Category
14 * Handle the Categories
15 *
16 * @package forge12\contactform7\CF7DoubleOptIn
17 */
18 class Category {
19 /**
20 * Stores the DB ID for the entry
21 *
22 * @var int
23 */
24 private $id = 0;
25
26 /**
27 * The name of the category
28 *
29 * @var string
30 */
31 private $name = '';
32 /**
33 * Stores the timestamp the opt-in mail has been sent to the user.
34 *
35 * @var string
36 */
37 private $createtime = "";
38 /**
39 * Stores the timestamp the opt-in has been confirmed by the user.
40 */
41 private $updatetime = "";
42 private LoggerInterface $logger;
43
44 /**
45 * The constructor
46 */
47 public function __construct( LoggerInterface $logger, array $properties = array() ) {
48 $this->logger = $logger;
49 $this->get_logger()->info( 'Constructor started.', [ 'plugin' => 'double-opt-in' ] );
50
51 foreach ( $properties as $name => $value ) {
52 if ( isset( $this->{$name} ) ) {
53 $this->{$name} = $value;
54 $this->get_logger()->debug( 'Property ' . $name . ' set.', [
55 'plugin' => 'double-opt-in',
56 'property' => $name,
57 'value' => $value,
58 ] );
59 } else {
60 $this->get_logger()->warning( 'Attempted to set a non-existent property.', [
61 'plugin' => 'double-opt-in',
62 'property' => $name,
63 ] );
64 }
65 }
66
67 $this->get_logger()->info( 'Constructor finished successfully.', [ 'plugin' => 'double-opt-in' ] );
68 }
69
70 public function get_logger() {
71 return $this->logger;
72 }
73
74 /**
75 * Return the ID of the Optin
76 *
77 * @return int
78 */
79 public function get_id() {
80 return $this->id;
81 }
82
83 /**
84 * Return the name of the category
85 *
86 * @return string
87 */
88 public function get_name() {
89 return $this->name;
90 }
91
92 /**
93 * Set the name of the category
94 *
95 * @param string $name
96 *
97 * @return void
98 */
99 public function set_name( $name ) {
100 $this->name = $name;
101 }
102
103 /**
104 * @param string $timestamp
105 */
106 public function set_createtime( $timestamp ) {
107 $this->createtime = $timestamp;
108 }
109
110 /**
111 * Return a timestamp
112 *
113 * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
114 * formatted string.
115 *
116 * @return string
117 */
118 public function get_createtime( $view = "raw" ) {
119 $this->get_logger()->info( 'Getting creation time.', [
120 'plugin' => 'double-opt-in',
121 'view' => $view
122 ] );
123
124 if ( empty( $this->createtime ) ) {
125 $this->get_logger()->warning( 'Creation time is empty. Setting to current time.', [
126 'plugin' => 'double-opt-in'
127 ] );
128 $this->set_createtime( time() );
129 }
130
131 if ( $view != "raw" ) {
132 $this->get_logger()->debug( 'Processing creation time for formatted view.', [
133 'plugin' => 'double-opt-in',
134 'time_value' => $this->createtime
135 ] );
136
137 if ( ! is_numeric( $this->createtime ) ) {
138 $date = date( 'd.m.Y', strtotime( $this->createtime ) );
139 $time = date( 'H:i:s', strtotime( $this->createtime ) );
140 $this->get_logger()->debug( 'Creation time is not numeric. Converting from string.', [
141 'plugin' => 'double-opt-in'
142 ] );
143 } else {
144 $date = date( 'd.m.Y', $this->createtime );
145 $time = date( 'H:i:s', $this->createtime );
146 $this->get_logger()->debug( 'Creation time is numeric. Converting from timestamp.', [
147 'plugin' => 'double-opt-in'
148 ] );
149 }
150
151 $formatted_time = $date . ' ' . __( '/', 'double-opt-in' ) . ' ' . $time;
152 $this->get_logger()->info( 'Returning formatted creation time.', [
153 'plugin' => 'double-opt-in',
154 'formatted_value' => $formatted_time
155 ] );
156 return $formatted_time;
157 } else {
158 $this->get_logger()->info( 'Returning raw creation time.', [
159 'plugin' => 'double-opt-in',
160 'raw_value' => $this->createtime
161 ] );
162 return $this->createtime;
163 }
164 }
165
166 /**
167 * @param string $timestamp
168 */
169 public function set_updatetime( $timestamp ) {
170 $this->updatetime = $timestamp;
171 }
172
173 /**
174 * Return a timestamp
175 *
176 * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
177 * formatted string.
178 *
179 * @return string
180 */
181 public function get_updatetime( $view = 'raw' ) {
182 $this->get_logger()->info( 'Retrieving update time.', [
183 'plugin' => 'double-opt-in',
184 'view' => $view
185 ] );
186
187 if ( empty( $this->updatetime ) ) {
188 $this->get_logger()->warning( 'Update time is empty. Setting to current time.', [
189 'plugin' => 'double-opt-in'
190 ] );
191 $this->set_updatetime( time() );
192 }
193
194 if ( $view != "raw" ) {
195 $this->get_logger()->debug( 'Processing update time for formatted view.', [
196 'plugin' => 'double-opt-in'
197 ] );
198
199 if ( ! is_numeric( $this->updatetime ) ) {
200 $date = date( 'd.m.Y', strtotime( $this->updatetime ) );
201 $time = date( 'H:i:s', strtotime( $this->updatetime ) );
202 $this->get_logger()->debug( 'Update time is not numeric; converting from string.', [
203 'plugin' => 'double-opt-in'
204 ] );
205 } else {
206 $date = date( 'd.m.Y', $this->updatetime );
207 $time = date( 'H:i:s', $this->updatetime );
208 $this->get_logger()->debug( 'Update time is numeric; converting from timestamp.', [
209 'plugin' => 'double-opt-in'
210 ] );
211 }
212
213 $formatted_time = $date . ' ' . __( '/', 'double-opt-in' ) . ' ' . $time;
214 $this->get_logger()->info( 'Returning formatted update time.', [
215 'plugin' => 'double-opt-in',
216 'formatted_value' => $formatted_time
217 ] );
218 return $formatted_time;
219 } else {
220 $this->get_logger()->info( 'Returning raw update time.', [
221 'plugin' => 'double-opt-in',
222 'raw_value' => $this->updatetime
223 ] );
224 return $this->updatetime;
225 }
226 }
227
228 /**
229 * Return the link to the category
230 *
231 * @return string
232 */
233 public function get_link_ui() {
234 $link = admin_url( 'admin.php?page=f12-cf7-doubleoptin_categories_view&id=' . $this->get_id() );
235
236 $this->get_logger()->info( 'Generating UI link for admin page.', [
237 'plugin' => 'double-opt-in',
238 'link' => $link,
239 'id' => $this->get_id(),
240 ] );
241
242 return $link;
243 }
244
245 /**
246 * Return a list of opt ins stored in the database.
247 *
248 * @param $atts array (
249 * 'perPage' => 10, // Posts per page
250 * 'page' => 0, // Current page
251 * 'order' => DESC, // Order (ASC/DESC)
252 * );
253 *
254 * @param null $numberOfPages - Stores the number of pages found if limited
255 *
256 * @return array<Category>
257 */
258 public static function get_list( $atts = array(), &$numberOfPages = null ) {
259 global $wpdb;
260
261 // Log the start of the get_list function.
262 Logger::getInstance()->info( 'Fetching a list of categories.', [
263 'plugin' => 'double-opt-in',
264 'atts' => $atts,
265 ] );
266
267 $attr = array(
268 'perPage' => 10,
269 'page' => 1,
270 'order' => 'DESC',
271 'orderBy' => 'ID',
272 );
273
274 foreach ( $atts as $key => $value ) {
275 if ( isset( $attr[ $key ] ) ) {
276 $attr[ $key ] = $atts[ $key ];
277 }
278 }
279
280 // Whitelist allowed columns for ORDER BY to prevent SQL injection
281 $allowed_order_by = array( 'ID', 'id', 'name', 'createtime', 'updatetime' );
282
283 // Validate orderBy against whitelist
284 if ( ! in_array( $attr['orderBy'], $allowed_order_by, true ) ) {
285 $attr['orderBy'] = 'ID';
286 }
287
288 // Validate order against whitelist
289 if ( ! in_array( strtoupper( $attr['order'] ), array( 'ASC', 'DESC' ), true ) ) {
290 $attr['order'] = 'DESC';
291 }
292
293 // Log the final attributes used for the query.
294 Logger::getInstance()->debug( 'Using the following attributes for the query.', [
295 'plugin' => 'double-opt-in',
296 'attributes' => $attr,
297 ] );
298
299 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin_categories';
300 $pageNum = (int) $attr['page'] - 1;
301
302 if ( $numberOfPages !== null ) {
303 // Log the start of counting items for pagination.
304 Logger::getInstance()->info( 'Counting total items for pagination.', [
305 'plugin' => 'double-opt-in',
306 ] );
307
308 $result = $wpdb->get_row( 'SELECT count(*) AS counter FROM ' . $tableName );
309
310 if ( $result ) {
311 $itemCounter = $result->counter;
312
313 $numberOfPages = 1;
314
315 if ( $attr['perPage'] != - 1 ) {
316 $numberOfPages = ceil( $itemCounter / (int) $attr['perPage'] );
317 }
318 // Log the calculated number of pages.
319 Logger::getInstance()->info( 'Calculated number of pages.', [
320 'plugin' => 'double-opt-in',
321 'total_items' => $itemCounter,
322 'items_per_page' => $attr['perPage'],
323 'total_pages' => $numberOfPages,
324 ] );
325 } else {
326 // Log a potential error if the count query fails.
327 Logger::getInstance()->error( 'Failed to count total items for pagination.', [
328 'plugin' => 'double-opt-in',
329 'query' => $wpdb->last_query,
330 'error' => $wpdb->last_error,
331 ] );
332 }
333 }
334
335 $offset = $pageNum * (int) $attr['perPage'];
336
337 $limit = '';
338
339 if ( $attr['perPage'] != - 1 ) {
340 $limit = ' LIMIT ' . (int) $attr['perPage'] . ' OFFSET ' . $offset;
341 }
342
343 $query = 'SELECT * FROM ' . $tableName . ' ORDER BY ' . $attr['orderBy'] . ' ' . $attr['order'] . $limit;
344 // Log the final query before execution.
345 Logger::getInstance()->debug( 'Executing database query to fetch categories.', [
346 'plugin' => 'double-opt-in',
347 'query' => $query,
348 ] );
349
350 $rows = $wpdb->get_results( $query, ARRAY_A );
351
352 if ( $rows === null ) {
353 // Log a potential error if the main query fails.
354 Logger::getInstance()->error( 'Failed to fetch categories from the database.', [
355 'plugin' => 'double-opt-in',
356 'query' => $wpdb->last_query,
357 'error' => $wpdb->last_error,
358 ] );
359 }
360
361 $list = array();
362 foreach ( $rows as $row ) {
363 $list[] = new Category( Logger::getInstance(), $row );
364 }
365
366 // Log the number of items retrieved.
367 Logger::getInstance()->info( 'Successfully retrieved ' . count($list) . ' categories.', [
368 'plugin' => 'double-opt-in',
369 'count' => count( $list ),
370 ] );
371
372 return $list;
373 }
374
375 /**
376 * Return a Category by the given ID
377 *
378 * @param $id
379 *
380 * @return Category|null
381 */
382 public static function get_by_id( $id ) {
383 global $wpdb;
384
385 // Log the start of the get_by_id function.
386 Logger::getInstance()->info( 'Attempting to retrieve category by ID.', [
387 'plugin' => 'double-opt-in',
388 'id' => $id,
389 ] );
390
391 $table = $wpdb->prefix . 'f12_cf7_doubleoptin_categories';
392
393 $query = $wpdb->prepare( 'SELECT * FROM ' . $table . ' WHERE id=%d', $id );
394
395 // Log the prepared query before execution.
396 Logger::getInstance()->debug( 'Executing database query to get category by ID.', [
397 'plugin' => 'double-opt-in',
398 'query' => $query,
399 ] );
400
401 $rows = $wpdb->get_results( $query, ARRAY_A );
402
403 if ( is_array( $rows ) && ! empty( $rows ) ) {
404 $rows = $rows[0];
405
406 // Log the successful retrieval of the category.
407 Logger::getInstance()->info( 'Category found and retrieved successfully.', [
408 'plugin' => 'double-opt-in',
409 'id' => $id,
410 'category' => $rows,
411 ] );
412
413 return new Category( Logger::getInstance(), $rows );
414 }
415
416 // Log that no category was found with the given ID.
417 Logger::getInstance()->warning( 'No category found with the given ID.', [
418 'plugin' => 'double-opt-in',
419 'id' => $id,
420 ] );
421
422 return null;
423 }
424
425 /**
426 * Delete a Category by the given id. All assigned Opt-Ins will be changed to be uncategorized.
427 *
428 * @param $id
429 *
430 * @return bool|int|\mysqli_result|resource|null
431 */
432 public static function delete_by_id( $id ) {
433 global $wpdb;
434
435 // Log the start of the deletion process.
436 Logger::getInstance()->info( 'Attempting to delete category by ID.', [
437 'plugin' => 'double-opt-in',
438 'id' => $id,
439 ] );
440
441 $table = $wpdb->prefix . 'f12_cf7_doubleoptin_categories';
442
443 // Ensure that the opt in class exists otherwise do nothing.
444 if ( class_exists( 'forge12\contactform7\CF7DoubleOptIn\OptIn' ) ) {
445 // Log the action before updating the category in the OptIn class.
446 Logger::getInstance()->info( 'Updating category for related opt-ins before deletion.', [
447 'plugin' => 'double-opt-in',
448 'old_id' => $id,
449 'new_id' => 0,
450 ] );
451
452 OptIn::bulk_update_category( $id, 0 );
453
454 // Log the deletion from the database.
455 $result = $wpdb->delete( $table, array( 'id' => $id ), array( '%d' ) );
456
457 if ( $result === false ) {
458 // Log an error if the deletion failed.
459 Logger::getInstance()->error( 'Failed to delete category from the database.', [
460 'plugin' => 'double-opt-in',
461 'id' => $id,
462 'error' => $wpdb->last_error,
463 ] );
464 } else {
465 // Log a successful deletion.
466 Logger::getInstance()->notice( 'Category deleted successfully.', [
467 'plugin' => 'double-opt-in',
468 'id' => $id,
469 'rows_deleted' => $result,
470 ] );
471 }
472
473 return $result;
474 }
475
476 // Log that the required class does not exist.
477 Logger::getInstance()->warning( 'Required OptIn class does not exist. Deletion process aborted.', [
478 'plugin' => 'double-opt-in',
479 'id' => $id,
480 ] );
481
482 return false;
483 }
484
485 /**
486 * Update or create the given Object
487 */
488 public function save() {
489 global $wpdb;
490
491 $this->get_logger()->info( 'Attempting to save category data.', [ 'plugin' => 'double-opt-in' ] );
492
493 $table = $wpdb->prefix . 'f12_cf7_doubleoptin_categories';
494
495 if ( ! empty( $this->get_id() ) && $this->get_id() > 0 ) {
496 $this->get_logger()->info( 'Updating existing category.', [
497 'plugin' => 'double-opt-in',
498 'id' => $this->get_id(),
499 ] );
500
501 $result = $wpdb->update( $table, array(
502 'name' => $this->get_name(),
503 'updatetime' => $this->get_updatetime()
504 ), array(
505 'id' => $this->get_id()
506 ) );
507
508 if ( $result === false ) {
509 $this->get_logger()->error( 'Failed to update category.', [
510 'plugin' => 'double-opt-in',
511 'id' => $this->get_id(),
512 'name' => $this->get_name(),
513 'error' => $wpdb->last_error,
514 ] );
515 } else {
516 $this->get_logger()->notice( 'Category updated successfully.', [
517 'plugin' => 'double-opt-in',
518 'id' => $this->get_id(),
519 'rows_affected' => $result,
520 ] );
521 }
522
523 return $result;
524 } else {
525 $this->get_logger()->info( 'Inserting new category.', [ 'plugin' => 'double-opt-in' ] );
526
527 $result = $wpdb->insert( $table, array(
528 'name' => $this->get_name(),
529 'createtime' => $this->get_createtime(),
530 'updatetime' => $this->get_updatetime(),
531 ) );
532
533 if ( $result > 0 ) {
534 $this->id = $wpdb->insert_id;
535 $this->get_logger()->notice( 'New category inserted successfully.', [
536 'plugin' => 'double-opt-in',
537 'id' => $this->id,
538 'name' => $this->get_name(),
539 ] );
540 return $this->id;
541 } else {
542 $this->get_logger()->error( 'Failed to insert new category.', [
543 'plugin' => 'double-opt-in',
544 'name' => $this->get_name(),
545 'error' => $wpdb->last_error,
546 ] );
547 }
548 }
549
550 $this->get_logger()->critical( 'Save operation failed for an unknown reason.', [
551 'plugin' => 'double-opt-in',
552 ] );
553
554 return false;
555 }
556 }
557 }