prefix . 'dpsupport_activity'; } public function add( $ticket_id, $action, $message = '', $user_id = 0 ) { global $wpdb; $ticket_id = absint( $ticket_id ); if ( ! $ticket_id ) { return false; } $user_id = $user_id ? absint( $user_id ) : absint( get_current_user_id() ); $user = $user_id ? get_user_by( 'id', $user_id ) : false; return false !== $wpdb->insert( $this->table(), array( 'ticket_id' => $ticket_id, 'user_id' => $user_id ?: null, 'user_name' => $user ? sanitize_text_field( $user->display_name ) : '', 'action' => sanitize_key( $action ), 'message' => sanitize_text_field( $message ), 'created_at' => current_time( 'mysql' ), ), array( '%d', '%d', '%s', '%s', '%s', '%s' ) ); } public function get_for_ticket( $ticket_id ) { global $wpdb; $ticket_id = absint( $ticket_id ); if ( ! $ticket_id ) { return array(); } $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE ticket_id = %d ORDER BY id ASC', $this->table(), $ticket_id ) ); return is_array( $rows ) ? $rows : array(); } public function creation_source( $ticket_id ) { $map = $this->creation_sources( array( $ticket_id ) ); return $map[ absint( $ticket_id ) ] ?? 'unknown'; } public function creation_sources( array $ticket_ids ) { global $wpdb; $ticket_ids = array_values( array_unique( array_filter( array_map( 'absint', $ticket_ids ) ) ) ); if ( ! $ticket_ids ) { return array(); } $placeholders = implode( ',', array_fill( 0, count( $ticket_ids ), '%d' ) ); $sql = 'SELECT id, ticket_id, user_id, action, message FROM %i WHERE ticket_id IN (' . $placeholders . ") AND action IN ('customer_created','frontend_created','ticket_created') ORDER BY id ASC"; $prepare_args = array_merge( array( $this->table() ), $ticket_ids ); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL is built only from a fixed SELECT plus locally generated integer placeholders and is fully prepared on the next line. $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier and ticket values are placeholders; IN placeholders are generated locally from integer IDs. $wpdb->prepare( $sql, $prepare_args ) ); $result = array_fill_keys( $ticket_ids, 'unknown' ); foreach ( (array) $rows as $row ) { $ticket_id = absint( $row->ticket_id ?? 0 ); if ( ! $ticket_id || 'unknown' !== ( $result[ $ticket_id ] ?? 'unknown' ) ) { continue; } switch ( sanitize_key( (string) ( $row->action ?? '' ) ) ) { case 'customer_created': $result[ $ticket_id ] = 'customer_portal'; break; case 'frontend_created': $result[ $ticket_id ] = ! empty( $row->user_id ) ? 'frontend_customer' : 'frontend_guest'; break; case 'ticket_created': $result[ $ticket_id ] = 'backend'; break; } } return $result; } } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching