| | |
| | | <?php |
| | | namespace JVBase\integrations\services; |
| | | use JVBase\integrations\Admin; |
| | | use JVBase\integrations\Instance; |
| | | use JVBase\integrations\Integrations; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | |
| | | |
| | | class Umami extends Integrations |
| | | { |
| | | private string $website_id; |
| | | private string $api_url; |
| | | private string $api_token; |
| | | |
| | | // Tracking configuration |
| | | private array $valid_events = [ |
| | | 'view_feed', |
| | | 'view_taxonomy', |
| | | 'view_profile', |
| | | 'view_shop', |
| | | 'view_content', |
| | | 'toggle_favourite', |
| | | 'click_profile', |
| | | 'click_content', |
| | | 'click_taxonomy', |
| | | 'click_shop', |
| | | 'user_session' |
| | | ]; |
| | | |
| | | // Database tables |
| | | private string $events_table; |
| | | private string $metrics_table; |
| | | |
| | | protected static array $instances = []; |
| | | public static function getInstance(?int $userID = null):self |
| | | { |
| | | $key = is_null($userID) ? 'base' : $userID; |
| | | if (!array_key_exists($key, self::$instances)) { |
| | | self::$instances[$key] = new static($userID); |
| | | } |
| | | return self::$instances[$key]; |
| | | } |
| | | |
| | | protected function __construct(?int $userID = null) |
| | | { |
| | | $this->service_name = 'umami'; |
| | | $this->title = 'Umami.js'; |
| | | $this->icon = 'chart-line'; |
| | | $this->apiBase = ''; // Set dynamically based on credentials |
| | | $this->apiEndpoints = [ |
| | | 'api/websites', |
| | | 'api/websites/{websiteId}/stats', |
| | | 'api/websites/{websiteId}/pageviews', |
| | | 'api/websites/{websiteId}/events' |
| | | ]; |
| | | |
| | | $this->fields = [ |
| | | 'website_id' => [ |
| | | 'label' => 'Website ID', |
| | | 'type' => 'text', |
| | | 'placeholder'=> 'Enter Umami Website ID', |
| | | 'hint' => 'Found in your Umami dashboard under Settings → Websites.', |
| | | 'required' => true |
| | | ], |
| | | ]; |
| | | |
| | | $this->advanced = [ |
| | | 'api_url' => [ |
| | | 'label' => 'API URL', |
| | | 'type' => 'text', |
| | | 'placeholder'=> 'https://analytics.yourdomain.com', |
| | | 'hint' => 'Your Umami server URL. Leave blank if using umami.is cloud service.' |
| | | ], |
| | | 'api_token' => [ |
| | | 'label' => 'API Token (Optional)', |
| | | 'type' => 'text', |
| | | 'subtype' => 'password', |
| | | 'placeholder'=> 'Enter API Token for analytics data', |
| | | 'hint' => 'Required only if you want to retrieve analytics data for dashboards.' |
| | | ] |
| | | ]; |
| | | |
| | | $this->instructions = [ |
| | | 'Login to your <a href="https://cloud.umami.is/settings/websites" target="_blank">umami.is</a> account, select your website, and copy the website ID' |
| | | ]; |
| | | |
| | | parent::__construct(); |
| | | |
| | | $this->actions = array_merge( |
| | | $this->actions, |
| | | [ |
| | | 'refresh_data' => 'handleRefreshData' |
| | | ] |
| | | ); |
| | | |
| | | global $wpdb; |
| | | $this->events_table = $wpdb->prefix . BASE . 'umami_events'; |
| | | $this->metrics_table = $wpdb->prefix . BASE . 'performance_metrics'; |
| | | } |
| | | |
| | | /** |
| | | * Initialize service-specific properties |
| | | */ |
| | | use Admin, Instance; |
| | | protected function initialize(): void |
| | | { |
| | | $this->website_id = $this->credentials['website_id'] ?? ''; |
| | | $this->api_url = $this->credentials['api_url'] ?? ''; |
| | | $this->api_token = $this->credentials['api_token'] ?? ''; |
| | | $this->ttl = intval($this->credentials['cache_duration'] ?? 60) * 60; |
| | | |
| | | // Set dynamic API base if provided |
| | | if (!empty($this->api_url)) { |
| | | $this->apiBase = rtrim($this->api_url, '/'); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Register additional WordPress hooks |
| | | */ |
| | | protected function registerAdditionalHooks(): void |
| | | { |
| | | // Add tracking script to frontend |
| | | add_action('wp_head', [$this, 'renderTrackingScript'], 5); |
| | | |
| | | // Add user session tracking |
| | | add_action('wp_footer', [$this, 'trackUserSession'], 100); |
| | | |
| | | // Schedule data collection if API is configured |
| | | if (!empty($this->api_token)) { |
| | | add_action('jvb_daily_umami_collection', [$this, 'collectDailyData']); |
| | | |
| | | if (!wp_next_scheduled('jvb_daily_umami_collection')) { |
| | | wp_schedule_event(time(), 'daily', 'jvb_daily_umami_collection'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /*************************************************************************** |
| | | * TRACKING SCRIPT METHODS |
| | | ***************************************************************************/ |
| | | |
| | | /** |
| | | * Render tracking script in head |
| | | */ |
| | | public function renderTrackingScript(): void |
| | | { |
| | | // Skip on local environments |
| | | if (JVB_TESTING) { |
| | | return; |
| | | } |
| | | if (!$this->isSetUp() || is_admin()) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | $script_url = $this->getTrackingScriptUrl(); |
| | | $website_id = $this->getWebsiteId(); |
| | | |
| | | if (!$website_id || !$script_url) { |
| | | return; |
| | | } |
| | | |
| | | // Preconnect for performance |
| | | $domain = parse_url($script_url, PHP_URL_HOST); |
| | | echo '<link rel="preconnect" href="https://' . esc_attr($domain) . '"/>' . "\n"; |
| | | |
| | | // Add tracking script |
| | | $attributes = [ |
| | | 'defer', |
| | | 'src="' . esc_url($script_url) . '"', |
| | | 'data-website-id="' . esc_attr($website_id) . '"' |
| | | ]; |
| | | |
| | | // Add optional configuration |
| | | if (!empty($this->credentials['respect_dnt']) && $this->credentials['respect_dnt']) { |
| | | $attributes[] = 'data-do-not-track="true"'; |
| | | } |
| | | |
| | | if (!empty($this->credentials['domains'])) { |
| | | $attributes[] = 'data-domains="' . esc_attr($this->credentials['domains']) . '"'; |
| | | } |
| | | |
| | | echo '<script ' . implode(' ', $attributes) . '></script>' . "\n"; |
| | | } |
| | | |
| | | /** |
| | | * Track user session information |
| | | */ |
| | | public function trackUserSession(): void |
| | | { |
| | | if (!$this->isSetUp() || is_admin()) { |
| | | return; |
| | | } |
| | | |
| | | $attributes = $this->buildTrackingAttributes('user_session', 'pageview', [ |
| | | 'logged_in' => is_user_logged_in() ? 'true' : 'false', |
| | | 'user_type' => $this->getCurrentUserType() |
| | | ]); |
| | | |
| | | if (empty($attributes)) { |
| | | return; |
| | | } |
| | | |
| | | echo '<div ' . $this->attributesToString($attributes) . ' style="display:none"></div>' . "\n"; |
| | | |
| | | // Add page-specific tracking |
| | | $this->trackPageView(); |
| | | } |
| | | |
| | | /** |
| | | * Track specific page views |
| | | */ |
| | | private function trackPageView(): void |
| | | { |
| | | $tracker_args = []; |
| | | $event = ''; |
| | | $type = ''; |
| | | |
| | | if (is_singular([BASE . 'artist', BASE . 'partner'])) { |
| | | // Profile view |
| | | global $post; |
| | | $event = 'view_profile'; |
| | | $type = get_post_type(); |
| | | $tracker_args = [ |
| | | 'id' => get_the_ID(), |
| | | 'from' => $this->getTrafficSource(), |
| | | 'owner_id' => $post->post_author |
| | | ]; |
| | | |
| | | // Check for specific content in query |
| | | foreach (['tattoo', 'piercing', 'artwork'] as $content_type) { |
| | | if (isset($_GET[$content_type])) { |
| | | $tracker_args['item'] = sanitize_text_field($_GET[$content_type]); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | } elseif (is_tax(BASE . 'shop')) { |
| | | // Shop view |
| | | $event = 'view_shop'; |
| | | $type = BASE . 'shop'; |
| | | $tracker_args = [ |
| | | 'id' => get_queried_object_id(), |
| | | 'from' => $this->getTrafficSource() |
| | | ]; |
| | | |
| | | } elseif (is_tax()) { |
| | | // Taxonomy view |
| | | $obj = get_queried_object(); |
| | | $event = 'view_taxonomy'; |
| | | $type = $obj->taxonomy; |
| | | $tracker_args = [ |
| | | 'id' => $obj->term_id, |
| | | 'from' => $this->getTrafficSource() |
| | | ]; |
| | | } |
| | | |
| | | if ($event && $type) { |
| | | $attributes = $this->buildTrackingAttributes($event, $type, $tracker_args); |
| | | echo '<div ' . $this->attributesToString($attributes) . ' style="display:none"></div>' . "\n"; |
| | | } |
| | | } |
| | | |
| | | /*************************************************************************** |
| | | * TRACKING ATTRIBUTE BUILDER METHODS |
| | | ***************************************************************************/ |
| | | |
| | | /** |
| | | * Build tracking attributes for events |
| | | */ |
| | | public function buildTrackingAttributes(string $event, string $type, array $args = []): array |
| | | { |
| | | if (!in_array($event, $this->valid_events)) { |
| | | return []; |
| | | } |
| | | |
| | | // Normalize type |
| | | $normalized_type = str_replace(BASE, '', $type); |
| | | |
| | | // Build base attributes |
| | | $attributes = [ |
| | | 'data-umami-event' => esc_attr($event), |
| | | 'data-umami-event-type' => esc_attr($normalized_type) |
| | | ]; |
| | | |
| | | // Add ID if provided |
| | | if (!empty($args['id'])) { |
| | | $attributes['data-umami-event-id'] = esc_attr($args['id']); |
| | | } |
| | | |
| | | // Add source information |
| | | if (!empty($args['source_id'])) { |
| | | $attributes['data-umami-event-source'] = esc_attr($args['source_id']); |
| | | if (!empty($args['source_type'])) { |
| | | $attributes['data-umami-event-source-type'] = esc_attr( |
| | | str_replace(BASE, '', $args['source_type']) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // Add owner information |
| | | if (!empty($args['owner_id'])) { |
| | | $attributes['data-umami-event-owner'] = esc_attr($args['owner_id']); |
| | | if (!empty($args['owner_type'])) { |
| | | $attributes['data-umami-event-owner-type'] = esc_attr( |
| | | str_replace(BASE, '', $args['owner_type']) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // Add traffic source |
| | | if (!empty($args['from'])) { |
| | | $attributes['data-umami-event-from'] = esc_attr($args['from']); |
| | | } |
| | | |
| | | // Add item reference |
| | | if (!empty($args['item'])) { |
| | | $attributes['data-umami-event-item'] = esc_attr($args['item']); |
| | | } |
| | | |
| | | // Add metadata |
| | | foreach ($args as $key => $value) { |
| | | if (in_array($key, ['id', 'source_id', 'source_type', 'owner_id', 'owner_type', 'from', 'item'])) { |
| | | continue; |
| | | } |
| | | $clean_key = 'data-umami-event-' . str_replace('_', '-', sanitize_key($key)); |
| | | $attributes[$clean_key] = esc_attr($value); |
| | | } |
| | | |
| | | return $attributes; |
| | | } |
| | | |
| | | /** |
| | | * Convert attributes array to HTML string |
| | | */ |
| | | public function attributesToString(array $attributes): string |
| | | { |
| | | $attr_strings = []; |
| | | foreach ($attributes as $key => $value) { |
| | | $attr_strings[] = $key . '="' . $value . '"'; |
| | | } |
| | | return implode(' ', $attr_strings); |
| | | } |
| | | |
| | | /** |
| | | * Public tracking methods for use in templates |
| | | */ |
| | | public function trackContentClick(int $id, string $type, array $args = []): string |
| | | { |
| | | $args['id'] = $id; |
| | | |
| | | // Auto-detect owner for content types |
| | | if (empty($args['owner_id']) && in_array($type, [BASE . 'tattoo', BASE . 'artwork', BASE . 'piercing'])) { |
| | | $post = get_post($id); |
| | | if ($post && !empty($post->post_author)) { |
| | | $args['owner_id'] = $post->post_author; |
| | | $args['owner_type'] = 'user'; |
| | | } |
| | | } |
| | | |
| | | return $this->attributesToString( |
| | | $this->buildTrackingAttributes('click_content', $type, $args) |
| | | ); |
| | | } |
| | | |
| | | public function trackProfileClick(int $id, string $type, array $args = []): string |
| | | { |
| | | $args['id'] = $id; |
| | | return $this->attributesToString( |
| | | $this->buildTrackingAttributes('click_profile', $type, $args) |
| | | ); |
| | | } |
| | | |
| | | public function trackTaxonomyClick(int $id, string $taxonomy, array $args = []): string |
| | | { |
| | | $args['id'] = $id; |
| | | return $this->attributesToString( |
| | | $this->buildTrackingAttributes('click_taxonomy', $taxonomy, $args) |
| | | ); |
| | | } |
| | | |
| | | public function trackContentTaxonomyClick(int $id, string $taxonomy, array $args = []): string |
| | | { |
| | | $args['id'] = $id; |
| | | return $this->attributesToString( |
| | | $this->buildTrackingAttributes('click_' . $taxonomy, $taxonomy, $args) |
| | | ); |
| | | } |
| | | |
| | | public function trackFavouriteToggle(int $id, string $type, bool $is_favourite, array $args = []): string |
| | | { |
| | | $args['id'] = $id; |
| | | $args['action'] = $is_favourite ? 'add' : 'remove'; |
| | | return $this->attributesToString( |
| | | $this->buildTrackingAttributes('toggle_favourite', $type, $args) |
| | | ); |
| | | } |
| | | |
| | | public function trackFeedView(int $id, string $type, array $args = []): array |
| | | { |
| | | $args['id'] = $id; |
| | | return $this->buildTrackingAttributes('view_feed', $type, $args); |
| | | } |
| | | |
| | | /*************************************************************************** |
| | | * API DATA COLLECTION METHODS |
| | | ***************************************************************************/ |
| | | |
| | | /** |
| | | * Get analytics data from Umami API |
| | | */ |
| | | public function getAnalytics(string $start_date, string $end_date, bool $use_cache = true): ?array |
| | | { |
| | | if (empty($this->api_url) || empty($this->api_token)) { |
| | | return null; |
| | | } |
| | | |
| | | // Check cache first |
| | | if ($use_cache && $this->cache) { |
| | | $cache_key = md5("analytics_{$start_date}_{$end_date}"); |
| | | $cached = $this->cache->get($cache_key); |
| | | if ($cached !== false) { |
| | | return $cached; |
| | | } |
| | | } |
| | | |
| | | try { |
| | | $url = rtrim($this->api_url, '/') . "/api/websites/{$this->website_id}/stats"; |
| | | $params = [ |
| | | 'startAt' => strtotime($start_date) * 1000, |
| | | 'endAt' => strtotime($end_date) * 1000 |
| | | ]; |
| | | |
| | | $response = wp_remote_get($url . '?' . http_build_query($params), [ |
| | | 'headers' => [ |
| | | 'Authorization' => 'Bearer ' . $this->api_token, |
| | | 'Accept' => 'application/json' |
| | | ], |
| | | 'timeout' => 30 |
| | | ]); |
| | | |
| | | if (is_wp_error($response)) { |
| | | $this->logError('API request failed', ['error' => $response->get_error_message()]); |
| | | return null; |
| | | } |
| | | |
| | | $body = wp_remote_retrieve_body($response); |
| | | $data = json_decode($body, true); |
| | | |
| | | if (json_last_error() !== JSON_ERROR_NONE) { |
| | | $this->logError('Invalid JSON response from API'); |
| | | return null; |
| | | } |
| | | |
| | | // Cache the result |
| | | if ($use_cache && $this->cache) { |
| | | $this->cache->set($cache_key, $data, $this->ttl); |
| | | } |
| | | |
| | | return $data; |
| | | |
| | | } catch (Exception $e) { |
| | | $this->logError('Exception during API call', ['error' => $e->getMessage()]); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get page views for a specific URL |
| | | */ |
| | | public function getPageViews(string $url, string $start_date, string $end_date): ?array |
| | | { |
| | | if (empty($this->api_url) || empty($this->api_token)) { |
| | | return null; |
| | | } |
| | | |
| | | $api_endpoint = rtrim($this->api_url, '/') . "/api/websites/{$this->website_id}/pageviews"; |
| | | $params = [ |
| | | 'startAt' => strtotime($start_date) * 1000, |
| | | 'endAt' => strtotime($end_date) * 1000, |
| | | 'url' => $url, |
| | | 'unit' => 'day' |
| | | ]; |
| | | |
| | | $response = wp_remote_get($api_endpoint . '?' . http_build_query($params), [ |
| | | 'headers' => [ |
| | | 'Authorization' => 'Bearer ' . $this->api_token, |
| | | 'Accept' => 'application/json' |
| | | ], |
| | | 'timeout' => 30 |
| | | ]); |
| | | |
| | | if (is_wp_error($response)) { |
| | | return null; |
| | | } |
| | | |
| | | return json_decode(wp_remote_retrieve_body($response), true); |
| | | } |
| | | |
| | | /** |
| | | * Get custom events data |
| | | */ |
| | | public function getEvents(string $event_name = '', string $start_date = '', string $end_date = ''): ?array |
| | | { |
| | | if (empty($this->api_url) || empty($this->api_token)) { |
| | | return null; |
| | | } |
| | | |
| | | $api_endpoint = rtrim($this->api_url, '/') . "/api/websites/{$this->website_id}/events"; |
| | | |
| | | $params = []; |
| | | if ($start_date && $end_date) { |
| | | $params['startAt'] = strtotime($start_date) * 1000; |
| | | $params['endAt'] = strtotime($end_date) * 1000; |
| | | } |
| | | if ($event_name) { |
| | | $params['event'] = $event_name; |
| | | } |
| | | |
| | | $url = $api_endpoint; |
| | | if (!empty($params)) { |
| | | $url .= '?' . http_build_query($params); |
| | | } |
| | | |
| | | $response = wp_remote_get($url, [ |
| | | 'headers' => [ |
| | | 'Authorization' => 'Bearer ' . $this->api_token, |
| | | 'Accept' => 'application/json' |
| | | ], |
| | | 'timeout' => 30 |
| | | ]); |
| | | |
| | | if (is_wp_error($response)) { |
| | | return null; |
| | | } |
| | | |
| | | return json_decode(wp_remote_retrieve_body($response), true); |
| | | } |
| | | |
| | | /** |
| | | * Collect daily analytics data and store in database |
| | | */ |
| | | public function collectDailyData(): void |
| | | { |
| | | if (!$this->isSetUp() || empty($this->api_token)) { |
| | | return; |
| | | } |
| | | |
| | | $yesterday = date('Y-m-d', strtotime('-1 day')); |
| | | $data = $this->getAnalytics($yesterday, $yesterday, false); |
| | | |
| | | if (!$data) { |
| | | $this->logError('Failed to collect daily data', ['date' => $yesterday]); |
| | | return; |
| | | } |
| | | |
| | | global $wpdb; |
| | | |
| | | // Store basic metrics |
| | | $wpdb->insert($this->metrics_table, [ |
| | | 'date' => $yesterday, |
| | | 'pageviews' => $data['pageviews']['value'] ?? 0, |
| | | 'visitors' => $data['visitors']['value'] ?? 0, |
| | | 'visits' => $data['visits']['value'] ?? 0, |
| | | 'bounces' => $data['bounces']['value'] ?? 0, |
| | | 'total_time' => $data['totaltime']['value'] ?? 0, |
| | | 'created_at' => current_time('mysql') |
| | | ]); |
| | | |
| | | // Collect and store events |
| | | $events = $this->getEvents('', $yesterday, $yesterday); |
| | | if ($events) { |
| | | foreach ($events as $event) { |
| | | $wpdb->insert($this->events_table, [ |
| | | 'date' => $yesterday, |
| | | 'event_name' => $event['x'] ?? '', |
| | | 'event_count' => $event['y'] ?? 0, |
| | | 'created_at' => current_time('mysql') |
| | | ]); |
| | | } |
| | | } |
| | | |
| | | $this->logDebug('Successfully collected daily data', ['date' => $yesterday]); |
| | | } |
| | | |
| | | /*************************************************************************** |
| | | * HELPER METHODS |
| | | ***************************************************************************/ |
| | | |
| | | /** |
| | | * Get current user type for tracking |
| | | */ |
| | | private function getCurrentUserType(): string |
| | | { |
| | | if (!is_user_logged_in()) { |
| | | return 'guest'; |
| | | } |
| | | |
| | | $user = wp_get_current_user(); |
| | | $roles = $user->roles; |
| | | |
| | | if (empty($roles)) { |
| | | return 'user'; |
| | | } |
| | | |
| | | $role = array_values($roles)[0]; |
| | | return str_replace(BASE, '', $role); |
| | | } |
| | | |
| | | /** |
| | | * Determine traffic source |
| | | */ |
| | | private function getTrafficSource(): string |
| | | { |
| | | if (isset($_GET['utm_source'])) { |
| | | return sanitize_text_field($_GET['utm_source']); |
| | | } |
| | | |
| | | if (isset($_SERVER['HTTP_REFERER'])) { |
| | | $referer = $_SERVER['HTTP_REFERER']; |
| | | $site_url = get_site_url(); |
| | | |
| | | if (strpos($referer, $site_url) === false) { |
| | | $domain = parse_url($referer, PHP_URL_HOST); |
| | | return $domain ?: 'external'; |
| | | } |
| | | |
| | | return 'internal'; |
| | | } |
| | | |
| | | return 'direct'; |
| | | } |
| | | |
| | | /** |
| | | * Get website ID |
| | | */ |
| | | public function getWebsiteId(): string |
| | | { |
| | | $this->ensureInitialized(); |
| | | return $this->website_id; |
| | | } |
| | | |
| | | /** |
| | | * Get tracking script URL |
| | | */ |
| | | public function getTrackingScriptUrl(): string |
| | | { |
| | | if (!empty($this->api_url)) { |
| | | return rtrim($this->api_url, '/') . '/script.js'; |
| | | } |
| | | |
| | | // Default to Umami cloud |
| | | return 'https://cloud.umami.is/script.js'; |
| | | } |
| | | |
| | | /*************************************************************************** |
| | | * REQUIRED ABSTRACT METHOD IMPLEMENTATIONS |
| | | ***************************************************************************/ |
| | | |
| | | /** |
| | | * Get request headers for API calls |
| | | */ |
| | | protected function getRequestHeaders(): array |
| | | { |
| | | $headers = [ |
| | | 'Accept' => 'application/json', |
| | | 'Content-Type' => 'application/json' |
| | | ]; |
| | | |
| | | if (!empty($this->api_token)) { |
| | | $headers['Authorization'] = 'Bearer ' . $this->api_token; |
| | | } |
| | | |
| | | return $headers; |
| | | } |
| | | |
| | | protected function handleRefreshData():array |
| | | { |
| | | // Collect today's data |
| | | $today = date('Y-m-d'); |
| | | $data = $this->getAnalytics($today, $today, false); |
| | | |
| | | if ($data) { |
| | | // Clear cache for today |
| | | $cache_key = md5("analytics_{$today}_{$today}"); |
| | | $this->cache->forget($cache_key); |
| | | |
| | | return [ |
| | | 'success' => true, |
| | | 'message' => 'Data refreshed successfully', |
| | | 'data' => $data |
| | | ]; |
| | | } |
| | | |
| | | return [ |
| | | 'success' => false, |
| | | 'message' => 'Something went wrong...' |
| | | ]; |
| | | } |
| | | |
| | | /** |
| | | * Process queued operations |
| | | */ |
| | | public function processOperation(array|\WP_Error $result, object $operation, array $data): array|\WP_Error |
| | | { |
| | | $prefix = strtolower($this->service_name) . '_'; |
| | | |
| | | switch ($operation->type) { |
| | | case $prefix . 'collect_analytics': |
| | | $this->collectDailyData(); |
| | | return ['success' => true, 'message' => 'Analytics collected']; |
| | | |
| | | default: |
| | | return $result; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Test API connection |
| | | */ |
| | | protected function performConnectionTest(): bool |
| | | { |
| | | // Basic tracking only needs website ID |
| | | if (empty($this->website_id)) { |
| | | return false; |
| | | } |
| | | |
| | | // If API credentials provided, test them |
| | | if (!empty($this->api_url) && !empty($this->api_token)) { |
| | | try { |
| | | $response = wp_remote_get(rtrim($this->api_url, '/') . '/api/websites', [ |
| | | 'headers' => [ |
| | | 'Authorization' => 'Bearer ' . $this->api_token, |
| | | 'Accept' => 'application/json' |
| | | ], |
| | | 'timeout' => 10 |
| | | ]); |
| | | |
| | | if (is_wp_error($response)) { |
| | | return false; |
| | | } |
| | | |
| | | return wp_remote_retrieve_response_code($response) === 200; |
| | | |
| | | } catch (Exception $e) { |
| | | $this->logError('Connection test failed', ['error' => $e->getMessage()]); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // Website ID exists, basic tracking will work |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Get service description |
| | | */ |
| | | public function getServiceDescription(): string |
| | | { |
| | | return "Privacy-focused analytics to understand your website traffic."; |
| | | } |
| | | |
| | | /** |
| | | * Create database tables for metrics storage |
| | | */ |
| | | public static function createTables(): void |
| | | { |
| | | global $wpdb; |
| | | $charset_collate = $wpdb->get_charset_collate(); |
| | | |
| | | // Events table |
| | | $events_table = $wpdb->prefix . BASE . 'umami_events'; |
| | | $events_sql = "CREATE TABLE IF NOT EXISTS $events_table ( |
| | | id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | date date NOT NULL, |
| | | event_name varchar(255) NOT NULL, |
| | | event_count int(11) NOT NULL DEFAULT 0, |
| | | event_data longtext, |
| | | created_at datetime NOT NULL, |
| | | PRIMARY KEY (id), |
| | | KEY date_event (date, event_name) |
| | | ) $charset_collate;"; |
| | | |
| | | // Metrics table |
| | | $metrics_table = $wpdb->prefix . BASE . 'performance_metrics'; |
| | | $metrics_sql = "CREATE TABLE IF NOT EXISTS $metrics_table ( |
| | | id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | date date NOT NULL, |
| | | pageviews int(11) NOT NULL DEFAULT 0, |
| | | visitors int(11) NOT NULL DEFAULT 0, |
| | | visits int(11) NOT NULL DEFAULT 0, |
| | | bounces int(11) NOT NULL DEFAULT 0, |
| | | total_time int(11) NOT NULL DEFAULT 0, |
| | | created_at datetime NOT NULL, |
| | | PRIMARY KEY (id), |
| | | UNIQUE KEY date_unique (date) |
| | | ) $charset_collate;"; |
| | | |
| | | require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); |
| | | dbDelta($events_sql); |
| | | dbDelta($metrics_sql); |
| | | // TODO: Implement initialize() method. |
| | | } |
| | | } |