Jake Vanderwerf
2026-01-02 b5abd615697146beeca6dba4acd057d049554a30
inc/managers/ErrorHandler.php
@@ -249,14 +249,31 @@
     *
     * @return bool Whether the notification is sent successfully
     */
    protected function notifyAdmin(string $component, string $message, array $context):bool
    {
        $admin_email = get_option('admin_email');
        $subject = "[edmonton.ink Critical Error] {$component}";
        $body = "Error: {$message}\n\nContext: " . print_r($context, true);
   protected function notifyAdmin(string $component, string $message, array $context):bool
   {
      $admin_email = get_option('admin_email');
      $subject = "[" . get_bloginfo('name') . " Critical Error] {$component}";
        return JVB()->email()->sendEmail($admin_email, $subject, $body);
    }
      $body = JVB()->email()->alert(
         'A critical error has occurred and requires immediate attention',
         'error'
      );
      $body .= JVB()->email()->h2('Error Details');
      $body .= JVB()->email()->card(
         '<p><strong>Component:</strong> ' . esc_html($component) . '</p>' .
         '<p><strong>Message:</strong></p>' .
         JVB()->email()->codeBlock($message),
         'Error Information'
      );
      if (!empty($context)) {
         $body .= JVB()->email()->h3('Additional Context');
         $body .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
      }
      return JVB()->email()->sendEmail($admin_email, $subject, $body, 'CRITICAL ERROR');
   }
    /**
     * Gather summary of the most important errors
@@ -350,79 +367,92 @@
     * Send daily error summary email to administrator
     * @return bool Whether email is sent
     */
    public function sendErrorSummary():bool
    {
        // Get summary data
        $summary = $this->gatherErrorSummary();
   public function sendErrorSummary():bool
   {
      $summary = $this->gatherErrorSummary();
        // Only send if there are errors
        if (empty($summary['frequent']) && empty($summary['critical'])) {
            return false;
        }
      if (empty($summary['frequent']) && empty($summary['critical'])) {
         return false;
      }
        $admin_email = get_option('admin_email');
        $site_name = get_bloginfo('name');
        $today = date('Y-m-d');
        $yesterday = date('Y-m-d', strtotime('-1 day'));
      $admin_email = get_option('admin_email');
      $site_name = get_bloginfo('name');
      $yesterday = date('Y-m-d', strtotime('-1 day'));
      $subject = "[{$site_name}] Daily Error Summary - " . date('Y-m-d');
        $subject = "[{$site_name}] Daily Error Summary - {$today}";
      // Header with alert
      $body = JVB()->email()->h1('Daily Error Summary');
      $body .= sprintf('<p>Error summary for <strong>%s</strong></p>', $yesterday);
        // Build email body
        $body = "= Error Summary for {$yesterday} =\n\n";
      // Summary stats in a grid
      if (!empty($summary['stats'])) {
         $stats = [
            JVB()->email()->stat($summary['stats']->total_errors, 'Total Errors'),
            JVB()->email()->stat($summary['stats']->critical_count, 'Critical', 'Requires attention'),
            JVB()->email()->stat($summary['stats']->error_count, 'Errors'),
            JVB()->email()->stat($summary['stats']->warning_count, 'Warnings')
         ];
         $body .= JVB()->email()->grid($stats, 4);
      }
        // Add frequent errors section
        if (!empty($summary['frequent'])) {
            $body .= "== Most Frequent Errors ==\n\n";
      // Alert if critical errors exist
      if (!empty($summary['critical'])) {
         $body .= JVB()->email()->alert(
            sprintf('Found %d critical errors that need immediate attention', count($summary['critical'])),
            'error'
         );
      }
            foreach ($summary['frequent'] as $index => $error) {
                $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n";
                $body .= "   Message: " . wp_trim_words($error->message, 20, '...') . "\n";
                $body .= "   Count: {$error->count}\n\n";
            }
        }
      $body .= JVB()->email()->spacer(20);
        // Add critical errors section
        if (!empty($summary['critical'])) {
            $body .= "== Recent Critical Errors ==\n\n";
      // Frequent errors section
      if (!empty($summary['frequent'])) {
         $body .= JVB()->email()->h2('Most Frequent Errors');
            foreach ($summary['critical'] as $index => $error) {
                $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n";
                $body .= "   Time: {$error->created_at}\n";
                $body .= "   Message: " . $error->message . "\n\n";
         foreach ($summary['frequent'] as $error) {
            $cardContent = JVB()->email()->badge($error->count . 'x', 'warning') . ' ';
            $cardContent .= '<strong>' . esc_html($error->error_type) . '</strong>';
            $cardContent .= '<p style="margin:10px 0 5px 0;font-size:13px;">' . esc_html(wp_trim_words($error->message, 15)) . '</p>';
            $cardContent .= '<p style="margin:0;font-size:12px;color:' . JVB()->email()->colours['dark-200'] . ';">
                Source: ' . esc_html($error->source) . ' |
                Logged in: ' . $error->logged_in_count . ' |
                Logged out: ' . $error->logged_out_count . '
            </p>';
                // Include context for critical errors if available
                if (!empty($error->context)) {
                    $context = json_decode($error->context, true);
                    if (is_array($context)) {
                        $body .= "   Context:\n";
                        foreach ($context as $key => $value) {
                            if (is_array($value) || is_object($value)) {
                                $value = json_encode($value);
                            }
                            $body .= "     - {$key}: {$value}\n";
                        }
                    }
                    $body .= "\n";
                }
            }
        }
            $body .= JVB()->email()->card($cardContent, $error->component);
         }
      }
        // Add dashboard link if available
        $admin_url = admin_url('admin.php?page=jvb-error-logs');
        $body .= "View detailed error logs in the dashboard: {$admin_url}\n\n";
      // Critical errors section
      if (!empty($summary['critical'])) {
         $body .= JVB()->email()->spacer(30);
         $body .= JVB()->email()->h2('Recent Critical Errors');
        // Send the email
        $sent = JVB()->email()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY');
         foreach ($summary['critical'] as $error) {
            $cardContent = '<p><strong>Time:</strong> ' . esc_html($error->created_at) . '</p>';
            $cardContent .= '<p><strong>Message:</strong></p>';
            $cardContent .= JVB()->email()->codeBlock($error->message);
        // Log that summary was sent
        if ($sent) {
            error_log("[ErrorHandler] Daily error summary sent to {$admin_email}");
        } else {
            error_log("[ErrorHandler] Daily error summary was not sent.");
        }
            // Include context if available
            if (!empty($error->context)) {
               $context = json_decode($error->context, true);
               if (is_array($context)) {
                  $cardContent .= '<p><strong>Context:</strong></p>';
                  $cardContent .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
               }
            }
        return $sent;
    }
            $body .= JVB()->email()->card($cardContent, $error->component . ': ' . $error->error_type);
         }
      }
      // Dashboard link
      $admin_url = admin_url('admin.php?page=jvb-error-logs');
      $body .= JVB()->email()->spacer(30);
      $body .= JVB()->email()->button($admin_url, 'View Detailed Logs');
      return JVB()->email()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY');
   }
    /**
     * Get HTML version of the error summary for nicer emails