jenkins-bot has submitted this change and it was merged.

Change subject: (bug 46665) Add HTML email support to email digest
......................................................................


(bug 46665) Add HTML email support to email digest

Change-Id: I3b881acbcf4b18fc0401364ea0a6bc993d2c2246
---
M Echo.i18n.php
M Echo.php
M formatters/BasicFormatter.php
M includes/EmailBatch.php
M includes/EmailFormatter.php
M tests/EmailFormatterTest.php
M tests/NotificationFormatterTest.php
7 files changed, 562 insertions(+), 195 deletions(-)

Approvals:
  Matthias Mullie: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/Echo.i18n.php b/Echo.i18n.php
index 5b6de50..dd1b4e7 100644
--- a/Echo.i18n.php
+++ b/Echo.i18n.php
@@ -19,8 +19,7 @@
        'prefs-newmessageindicator' => 'New message indicator',
        'echo-pref-send-me' => 'Send me:',
        'echo-pref-send-to' => 'Send to:',
-       // Update this to 'Email format' when HTML email is ready for email 
digest
-       'echo-pref-email-format' => 'Individual email format:',
+       'echo-pref-email-format' => 'Email format:',
        'echo-pref-web' => 'Web',
        'echo-pref-email' => 'Email',
        'echo-pref-email-frequency-never' => 'Do not send me any email 
notifications',
@@ -142,23 +141,11 @@
        'echo-email-batch-bullet' => '•', # only translate this message to 
other languages if you have to change it
        'echo-email-batch-subject-daily' => 'You have {{PLURAL:$2|a new 
notification|new notifications}} at {{SITENAME}}',
        'echo-email-batch-subject-weekly' => 'You have {{PLURAL:$2|a new 
notification|new notifications}} at {{SITENAME}} this week',
-       'echo-email-batch-body-daily' => 'Hi $1,
-
-You have {{PLURAL:$3|a new notification|new notifications}} on {{SITENAME}}. 
View {{PLURAL:$3|it|them}} here:
-{{canonicalurl:{{#special:Notifications}}}}
-
-$4
-
-$5',
-       'echo-email-batch-body-weekly' => 'Hi $1,
-
-You have {{PLURAL:$3|a new notification|new notifications}} on {{SITENAME}} 
this week. View {{PLURAL:$3|it|them}} here:
-{{canonicalurl:{{#special:Notifications}}}}
-
-$4
-
-$5',
-
+       'echo-email-batch-body-intro-daily' => "Hi $1,
+Here's a summary of today's activity on {{SITENAME}} for you",
+       'echo-email-batch-body-intro-weekly' => "Hi $1,
+Here's a summary of this week's activity on {{SITENAME}} for you",
+       'echo-email-batch-link-text-view-all-notifications' => 'View all 
notifications',
        // Supressed Revisions
        'echo-rev-deleted-text-view' => 'This page revision has been 
suppressed',
 );
@@ -495,22 +482,11 @@
 * $2 is a numeric count, this is used for plural support
 See also:
 * {{msg-mw|Echo-email-batch-subject-daily}}',
-       'echo-email-batch-body-daily' => 'Daily e-mail batch body. Parameters:
-* $1 is a username
-* $2 is currently not used, could be a numeric count or "10+". See also: 
{{msg-mw|echo-notification-count|optional message|notext=1}}.
-* $3 is a numeric count, this is used for plural support
-* $4 is the e-mail batch content separated by "-------..." 
({{msg-mw|echo-email-batch-separator}})
-* $5 is the e-mail footer, {{msg-mw|echo-email-footer-default}}
-See also:
-* {{msg-mw|Echo-email-batch-body-weekly}}',
-       'echo-email-batch-body-weekly' => 'Weekly e-mail batch body. Parameters:
-* $1 is a username
-* $2 is currently not used, could be a numeric count or "10+". See also: 
{{msg-mw|echo-notification-count|optional message|notext=1}}.
-* $3 is a numeric count, this is used for plural support
-* $4 is the e-mail batch content separated by "--------..." 
({{msg-mw|echo-email-batch-separator}})
-* $5 is the e-mail footer, {{msg-mw|echo-email-footer-default}}
-See also:
-* {{msg-mw|Echo-email-batch-body-daily}}',
+       'echo-email-batch-body-intro-daily' => "Introduction text for daily 
email digest.  Parameters:
+* $1 is a username",
+       'echo-email-batch-body-intro-weekly' => "Introduction text for weekly 
email digest.  Parameters:
+* $1 is a username",
+       'echo-email-batch-link-text-view-all-notifications' => 'The link text 
for the primary action in daily and weekly email digest',
        'echo-rev-deleted-text-view' => 'Short message displayed instead of 
edit content when revision text is suppressed.',
 );
 
diff --git a/Echo.php b/Echo.php
index 66eb6b9..9775edf 100644
--- a/Echo.php
+++ b/Echo.php
@@ -68,6 +68,9 @@
 $wgAutoloadClasses['EchoEmailMode'] = $dir . 'includes/EmailFormatter.php';
 $wgAutoloadClasses['EchoEmailSingle'] = $dir . 'includes/EmailFormatter.php';
 $wgAutoloadClasses['EchoEmailDigest'] = $dir . 'includes/EmailFormatter.php';
+$wgAutoloadClasses['EchoEmailDecorator'] = $dir . 
'includes/EmailFormatter.php';
+$wgAutoloadClasses['EchoTextEmailDecorator'] = $dir . 
'includes/EmailFormatter.php';
+$wgAutoloadClasses['EchoHTMLEmailDecorator'] = $dir . 
'includes/EmailFormatter.php';
 
 // Internal stuff
 $wgAutoloadClasses['EchoNotifier'] = $dir . 'Notifier.php';
diff --git a/formatters/BasicFormatter.php b/formatters/BasicFormatter.php
index e2b3eab..6e56f3b 100644
--- a/formatters/BasicFormatter.php
+++ b/formatters/BasicFormatter.php
@@ -258,10 +258,9 @@
         * @return array
         */
        protected function formatEmail( $event, $user, $type ) {
-               if ( $this->bundleData['use-bundle'] && 
$this->email['batch-bundle-body'] ) {
-                       $key = $this->email['batch-bundle-body'];
-               } else {
-                       $key = $this->email['batch-body'];
+               // Email digest
+               if ( $type === 'emaildigest' ) {
+                       return $this->formatEmailDigest( $event, $user );
                }
 
                // Echo single email
@@ -274,8 +273,6 @@
                        'subject' => $this->formatFragment( 
$this->email['subject'], $event, $user )->text(),
                        // Single email text body
                        'body' => $textEmailFormatter->formatEmail(),
-                       // Email digest text body
-                       'batch-body' => $this->formatFragment( $key, $event, 
$user )->text()
                );
 
                $format = MWEchoNotifUser::newFromUser( $user 
)->getEmailFormat();
@@ -295,6 +292,32 @@
        }
 
        /**
+        * Format text and/or html verion of email digest fragment for this 
event
+        * @param $event EchoEvent
+        * @param $user User
+        * @return array
+        */
+       protected function formatEmailDigest( $event, $user ) {
+               if ( $this->bundleData['use-bundle'] && 
$this->email['batch-bundle-body'] ) {
+                       $key = $this->email['batch-bundle-body'];
+               } else {
+                       $key = $this->email['batch-body'];
+               }
+
+               // Email digest text body
+               $content = array( 'batch-body' => $this->formatFragment( $key, 
$event, $user )->text() );
+               $format = MWEchoNotifUser::newFromUser( $user 
)->getEmailFormat();
+               if ( $format == EchoHooks::EMAIL_FORMAT_HTML ) {
+                       $outputFormat = $this->outputFormat;
+                       $this->setOutputFormat( 'htmlemail' );
+                       $content['batch-body-html'] = $this->formatFragment( 
$key, $event, $user )->parse();
+                       $content['icon'] = $this->icon;
+                       $this->setOutputFormat( $outputFormat );
+               }
+               return $content;
+       }
+
+       /**
         * Creates a notification fragment based on a message and parameters
         *
         * @param $details array An i18n message and parameters to pass to the 
message
diff --git a/includes/EmailBatch.php b/includes/EmailBatch.php
index cd6686d..50f9202 100644
--- a/includes/EmailBatch.php
+++ b/includes/EmailBatch.php
@@ -159,52 +159,12 @@
                // get the category for this event
                $category = $event->getCategory();
                $event->setBundleHash( $hash );
-               $email = EchoNotificationController::formatNotification( 
$event, $this->mUser, 'email', 'email' );
+               $email = EchoNotificationController::formatNotification( 
$event, $this->mUser, 'email', 'emaildigest' );
 
                if ( !isset( $this->content[$category] ) ) {
                        $this->content[$category] = array();
                }
-               $this->content[$category][] = $email['batch-body'];
-       }
-
-       /**
-        * Concatenate the list of contents with 'echo-email-batch-separator'
-        * grouped by category
-        * @return string
-        */
-       protected function listToText() {
-               if ( !$this->content ) {
-                       return '';
-               }
-
-               $result = array();
-               // build the text section for each category
-               foreach( $this->content as $category => $notifs ) {
-                       // Messages that can be used here:
-                       // * echo-category-title-system
-                       // * echo-category-title-other
-                       // * echo-category-title-edit-user-talk
-                       // * echo-category-title-reverted
-                       // * echo-category-title-article-linked
-                       // * echo-category-title-mention
-                       $output = wfMessage( 'echo-category-title-' . $category 
)->numParams( count( $notifs ) )->text()
-                               . wfMessage( 'colon-separator' )->text() . "\n";
-
-                       foreach( $notifs as $notif ) {
-                               $output .= "\n " . wfMessage( 
'echo-email-batch-bullet' )->text() . ' ' . $notif;
-                       }
-                       $result[] = $output;
-               }
-
-               // for prepending and appending 'echo-email-batch-separator'
-               $result = array_merge( array( '' ), $result, array( '' ) );
-
-               return trim(
-                               implode(
-                                       "\n\n" . wfMessage( 
'echo-email-batch-separator' )->text() . "\n\n",
-                                       $result
-                               )
-                       );
+               $this->content[$category][] = $email;
        }
 
        /**
@@ -216,13 +176,7 @@
         * Send the batch email
         */
        public function sendEmail() {
-               global $wgNotificationSender, $wgNotificationSenderName, 
$wgNotificationReplyName, $wgEchoEmailFooterAddress;
-
-               // global email footer
-               $footer = wfMessage( 'echo-email-footer-default' )
-                               ->inLanguage( $this->mUser->getOption( 
'language' ) )
-                               ->params( $wgEchoEmailFooterAddress, '' )
-                               ->text();
+               global $wgNotificationSender, $wgNotificationSenderName, 
$wgNotificationReplyName;
 
                // @Todo - replace them with the CONSTANT in 33810 once it is 
merged 
                if ( $this->mUser->getOption( 'echo-email-frequency' ) == 7 ) {
@@ -233,6 +187,22 @@
                        $emailDeliveryMode = 'daily_digest';
                }
 
+               // Echo digest email mode
+               $emailDigest = new EchoEmailDigest( $this->mUser, 
$this->content, $frequency );
+
+               $textEmailFormatter = new EchoTextEmailFormatter( $emailDigest 
);
+
+               $body = $textEmailFormatter->formatEmail();
+
+               $format = MWEchoNotifUser::newFromUser( $this->mUser 
)->getEmailFormat();
+               if ( $format == EchoHooks::EMAIL_FORMAT_HTML ) {
+                       $htmlEmailFormatter = new EchoHTMLEmailFormatter( 
$emailDigest );
+                       $body = array(
+                               'text' => $body,
+                               'html' => $htmlEmailFormatter->formatEmail()
+                       );
+               }
+
                // email subject
                if ( $this->count > self::$displaySize ) {
                        $count = wfMessage( 'echo-notification-count' 
)->params( self::$displaySize )->text();
@@ -240,13 +210,6 @@
                        $count = $this->count;
                }
                $subject = wfMessage( 'echo-email-batch-subject-' . $frequency 
)->params( $count, $this->count )->text();
-               $body = wfMessage( 'echo-email-batch-body-' . $frequency 
)->params(
-                               $this->mUser->getName(),
-                               $count,
-                               $this->count,
-                               $this->listToText(),
-                               $footer
-                       )->text();
 
                $toAddress = new MailAddress( $this->mUser );
                $fromAddress = new MailAddress( $wgNotificationSender, 
$wgNotificationSenderName );
diff --git a/includes/EmailFormatter.php b/includes/EmailFormatter.php
index e5f0bab..a4f9cfe 100644
--- a/includes/EmailFormatter.php
+++ b/includes/EmailFormatter.php
@@ -34,18 +34,18 @@
         */
        public function __construct( EchoEmailMode $emailMode ) {
                parent::__construct( $emailMode );
+               $this->emailMode->attachDecorator( new EchoTextEmailDecorator() 
);
        }
 
        /**
-        * Formatting text email notification
-        * @return string
+        * {@inheritDoc}
         */
        public function formatEmail() {
                $template = $this->emailMode->getTextTemplate();
 
                foreach ( $this->emailMode->getComponent() as $val ) {
                        $func = 'build' . ucfirst( $val );
-                       $template = str_replace( "%%$val%%", 
$this->emailMode->$func( 'text' ), $template );
+                       $template = str_replace( "%%$val%%", 
$this->emailMode->$func(), $template );
                }
 
                // Remove redundant newline characters
@@ -73,18 +73,18 @@
         */
        public function __construct( EchoEmailMode $emailMode ) {
                parent::__construct( $emailMode );
+               $this->emailMode->attachDecorator( new EchoHTMLEmailDecorator() 
);
        }
 
        /**
-        * Formatting HTML email notification
-        * @return string
+        * {@inheritDoc}
         */
        public function formatEmail() {
                $template = $this->emailMode->getHTMLTemplate();
 
                foreach ( $this->emailMode->getComponent() as $val ) {
                        $func = 'build' . ucfirst( $val );
-                       $template = str_replace( "%%$val%%", 
$this->emailMode->$func( 'html' ), $template );
+                       $template = str_replace( "%%$val%%", 
$this->emailMode->$func(), $template );
                }
 
                return $template;
@@ -97,15 +97,22 @@
 abstract class EchoEmailMode {
 
        /**
-        * Email components
         * @var array
+        * Email components
         */
        protected $component;
 
        /**
         * @var User
+        * The user who receives email notifications
         */
        protected $user;
+
+       /**
+        * @var EchoEmailDecorator
+        * Email decorator
+        */
+       protected $decorator;
 
        /**
         * @param $user User
@@ -115,6 +122,9 @@
                $this->user = $user;
                // All email delivery mode share the same footer
                $this->component = array_merge( $component, array( 'footer' ) );
+               // Initialize with a text decorator, the decorator can be 
altered
+               // via attacheDecorator() based on text/html emails
+               $this->decorator = new EchoTextEmailDecorator();
        }
 
        /**
@@ -131,27 +141,11 @@
 
        /**
         * Get the footer component
-        * @param $format string 'text'/'html'
         * @return string
         */
-       public function buildFooter( $format ) {
+       public function buildFooter() {
                global $wgEchoEmailFooterAddress;
-
-               if ( $format === 'text' ) {
-                       return wfMessage( 'echo-email-footer-default' )
-                                       ->params(
-                                               $wgEchoEmailFooterAddress,
-                                               wfMessage( 
'echo-email-batch-separator' )->text()
-                                       )
-                                       ->text();
-               } else {
-                       $title = SpecialPage::getTitleFor( 'Preferences' );
-                       $title->setFragment( "#mw-prefsection-echo" );
-                       return wfMessage( 'echo-email-footer-default-html' )
-                                       ->params( $wgEchoEmailFooterAddress )
-                                       ->rawParams( $title->getFullURL( '', 
false, PROTO_HTTPS ) )
-                                       ->text();
-               }
+               return $this->decorator->decorateFooter( 
$wgEchoEmailFooterAddress );
        }
 
        /**
@@ -163,24 +157,35 @@
        }
 
        /**
-        * The style for primary link
+        * Get the notification icon path
+        * @param $icon string
         * @return string
         */
-       protected function getPrimaryLinkCSS() {
-               return 'cursor:pointer; text-align:center;
-                       text-decoration:none; padding:.45em 1.2em .45em;
-                       color:#D9EEF7; background:#3366BB;
-                       font-family: arial;font-size: 13px;';
+       public static function getNotifIcon( $icon ) {
+               global $wgEchoNotificationIcons, $wgExtensionAssetsPath;
+
+               $iconInfo = $wgEchoNotificationIcons[$icon];
+               if ( isset( $iconInfo['url'] ) && $iconInfo['url'] ) {
+                       $iconUrl = $iconInfo['url'];
+               } else {
+                       if ( !isset( $iconInfo['path'] ) || !$iconInfo['path'] 
) {
+                               $iconInfo = 
$wgEchoNotificationIcons['placeholder'];
+                       }
+                       $iconUrl = "$wgExtensionAssetsPath/{$iconInfo['path']}";
+               }
+
+               // Use http for image path, there is no need for https
+               return wfExpandUrl( $iconUrl, PROTO_HTTP );
        }
 
        /**
-        * The style for secondary link
-        * @return string
+        * Attach an email decorator to the email mode object
+        * @param $decorator EchoEmailDecorator
         */
-       protected function getSecondaryLinkCSS() {
-               return 'text-decoration: none;font-size: 10px;font-family: 
arial;
-                       color: #808184';
+       public function attachDecorator( EchoEmailDecorator $decorator ) {
+               $this->decorator = $decorator;
        }
+
 }
 
 /**
@@ -211,10 +216,9 @@
 
        /**
         * Build the intro component
-        * @param $format string 'text'/'html'
         * @return string
         */
-       public function buildIntro( $format ) {
+       public function buildIntro() {
                $bundle = $this->notifFormatter->getValue( 'bundleData' );
                $email  = $this->notifFormatter->getValue( 'email' );
 
@@ -230,19 +234,14 @@
                        $this->user
                );
 
-               if ( $format === 'text' ) {
-                       return $message->text();
-               } else {
-                       return $message->parse();
-               }
+               return $this->decorator->decorateIntro( $message );
        }
 
        /**
         * Build the summary component
-        * @param $format string 'text'/'html'
         * @return string
         */
-       public function buildSummary( $format ) {
+       public function buildSummary() {
                return $this->notifFormatter->formatRevisionComment(
                        $this->event,
                        $this->user
@@ -251,10 +250,9 @@
 
        /**
         * Build the action component
-        * @param $format string 'text'/'html'
         * @return string
         */
-       public function buildAction( $format ) {
+       public function buildAction() {
                $link = array();
                $ranks = array( 'primary', 'secondary' );
 
@@ -266,56 +264,30 @@
                                continue;
                        }
 
-                       // Plain text email
-                       if ( $format === 'text' ) {
-                               $url = $this->notifFormatter->getLink( 
$this->event, $this->user, $rank, false, true );
-
-                               $link[] = wfMessage( $message )->text()
-                                       . wfMessage( 'colon-separator' )->text()
-                                       . '<'
-                                       . 
$this->notifFormatter->sanitizeEmailLink( $url )
-                                       . '>';
-                       // HTML email
-                       } else {
-                               if ( $rank === 'primary' ) {
-                                       $style = $this->getPrimaryLinkCSS();
-                               } else {
-                                       $style = $this->getSecondaryLinkCSS();
-                               }
-
-                               $link[] = $this->notifFormatter->getLink( 
$this->event, $this->user, $rank, false, false, $style );
-                       }
+                       $link[] = $this->decorator->decorateSingleAction(
+                               $this->notifFormatter,
+                               $this->event,
+                               $this->user,
+                               $rank,
+                               $message
+                       );
                }
 
                // Add some spacing between the two action links
-               $spacing = ( $format === 'text' ) ? "\n\n" : "&nbsp;&nbsp;";
-               return implode( $spacing, $link );
+               $spacing = $this->decorator->getActionLinkSeparator();
+               return implode( $spacing . $spacing, $link );
        }
 
        /**
         * Build the email icon component
-        * @param $format string 'text'/'html'
         * @return string
         */
-       public function buildEmailIcon( $format ) {
-               global  $wgEchoNotificationIcons, $wgExtensionAssetsPath;
-
-               $iconInfo = 
$wgEchoNotificationIcons[$this->notifFormatter->getValue( 'icon' )];
-               if ( isset( $iconInfo['url'] ) && $iconInfo['url'] ) {
-                       $iconUrl = $iconInfo['url'];
-               } else {
-                       if ( !isset( $iconInfo['path'] ) || !$iconInfo['path'] 
) {
-                               $iconInfo = 
$wgEchoNotificationIcons['placeholder'];
-                       }
-                       $iconUrl = "$wgExtensionAssetsPath/{$iconInfo['path']}";
-               }
-
-               return wfExpandUrl( $iconUrl );
+       public function buildEmailIcon() {
+               return EchoEmailMode::getNotifIcon( 
$this->notifFormatter->getValue( 'icon' ) );
        }
 
        /**
-        * Get template for text email
-        * @return string
+        * {@inheritDoc}
         */
        public function getTextTemplate() {
                return <<< EOF
@@ -330,8 +302,7 @@
        }
 
        /**
-        * Get template for html email
-        * @return string
+        * {@inheritDoc}
         */
        public function getHTMLTemplate() {
                return <<< EOF
@@ -397,19 +368,424 @@
 
 /**
  * Class that represents email digest delivery mode
- * @Todo - To be completed for email digest
  */
 class EchoEmailDigest extends EchoEmailMode {
 
-       public function __construct( $user ) {
-               parent::__construct( $user, array( 'header', 'intro', 
'digestList' ) );
+       /**
+        * @var string
+        * The mode of email digest, 'weekly' or 'daily'
+        */
+       protected $digestMode;
+
+       /**
+        * @var array
+        * Raw email digest list
+        */
+       protected $rawDigestList;
+
+       /**
+        * @param $user User
+        * @param $rawDigestList array the raw notification event list
+        * @param $digestMode string 'daily'/'weekly'
+        */
+       public function __construct( User $user, array $rawDigestList, 
$digestMode = 'daily' ) {
+               parent::__construct( $user, array( 'intro', 'digestList', 
'action' ) );
+               // Some data validation
+               if ( !in_array( $digestMode, array( 'daily', 'weekly' ) ) ) {
+                       $digestMode = 'daily';
+               }
+               $this->digestMode = $digestMode;
+               $this->rawDigestList = $rawDigestList;
        }
 
-       public function buildHeader() {}
-       public function buildIntro() {}
-       public function buildDigestList() {}
+       /**
+        * Build the intro component
+        * @return string
+        */
+       public function buildIntro() {
+               $message = wfMessage(
+                       'echo-email-batch-body-intro-' . $this->digestMode,
+                       $this->user->getName()
+               );
 
-       public function getTextTemplate() {}
-       public function getHTMLTemplate() {}
+               return $this->decorator->decorateIntro( $message );
+       }
+
+       /**
+        * Build the digestList component
+        * @return string
+        */
+       public function buildDigestList() {
+               if ( !$this->rawDigestList ) {
+                       return '';
+               }
+
+               return $this->decorator->decorateDigestList( 
$this->rawDigestList );
+       }
+
+       /**
+        * Build the action component
+        * @return string
+        */
+       public function buildAction() {
+               $title = SpecialPage::getTitleFor( 'Notifications' );
+
+               return $this->decorator->decorateDigestAction( $title );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function getTextTemplate() {
+               return <<< EOF
+%%intro%%
+
+%%digestList%%
+
+%%action%%
+
+%%footer%%
+
+EOF;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function getHTMLTemplate() {
+               return <<< EOF
+<html><head></head><body>
+<table cellspacing="0" cellpadding="0" border="0" width="100%" align="center">
+<tr>
+       <td bgcolor="#E6E7E8"><center>
+               <br /><br />
+               <table cellspacing="0" cellpadding="0" border="0" width="600">
+                       <tr>
+                               <td bgcolor="#FFFFFF" width="35">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="31">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="469" 
style="line-height:40px;">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="65">&nbsp;</td>
+                       </tr>
+                       <tr>
+                               <td bgcolor="#FFFFFF" width="35" 
rowspan="2">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="31" 
rowspan="2">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="469" 
align="center" style="font-family:arial; font-size:13px; line-height:20px; 
color: #A6A8AB; text-align: center;">%%intro%%</td>
+                               <td bgcolor="#FFFFFF" width="65" 
rowspan="2">&nbsp;</td>
+                       </tr>
+                       <tr>
+                               <td bgcolor="#FFFFFF" width="469" align="left" 
style="font-family: arial; font-size:16px; line-height: 20px; font-weight: 
600;">
+                                       <table cellspacing="0" cellpadding="0" 
border="0" width="100%">
+                                               <tr>
+                                                       <td bgcolor="#FFFFFF" 
align="left" style="font-family: arial; font-size:13px; color: #58585B; 
padding-top: 25px;">
+                                                               %%digestList%%
+                                                       </td>
+                                               </tr>
+                                       </table>
+                                       <br /><br />
+                               </td>
+                       </tr>
+                       <tr>
+                               <td bgcolor="#FFFFFF" width="35">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="31">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="469" 
style="line-height:60px;" align="center">%%action%%</td>
+                               <td bgcolor="#FFFFFF" width="65">&nbsp;</td>
+                       </tr>
+                       <tr>
+                               <td bgcolor="#FFFFFF" width="35">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="31">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="469" 
style="line-height:40px;">&nbsp;</td>
+                               <td bgcolor="#FFFFFF" width="65">&nbsp;</td>
+                       </tr>
+                       <tr>
+                               <td bgcolor="#F8F8F8" width="35">&nbsp;</td>
+                               <td bgcolor="#F8F8F8" width="31">&nbsp;</td>
+                               <td bgcolor="#F8F8F8" width="469" align="left" 
style="font-family:arial; font-size:10px; line-height:13px; color:#B7B7B7; 
padding: 10px 20px;"><br />
+                                       %%footer%%
+                                       <br /><br />
+                               </td>
+                               <td bgcolor="#F8F8F8" width="65">&nbsp;</td>
+                       </tr>
+                       <tr>
+                               <td colspan="4">&nbsp;</td>
+                       </tr>
+               </table>
+               <br><br></center>
+       </td>
+</tr>
+</table>
+</body></html>
+EOF;
+       }
 
 }
+
+/**
+ * Email decorator interface
+ */
+interface EchoEmailDecorator {
+       /**
+        * Decorate the intro for all modes
+        * @param $message Message the intro message object
+        * @return string
+        */
+       public function decorateIntro( $message );
+
+       /**
+        * Decorate the digest list for digest mode
+        * @param $digestList array
+        * @return string
+        */
+       public function decorateDigestList( $digestList );
+
+       /**
+        * Decorate the primary action for digest mode
+        * @param $title Title
+        * @return string
+        */
+       public function decorateDigestAction( $title );
+
+       /**
+        * Decorate the footer for all mode
+        * @param $address string
+        * @return string
+        */
+       public function decorateFooter( $address );
+
+       /**
+        * Decorate the actions for single mode
+        * @param $notifFormatter EchoBasicFormatter
+        * @param $event EchoEvent
+        * @param $user User
+        * @param $rank string
+        * @param $message string
+        * @return string
+        */
+       public function decorateSingleAction( $notifFormatter, $event, $user, 
$rank, $message );
+
+       /**
+        * Get the spacing for between action links
+        * @return string
+        */
+       public function getActionLinkSeparator();
+}
+
+/**
+ * Text email decorator
+ */
+class EchoTextEmailDecorator implements EchoEmailDecorator {
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateIntro( $message ) {
+               return $message->text();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateDigestList( $digestList ) {
+               $result = array();
+
+               // build the text section for each category
+               foreach( $digestList as $category => $notifs ) {
+                       $output = wfMessage( 'echo-category-title-' . $category 
)->numParams( count( $notifs ) )->text()
+                               . wfMessage( 'colon-separator' )->text() . "\n";
+
+                       foreach( $notifs as $notif ) {
+                               $output .= "\n " . wfMessage( 
'echo-email-batch-bullet' )->text() . ' ' . $notif['batch-body'];
+                       }
+                       $result[] = $output;
+               }
+
+               // for prepending and appending 'echo-email-batch-separator'
+               $result = array_merge( array( '' ), $result, array( '' ) );
+
+               return trim(
+                       implode(
+                               "\n\n" . wfMessage( 
'echo-email-batch-separator' )->text() . "\n\n",
+                               $result
+                       )
+               );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateDigestAction( $title ) {
+               return wfMessage( 
'echo-email-batch-link-text-view-all-notifications' )->text()
+                       . wfMessage( 'colon-separator' )->text()
+                       . '<'
+                       . $title->getFullURL( '', false, PROTO_HTTPS )
+                       . '>';
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateFooter( $address ) {
+               return wfMessage( 'echo-email-footer-default' )
+                               ->params(
+                                       $address,
+                                       wfMessage( 'echo-email-batch-separator' 
)->text()
+                               )
+                               ->text();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateSingleAction( $notifFormatter, $event, $user, 
$rank, $message ) {
+               $url = $notifFormatter->getLink( $event, $user, $rank, false, 
true );
+
+               return wfMessage( $message )->text()
+                       . wfMessage( 'colon-separator' )->text()
+                       . '<'
+                       . $notifFormatter->sanitizeEmailLink( $url )
+                       . '>';
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function getActionLinkSeparator() {
+               return "\n";
+       }
+}
+
+/**
+ * HTML email decorator
+ */
+class EchoHTMLEmailDecorator implements EchoEmailDecorator {
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateIntro( $message ) {
+               return nl2br( $message->parse() );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateDigestList( $digestList ) {
+               $result = array();
+               // build the html section for each category
+               foreach( $digestList as $category => $notifs ) {
+                       $output = $this->applyStyleToCategory(
+                               wfMessage( 'echo-category-title-' . $category )
+                                       ->numParams( count( $notifs ) )
+                                       ->escaped()
+                       );
+                       foreach( $notifs as $notif ) {
+                               $output .= "\n" . $this->applyStyleToEvent( 
$notif );
+                       }
+                       $result[] = '<table border="0" width="100%">' . $output 
. '</table>';
+               }
+
+               return trim( implode( "\n", $result ) );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateDigestAction( $title ) {
+               return Linker::link(
+                       $title,
+                       wfMessage( 
'echo-email-batch-link-text-view-all-notifications' )->escaped(),
+                       array( 'style' => $this->getPrimaryLinkCSS() ),
+                       array(),
+                       array( 'https' )
+               );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateFooter( $address ) {
+               $title = SpecialPage::getTitleFor( 'Preferences' );
+               $title->setFragment( "#mw-prefsection-echo" );
+               return wfMessage( 'echo-email-footer-default-html' )
+                               ->params( $address )
+                               ->rawParams( $title->getFullURL( '', false, 
PROTO_HTTPS ) )
+                               ->text();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function decorateSingleAction( $notifFormatter, $event, $user, 
$rank, $message ) {
+               if ( $rank === 'primary' ) {
+                       $style = $this->getPrimaryLinkCSS();
+               } else {
+                       $style = $this->getSecondaryLinkCSS();
+               }
+
+               return $notifFormatter->getLink( $event, $user, $rank, false, 
false, $style );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function getActionLinkSeparator() {
+               return "&nbsp;";
+       }
+
+       /**
+        * The style for primary link
+        * @return string
+        */
+       protected function getPrimaryLinkCSS() {
+               return 'cursor:pointer; text-align:center; 
text-decoration:none; padding:.45em 1.2em .45em;
+                       color:#D9EEF7; background:#3366BB; font-family: 
arial;font-size: 13px;';
+       }
+
+       /**
+        * The style for secondary link
+        * @return string
+        */
+       protected function getSecondaryLinkCSS() {
+               return 'text-decoration: none;font-size: 10px;font-family: 
arial; color: #808184';
+       }
+
+       /**
+        * Apply style to notification category header
+        * @param $category string
+        * @return string
+        */
+       protected function applyStyleToCategory( $category ) {
+               return <<< EOF
+<tr>
+       <td colspan="2" style="color: #A87B4F; font-weight: normal; font-size: 
13px; padding-top: 15px;">
+               $category <br />
+               <hr style="background-color:#FFFFFF; color:#FFFFFF; border: 1px 
solid #F2F2F2;" />
+       </td>
+</tr>
+EOF;
+       }
+
+       /**
+        * Apply style to individual notification event
+        * @param $notif array an array containts keys: icon, batch-body, 
batch-body-html
+        * @return string
+        */
+       protected function applyStyletoEvent( $notif ) {
+               // notification icon
+               $icon = EchoEmailMode::getNotifIcon( $notif['icon'] );
+               // notification text
+               $text = $notif['batch-body-html'];
+
+               return <<< EOF
+<tr>
+       <td width="30">
+               <img src="$icon" width="70%" height="70%" 
style="vertical-align:middle;">
+       </td>
+       <td style="font-family: arial; font-size:13px; color: #58585B;">
+               $text
+       </td>
+</tr>
+EOF;
+       }
+
+}
+
diff --git a/tests/EmailFormatterTest.php b/tests/EmailFormatterTest.php
index 9f79eb7..67d5e56 100644
--- a/tests/EmailFormatterTest.php
+++ b/tests/EmailFormatterTest.php
@@ -3,6 +3,7 @@
 class EchoEmailFormatterTest extends MediaWikiTestCase {
 
        private $emailSingle;
+       private $emailDigest;
 
        public function setUp() {
                parent::setUp();
@@ -18,11 +19,15 @@
                $formatter->setOutputFormat( 'email' );
 
                $this->emailSingle = new EchoEmailSingle( $formatter, $event, 
User::newFromId( 2 ) );
+
+               $content[$event->getCategory()][] = 
EchoNotificationController::formatNotification( $event, User::newFromId( 2 ), 
'email', 'emaildigest' );
+               $this->emailDigest = new EchoEmailDigest( User::newFromId( 2 ), 
$content );
        }
 
        public function testEmailFormatter() {
                $pattern = '/%%(.*?)%%/is';
 
+               // Single email mode
                $textFormatter = new EchoTextEmailFormatter( $this->emailSingle 
);
                $this->assertRegExp( $pattern, 
$this->emailSingle->getTextTemplate() );
                $this->assertEquals( 0, preg_match ( $pattern, 
$textFormatter->formatEmail() ) );
@@ -30,11 +35,29 @@
                $htmlFormatter = new EchoHTMLEmailFormatter( $this->emailSingle 
);
                $this->assertRegExp( $pattern, 
$this->emailSingle->getHTMLTemplate() );
                $this->assertEquals( 0, preg_match ( $pattern, 
$htmlFormatter->formatEmail() ) );
+
+               // Digest email mode
+               $textFormatter = new EchoTextEmailFormatter( $this->emailDigest 
);
+               $this->assertRegExp( $pattern, 
$this->emailSingle->getTextTemplate() );
+               $this->assertEquals( 0, preg_match ( $pattern, 
$textFormatter->formatEmail() ) );
+
+               $htmlFormatter = new EchoHTMLEmailFormatter( $this->emailDigest 
);
+               $this->assertRegExp( $pattern, 
$this->emailSingle->getHTMLTemplate() );
+               $this->assertEquals( 0, preg_match ( $pattern, 
$htmlFormatter->formatEmail() ) );
        }
 
        public function testBuildAction() {
-               $this->assertEquals( 0, preg_match ( '/<a /i', 
$this->emailSingle->buildAction( 'text' ) ) );
-               $this->assertRegExp( '/<a /i', $this->emailSingle->buildAction( 
'html' ) );
+               $this->emailSingle->attachDecorator( new 
EchoTextEmailDecorator() );
+               $this->assertEquals( 0, preg_match ( '/<a /i', 
$this->emailSingle->buildAction() ) );
+
+               $this->emailSingle->attachDecorator( new 
EchoHTMLEmailDecorator() );
+               $this->assertRegExp( '/<a /i', 
$this->emailSingle->buildAction() );
+
+               $this->emailDigest->attachDecorator( new 
EchoTextEmailDecorator() );
+               $this->assertEquals( 0, preg_match ( '/<a /i', 
$this->emailDigest->buildAction() ) );
+
+               $this->emailDigest->attachDecorator( new 
EchoHTMLEmailDecorator() );
+               $this->assertRegExp( '/<a /i', 
$this->emailDigest->buildAction() );
        }
 
        protected function mockEvent( $type ) {
@@ -48,6 +71,9 @@
                $event->expects( $this->any() )
                        ->method( 'getType' )
                        ->will( $this->returnValue( $type ) );
+               $event->expects( $this->any() )
+                       ->method( 'getCategory' )
+                       ->will( $this->returnValue( 
EchoNotificationController::getNotificationCategory( $type ) ) );
                return $event;
        }
 
diff --git a/tests/NotificationFormatterTest.php 
b/tests/NotificationFormatterTest.php
index d1afd9f..34dd8e0 100644
--- a/tests/NotificationFormatterTest.php
+++ b/tests/NotificationFormatterTest.php
@@ -174,7 +174,7 @@
                // generic assertion, could do better
                if ( $format === 'email' ) {
                        $this->assertInternalType( 'array', $result );
-                       $this->assertCount( 3, $result );
+                       $this->assertCount( 2, $result );
                } else {
                        $this->assertInternalType( 'string', $result );
                        $this->assertGreaterThan( 0, strlen( $result ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/72672
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I3b881acbcf4b18fc0401364ea0a6bc993d2c2246
Gerrit-PatchSet: 18
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: Bsitu <bs...@wikimedia.org>
Gerrit-Reviewer: Bsitu <bs...@wikimedia.org>
Gerrit-Reviewer: EBernhardson (WMF) <ebernhard...@wikimedia.org>
Gerrit-Reviewer: Matthias Mullie <mmul...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to