driver = $driver; } /** * Insert a record * * @param array $record New record. * * @return int */ public function insert( $record ) { if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) { return false; } /** * Filter allows modification of record information * * @param array $record * * @return array */ $record = apply_filters( 'mainwp_module_log_record_array', $record ); $data = $this->sanitize_record( $record ); if ( empty( $data ) ) { return false; } $record_id = $this->driver->insert_record( $data ); if ( ! $record_id ) { /** * Fires on a record insertion error * * @param array $record * @param mixed $result */ do_action( 'mainwp_module_log_record_insert_error', $record, false ); return false; } /** * Fires after a record has been inserted * * @param int $record_id * @param array $record */ do_action( 'mainwp_module_log_record_inserted', $record_id, $record ); return absint( $record_id ); } /** * Ensure the record matches our schema. * * @param array $record Record to store. * * @return array */ protected function sanitize_record( $record ) { if ( ! is_array( $record ) ) { return array(); } $record_defaults = array( 'site_id' => null, 'user_id' => null, 'object_id' => null, 'created' => null, 'item' => null, 'connector' => null, 'context' => null, 'action' => null, 'state' => null, 'duration' => null, 'meta' => array(), ); // Records can have only these fields. $record = array_intersect_key( $record, $record_defaults ); // Sanitize all record values. return array_map( function ( $value ) { if ( ! is_array( $value ) ) { return wp_strip_all_tags( $value ); } return $value; }, $record ); } /** * Get logs records * * @param array $args Arguments to filter result by. * * @return array Log Records */ public function get_records( $args ) { $defaults = array( // Search param. 'search' => null, 'search_field' => 'item', 'records_per_page' => get_option( 'posts_per_page', 20 ), 'paged' => 1, // Order. 'order' => 'desc', 'orderby' => 'date', ); $args = wp_parse_args( $args, $defaults ); /** * Filter allows additional arguments to query $args * * @return array Array of query arguments */ $args = apply_filters( 'mainwp_module_log_query_args', $args ); $result = (array) $this->driver->get_records( $args ); $this->found_records_count = isset( $result['count'] ) ? $result['count'] : 0; return empty( $result['items'] ) ? array() : $result['items']; } /** * Helper function, backwards compatibility * * @param array $args Argument to filter result by. * * @return array Log Records */ public function query( $args ) { return $this->get_records( $args ); } /** * Return the number of records found in last request * * @return int */ public function get_found_records_count() { return $this->found_records_count; } /** * Public getter to return table names * * @return array */ public function get_table_names() { return $this->driver->get_table_names(); } /** * Create compact logs and erase records from the database. * * @param int $start_time start time to compact. * @param int $end_time end time to compact. * * @return mixed results. */ public function create_compact_and_erase_records( $start_time, $end_time ) { //phpcs:ignore -- NOSONAR - complex. global $wpdb; $where_compact = $wpdb->prepare( ' AND `logs`.`created` >= %d AND `logs`.`created` <= %d ', $start_time, $end_time ); $sql = "SELECT `logs`.* FROM {$wpdb->mainwp_tbl_logs} logs WHERE `logs`.`connector` != 'compact' " . $where_compact; $logs = MainWP_DB::instance()->query( $sql ); $stats_data = array(); $done = false; while ( $logs && ( $item = MainWP_DB::fetch_object( $logs ) ) ) { $conn = $item->connector; $cont = $item->context; $act = $item->action; if ( empty( $conn ) || empty( $cont ) || empty( $act ) ) { continue; } $year = (int) gmdate( 'Y', $item->created ); if ( $year < 2020 ) { return; } if ( ! isset( $stats_data[ $year ] ) ) { $stats_data[ $year ] = array(); } if ( ! isset( $stats_data[ $year ][ $conn ] ) ) { $stats_data[ $year ][ $conn ] = array(); } if ( ! isset( $stats_data[ $year ][ $conn ][ $cont ] ) ) { $stats_data[ $year ][ $conn ][ $cont ] = array(); } if ( ! isset( $stats_data[ $year ][ $conn ][ $cont ][ $act ] ) ) { $stats_data[ $year ][ $conn ][ $cont ][ $act ] = array( 'count' => 0, 'duration' => 0, ); } $stats_data[ $year ][ $conn ][ $cont ][ $act ]['count'] += 1; $stats_data[ $year ][ $conn ][ $cont ][ $act ]['duration'] += $item->duration; $done = true; } if ( $done ) { foreach ( $stats_data as $year => $data ) { $year_data = array( 'data' => $data, ); do_action( 'mainwp_compact_action', 'saved', $year, $year_data, $start_time, $end_time ); } $this->erase_log_records( $start_time, $end_time ); } } /** * Clears logs records from the database. * * @param int $start_time start time. * @param int $end_time start end. * * @return int results number. */ private function erase_log_records( $start_time, $end_time ) { global $wpdb; $where = $wpdb->prepare( ' AND `logs`.`created` >= %d AND `logs`.`created` <= %d', $start_time, $end_time ); return $wpdb->query( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Destructive operation; caching DELETE is inappropriate. "DELETE `logs`, `meta` FROM {$wpdb->mainwp_tbl_logs} AS `logs` LEFT JOIN {$wpdb->mainwp_tbl_logs_meta} AS `meta` ON `meta`.`meta_log_id` = `logs`.`log_id` WHERE `logs`.`connector` != 'compact' " . $where // phpcs:ignore -- escaped. ); } /** * Metho check if site action log existed. * * @param int $site_id Site Id. * @param string $object_id Object Id. * * @return mixed Results. */ public function is_site_action_log_existed( $site_id, $object_id ) { global $wpdb; $cache_key = 'mainwp_log_exists_' . md5( $site_id . '_' . $object_id ); // NOSONAR - MD5 used for cache key generation only, not cryptographic purposes. $cached = wp_cache_get( $cache_key ); if ( false !== $cached ) { return $cached; } $result = $wpdb->query( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->prepare( "SELECT `log_id` FROM {$wpdb->mainwp_tbl_logs} WHERE `site_id` = %d AND `object_id` = %s LIMIT 1 ", $site_id, $object_id ) ); wp_cache_set( $cache_key, $result, '', HOUR_IN_SECONDS ); return $result; } }