__( 'Calls', 'dealroot' ), 'whatsapp' => 'WhatsApp', 'email' => __( 'Emails', 'dealroot' ), 'telegram' => 'Telegram', 'viber' => 'Viber', 'sms' => 'SMS', 'directions' => __( 'Directions', 'dealroot' ), ); } /** * Stores a click with the visitor's attribution. * * @param string $type Click type. * @param string $target Number, address or link. * @param string $page Page path. * @return int Event ID. */ public static function insert( $type, $target, $page ) { global $wpdb; if ( ! array_key_exists( $type, self::types() ) ) { return 0; } $columns = Attribution::to_columns( Attribution::from_request() ); $row = array( 'created_at' => current_time( 'mysql', true ), 'type' => $type, 'target' => mb_substr( sanitize_text_field( $target ), 0, 191 ), 'page' => mb_substr( sanitize_text_field( strtok( (string) $page, '#' ) ), 0, 255 ), 'channel' => $columns['channel'] ?? '', 'source' => $columns['source'] ?? '', 'medium' => $columns['medium'] ?? '', 'campaign' => $columns['campaign'] ?? '', 'last_channel' => $columns['last_channel'] ?? '', 'last_source' => $columns['last_source'] ?? '', 'last_medium' => $columns['last_medium'] ?? '', 'last_campaign' => $columns['last_campaign'] ?? '', 'landing_page' => mb_substr( (string) ( $columns['landing_page'] ?? '' ), 0, 255 ), ); // Click IDs, so a call that becomes a lead later can be reported to the ad platform. foreach ( array( 'gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid' ) as $click_id ) { $row[ $click_id ] = mb_substr( (string) ( $columns[ $click_id ] ?? '' ), 0, 191 ); } $inserted = $wpdb->insert( Installer::events_table(), $row ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery if ( $inserted ) { /** * Fires after a contact link click is stored. * * @param int $event_id Event ID. * @param array $row Stored values. */ do_action( 'dealroot_contact_click', (int) $wpdb->insert_id, $row ); } return $inserted ? (int) $wpdb->insert_id : 0; } /** * Click counts per group and type. * * @param string $from UTC start, empty for all time. * @param string $to UTC end, empty for now. * @param string $group Column to group by. * @param bool|string $by_channel True to keep the bought clicks of a source apart, "campaign" to split visits without a campaign by channel. * @return array Group key => array( type => count, '_total' => count ). */ public static function counts( $from, $to, $group, $by_channel = false ) { global $wpdb; $group = in_array( $group, Reports::GROUP_COLUMNS, true ) ? $group : 'channel'; $channel = $by_channel ? ( 0 === strpos( $group, 'last_' ) ? 'last_channel' : 'channel' ) : $group; list( $start, $end ) = Reports::sql_range( $from, $to ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT %i AS grp, %i AS chn, type, COUNT(*) AS total FROM %i WHERE created_at >= %s AND created_at < %s GROUP BY %i, %i, type', $group, $channel, Installer::events_table(), $start, $end, $group, $channel ) ); $counts = array(); foreach ( (array) $rows as $row ) { $key = Reports::split_key( $by_channel, (string) $row->grp, (string) $row->chn ); if ( ! isset( $counts[ $key ] ) ) { $counts[ $key ] = array( '_total' => 0 ); } $counts[ $key ][ $row->type ] = ( $counts[ $key ][ $row->type ] ?? 0 ) + (int) $row->total; $counts[ $key ]['_total'] += (int) $row->total; } return $counts; } /** * Whether any click has been counted yet. * * @return bool */ public static function has_data() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT 1 FROM %i LIMIT 1', Installer::events_table() ) ); } /** * Removes clicks older than the retention window. * * @param int $days Age in days. * @return int Rows deleted. */ public static function purge_older_than( $days ) { global $wpdb; if ( $days < 1 ) { return 0; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (int) $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE created_at < %s', Installer::events_table(), gmdate( 'Y-m-d H:i:s', time() - (int) $days * DAY_IN_SECONDS ) ) ); } }