
/** * ------------------------------------------------------------------ * AppealMate - Auto UTM Tracking for ALL Email Links (WPCode Safe) * ------------------------------------------------------------------ * - Adds UTM params to all links inside email HTML * - Keeps case_# for case links * - Adds readable utm_content for other links (logo, linkedin, etc.) * - Skips mailto:, tel:, #anchors, and %UNSUBSCRIBELINK% * - Does not overwrite existing UTM params if already present */ if (!function_exists('appealmate_append_utm')) { function appealmate_append_utm($url, $utm_source, $utm_medium, $utm_campaign, $utm_content) { if (!$url) return $url; // Skip unsubscribe placeholder if (strpos($url, '%UNSUBSCRIBELINK%') !== false) return $url; // Skip mailto, tel, anchors if (stripos($url, 'mailto:') === 0) return $url; if (stripos($url, 'tel:') === 0) return $url; if (stripos($url, '#') === 0) return $url; // Only process http(s) if (!preg_match('#^https?://#i', $url)) return $url; $parts = wp_parse_url($url); if (empty($parts['host']) || empty($parts['scheme'])) return $url; $query = array(); if (!empty($parts['query'])) { parse_str($parts['query'], $query); } // Add UTMs if missing if (!isset($query['utm_source'])) $query['utm_source'] = $utm_source; if (!isset($query['utm_medium'])) $query['utm_medium'] = $utm_medium; if (!isset($query['utm_campaign'])) $query['utm_campaign'] = $utm_campaign; if (!isset($query['utm_content'])) $query['utm_content'] = $utm_content; // Rebuild $new_url = $parts['scheme'] . '://' . $parts['host']; if (!empty($parts['path'])) $new_url .= $parts['path']; $new_url .= '?' . http_build_query($query); if (!empty($parts['fragment'])) $new_url .= '#' . $parts['fragment']; return $new_url; } } if (!function_exists('appealmate_add_utms_to_all_email_links')) { function appealmate_add_utms_to_all_email_links($html, $utm_source, $utm_medium, $utm_campaign) { if (!class_exists('DOMDocument')) { return $html; // fallback } libxml_use_internal_errors(true); $dom = new DOMDocument(); // Load HTML safely (UTF-8) $dom->loadHTML('' . $html); $links = $dom->getElementsByTagName('a'); foreach ($links as $link) { $href = $link->getAttribute('href'); if (!$href) continue; // Skip unsubscribe placeholder if (strpos($href, '%UNSUBSCRIBELINK%') !== false) continue; // Determine utm_content based on link text $text = trim($link->textContent); $utm_content = $text !== '' ? sanitize_title($text) : 'link'; // Apply UTMs $new_href = appealmate_append_utm( $href, $utm_source, $utm_medium, $utm_campaign, $utm_content ); $link->setAttribute('href', $new_href); } // Return body only $body = $dom->getElementsByTagName('body')->item(0); $new_html = ''; foreach ($body->childNodes as $child) { $new_html .= $dom->saveHTML($child); } libxml_clear_errors(); return $new_html; } } // --------------------------- // Cron scheduling (named functions only; WPCode safe) // --------------------------- if (!function_exists('appealmate_add_weekly_schedule')) { function appealmate_add_weekly_schedule($schedules) { if (!isset($schedules['appealmate_weekly'])) { $schedules['appealmate_weekly'] = array( 'interval' => WEEK_IN_SECONDS, 'display' => __('AppealMate Weekly', 'appealmate'), ); } return $schedules; } } add_filter('cron_schedules', 'appealmate_add_weekly_schedule'); if (!function_exists('appealmate_next_monday_0610_utc_timestamp')) { function appealmate_next_monday_0610_utc_timestamp() { // Calculate next Monday at 9:00 AM New York time $now = new DateTime('now', new DateTimeZone('America/New_York')); $monday = new DateTime('monday this week 09:00', new DateTimeZone('America/New_York')); if ($now >= $monday) { $monday->modify('+1 week'); } return $monday->getTimestamp(); } } if (!function_exists('appealmate_schedule_newsletter_event')) { function appealmate_schedule_newsletter_event() { if (!wp_next_scheduled('appealmate_send_newsletter')) { $firstRun = appealmate_next_monday_0610_utc_timestamp(); wp_schedule_event($firstRun, 'appealmate_weekly', 'appealmate_send_newsletter'); } } } add_action('init', 'appealmate_schedule_newsletter_event'); // --------------------------- // Check if a newsletter is published today (UTC) // --------------------------- if (!function_exists('appealmate_has_newsletter_published_today')) { function appealmate_has_newsletter_published_today() { $now_utc_ts = current_time('timestamp', true); $start_utc = gmdate('Y-m-d 00:00:00', $now_utc_ts); $end_utc = gmdate('Y-m-d 23:59:59', $now_utc_ts); $args = array( 'post_type' => 'newsletter', 'post_status' => 'publish', 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => true, 'date_query' => array( array( 'column' => 'post_date_gmt', 'after' => $start_utc, 'before' => $end_utc, 'inclusive' => true, ), ), ); $q = new WP_Query($args); return $q->have_posts(); } } // --------------------------- // Build cases HTML from today's latest 'newsletter' post (with categories) // --------------------------- if (!function_exists('appealmate_build_top_three_cases_from_today_newsletter')) { function appealmate_build_top_three_cases_from_today_newsletter() { // Find latest newsletter published today (UTC) $now_utc_ts = current_time('timestamp', true); $start_utc = gmdate('Y-m-d 00:00:00', $now_utc_ts); $end_utc = gmdate('Y-m-d 23:59:59', $now_utc_ts); $q = new WP_Query(array( 'post_type' => 'newsletter', 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, 'fields' => 'ids', 'date_query' => array( array( 'column' => 'post_date_gmt', 'after' => $start_utc, 'before' => $end_utc, 'inclusive' => true, ), ), )); if (!$q->have_posts()) { return ''; } $post_id = (int) $q->posts[0]; $content = (string) get_post_field('post_content', $post_id); if ($content === '') { return ''; } if (!class_exists('DOMDocument')) { return ''; } $cards_html = array(); libxml_use_internal_errors(true); $dom = new DOMDocument(); $html = '' . '' . $content . ''; $dom->loadHTML($html); $xpath = new DOMXPath($dom); // Find all .case-card elements $nodes = $xpath->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' case-card ')]"); $max = $nodes ? $nodes->length : 0; for ($i = 0; $i < $max; $i++) { $node = $nodes->item($i); // Title $titleNode = $xpath->query(".//*[contains(concat(' ', normalize-space(@class), ' '), ' case-title ')]", $node)->item(0); $title_full = $titleNode ? trim($titleNode->textContent) : ''; // Extract trailing (...) meta into $meta and strip from title $meta = ''; $title_clean = $title_full; if (preg_match('/\s*\(([^()]*)\)\s*$/u', $title_full, $m)) { $meta = trim($m[1]); $title_clean = trim(preg_replace('/\s*\([^()]*\)\s*$/u', '', $title_full)); } // Summary $summaryNode = $xpath->query(".//*[contains(concat(' ', normalize-space(@class), ' '), ' case-summary ')]", $node)->item(0); $summary = $summaryNode ? trim($summaryNode->textContent) : ''; // Link $linkNode = $xpath->query(".//a[contains(concat(' ', normalize-space(@class), ' '), ' case-link ')]", $node)->item(0); $href = $linkNode ? trim($linkNode->getAttribute('href')) : '#'; // Categories — read from data-categories attribute (pipe-separated) $data_cats_raw = $node->getAttribute('data-categories'); $cat_tags_html = ''; if ($data_cats_raw !== '') { $cat_names = array_filter(array_map('trim', explode('|', $data_cats_raw))); $tag_parts = array(); foreach ($cat_names as $cat_name) { $term = get_term_by('name', $cat_name, 'case_category'); if ($term && !is_wp_error($term)) { $term_url = get_term_link($term); $cat_url = !is_wp_error($term_url) ? esc_url($term_url) : '#'; } else { $cat_url = '#'; } // Email-safe inline-styled pill tag $tag_parts[] = '' . esc_html($cat_name) . ''; } if (!empty($tag_parts)) { $cat_tags_html = '
| '
. ' '
. $title_clean_esc
. ' '
. ($meta_esc !== '' ? '' . $meta_esc . ' ' : '')
. $cat_tags_html // ← categories injected here, below meta, above summary divider
. ' '
. $summary_esc
. ' '
. '
| '
. '
|
