install(); } /** * Install db. */ public function install() { global $wpdb; $currentVersion = get_site_option( 'mainwp_module_cost_tracker_db_version' ); $rslt = $this->query( "SHOW TABLES LIKE '" . $this->table_name( 'cost_tracker' ) . "'" ); if ( empty( static::num_rows( $rslt ) ) ) { $currentVersion = false; } if ( $currentVersion === $this->cost_tracker_db_version ) { return; } $charset_collate = $wpdb->get_charset_collate(); $sql = array(); $tbl = 'CREATE TABLE `' . $this->table_name( 'cost_tracker' ) . '` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` text NOT NULL, `url` text NOT NULL, `type` varchar(20) NOT NULL, `product_type` varchar(20) NOT NULL, `slug` varchar(191) NOT NULL DEFAULT "", `license_type` varchar(20) NOT NULL, `cost_status` varchar(20) NOT NULL, `payment_method` varchar(50) NOT NULL, `price` decimal(26,8) NOT NULL, `renewal_type` varchar(20) NOT NULL, `last_renewal` int(11) NOT NULL, `next_renewal` int(11) NOT NULL, `next_renewal_today` int(11) NOT NULL, `last_alert` int(11) NOT NULL, `cost_icon` varchar(64) NOT NULL DEFAULT "", `cost_color` varchar(64) NOT NULL DEFAULT "", `sites` text NOT NULL, `groups` text NOT NULL, `clients` text NOT NULL, `note` text NOT NULL'; if ( empty( $currentVersion ) ) { $tbl .= ', PRIMARY KEY (`id`) '; } $tbl .= ') ' . $charset_collate; $sql[] = $tbl; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // NOSONAR - WP compatible. foreach ( $sql as $query ) { dbDelta( $query ); } $this->update_db_cost( $currentVersion ); update_option( 'mainwp_module_cost_tracker_db_version', $this->cost_tracker_db_version ); } /** * Method update_db_cost(). * * @param array $current_version DB version number. * * @return void */ public function update_db_cost( $current_version ) { //phpcs:ignore -- NOSONAR - complex. if ( ! empty( $current_version ) ) { if ( version_compare( $current_version, '1.0.8', '<' ) ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $this->wpdb->query( "ALTER TABLE {$table} MODIFY COLUMN price decimal(26,8)" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } if ( version_compare( $current_version, '1.0.9', '<' ) ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $this->wpdb->query( "ALTER TABLE {$table} DROP COLUMN author" ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } if ( version_compare( $current_version, '1.0.13', '<' ) ) { $costs = $this->get_cost_tracker_by( 'all' ); foreach ( $costs as $cost ) { $obj_name = ''; if ( ! empty( $cost->sites ) ) { $items = json_decode( $cost->sites, true ); $obj_name = 'site'; } elseif ( ! empty( $cost->groups ) ) { $items = json_decode( $cost->groups, true ); $obj_name = 'tag'; } elseif ( ! empty( $cost->clients ) ) { $items = json_decode( $cost->clients, true ); $obj_name = 'client'; } else { continue; } if ( ! empty( $items ) ) { foreach ( $items as $it_id ) { $this->wpdb->insert( $this->table_name( 'lookup_item_objects' ), array( 'item_id' => $cost->id, 'item_name' => 'cost', 'object_id' => $it_id, 'object_name' => $obj_name, ) ); } } } } } } /** * Method update_cost_tracker(). * * @param array $update Cost tracker array data. * * @throws \MainWP_Exception Existed cost tracker error. * @return mixed Result */ public function update_cost_tracker( $update ) { //phpcs:ignore -- NOSONAR - complex. /** * WP database. * * @global object */ global $wpdb; if ( ! is_array( $update ) ) { return false; } $id = isset( $update['id'] ) ? $update['id'] : 0; if ( ! empty( $update['product_type'] ) && ! empty( $update['slug'] ) && in_array( $update['product_type'], array( 'plugin', 'theme' ), true ) ) { // check existed cost tracker for this plugin / theme . $current = $this->get_cost_tracker_by( 'slug', $update['slug'], $update['product_type'] ); if ( is_array( $current ) && ! empty( $current ) ) { $existed = false; if ( 1 === count( $current ) ) { $current = current( $current ); if ( is_object( $current ) ) { if ( ! empty( $update['id'] ) ) { if ( ! empty( $current->id ) && (int) $current->id !== (int) $update['id'] ) { $existed = true; // to fix. } elseif ( ! empty( $current->id ) ) { $id = $current->id; // to update. } } else { $existed = true; // to fix: existed one. } } } else { $existed = true; // to fix found multi items. } if ( $existed ) { $error = esc_html__( 'A cost tracker for this plugin already exists.', 'mainwp' ); if ( 'theme' === $update['product_type'] ) { $error = esc_html__( 'A cost tracker for this theme already exists.', 'mainwp' ); } throw new MainWP_Exception( esc_html( $error ) ); } } } if ( isset( $update['id'] ) ) { unset( $update['id'] ); } if ( ! empty( $id ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- write operation; caching not applicable for UPDATE. $wpdb->update( $this->table_name( 'cost_tracker' ), $update, array( 'id' => intval( $id ) ) ); $this->invalidate_cost_tracker_caches( $id ); return $this->get_cost_tracker_by( 'id', $id ); } else { if ( isset( $update['id'] ) ) { unset( $update['id'] ); } if ( $wpdb->insert( $this->table_name( 'cost_tracker' ), $update ) ) { $this->invalidate_cost_tracker_caches(); return $this->get_cost_tracker_by( 'id', $wpdb->insert_id ); } } return false; } /** * Method update_selected_lookup_cost(). * * @param int $item_id Cost id. * @param array $selected_sites selected sites. * @param array $selected_groups selected tags. * @param array $selected_clients selected clients. * * @return mixed Result */ public function update_selected_lookup_cost( $item_id, $selected_sites = false, $selected_groups = false, $selected_clients = false ) { if ( empty( $item_id ) ) { return false; } $obj_name = ''; $new_obj_ids = array(); if ( ! empty( $selected_sites ) ) { $obj_name = 'site'; $new_obj_ids = $selected_sites; } elseif ( ! empty( $selected_groups ) ) { $obj_name = 'tag'; $new_obj_ids = $selected_groups; } elseif ( ! empty( $selected_clients ) ) { $obj_name = 'client'; $new_obj_ids = $selected_clients; } if ( ! empty( $obj_name ) ) { static::get_instance()->update_lookup_cost( $item_id, $obj_name, $new_obj_ids ); } else { // to support saving cost without selected sites, tags, clients. MainWP_DB::instance()->delete_lookup_items( 'object_name', array( 'item_name' => 'cost', 'item_id' => $item_id, 'object_names' => array( 'site', 'tag', 'client' ), ) ); } return true; } /** * Method update_lookup_cost(). * * @param int $item_id item id to insert lookup value. * @param string $obj_name loockup object name. * @param array $new_obj_ids New|Update object ids. * * @return mixed Result */ public function update_lookup_cost( $item_id, $obj_name, $new_obj_ids ) { //phpcs:ignore -- NOSONAR - complex. $allows = array( 'site', 'tag', 'client' ); if ( empty( $item_id ) || ! is_array( $new_obj_ids ) || ! in_array( $obj_name, $allows ) ) { return false; } $remove_obj_names = array_diff( $allows, array( $obj_name ) ); $found_look_ids = array(); $existed_look_id = array(); $results = MainWP_DB::instance()->get_lookup_items( 'cost', $item_id, $obj_name ); if ( $results ) { foreach ( $results as $item ) { $found_look_ids[] = $item->lookup_id; if ( ! empty( $new_obj_ids ) && in_array( $item->object_id, $new_obj_ids ) ) { $existed_look_id[ $item->object_id ] = $item->lookup_id; } } } $new_look_ids = array(); foreach ( $new_obj_ids as $obj_id ) { if ( isset( $existed_look_id[ $obj_id ] ) ) { $new_look_ids[] = $existed_look_id[ $obj_id ]; continue; } $insert_id = MainWP_DB::instance()->insert_lookup_item( 'cost', $item_id, $obj_name, $obj_id ); if ( $insert_id ) { $new_look_ids[] = $this->wpdb->insert_id; } } $remove_ids = array_diff( $found_look_ids, $new_look_ids ); if ( $remove_ids ) { MainWP_DB::instance()->delete_lookup_items( 'lookup_id', array( 'lookup_ids' => $remove_ids ) ); } if ( ! empty( $remove_obj_names ) ) { MainWP_DB::instance()->delete_lookup_items( 'object_name', array( 'item_name' => 'cost', 'item_id' => $item_id, 'object_names' => $remove_obj_names, ) ); } } /** * Method get_cost_tracker_by(). * * @param string $by Get by. * @param mixed $value Value. * @param array $params Others params. * * @return mixed Result */ public function get_cost_tracker_by( $by = 'id', $value = null, $params = array() ) { //phpcs:ignore -- NOSONAR - complex method. global $wpdb; if ( ! is_array( $params ) ) { $params = array(); } $s = ''; $exclude = array(); $include = array(); $status = array(); $types = array(); $where = ''; $limit = ''; if ( $params && is_array( $params ) ) { $s = isset( $params['s'] ) ? $params['s'] : ''; $exclude = isset( $params['exclude'] ) && ! empty( $params['exclude'] ) ? wp_parse_id_list( $params['exclude'] ) : array(); $include = isset( $params['include'] ) && ! empty( $params['include'] ) ? wp_parse_id_list( $params['include'] ) : array(); $status = isset( $params['status'] ) && ! empty( $params['status'] ) ? wp_parse_list( $params['status'] ) : array(); $product_type = isset( $params['category'] ) && ! empty( $params['category'] ) ? wp_parse_list( $params['category'] ) : array(); $types = isset( $params['type'] ) && ! empty( $params['type'] ) ? wp_parse_list( $params['type'] ) : array(); $page = isset( $params['paged'] ) ? intval( $params['paged'] ) : false; $per_page = isset( $params['items_per_page'] ) ? intval( $params['items_per_page'] ) : false; if ( ! empty( $s ) ) { $where .= ' AND ( ct.id LIKE "%' . $this->escape( $s ) . '%" OR ct.name LIKE "%' . $this->escape( $s ) . '%" ) '; } if ( ! empty( $exclude ) ) { $where .= ' AND ct.id NOT IN (' . implode( ',', $exclude ) . ') '; } if ( ! empty( $include ) ) { $where .= ' AND ct.id IN (' . implode( ',', $include ) . ') '; } if ( ! empty( $status ) ) { $where .= ' AND ct.cost_status IN (' . $this->prepare_fields_array( $status ) . ') '; } if ( ! empty( $product_type ) && ! in_array( 'any', $product_type ) ) { $where .= ' AND ct.product_type IN (' . $this->prepare_fields_array( $product_type ) . ') '; } if ( ! empty( $types ) && ! in_array( 'any', $types ) ) { $where .= ' AND ct.type IN (' . $this->prepare_fields_array( $types ) . ') '; } if ( ! empty( $page ) && ! empty( $per_page ) ) { $limit = ' LIMIT ' . ( $page - 1 ) * $per_page . ',' . $per_page; } } $product_type = isset( $params['product_type'] ) ? $params['product_type'] : ''; $selected_ids = isset( $params['selected_ids'] ) ? $params['selected_ids'] : array(); if ( is_array( $selected_ids ) ) { $selected_ids = MainWP_Utility::array_numeric_filter( $selected_ids ); } else { $selected_ids = array(); } if ( 'all' === $by ) { if ( ! empty( $selected_ids ) ) { $where .= ' AND ct.id IN (' . implode( ',', $selected_ids ) . ') '; } $table = esc_sql( $this->table_name( 'cost_tracker' ) ); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table is escaped with esc_sql(); $where and $limit are from internal arrays only return $wpdb->get_results( "SELECT * FROM {$table} ct WHERE 1 {$where}{$limit}" ); } $where = ''; $limit = ''; $sql = ''; if ( 'id' === $by && is_numeric( $value ) ) { $cache_key = $this->get_cache_key_for_cost_tracker( $by, $value, $params ); $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false !== $cached ) { return $cached; } $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `id`=%d ", $value ); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with $wpdb->prepare() above; table name is escaped with esc_sql(). $result = $wpdb->get_row( $sql, OBJECT ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); return $result; } elseif ( 'count' === $by ) { $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. $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false !== $cached ) { return $cached; } $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT count(*) FROM {$table}"; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with static table name that is escaped with esc_sql(); no dynamic input. $result = $wpdb->get_var( $sql ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); return $result; } elseif ( 'id' === $by && false !== strpos( $value, ',' ) ) { $cost_ids = explode( ',', $value ); $cost_ids = MainWP_Utility::array_numeric_filter( $cost_ids ); if ( ! empty( $cost_ids ) ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT * FROM {$table} WHERE `id` IN (" . implode( ',', $cost_ids ) . ' )'; } } elseif ( 'site_id' === $by || 'client_id' === $by ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT * FROM {$table}"; } elseif ( 'slug' === $by && is_string( $value ) ) { if ( in_array( $product_type, array( 'plugin', 'theme' ), true ) ) { $cache_key = $this->get_cache_key_for_cost_tracker( $by, $value, array( 'product_type' => $product_type ) ); $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false !== $cached ) { return $cached; } $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `slug`=%s AND product_type = %s ", $value, $product_type ); $result = $wpdb->get_row( $sql, OBJECT ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); return $result; } else { $where = ''; if ( ! empty( $product_type ) ) { $where = $wpdb->prepare( ' AND product_type = %s ', $product_type ); } $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE `slug`=%s " . $where, $value ); } } $data = array(); if ( ! empty( $sql ) ) { $cache_key = $this->get_cache_key_for_cost_tracker( $by, $value, $params ); $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false !== $cached ) { return $cached; } // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed from escaped table names and prepared statements above $result = $wpdb->get_results( $sql, OBJECT ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); if ( $result ) { if ( 'site_id' === $by ) { $site_id = intval( $value ); foreach ( $result as $cost ) { if ( empty( $cost->id ) ) { continue; } $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array(); if ( is_array( $sites ) && in_array( $site_id, $sites ) ) { $data[] = $cost; } } } elseif ( 'client_id' === $by ) { $client_id = intval( $value ); foreach ( $result as $cost ) { if ( empty( $cost->id ) ) { continue; } $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array(); if ( is_array( $clients ) && in_array( $client_id, $clients ) ) { $data[] = $cost; } } } else { foreach ( $result as $cost ) { if ( empty( $cost->id ) ) { continue; } $data[] = $cost; } } } } return $data; } /** * Method prepare_fields_array(). * * @param array $values array of string. * * @return string Escaped string result. */ public function prepare_fields_array( $values = array() ) { $tmp = ''; foreach ( $values as $value ) { $tmp .= '"' . $this->escape( $value ) . '",'; } return rtrim( $tmp, ',' ); } /** * Method delete_cost_tracker(). * * @param string $by Delete by. * @param mixed $value Value. * * @return mixed Result */ public function delete_cost_tracker( $by = 'id', $value = null ) { global $wpdb; if ( empty( $value ) ) { return false; } if ( 'id' !== $by && 'site_id' !== $by ) { return false; } $deleted = false; if ( 'id' === $by ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); if ( $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE id=%d ", $value ) ) ) { //phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching -- delete operation that invalidates caches below. $deleted = true; } } else { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); 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. $deleted = true; } } if ( $deleted ) { $lookup_table = esc_sql( $this->table_name( 'lookup_item_objects' ) ); $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. $this->invalidate_cost_tracker_caches( 'id' === $by ? $value : null ); } return $deleted; } /** * Method get_all_cost_trackers_by_clients(). * * @param array $client_ids Client ids. * @param array $params Orther params. * * @return mixed Result */ public function get_all_cost_trackers_by_clients( $client_ids, $params = array() ) { //phpcs:ignore -- NOSONAR - complex method. if ( is_string( $client_ids ) ) { $client_ids = explode( ',', $client_ids ); } if ( ! is_array( $client_ids ) || empty( $client_ids ) ) { return array(); } if ( ! is_array( $params ) ) { $params = array(); } $get_sites_of_cost = isset( $params['get_cost_sites'] ) && $params['get_cost_sites'] ? true : false; $clients_site_ids = array(); $client_sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( $client_ids ); if ( $client_sites ) { foreach ( $client_sites as $website ) { if ( empty( $website->client_id ) ) { continue; } if ( ! isset( $clients_site_ids[ $website->client_id ] ) ) { $clients_site_ids[ $website->client_id ] = array(); } $clients_site_ids[ $website->client_id ][] = $website->id; } } global $wpdb; $clients_costs = array(); $clients_costs_ids = array(); $cache_key = 'mainwp_cost_tracker_all'; $result = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false === $result ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT * FROM {$table}"; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql uses esc_sql() for table name; results cached immediately after retrieval $result = $wpdb->get_results( $sql, OBJECT ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); } if ( $result ) { foreach ( $result as $cost ) { $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array(); $groups = ! empty( $cost->groups ) ? json_decode( $cost->groups, true ) : array(); $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array(); if ( ! is_array( $sites ) ) { $sites = array(); } if ( ! is_array( $groups ) ) { $groups = array(); } if ( ! is_array( $clients ) ) { $clients = array(); } if ( empty( $sites ) && empty( $groups ) && empty( $clients ) ) { continue; } // to reduce db queries for this case. if ( ! empty( $sites ) && empty( $groups ) && empty( $clients ) ) { foreach ( $clients_site_ids as $client_id => $client_site_ids ) { foreach ( $sites as $siteid ) { if ( in_array( $siteid, $client_site_ids ) ) { if ( ! isset( $clients_costs[ $client_id ] ) ) { $clients_costs[ $client_id ] = array(); } if ( ! isset( $clients_costs_ids[ $client_id ] ) ) { $clients_costs_ids[ $client_id ] = array(); } if ( $get_sites_of_cost ) { $cost->cost_sites_ids = $sites; // sites ids. } if ( ! in_array( $cost->id, $clients_costs_ids[ $client_id ] ) ) { $cost->count_sites = count( $sites ); $cost->number_client_costs_sites = array_intersect( $client_site_ids, $sites ); $clients_costs[ $client_id ][] = $cost; $clients_costs_ids[ $client_id ][] = $cost->id; } break; } } } continue; } $params = array( 'sites' => $sites, 'groups' => $groups, 'clients' => $clients, ); // to do: should create relation of: costs, sites, groups, clients in other way. // to make it more easier in queries. $cost_sites = MainWP_DB::instance()->get_db_sites( $params ); // get sites of cost tracker. $cost_sites_ids = array(); if ( is_array( $cost_sites ) ) { foreach ( $cost_sites as $cost_site ) { $cost_sites_ids[] = $cost_site->id; // sites ids of cost tracker. } foreach ( $clients_site_ids as $client_id => $client_site_ids ) { foreach ( $cost_sites as $cost_site ) { if ( in_array( $cost_site->id, $client_site_ids ) ) { if ( ! isset( $clients_costs[ $client_id ] ) ) { $clients_costs[ $client_id ] = array(); } if ( ! isset( $clients_costs_ids[ $client_id ] ) ) { $clients_costs_ids[ $client_id ] = array(); } if ( $get_sites_of_cost ) { $cost->cost_sites_ids = $cost_sites_ids; } if ( ! in_array( $cost->id, $clients_costs_ids[ $client_id ] ) ) { $cost->count_sites = count( $cost_sites ); $cost->number_client_costs_sites = array_intersect( $client_site_ids, $cost_sites_ids ); $clients_costs[ $client_id ][] = $cost; $clients_costs_ids[ $client_id ][] = $cost->id; } break; } } } } } } return $clients_costs; } /** * Method get_sites_of_cost(). * * Get sites of cost. * * @param object $cost Cost data. * * @return mixed Result */ public function get_sites_of_cost( $cost ) { //phpcs:ignore -- NOSONAR - complex method. if ( empty( $cost ) || ! is_object( $cost ) ) { return array(); } $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array(); // NOSONAR -- duplicate. $groups = ! empty( $cost->groups ) ? json_decode( $cost->groups, true ) : array(); // NOSONAR -- duplicate. $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array(); // NOSONAR -- duplicate. if ( ! is_array( $sites ) ) { // NOSONAR -- duplicate. $sites = array(); // NOSONAR -- duplicate. } if ( ! is_array( $groups ) ) { // NOSONAR -- duplicate. $groups = array(); // NOSONAR -- duplicate. } if ( ! is_array( $clients ) ) { // NOSONAR -- duplicate. $clients = array(); // NOSONAR -- duplicate. } if ( empty( $sites ) && empty( $groups ) && empty( $clients ) ) { // NOSONAR -- duplicate. return array(); // NOSONAR -- duplicate. } $params = array( // NOSONAR -- duplicate. 'sites' => $sites, // NOSONAR -- duplicate. 'groups' => $groups, // NOSONAR -- duplicate. 'clients' => $clients, // NOSONAR -- duplicate. ); return MainWP_DB::instance()->get_db_sites( $params ); // NOSONAR - get sites of cost tracker. } /** * Method get_all_cost_trackers_by_sites(). * * @param array $sites_ids Sites ids. * * @return mixed Result */ public function get_all_cost_trackers_by_sites( $sites_ids ) { //phpcs:ignore -- NOSONAR - complex method. if ( is_string( $sites_ids ) ) { $sites_ids = explode( ',', $sites_ids ); } if ( ! is_array( $sites_ids ) || empty( $sites_ids ) ) { return array(); } $sites_ids = array_map( 'intval', $sites_ids ); global $wpdb; $sites_costs = array(); $sites_costs_ids = array(); $cache_key = 'mainwp_cost_tracker_all'; $result = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false === $result ) { $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT * FROM {$table}"; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql uses esc_sql() for table name; results cached immediately after retrieval $result = $wpdb->get_results( $sql, OBJECT ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); } if ( $result ) { foreach ( $result as $cost ) { $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array(); $groups = ! empty( $cost->groups ) ? json_decode( $cost->groups, true ) : array(); $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array(); if ( ! is_array( $sites ) ) { $sites = array(); } if ( ! is_array( $groups ) ) { $groups = array(); } if ( ! is_array( $clients ) ) { $clients = array(); } if ( empty( $sites ) && empty( $groups ) && empty( $clients ) ) { continue; } // to reduce db queries for this case. if ( ! empty( $sites ) && empty( $groups ) && empty( $clients ) ) { foreach ( $sites_ids as $site_id ) { if ( in_array( $site_id, $sites ) ) { if ( ! isset( $sites_costs[ $site_id ] ) ) { $sites_costs[ $site_id ] = array(); } if ( ! isset( $sites_costs_ids[ $site_id ] ) ) { $sites_costs_ids[ $site_id ] = array(); } if ( ! in_array( $cost->id, $sites_costs_ids[ $site_id ] ) ) { $cost->count_sites = count( $sites ); $sites_costs[ $site_id ][] = $cost; $sites_costs_ids[ $site_id ][] = $cost->id; } } } continue; } $params = array( 'sites' => $sites, 'groups' => $groups, 'clients' => $clients, ); // to do: should create relation of: costs, sites, groups, clients in other way. // to make it more easier in queries. $cost_sites = MainWP_DB::instance()->get_db_sites( $params ); if ( is_array( $cost_sites ) ) { foreach ( $sites_ids as $site_id ) { foreach ( $cost_sites as $cost_site ) { if ( (int) $cost_site->id === $site_id ) { if ( ! isset( $sites_costs[ $site_id ] ) ) { $sites_costs[ $site_id ] = array(); } if ( ! isset( $sites_costs_ids[ $site_id ] ) ) { $sites_costs_ids[ $site_id ] = array(); } if ( ! in_array( $cost->id, $sites_costs_ids[ $site_id ] ) ) { $cost->count_sites = count( $cost_sites ); $sites_costs[ $site_id ][] = $cost; $sites_costs_ids[ $site_id ][] = $cost->id; } } } } } } } return $sites_costs; } /** * Method get_summary_data(). * * @param array $params Params. * * @return mixed Result */ public function get_summary_data( $params = array() ) { if ( ! is_array( $params ) ) { $params = array(); } global $wpdb; $sum_data = isset( $params['sum_data'] ) ? $params['sum_data'] : ''; $where = ''; $sql = ''; if ( 'all' === $sum_data ) { $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. $cached = wp_cache_get( $cache_key, 'mainwp_cost_tracker' ); if ( false !== $cached ) { return $cached; } $where .= ' AND co.cost_status = "active" AND co.type = "subscription" '; $table = esc_sql( $this->table_name( 'cost_tracker' ) ); $sql = "SELECT * FROM {$table} co WHERE 1 {$where} ORDER BY co.next_renewal ASC "; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $sql is safely constructed with esc_sql() table name and static WHERE clause $result = $wpdb->get_results( $sql ); wp_cache_set( $cache_key, $result, 'mainwp_cost_tracker' ); return $result; } else { return false; } } /** * Method update_next_renewal_today() * * @return void */ public function update_next_renewal_today() { $costs = $this->get_cost_tracker_by( 'all' ); if ( $costs ) { foreach ( $costs as $cost ) { $next_renewal = Cost_Tracker_Admin::get_next_renewal( $cost->last_renewal, $cost->renewal_type ); $next_today = Cost_Tracker_Admin::calc_next_renewal_today( $cost, $next_renewal ); if ( $next_today !== $cost->next_renewal_today ) { $update = array( 'id' => $cost->id, 'next_renewal_today' => $next_today, ); $this->update_cost_tracker( $update ); } } } update_option( 'module_cost_tracker_calc_today_next_renewal', gmdate( 'Y-m-d' ) ); } /** * Generate cache key for get_cost_tracker_by queries. * * @param string $by Query type. * @param mixed $value Query value. * @param array $params Additional parameters. * * @return string Cache key. */ private function get_cache_key_for_cost_tracker( $by, $value, $params = array() ) { $identifier = wp_json_encode( array( 'method' => 'get_cost_tracker_by', 'by' => $by, 'value' => $value, 'params' => $params, ) ); return 'mainwp_cost_tracker_' . md5( $identifier ); // NOSONAR - MD5 used for cache key generation only, not cryptographic (security) purposes. } /** * Invalidate cost tracker caches. * * @param int|null $cost_id Cost ID to invalidate specific caches, or null for all caches. * * @return void */ private function invalidate_cost_tracker_caches( $cost_id = null ) { 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. wp_cache_delete( 'mainwp_cost_tracker_all', 'mainwp_cost_tracker' ); 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. if ( $cost_id ) { $cost_id = absint( $cost_id ); 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. } } }