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" : " "; - 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"> </td> + <td bgcolor="#FFFFFF" width="31"> </td> + <td bgcolor="#FFFFFF" width="469" style="line-height:40px;"> </td> + <td bgcolor="#FFFFFF" width="65"> </td> + </tr> + <tr> + <td bgcolor="#FFFFFF" width="35" rowspan="2"> </td> + <td bgcolor="#FFFFFF" width="31" rowspan="2"> </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"> </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"> </td> + <td bgcolor="#FFFFFF" width="31"> </td> + <td bgcolor="#FFFFFF" width="469" style="line-height:60px;" align="center">%%action%%</td> + <td bgcolor="#FFFFFF" width="65"> </td> + </tr> + <tr> + <td bgcolor="#FFFFFF" width="35"> </td> + <td bgcolor="#FFFFFF" width="31"> </td> + <td bgcolor="#FFFFFF" width="469" style="line-height:40px;"> </td> + <td bgcolor="#FFFFFF" width="65"> </td> + </tr> + <tr> + <td bgcolor="#F8F8F8" width="35"> </td> + <td bgcolor="#F8F8F8" width="31"> </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"> </td> + </tr> + <tr> + <td colspan="4"> </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 " "; + } + + /** + * 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