/** * ------------------------------------------------------------------ * 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 = '
' . implode('', $tag_parts) . '
'; } } // Escape all fields $title_clean_esc = esc_html($title_clean); $meta_esc = esc_html($meta); $summary_esc = esc_html($summary); $href_esc = esc_url($href); // Build email card block $one = '' . '' . ' ' . ' ' . ' ' . '
' . '
' . $title_clean_esc . '
' . ($meta_esc !== '' ? '
' . $meta_esc . '
' : '') . $cat_tags_html // ← categories injected here, below meta, above summary divider . '
' . $summary_esc . '
' . ' ' . ' ' . ' ' . ' ' . '
' . ' Read more' . '
' . '
'; // Add spacer after each except last if ($i < $max - 1) { $one .= '
 
'; } $cards_html[] = $one; } return implode("\n", $cards_html); } } // --------------------------- // Get today's latest 'newsletter' permalink (for CTA button) // --------------------------- if (!function_exists('appealmate_get_today_newsletter_permalink')) { function appealmate_get_today_newsletter_permalink() { $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()) { $post_id = (int) $q->posts[0]; $permalink = get_permalink($post_id); if ($permalink) { return $permalink; } } return home_url('/'); } } // --------------------------- // ActiveCampaign functions // --------------------------- if (!function_exists('appealmate_create_activecampaign_message')) { function appealmate_create_activecampaign_message($apiKey, $subject, $html, $listId) { $apiUrl = 'https://appealmate.api-us1.com/admin/api.php?api_action=message_add&api_output=json'; $params = array( 'api_key' => $apiKey, 'fromemail' => 'newsletter@appealmate.com', 'fromname' => 'AppealMate AI', 'reply2' => 'newsletter@appealmate.com', 'subject' => $subject, 'charset' => 'utf-8', 'encoding' => 'quoted-printable', 'priority' => 3, 'format' => 'mime', 'text' => 'You are receiving the weekly AppealMate Appellate Roundup. View this newsletter online: https://appealmate.com/newsletter', 'htmlunsub' => 0, 'html' => $html, 'p' => array($listId => $listId), ); $response = wp_remote_post($apiUrl, array( 'timeout' => 30, 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'), 'body' => $params, )); if (is_wp_error($response)) { error_log('❌ ActiveCampaign message_add error: ' . $response->get_error_message()); return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (!empty($data['id'])) { error_log('✅ Created message ID: ' . $data['id']); return (int) $data['id']; } else { error_log('❌ Failed to parse message ID: ' . $body); return false; } } } if (!function_exists('appealmate_create_activecampaign_campaign')) { function appealmate_create_activecampaign_campaign($apiKey, $listId, $messageId, $date) { $apiUrl = 'https://appealmate.api-us1.com/admin/api.php?api_action=campaign_create&api_output=json'; $params = array( 'api_key' => $apiKey, 'name' => 'newsletter-' . $date, 'type' => 'single', 'sdate' => '', 'status' => 1, 'public' => 1, 'tracklinks' => 'all', 'p' => array($listId => $listId), 'm' => array($messageId => 100), ); $response = wp_remote_post($apiUrl, array( 'timeout' => 30, 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'), 'body' => $params, )); if (is_wp_error($response)) { error_log('❌ ActiveCampaign campaign_create error: ' . $response->get_error_message()); return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (!empty($data['id'])) { error_log('✅ Created campaign ID: ' . $data['id']); return (int) $data['id']; } else { error_log('❌ Failed to parse campaign ID: ' . $body); return false; } } } if (!function_exists('appealmate_send_activecampaign_now')) { function appealmate_send_activecampaign_now($apiKey, $campaignId, $subject) { $apiUrl = 'https://appealmate.api-us1.com/admin/api.php?api_action=campaign_send&api_output=json'; $params = array( 'api_key' => $apiKey, 'campaignid' => $campaignId, ); $response = wp_remote_post($apiUrl, array( 'timeout' => 30, 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'), 'body' => $params, )); if (is_wp_error($response)) { error_log('❌ ActiveCampaign send error: ' . $response->get_error_message()); return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['result_code']) && $data['result_code'] == 1) { error_log('✅ Sent campaign ID ' . $campaignId . ' ("' . $subject . '") successfully'); return true; } else { error_log('❌ Failed to send campaign: ' . $body); return false; } } } // --------------------------- // The newsletter job (runs via WP-Cron) // --------------------------- if (!function_exists('appealmate_send_newsletter_cb')) { function appealmate_send_newsletter_cb() { // Only proceed if a newsletter post is published today (UTC). if (!appealmate_has_newsletter_published_today()) { $msg = 'Skipped: No newsletter published today (UTC).'; error_log('[AppealMate Newsletter] ' . $msg); set_transient('appealmate_newsletter_last_result', $msg, WEEK_IN_SECONDS); return; } $message = <<<'HTML' AppealMate Appellate Roundup
HTML; // Replace placeholders $message = str_replace('{{APPEALMATE_DATE_UTC}}', gmdate('F j, Y'), $message); $message = str_replace('{{APPEALMATE_TOP3}}', appealmate_build_top_three_cases_from_today_newsletter(), $message); $message = str_replace('{{APPEALMATE_NEWSLETTER_URL}}', esc_url(appealmate_get_today_newsletter_permalink()), $message); $date = gmdate('F j, Y'); $subject3 = 'Your Weekly Appellate Roundup from AppealMate'; $apiKey = 'c6affa96a606907934cd6e42c9ab4149c0fafc7c2fa68d65096a22a04b8716aa45900a4d'; $listId = 9; $messageId = appealmate_create_activecampaign_message($apiKey, $subject3, $message, $listId); if ($messageId) { $campaignId = appealmate_create_activecampaign_campaign($apiKey, $listId, $messageId, $date); if ($campaignId) { appealmate_send_activecampaign_now($apiKey, $campaignId, $subject3); } } update_option('appealmate_latest_newsletter_html', $message); update_option('appealmate_latest_newsletter_date', gmdate('Y-m-d H:i:s')); } } add_action('appealmate_send_newsletter', 'appealmate_send_newsletter_cb'); Kapnick, J. (dissenting) Archives - Appealmate

Kapnick, J. (dissenting)

Matter of Brittany W. v. Suzanne Miles-Gustave, et al. February 23, 2026

Matter of Brittany W. v. Suzanne Miles-Gustave, et al. Dissent Court Appellate Division, First Department Date Decided 2026-02-17 Judges Kern, J.P.Scarpulla, J.Kapnick, J. (dissenting)Shulman, J.Hagler, J. Attorneys and Parties Brittany W. Petitioner Attorneys: Ethan Singer New York State Office of Children and Family Services (OCFS) / Suzanne Miles-Gustave State Respondent Attorneys: Joshua N. Cohen New…