options['key']); if (!$verified) { header("HTTP/1.0 404 Not Found"); die('Url not verified'); } $user = Newsletter::instance()->get_user($user_id); if (!$user) { header("HTTP/1.0 404 Not Found"); die('Invalid subscriber'); } // Test emails if (empty($email_id)) { header('Location: ' . esc_url_raw($url)); die(); } $email = $this->get_email($email_id); if (!$email) { header("HTTP/1.0 404 Not Found"); die('Invalid newsletter'); } setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/'); $is_action = strpos($url, '?na='); $ip = $this->get_remote_ip(); $ip = $this->process_ip($ip); if (!$is_action) { $this->add_click($url, $user_id, $email_id, $ip); $this->update_open_value(self::SENT_CLICK, $user_id, $email_id, $ip); } else { // Track an action as an email read and not a click $this->update_open_value(self::SENT_READ, $user_id, $email_id, $ip); } $this->update_user_ip($user, $ip); $this->update_user_last_activity($user); header('Location: ' . apply_filters('newsletter_redirect_url', $url, $email, $user)); die(); } // Newsletter Open Traking Image if (isset($_GET['noti'])) { $this->logger->debug('Open tracking: ' . $_GET['noti']); list($email_id, $user_id, $signature) = explode(';', base64_decode($_GET['noti']), 3); $email = $this->get_email($email_id); if (!$email) { $this->logger->error('Open tracking request for unexistant email'); die(); } $user = $this->get_user($user_id); if (!$user) { $this->logger->error('Open tracking request for unexistant subscriber'); die(); } if ($email->token) { $this->logger->debug('Signature: ' . $signature); $s = md5($email_id . $user_id . $email->token); if ($s != $signature) { $this->logger->error('Open tracking request with wrong signature. Email token: ' . $email->token); die(); } } else { $this->logger->info('Email with no token hence not signature to check'); } $ip = $this->get_remote_ip(); $ip = $this->process_ip($ip); $this->add_click('', $user_id, $email_id, $ip); $this->update_open_value(self::SENT_READ, $user_id, $email_id, $ip); $this->update_user_last_activity($user); header('Content-Type: image/gif', true); echo base64_decode('_R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); die(); } } function upgrade() { global $wpdb, $charset_collate; parent::upgrade(); $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_stats` ( `id` int(11) NOT NULL AUTO_INCREMENT, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `url` varchar(255) NOT NULL DEFAULT '', `user_id` int(11) NOT NULL DEFAULT '0', `email_id` varchar(10) NOT NULL DEFAULT '0', `link_id` int(11) NOT NULL DEFAULT '0', `ip` varchar(20) NOT NULL DEFAULT '', `country` varchar(4) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `email_id` (`email_id`), KEY `user_id` (`user_id`) ) $charset_collate;"; dbDelta($sql); if (empty($this->options['key'])) { $this->options['key'] = md5($_SERVER['REMOTE_ADDR'] . rand(100000, 999999) . time()); $this->save_options($this->options); } } function admin_menu() { $this->add_admin_page('index', 'Statistics'); $this->add_admin_page('view', 'Statistics'); $this->add_admin_page('newsletters', 'Statistics'); $this->add_admin_page('settings', 'Statistics'); $this->add_admin_page('view_retarget', 'Statistics'); $this->add_admin_page('view_urls', 'Statistics'); $this->add_admin_page('view_users', 'Statistics'); } function relink($text, $email_id, $user_id, $email_token = '') { $this->relink_email_id = $email_id; $this->relink_user_id = $user_id; $this->relink_email_token = $email_token; $this->logger->debug('Relink with token: ' . $email_token); $text = preg_replace_callback('/(<[aA][^>]+href[\s]*=[\s]*["\'])([^>"\']+)(["\'][^>]*>)(.*?)(<\/[Aa]>)/is', array($this, 'relink_callback'), $text); $signature = md5($email_id . $user_id . $email_token); $text = str_replace('', '', $text); return $text; } function relink_callback($matches) { $href = trim(str_replace('&', '&', $matches[2])); // Do not replace the tracking or subscription/unsubscription links. //if (strpos($href, '/newsletter/') !== false) { // return $matches[0]; //} // Do not replace URL which are tags (special case for ElasticEmail) if (strpos($href, '{') === 0) { return $matches[0]; } // if (strpos($href, '?na=') !== false) { // return $matches[0]; // } // Do not relink anchors if (substr($href, 0, 1) == '#') { return $matches[0]; } // Do not relink mailto: if (substr($href, 0, 7) == 'mailto:') { return $matches[0]; } // This is the link text which is added to the tracking data $anchor = ''; // if ($this->options['anchor'] == 1) { // $anchor = trim(str_replace(';', ' ', $matches[4])); // // Keep images but not other tags // $anchor = strip_tags($anchor, ''); // // // Truncate if needed to avoid to much long URLs // if (stripos($anchor, ' 100) { // $anchor = substr($anchor, 0, 100); // } // } $r = $this->relink_email_id . ';' . $this->relink_user_id . ';' . $href . ';' . $anchor; $r = $r . ';' . md5($r . $this->options['key']); $r = base64_encode($r); $r = urlencode($r); $url = home_url('/') . '?nltr=' . $r; return $matches[1] . $url . $matches[3] . $matches[4] . $matches[5]; } function get_statistics_url($email_id) { $page = apply_filters('newsletter_statistics_view', 'newsletter_statistics_view'); return 'admin.php?page=' . $page . '&id=' . $email_id; } function maybe_fix_sent_stats($email) { global $wpdb; // Very old emails was missing the send_on if ($email->send_on == 0) { $this->query($wpdb->prepare("update " . NEWSLETTER_EMAILS_TABLE . " set send_on=unix_timestamp(created) where id=%d limit 1", $email->id)); $email = $this->get_email($email->id); } if ($email->status == 'sending') { return; } if ($email->type == 'followup') { return; } $count = $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_SENT_TABLE . " where email_id=%d", $email->id)); if ($count) { return; } if (empty($email->query)) { $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'"; } $query = $email->query . " and unix_timestamp(created)<" . $email->send_on; $query = str_replace('*', 'id, ' . $email->id . ', ' . $email->send_on, $query); $this->query("insert ignore into " . NEWSLETTER_SENT_TABLE . " (user_id, email_id, time) " . $query); } function update_stats($email) { global $wpdb; $wpdb->query($wpdb->prepare("update " . $wpdb->prefix . "newsletter_sent s1 join " . $wpdb->prefix . "newsletter_stats s2 on s1.user_id=s2.user_id and s1.email_id=s2.email_id and s1.email_id=%d set s1.open=1, s1.ip=s2.ip", $email->id)); $wpdb->query($wpdb->prepare("update " . $wpdb->prefix . "newsletter_sent s1 join " . $wpdb->prefix . "newsletter_stats s2 on s1.user_id=s2.user_id and s1.email_id=s2.email_id and s2.url<>'' and s1.email_id=%d set s1.open=2, s1.ip=s2.ip", $email->id)); } function reset_stats($email) { global $wpdb; $email_id = $this->to_int_id($email); $this->query("delete from " . $wpdb->prefix . "newsletter_sent where email_id=" . $email_id); $this->query("delete from " . $wpdb->prefix . "newsletter_stats where email_id=" . $email_id); } function get_total_count($email_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_SENT_TABLE . " where email_id=%d", $this->to_int_id($email_id))); } function get_open_count($email_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_SENT_TABLE . " where open>0 and email_id=%d", $this->to_int_id($email_id))); } function get_click_count($email_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_SENT_TABLE . " where open>1 and email_id=%d", $this->to_int_id($email_id))); } function get_error_count($email_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_SENT_TABLE . " where status>0 and email_id=%d", $this->to_int_id($email_id))); } function add_click($url, $user_id, $email_id, $ip = null) { global $wpdb; if (is_null($ip)) { $ip = $this->get_remote_ip(); } $ip = $this->process_ip($ip); $this->insert(NEWSLETTER_STATS_TABLE, array( 'email_id' => $email_id, 'user_id' => $user_id, 'url' => $url, 'ip' => $ip ) ); } function update_open_value($value, $user_id, $email_id, $ip = null) { global $wpdb; if (is_null($ip)) { $ip = $this->get_remote_ip(); } $ip = $this->process_ip($ip); $this->query($wpdb->prepare("update " . NEWSLETTER_SENT_TABLE . " set open=%d, ip=%s where email_id=%d and user_id=%d and open<%d limit 1", $value, $ip, $email_id, $user_id, $value)); } } NewsletterStatistics::instance();