PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.2
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.2
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
← All changes | modules/cost-tracker/classes/class-cost-tracker-db.php +88 -68 6.1 → 6.2.2 View file →
@@ -133,13 +133,13 @@
133 133 public function update_db_cost( $current_version ) { //phpcs:ignore -- NOSONAR - complex.
134 134 if ( ! empty( $current_version ) ) {
135 135 if ( version_compare( $current_version, '1.0.8', '<' ) ) {
136 136 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
137 - $this->wpdb->query( "ALTER TABLE {$table} MODIFY COLUMN price decimal(26,8)" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
137 + $this->wpdb->query( "ALTER TABLE {$table} MODIFY COLUMN price decimal(26,8)" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is esc_sql'd and a table name cannot be a prepare() placeholder.
138 138 }
139 139 if ( version_compare( $current_version, '1.0.9', '<' ) ) {
140 140 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
141 - $this->wpdb->query( "ALTER TABLE {$table} DROP COLUMN author" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
141 + $this->wpdb->query( "ALTER TABLE {$table} DROP COLUMN author" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is esc_sql'd and a table name cannot be a prepare() placeholder.
142 142 }
143 143
144 144 if ( version_compare( $current_version, '1.0.13', '<' ) ) {
145 145 $costs = $this->get_cost_tracker_by( 'all' );
@@ -240,17 +240,11 @@
240 240 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- write operation; caching not applicable for UPDATE.
241 241 $wpdb->update( $this->table_name( 'cost_tracker' ), $update, array( 'id' => intval( $id ) ) );
242 242 $this->invalidate_cost_tracker_caches( $id );
243 243 return $this->get_cost_tracker_by( 'id', $id );
244 - } else {
245 - if ( isset( $update['id'] ) ) {
246 - unset( $update['id'] );
247 - }
248 -
249 - if ( $wpdb->insert( $this->table_name( 'cost_tracker' ), $update ) ) {
250 - $this->invalidate_cost_tracker_caches();
251 - return $this->get_cost_tracker_by( 'id', $wpdb->insert_id );
252 - }
244 + } elseif ( $wpdb->insert( $this->table_name( 'cost_tracker' ), $update ) ) {
245 + $this->invalidate_cost_tracker_caches();
246 + return $this->get_cost_tracker_by( 'id', $wpdb->insert_id );
253 247 }
254 248 return false;
255 249 }
256 250
@@ -269,26 +263,15 @@
269 263 if ( empty( $item_id ) ) {
270 264 return false;
271 265 }
272 266
273 - $obj_name = '';
267 + $selected_lists = array(
268 + 'site' => is_array( $selected_sites ) ? $selected_sites : array(),
269 + 'tag' => is_array( $selected_groups ) ? $selected_groups : array(),
270 + 'client' => is_array( $selected_clients ) ? $selected_clients : array(),
271 + );
274 272
275 - $new_obj_ids = array();
276 -
277 - if ( ! empty( $selected_sites ) ) {
278 - $obj_name = 'site';
279 - $new_obj_ids = $selected_sites;
280 - } elseif ( ! empty( $selected_groups ) ) {
281 - $obj_name = 'tag';
282 - $new_obj_ids = $selected_groups;
283 - } elseif ( ! empty( $selected_clients ) ) {
284 - $obj_name = 'client';
285 - $new_obj_ids = $selected_clients;
286 - }
287 -
288 - if ( ! empty( $obj_name ) ) {
289 - static::get_instance()->update_lookup_cost( $item_id, $obj_name, $new_obj_ids );
290 - } else {
273 + if ( ! array_filter( $selected_lists ) ) {
291 274 // to support saving cost without selected sites, tags, clients.
292 275 MainWP_DB::instance()->delete_lookup_items(
293 276 'object_name',
294 277 array(
@@ -296,10 +279,17 @@
296 279 'item_id' => $item_id,
297 280 'object_names' => array( 'site', 'tag', 'client' ),
298 281 )
299 282 );
283 + return true;
284 + }
300 285
286 + // A cost can be saved against sites, tags and clients at once, so each list is
287 + // synced against its own object name; an empty list prunes its own rows.
288 + foreach ( $selected_lists as $obj_name => $new_obj_ids ) {
289 + static::get_instance()->update_lookup_cost( $item_id, $obj_name, $new_obj_ids );
301 290 }
291 +
302 292 return true;
303 293 }
304 294
305 295 /**
@@ -318,10 +308,8 @@
318 308 if ( empty( $item_id ) || ! is_array( $new_obj_ids ) || ! in_array( $obj_name, $allows ) ) {
319 309 return false;
320 310 }
321 311
322 - $remove_obj_names = array_diff( $allows, array( $obj_name ) );
323 -
324 312 $found_look_ids = array();
325 313 $existed_look_id = array();
326 314
327 315 $results = MainWP_DB::instance()->get_lookup_items( 'cost', $item_id, $obj_name );
@@ -349,20 +337,8 @@
349 337 $remove_ids = array_diff( $found_look_ids, $new_look_ids );
350 338 if ( $remove_ids ) {
351 339 MainWP_DB::instance()->delete_lookup_items( 'lookup_id', array( 'lookup_ids' => $remove_ids ) );
352 340 }
353 -
354 - if ( ! empty( $remove_obj_names ) ) {
355 - MainWP_DB::instance()->delete_lookup_items(
356 - 'object_name',
357 - array(
358 - 'item_name' => 'cost',
359 - 'item_id' => $item_id,
360 - 'object_names' => $remove_obj_names,
361 -
362 - )
363 - );
364 - }
365 341 }
366 342
367 343 /**
368 344 * Method get_cost_tracker_by().
@@ -443,9 +419,9 @@
443 419 if ( ! empty( $selected_ids ) ) {
444 420 $where .= ' AND ct.id IN (' . implode( ',', $selected_ids ) . ') ';
445 421 }
446 422 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
447 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table is escaped with esc_sql(); $where and $limit are from internal arrays only
423 + // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is escaped with esc_sql(); $where holds id lists from wp_parse_id_list() and values escaped through prepare_fields_array(); $limit is built from intval()ed paging.
448 424 return $wpdb->get_results( "SELECT * FROM {$table} ct WHERE 1 {$where}{$limit}" );
449 425 }
450 426
451 427 $where = '';
@@ -459,15 +435,22 @@
459 435 return $cached;
460 436 }
461 437
462 438 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
463 - $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `id`=%d ", $value );
439 + $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `id`=%d ", $value ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is esc_sql'd.
464 440 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql().
465 441 $result = $wpdb->get_row( $sql, OBJECT );
466 442 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
467 443 return $result;
468 444 } elseif ( 'count' === $by ) {
469 - $cache_key = 'mainwp_cost_tracker_' . md5( wp_json_encode( array( 'method' => 'get_cost_tracker_by', 'by' => 'count' ) ) ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
445 + $cache_key = 'mainwp_cost_tracker_' . md5( // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
446 + wp_json_encode(
447 + array(
448 + 'method' => 'get_cost_tracker_by',
449 + 'by' => 'count',
450 + )
451 + )
452 + );
470 453 $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' );
471 454 if ( false !== $cached ) {
472 455 return $cached;
473 456 }
@@ -472,9 +455,9 @@
472 455 return $cached;
473 456 }
474 457
475 458 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
476 - $sql = "SELECT count(*) FROM {$table}";
459 + $sql = "SELECT count(*) FROM {$table}";
477 460 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with static table name that is escaped with esc_sql(); no dynamic input.
478 461 $result = $wpdb->get_var( $sql );
479 462 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
480 463 return $result;
@@ -482,13 +465,13 @@
482 465 $cost_ids = explode( ',', $value );
483 466 $cost_ids = MainWP_Utility::array_numeric_filter( $cost_ids );
484 467 if ( ! empty( $cost_ids ) ) {
485 468 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
486 - $sql = "SELECT * FROM {$table} WHERE `id` IN (" . implode( ',', $cost_ids ) . ' )';
469 + $sql = "SELECT * FROM {$table} WHERE `id` IN (" . implode( ',', $cost_ids ) . ' )';
487 470 }
488 471 } elseif ( 'site_id' === $by || 'client_id' === $by ) {
489 472 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
490 - $sql = "SELECT * FROM {$table}";
473 + $sql = "SELECT * FROM {$table}";
491 474 } elseif ( 'slug' === $by && is_string( $value ) ) {
492 475 if ( in_array( $product_type, array( 'plugin', 'theme' ), true ) ) {
493 476 $cache_key = $this->get_cache_key_for_cost_tracker( $by, $value, array( 'product_type' => $product_type ) );
494 477 $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' );
@@ -496,9 +479,9 @@
496 479 return $cached;
497 480 }
498 481
499 482 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
500 - $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `slug`=%s AND product_type = %s ", $value, $product_type );
483 + $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `slug`=%s AND product_type = %s ", $value, $product_type ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is esc_sql'd.
501 484 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql().
502 485 $result = $wpdb->get_row( $sql, OBJECT );
503 486 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
504 487 return $result;
@@ -507,9 +490,9 @@
507 490 if ( ! empty( $product_type ) ) {
508 491 $where = $wpdb->prepare( ' AND product_type = %s ', $product_type );
509 492 }
510 493 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
511 - // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $where is itself from $wpdb->prepare(); concatenation into prepare is safe here.
494 + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is esc_sql'd; $where is itself from $wpdb->prepare(), so concatenation into prepare is safe here.
512 495 $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `slug`=%s " . $where, $value );
513 496 }
514 497 }
515 498
@@ -596,14 +579,14 @@
596 579 $deleted = false;
597 580
598 581 if ( 'id' === $by ) {
599 582 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
600 - if ( $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE id=%d ", $value ) ) ) { //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- delete operation that invalidates caches below.
583 + if ( $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE id=%d ", $value ) ) ) { //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- delete operation that invalidates caches below; table name is esc_sql'd.
601 584 $deleted = true;
602 585 }
603 586 } else {
604 587 $table = esc_sql( $this->table_name( 'cost_tracker' ) );
605 - if ( $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE site_id=%d ", $value ) ) ) { //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- delete operation that invalidates caches below.
588 + if ( $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE site_id=%d ", $value ) ) ) { //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- delete operation that invalidates caches below; table name is esc_sql'd.
606 589 $deleted = true;
607 590 }
608 591 }
609 592
@@ -608,9 +591,9 @@
608 591 }
609 592
610 593 if ( $deleted ) {
611 594 $lookup_table = esc_sql( $this->table_name( 'lookup_item_objects' ) );
612 - $wpdb->query( $wpdb->prepare( "DELETE FROM {$lookup_table} WHERE item_id=%d AND item_name = \"cost\"", $value ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- delete operation; caching is not applicable for write operations.
595 + $wpdb->query( $wpdb->prepare( "DELETE FROM {$lookup_table} WHERE item_id=%d AND item_name = \"cost\"", $value ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- delete operation; caching is not applicable for write operations; table name is esc_sql'd.
613 596 $this->invalidate_cost_tracker_caches( 'id' === $by ? $value : null );
614 597 }
615 598
616 599 $snapshot_info = Cost_Tracker_Admin::get_costs_snapshot_info();
@@ -623,9 +606,8 @@
623 606 * @param bool $deleted Deleted.
624 607 * @param array $snapshot_info costs snapshot info.
625 608 *
626 609 * @since 6.0
627 - *
628 610 */
629 611 do_action( 'mainwp_cost_tracker_deleted', $by, $value, $deleted, $snapshot_info );
630 612
631 613 return $deleted;
@@ -677,10 +659,10 @@
677 659 $cache_key = 'mainwp_cost_tracker_all';
678 660 $result = wp_cache_get( $cache_key, 'mainwp_cost_tracker' );
679 661
680 662 if ( false === $result ) {
681 - $table = esc_sql( $this->table_name( 'cost_tracker' ) );
682 - $sql = "SELECT * FROM {$table}";
663 + $table = esc_sql( $this->table_name( 'cost_tracker' ) );
664 + $sql = "SELECT * FROM {$table}";
683 665 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql(); results cached immediately after retrieval
684 666 $result = $wpdb->get_results( $sql, OBJECT );
685 667 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
686 668 }
@@ -850,10 +832,10 @@
850 832 $cache_key = 'mainwp_cost_tracker_all';
851 833 $result = wp_cache_get( $cache_key, 'mainwp_cost_tracker' );
852 834
853 835 if ( false === $result ) {
854 - $table = esc_sql( $this->table_name( 'cost_tracker' ) );
855 - $sql = "SELECT * FROM {$table}";
836 + $table = esc_sql( $this->table_name( 'cost_tracker' ) );
837 + $sql = "SELECT * FROM {$table}";
856 838 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql(); results cached immediately after retrieval
857 839 $result = $wpdb->get_results( $sql, OBJECT );
858 840 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
859 841 }
@@ -952,9 +934,16 @@
952 934 $sum_data = isset( $params['sum_data'] ) ? $params['sum_data'] : '';
953 935 $where = '';
954 936 $sql = '';
955 937 if ( 'all' === $sum_data ) {
956 - $cache_key = 'mainwp_cost_tracker_' . md5( wp_json_encode( array( 'method' => 'get_summary_data', 'sum_data' => 'all' ) ) ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
938 + $cache_key = 'mainwp_cost_tracker_' . md5( // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
939 + wp_json_encode(
940 + array(
941 + 'method' => 'get_summary_data',
942 + 'sum_data' => 'all',
943 + )
944 + )
945 + );
957 946 $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' );
958 947 if ( false !== $cached ) {
959 948 return $cached;
960 949 }
@@ -959,10 +948,10 @@
959 948 return $cached;
960 949 }
961 950
962 951 $where .= ' AND co.cost_status = "active" AND co.type = "subscription" ';
963 - $table = esc_sql( $this->table_name( 'cost_tracker' ) );
964 - $sql = "SELECT * FROM {$table} co WHERE 1 {$where} ORDER BY co.next_renewal ASC ";
952 + $table = esc_sql( $this->table_name( 'cost_tracker' ) );
953 + $sql = "SELECT * FROM {$table} co WHERE 1 {$where} ORDER BY co.next_renewal ASC ";
965 954 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql().
966 955 $result = $wpdb->get_results( $sql );
967 956 wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' );
968 957 return $result;
@@ -1006,12 +995,12 @@
1006 995 */
1007 996 private function get_cache_key_for_cost_tracker( $by, $value, $params = array() ) {
1008 997 $identifier = wp_json_encode(
1009 998 array(
1010 - 'method' => 'get_cost_tracker_by',
1011 - 'by' => $by,
1012 - 'value' => $value,
1013 - 'params' => $params,
999 + 'method' => 'get_cost_tracker_by',
1000 + 'by' => $by,
1001 + 'value' => $value,
1002 + 'params' => $params,
1014 1003 )
1015 1004 );
1016 1005 return 'mainwp_cost_tracker_' . md5( $identifier ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1017 1006 }
@@ -1023,14 +1012,45 @@
1023 1012 *
1024 1013 * @return void
1025 1014 */
1026 1015 private function invalidate_cost_tracker_caches( $cost_id = null ) {
1027 - wp_cache_delete( 'mainwp_cost_tracker_' . md5( wp_json_encode( array( 'method' => 'get_cost_tracker_by', 'by' => 'count' ) ) ), 'mainwp_cost_tracker' ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1016 + wp_cache_delete(
1017 + 'mainwp_cost_tracker_' . md5( // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1018 + wp_json_encode(
1019 + array(
1020 + 'method' => 'get_cost_tracker_by',
1021 + 'by' => 'count',
1022 + )
1023 + )
1024 + ),
1025 + 'mainwp_cost_tracker'
1026 + );
1028 1027 wp_cache_delete( 'mainwp_cost_tracker_all', 'mainwp_cost_tracker' );
1029 - wp_cache_delete( 'mainwp_cost_tracker_' . md5( wp_json_encode( array( 'method' => 'get_summary_data', 'sum_data' => 'all' ) ) ), 'mainwp_cost_tracker' ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1028 + wp_cache_delete(
1029 + 'mainwp_cost_tracker_' . md5( // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1030 + wp_json_encode(
1031 + array(
1032 + 'method' => 'get_summary_data',
1033 + 'sum_data' => 'all',
1034 + )
1035 + )
1036 + ),
1037 + 'mainwp_cost_tracker'
1038 + );
1030 1039
1031 1040 if ( $cost_id ) {
1032 1041 $cost_id = absint( $cost_id );
1033 - wp_cache_delete( 'mainwp_cost_tracker_' . md5( wp_json_encode( array( 'method' => 'get_cost_tracker_by', 'by' => 'id', 'value' => $cost_id ) ) ), 'mainwp_cost_tracker' ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1042 + wp_cache_delete(
1043 + 'mainwp_cost_tracker_' . md5( // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes.
1044 + wp_json_encode(
1045 + array(
1046 + 'method' => 'get_cost_tracker_by',
1047 + 'by' => 'id',
1048 + 'value' => $cost_id,
1049 + )
1050 + )
1051 + ),
1052 + 'mainwp_cost_tracker'
1053 + );
1034 1054 }
1035 1055 }
1036 1056 }