Jake Vanderwerf
8 days ago 3b83905603d44b1a08f8b2b36a605808ce686ad6
inc/managers/EmailManager.php
@@ -24,7 +24,7 @@
class EmailManager
{
   public array $colours = JVB_EMAIL['colours'];
   public array $colours;
   private string $title = JVB_EMAIL['content']['title'];
   private string $prefix = JVB_EMAIL['content']['subjectPrefix'];
   private string $signature = JVB_EMAIL['content']['signature'];
@@ -42,26 +42,26 @@
        $this->site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
        $this->site_url = get_site_url();
      $this->footer = (is_array(JVB_EMAIL['content']['footer'])) ? implode('', JVB_EMAIL['content']['footer']) : JVB_EMAIL['content']['footer'];
      $this->colours = JVB_COLOURS;
        add_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
        // User registration emails
        add_filter('wp_new_user_notification_email', [$this, 'customizeNewUserEmail'], 10, 3);
        add_filter('wp_new_user_notification_email_admin', [$this, 'customizeNewUserEmailAdmin'], 10, 3);
        add_filter('wp_new_user_notification_email', [$this, 'customizeNewUserEmail'], 999, 3);
        add_filter('wp_new_user_notification_email_admin', [$this, 'customizeNewUserEmailAdmin'], 999, 3);
        // Password reset emails
        add_filter('retrieve_password_message', [$this, 'customizePasswordResetEmail'], 10, 4);
        add_filter('retrieve_password_title', [$this, 'customizePasswordResetTitle'], 10, 3);
        add_filter('retrieve_password_message', [$this, 'customizePasswordResetEmail'], 999, 4);
        add_filter('retrieve_password_title', [$this, 'customizePasswordResetTitle'], 999, 3);
        // User email change emails
        add_filter('email_change_email', [$this, 'customizeEmailChangeEmail'], 10, 3);
        add_filter('new_user_email_content', [$this, 'customizeNewUserEmailContent'], 10, 2);
        add_filter('email_change_email', [$this, 'customizeEmailChangeEmail'], 999, 3);
        add_filter('new_user_email_content', [$this, 'customizeNewUserEmailContent'], 999, 2);
        // Password change notification
        add_filter('password_change_email', [$this, 'customizePasswordChangeEmail'], 10, 3);
        add_filter('password_change_email', [$this, 'customizePasswordChangeEmail'], 999, 3);
        // User request/export data emails
        add_filter('user_request_action_email_content', [$this, 'customizeUserRequestEmail'], 10, 2);
        add_filter('wp_privacy_personal_data_email_content', [$this, 'customizePersonalDataEmail'], 10, 3);
        add_filter('user_request_action_email_content', [$this, 'customizeUserRequestEmail'], 999, 2);
        add_filter('wp_privacy_personal_data_email_content', [$this, 'customizePersonalDataEmail'], 999, 3);
    }
    /**
@@ -82,13 +82,13 @@
     * @param string $header Optional header text for the template
     * @return bool Whether the email was sent successfully
     */
    public function sendEmail(string $to, string $subject, string $message, string $header = '', array $headers = [], array $attachments = []):bool
    public function sendEmail(string $to, string $subject, string $message, string $header = '', string $preheader = '', array $headers = [], array $attachments = []):bool
    {
        // Make sure the content type is set to HTML
        add_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
        // Format the message with our template
        $formatted_message = $this->getEmailTemplate($message, $header);
        $formatted_message = $this->getEmailTemplate($message, $header, $preheader);
        // Send the email
        $result = wp_mail($to, $subject, $formatted_message, $headers, $attachments);
@@ -105,7 +105,7 @@
     *
     * @return string
     */
    private function getEmailTemplate(string $content, string $headerText = ''):string
    private function getEmailTemplate(string $content, string $headerText = '', string $preheader = ''):string
    {
      $custom_logo_id = get_theme_mod( 'custom_logo' );
      $logo_thumbnail = wp_get_attachment_image_src( $custom_logo_id, 'custom-logo-thumbnail' );
@@ -118,6 +118,14 @@
         $headerText = $this->title;
        }
      $preheaderHtml = '';
      if (!empty($preheader)) {
         $preheaderHtml = '
        <div style="display:none;font-size:1px;color:#fefefe;line-height:1px;font-family:Helvetica,Arial,sans-serif;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
            ' . esc_html($preheader) . '
        </div>';
      }
        return '<!DOCTYPE html>
        <html>
        <head>
@@ -395,7 +403,11 @@
     */
    public function customizePasswordResetEmail(string $message, string $key, string $user_login, WP_User $user):string
    {
      $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
      return $this->passwordResetEmail($user, $key);
    }
   public function passwordResetEmail(WP_User $user, string $key):string {
      $reset_url = network_site_url("login/?action=resetpass&key=$key&login=" . rawurlencode($user->user_login), 'login');
      $content = sprintf(
         '<p>Hi %s!</p>
         <p>We received a request to reset the password for an account associated with this email:</p>
@@ -408,14 +420,18 @@
         %s
         <p>This password reset link is only valid for 24 hours.</p>',
         $user->display_name,
         $user_login,
         $user->user_login,
         $this->button($reset_url,'Reset Password'),
         $this->link($reset_url),
         $this->divider()
      );
      $content = apply_filters('jvbPasswordResetEmail', $content, $user_login, $user, $reset_url);
        return $this->getEmailTemplate($content, 'Password Reset');
    }
      return apply_filters('jvbPasswordResetEmail', $content, $user->user_login, $user, $reset_url);
   }
   public function sendPasswordResetEmail(WP_User $user, string $key):bool
   {
      return $this->sendEmail($user->user_email, $this->passwordResetTitle(), $this->passwordResetEmail($user, $key), '', 'Reset your Password');
   }
    /**
     * Customize the password reset email title
@@ -426,10 +442,14 @@
     */
    public function customizePasswordResetTitle(string $title, string $user_login, WP_User $user_data):string
    {
      return $this->passwordResetTitle();
   }
   public function passwordResetTitle():string
   {
      $prefix = JVB_EMAIL['types']['resetPass']['showPrefix']??true;
      $prefix = ($prefix) ? $this->prefix : '';
      return $prefix.JVB_EMAIL['types']['resetPass']['subject']?:'Password Reset';
   }
    /**
@@ -671,15 +691,15 @@
         }
         $content .= sprintf(
            '<div style="padding:10px 0;border-bottom:1px solid %s;background-color:%s;">
               <span style="display:inline-block;vertical-align:top;font-weight:600;color:%s;width:%s;">%s</span>
               <div style="display:inline-block;vertical-align:top;width:%s;">%s</div>
            </div>',
        <span style="display:inline-block;vertical-align:top;font-weight:600;color:%s;width:%s;">%s</span>
        <div style="display:inline-block;vertical-align:top;width:%s;">%s</div>
    </div>',
            $this->colours['dark-200'],
            ($index%2 === 0) ? $this->colours['light-100'] : $this->colours['light-50'],
            $this->colours['dark-200'],
            '19%',
            '30%',  // Changed from 19% to 30%
            $item['label'],
            '80%',
            '68%',  // Changed from 80% to 68% (30% + 68% = 98%, leaving 2% for spacing)
            $item['value']
         );
      }
@@ -736,11 +756,35 @@
      );
   }
   public function grid(array $items, int $columns = 2):string
   public function grid(array $items, int $columns = 2, string $title = '', string|array $description = '', string $after = ''):string
   {
      $width = floor(100 / $columns) - 2; // 2% gap
      $html = '<div style="display:table;width:100%;margin:20px 0;">';
      $html = '';
      if (!empty($title) || !empty($description)) {
         $html .= '<div>';
         if (!empty($title)) {
            $html .= sprintf(
               '<h2>%s</h2>',
               $title
            );
         }
         if (!empty($description)) {
            if (is_string($description)) {
               if (str_starts_with($description, '<p>')) {
                  $html .= $description;
               }else {
                  $html .= sprintf(
                     '<p>%s</p>',
                     $description
                  );
               }
            } else {
               $html .= implode('',array_map(function ($p) { return sprintf('<p>%s</p>', $p); }, $description));
            }
         }
      }
      $html .= '<div style="display:table;width:100%;margin:20px 0;">';
      foreach ($items as $index => $item) {
         if ($index > 0 && $index % $columns === 0) {
            $html .= '</div><div style="display:table;width:100%;margin:20px 0;">';
@@ -751,11 +795,16 @@
            $item
         );
      }
      $html .= '</div>';
      $html .= '</div>'.$after;
      if (!empty($title) || !empty($description)) {
         $html .= '</div>';
      }
      return $html;
   }
   public function image(string $src, string $alt = '', int $maxWidth = 600):string
   {
      return sprintf(