2013-06-24 00:22:08 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract class for formatting email notifications
|
|
|
|
*/
|
|
|
|
abstract class EchoEmailFormatter {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var EchoEmailMode
|
|
|
|
*/
|
|
|
|
protected $emailMode;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $emailMode EchoEmailMode
|
|
|
|
*/
|
|
|
|
public function __construct( EchoEmailMode $emailMode ) {
|
|
|
|
$this->emailMode = $emailMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract method for formatting email
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function formatEmail();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Formatter class for formatting text email notification
|
|
|
|
*/
|
|
|
|
class EchoTextEmailFormatter extends EchoEmailFormatter {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $emailMode EchoEmailMode
|
|
|
|
*/
|
|
|
|
public function __construct( EchoEmailMode $emailMode ) {
|
|
|
|
parent::__construct( $emailMode );
|
2013-07-09 00:41:08 +00:00
|
|
|
$this->emailMode->attachDecorator( new EchoTextEmailDecorator() );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* {@inheritDoc}
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
public function formatEmail() {
|
|
|
|
$template = $this->emailMode->getTextTemplate();
|
|
|
|
|
|
|
|
foreach ( $this->emailMode->getComponent() as $val ) {
|
|
|
|
$func = 'build' . ucfirst( $val );
|
2013-07-09 00:41:08 +00:00
|
|
|
$template = str_replace( "%%$val%%", $this->emailMode->$func(), $template );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove redundant newline characters
|
|
|
|
return $this->removeExtraNewLine( $template );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove extra newline from a text content
|
|
|
|
* @param $text string
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function removeExtraNewLine( $text ) {
|
|
|
|
return preg_replace( "/\n{3,}/", "\n\n", $text );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Formatter class for formatting HTML email notification
|
|
|
|
*/
|
|
|
|
class EchoHTMLEmailFormatter extends EchoEmailFormatter {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $emailMode EchoEmailMode
|
|
|
|
*/
|
|
|
|
public function __construct( EchoEmailMode $emailMode ) {
|
|
|
|
parent::__construct( $emailMode );
|
2013-07-09 00:41:08 +00:00
|
|
|
$this->emailMode->attachDecorator( new EchoHTMLEmailDecorator() );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* {@inheritDoc}
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
public function formatEmail() {
|
|
|
|
$template = $this->emailMode->getHTMLTemplate();
|
|
|
|
|
|
|
|
foreach ( $this->emailMode->getComponent() as $val ) {
|
|
|
|
$func = 'build' . ucfirst( $val );
|
2013-07-09 00:41:08 +00:00
|
|
|
$template = str_replace( "%%$val%%", $this->emailMode->$func(), $template );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $template;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract entity that represents an email delivery mode
|
|
|
|
*/
|
|
|
|
abstract class EchoEmailMode {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
2013-07-09 00:41:08 +00:00
|
|
|
* Email components
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
protected $component;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var User
|
2013-07-09 00:41:08 +00:00
|
|
|
* The user who receives email notifications
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
protected $user;
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* @var EchoEmailDecorator
|
|
|
|
* Email decorator
|
|
|
|
*/
|
|
|
|
protected $decorator;
|
|
|
|
|
2013-09-17 23:52:57 +00:00
|
|
|
/**
|
|
|
|
* @var Language
|
|
|
|
* The language object for the user language
|
|
|
|
*/
|
|
|
|
protected $lang;
|
|
|
|
|
2013-06-24 00:22:08 +00:00
|
|
|
/**
|
|
|
|
* @param $user User
|
|
|
|
* @param $component array
|
|
|
|
*/
|
|
|
|
public function __construct( User $user, array $component ) {
|
|
|
|
$this->user = $user;
|
|
|
|
// All email delivery mode share the same footer
|
|
|
|
$this->component = array_merge( $component, array( 'footer' ) );
|
2013-07-09 00:41:08 +00:00
|
|
|
// Initialize with a text decorator, the decorator can be altered
|
2015-08-13 17:51:33 +00:00
|
|
|
// via attachDecorator() based on text/html emails
|
2013-07-09 00:41:08 +00:00
|
|
|
$this->decorator = new EchoTextEmailDecorator();
|
2013-09-17 23:52:57 +00:00
|
|
|
$this->lang = Language::factory( $user->getOption( 'language' ) );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get text email template
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function getTextTemplate();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get html email template
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function getHTMLTemplate();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the footer component
|
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function buildFooter() {
|
2013-06-24 00:22:08 +00:00
|
|
|
global $wgEchoEmailFooterAddress;
|
2013-07-31 02:02:16 +00:00
|
|
|
return $this->decorator->decorateFooter( $wgEchoEmailFooterAddress, $this->user );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter method for email template component
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getComponent() {
|
|
|
|
return $this->component;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* Get the notification icon path
|
|
|
|
* @param $icon string
|
2013-06-24 00:22:08 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public static function getNotifIcon( $icon ) {
|
2015-08-05 19:41:12 +00:00
|
|
|
global $wgLang;
|
|
|
|
|
|
|
|
$iconUrl = EchoNotificationFormatter::getIconUrl( $icon, $wgLang->getDir() );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
2015-07-06 10:08:03 +00:00
|
|
|
return wfExpandUrl( $iconUrl, PROTO_CANONICAL );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* Attach an email decorator to the email mode object
|
|
|
|
* @param $decorator EchoEmailDecorator
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function attachDecorator( EchoEmailDecorator $decorator ) {
|
|
|
|
$this->decorator = $decorator;
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
2013-07-09 00:41:08 +00:00
|
|
|
|
2013-07-31 02:02:16 +00:00
|
|
|
/**
|
|
|
|
* Format the message in the user's language
|
|
|
|
* @param $message string
|
|
|
|
* @param $user User
|
|
|
|
* @return Message
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public static function message( $message, User $user ) {
|
2013-07-31 02:02:16 +00:00
|
|
|
return wfMessage( $message )->inLanguage( $user->getOption( 'language' ) );
|
|
|
|
}
|
|
|
|
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Entity that represents a single email delivery mode
|
|
|
|
*/
|
|
|
|
class EchoEmailSingle extends EchoEmailMode {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var EchoBasicFormatter
|
|
|
|
*/
|
|
|
|
protected $notifFormatter;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var EchoEvent
|
|
|
|
*/
|
|
|
|
protected $event;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $notifFormatter EchoBasicFormatter
|
|
|
|
* @param $event EchoEvent
|
|
|
|
* @param $user User
|
|
|
|
*/
|
|
|
|
public function __construct( EchoBasicFormatter $notifFormatter, EchoEvent $event, User $user ) {
|
|
|
|
parent::__construct( $user, array ( 'emailIcon', 'intro', 'summary', 'action' ) );
|
|
|
|
$this->notifFormatter = $notifFormatter;
|
|
|
|
$this->event = $event;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the intro component
|
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function buildIntro() {
|
2013-06-24 00:22:08 +00:00
|
|
|
$bundle = $this->notifFormatter->getValue( 'bundleData' );
|
|
|
|
$email = $this->notifFormatter->getValue( 'email' );
|
|
|
|
|
|
|
|
if ( $bundle['use-bundle'] && $email['batch-bundle-body']['message'] ) {
|
|
|
|
$detail = $email['batch-bundle-body'];
|
|
|
|
} else {
|
|
|
|
$detail = $email['batch-body'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$message = $this->notifFormatter->formatFragment(
|
|
|
|
$detail,
|
|
|
|
$this->event,
|
|
|
|
$this->user
|
|
|
|
);
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
return $this->decorator->decorateIntro( $message );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the summary component
|
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function buildSummary() {
|
2013-07-24 03:50:43 +00:00
|
|
|
return $this->decorator->decorateRevisionSnippet(
|
|
|
|
$this->notifFormatter->getRevisionSnippet(
|
|
|
|
$this->event,
|
|
|
|
$this->user
|
|
|
|
)
|
2013-06-24 00:22:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the action component
|
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function buildAction() {
|
2013-06-24 00:22:08 +00:00
|
|
|
$link = array();
|
|
|
|
$ranks = array( 'primary', 'secondary' );
|
|
|
|
|
|
|
|
foreach ( $ranks as $rank ) {
|
|
|
|
$message = $this->event->getLinkMessage( $rank );
|
|
|
|
|
|
|
|
// Valid call to action should have link text
|
|
|
|
if ( !$message ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
$link[] = $this->decorator->decorateSingleAction(
|
|
|
|
$this->notifFormatter,
|
|
|
|
$this->event,
|
|
|
|
$this->user,
|
|
|
|
$rank,
|
|
|
|
$message
|
|
|
|
);
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add some spacing between the two action links
|
2013-07-09 00:41:08 +00:00
|
|
|
$spacing = $this->decorator->getActionLinkSeparator();
|
|
|
|
return implode( $spacing . $spacing, $link );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the email icon component
|
|
|
|
* @return string
|
|
|
|
*/
|
2013-07-09 00:41:08 +00:00
|
|
|
public function buildEmailIcon() {
|
|
|
|
return EchoEmailMode::getNotifIcon( $this->notifFormatter->getValue( 'icon' ) );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* {@inheritDoc}
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
public function getTextTemplate() {
|
|
|
|
return <<< EOF
|
|
|
|
%%intro%%
|
|
|
|
|
|
|
|
%%summary%%
|
|
|
|
|
|
|
|
%%action%%
|
|
|
|
|
|
|
|
%%footer%%
|
|
|
|
EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-07-09 00:41:08 +00:00
|
|
|
* {@inheritDoc}
|
2013-06-24 00:22:08 +00:00
|
|
|
*/
|
|
|
|
public function getHTMLTemplate() {
|
2013-09-17 23:52:57 +00:00
|
|
|
$alignStart = $this->lang->alignStart();
|
2013-06-24 00:22:08 +00:00
|
|
|
return <<< EOF
|
2013-08-21 22:04:44 +00:00
|
|
|
<html><head>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
<style>
|
|
|
|
@media only screen and (max-width: 480px){
|
|
|
|
table[id="email-container"]{max-width:600px !important; width:100% !important;}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head><body>
|
2013-09-17 23:52:57 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0" width="100%" align="center" lang="{$this->lang->getCode()}" dir="{$this->lang->getDir()}">
|
2013-06-24 00:22:08 +00:00
|
|
|
<tr>
|
|
|
|
<td bgcolor="#E6E7E8"><center>
|
|
|
|
<br /><br />
|
2013-08-21 22:04:44 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0" width="600" id="email-container">
|
2013-06-24 00:22:08 +00:00
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" width="5%"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="10%"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="80%" style="line-height:40px;"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="5%"> </td>
|
2013-06-24 00:22:08 +00:00
|
|
|
</tr><tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" rowspan="2"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" align="center" valign="top" rowspan="2"><img src="%%emailIcon%%" alt="" height="30" width="30"></td>
|
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; font-size:13px; line-height:20px; color:#6D6E70;">%%intro%%</td>
|
|
|
|
<td bgcolor="#FFFFFF" rowspan="2"> </td>
|
2013-06-24 00:22:08 +00:00
|
|
|
</tr><tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; line-height: 20px; font-weight: 600;">
|
2013-06-24 00:22:08 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0">
|
|
|
|
<tr>
|
2013-09-17 23:52:57 +00:00
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; padding-top: 8px; font-size:13px; font-weight: bold; color: #58585B;">
|
2013-06-24 00:22:08 +00:00
|
|
|
%%summary%%
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<table cellspacing="0" cellpadding="0" border="0">
|
|
|
|
<tr>
|
2013-09-17 23:52:57 +00:00
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; font-size:14px; padding-top: 25px;">
|
2013-06-24 00:22:08 +00:00
|
|
|
%%action%%
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</td>
|
|
|
|
</tr><tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" style="line-height:40px;"> </td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
2013-06-24 00:22:08 +00:00
|
|
|
</tr><tr>
|
2014-01-14 06:23:49 +00:00
|
|
|
<td> </td>
|
|
|
|
<td> </td>
|
|
|
|
<td align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; font-size:10px; line-height:13px; color:#6D6E70; padding:10px 20px;"><br />
|
2013-06-24 00:22:08 +00:00
|
|
|
%%footer%%
|
|
|
|
<br /><br />
|
|
|
|
</td>
|
2014-01-14 06:23:49 +00:00
|
|
|
<td> </td>
|
2013-06-24 00:22:08 +00:00
|
|
|
</tr><tr>
|
|
|
|
<td colspan="4"> </td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<br><br></center>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</body></html>
|
|
|
|
EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class that represents email digest delivery mode
|
|
|
|
*/
|
|
|
|
class EchoEmailDigest extends EchoEmailMode {
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the intro component
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function buildIntro() {
|
2013-07-27 02:50:17 +00:00
|
|
|
// Give grep a chance to find the usages:
|
|
|
|
// echo-email-batch-body-intro-daily, echo-email-batch-body-intro-weekly
|
2013-07-31 02:02:16 +00:00
|
|
|
$message = EchoEmailMode::message(
|
|
|
|
'echo-email-batch-body-intro-' . $this->digestMode, $this->user
|
|
|
|
)->params( $this->user->getName() );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
return $this->decorator->decorateIntro( $message );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the digestList component
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function buildDigestList() {
|
|
|
|
if ( !$this->rawDigestList ) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2013-07-31 02:02:16 +00:00
|
|
|
return $this->decorator->decorateDigestList( $this->rawDigestList, $this->user );
|
2013-07-09 00:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the action component
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function buildAction() {
|
|
|
|
$title = SpecialPage::getTitleFor( 'Notifications' );
|
|
|
|
|
2013-07-31 02:02:16 +00:00
|
|
|
return $this->decorator->decorateDigestAction( $title, $this->user );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function getTextTemplate() {
|
|
|
|
return <<< EOF
|
|
|
|
%%intro%%
|
|
|
|
|
|
|
|
%%digestList%%
|
2013-06-24 00:22:08 +00:00
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
%%action%%
|
|
|
|
|
|
|
|
%%footer%%
|
|
|
|
|
|
|
|
EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function getHTMLTemplate() {
|
2013-09-17 23:52:57 +00:00
|
|
|
$alignStart = $this->lang->alignStart();
|
2013-07-09 00:41:08 +00:00
|
|
|
return <<< EOF
|
2013-08-21 22:04:44 +00:00
|
|
|
<html><head>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
<style>
|
|
|
|
@media only screen and (max-width: 480px){
|
|
|
|
table[id="email-container"]{max-width:600px !important; width:100% !important;}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head><body>
|
2013-09-17 23:52:57 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0" width="100%" align="center" lang="{$this->lang->getCode()}" dir="{$this->lang->getDir()}">
|
2013-07-09 00:41:08 +00:00
|
|
|
<tr>
|
|
|
|
<td bgcolor="#E6E7E8"><center>
|
|
|
|
<br /><br />
|
2013-08-21 22:04:44 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0" width="600" id="email-container">
|
2013-07-09 00:41:08 +00:00
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" width="5%"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="6%"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="79%" style="line-height:40px;"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" width="10%"> </td>
|
2013-07-09 00:41:08 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" rowspan="2"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" rowspan="2"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" align="center" style="font-family: Arial, Helvetica, sans-serif; font-size:13px; line-height:20px; color:#6D6E70; text-align: center;">%%intro%%</td>
|
|
|
|
<td bgcolor="#FFFFFF" rowspan="2"> </td>
|
2013-07-09 00:41:08 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; line-height: 20px; font-weight: 600;">
|
2013-07-09 00:41:08 +00:00
|
|
|
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
|
|
<tr>
|
2013-09-17 23:52:57 +00:00
|
|
|
<td bgcolor="#FFFFFF" align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; font-size:13px; color: #58585B; padding-top: 25px;">
|
2013-07-09 00:41:08 +00:00
|
|
|
%%digestList%%
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<br /><br />
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" style="line-height:60px;" align="center">%%action%%</td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
2013-07-09 00:41:08 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2013-08-21 22:04:44 +00:00
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
|
|
|
<td bgcolor="#FFFFFF" style="line-height:40px;"> </td>
|
|
|
|
<td bgcolor="#FFFFFF"> </td>
|
2013-07-09 00:41:08 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2014-01-14 06:23:49 +00:00
|
|
|
<td> </td>
|
|
|
|
<td> </td>
|
|
|
|
<td align="{$alignStart}" style="font-family: Arial, Helvetica, sans-serif; font-size:10px; line-height:13px; color:#6D6E70; padding: 10px 20px;"><br />
|
2013-07-09 00:41:08 +00:00
|
|
|
%%footer%%
|
|
|
|
<br /><br />
|
|
|
|
</td>
|
2014-01-14 06:23:49 +00:00
|
|
|
<td> </td>
|
2013-07-09 00:41:08 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="4"> </td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<br><br></center>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</body></html>
|
|
|
|
EOF;
|
|
|
|
}
|
2013-06-24 00:22:08 +00:00
|
|
|
|
|
|
|
}
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2013-07-31 02:02:16 +00:00
|
|
|
* @param $user User
|
2013-07-09 00:41:08 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestList( $digestList, User $user );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Decorate the primary action for digest mode
|
|
|
|
* @param $title Title
|
2013-07-31 02:02:16 +00:00
|
|
|
* @param $user User
|
2013-07-09 00:41:08 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestAction( $title, User $user );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Decorate the footer for all mode
|
|
|
|
* @param $address string
|
2013-07-31 02:02:16 +00:00
|
|
|
* @param $user User
|
2013-07-09 00:41:08 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateFooter( $address, User $user );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Decorate the actions for single mode
|
|
|
|
* @param $notifFormatter EchoBasicFormatter
|
|
|
|
* @param $event EchoEvent
|
|
|
|
* @param $user User
|
|
|
|
* @param $rank string
|
|
|
|
* @param $message string
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateSingleAction( $notifFormatter, EchoEvent $event, User $user, $rank, $message );
|
2013-07-09 00:41:08 +00:00
|
|
|
|
2013-07-24 03:50:43 +00:00
|
|
|
/**
|
|
|
|
* Decorate a revision snippet
|
2014-05-27 18:28:37 +00:00
|
|
|
* @param string $snippet the raw revision snippet
|
2013-07-24 03:50:43 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function decorateRevisionSnippet( $snippet );
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* 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}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestList( $digestList, User $user ) {
|
2013-07-09 00:41:08 +00:00
|
|
|
$result = array();
|
|
|
|
|
|
|
|
// build the text section for each category
|
|
|
|
foreach( $digestList as $category => $notifs ) {
|
2013-07-31 02:02:16 +00:00
|
|
|
$output = EchoEmailMode::message( 'echo-category-title-' . $category, $user )->numParams( count( $notifs ) )->text()
|
|
|
|
. EchoEmailMode::message( 'colon-separator', $user )->text() . "\n";
|
2013-07-09 00:41:08 +00:00
|
|
|
|
|
|
|
foreach( $notifs as $notif ) {
|
2013-07-31 02:02:16 +00:00
|
|
|
$output .= "\n " . EchoEmailMode::message( 'echo-email-batch-bullet', $user )->text() . ' ' . $notif['batch-body'];
|
2013-07-09 00:41:08 +00:00
|
|
|
}
|
|
|
|
$result[] = $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
// for prepending and appending 'echo-email-batch-separator'
|
|
|
|
$result = array_merge( array( '' ), $result, array( '' ) );
|
|
|
|
|
|
|
|
return trim(
|
|
|
|
implode(
|
2013-07-31 02:02:16 +00:00
|
|
|
"\n\n" . EchoEmailMode::message( 'echo-email-batch-separator', $user )->text() . "\n\n",
|
2013-07-09 00:41:08 +00:00
|
|
|
$result
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestAction( $title, User $user ) {
|
2013-07-31 02:02:16 +00:00
|
|
|
return EchoEmailMode::message( 'echo-email-batch-link-text-view-all-notifications', $user )->text()
|
|
|
|
. EchoEmailMode::message( 'colon-separator', $user )->text()
|
2013-07-09 00:41:08 +00:00
|
|
|
. '<'
|
2015-08-13 17:51:33 +00:00
|
|
|
. $title->getFullURL( '', false, PROTO_CANONICAL )
|
2013-07-09 00:41:08 +00:00
|
|
|
. '>';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateFooter( $address, User $user ) {
|
2013-07-31 02:02:16 +00:00
|
|
|
return EchoEmailMode::message( 'echo-email-footer-default', $user )
|
2013-07-09 00:41:08 +00:00
|
|
|
->params(
|
|
|
|
$address,
|
2013-07-31 02:02:16 +00:00
|
|
|
EchoEmailMode::message( 'echo-email-batch-separator', $user )->text()
|
2013-07-09 00:41:08 +00:00
|
|
|
)
|
|
|
|
->text();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateSingleAction( $notifFormatter, EchoEvent $event, User $user, $rank, $message ) {
|
2013-07-09 00:41:08 +00:00
|
|
|
$url = $notifFormatter->getLink( $event, $user, $rank, false, true );
|
|
|
|
|
2013-07-31 02:02:16 +00:00
|
|
|
return EchoEmailMode::message( $message, $user )->text()
|
|
|
|
. EchoEmailMode::message( 'colon-separator', $user )->text()
|
2013-07-09 00:41:08 +00:00
|
|
|
. '<'
|
|
|
|
. $notifFormatter->sanitizeEmailLink( $url )
|
|
|
|
. '>';
|
|
|
|
}
|
|
|
|
|
2013-07-24 03:50:43 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function decorateRevisionSnippet( $snippet ) {
|
|
|
|
// Doing nothing now, but there is a potential to wrap the text
|
|
|
|
// around snippet with quote in plain text email
|
|
|
|
return $snippet;
|
|
|
|
}
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function getActionLinkSeparator() {
|
|
|
|
return "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* HTML email decorator
|
|
|
|
*/
|
|
|
|
class EchoHTMLEmailDecorator implements EchoEmailDecorator {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function decorateIntro( $message ) {
|
|
|
|
return nl2br( $message->parse() );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestList( $digestList, User $user ) {
|
2013-07-09 00:41:08 +00:00
|
|
|
$result = array();
|
|
|
|
// build the html section for each category
|
|
|
|
foreach( $digestList as $category => $notifs ) {
|
|
|
|
$output = $this->applyStyleToCategory(
|
2013-07-31 02:02:16 +00:00
|
|
|
EchoEmailMode::message( 'echo-category-title-' . $category, $user )
|
2013-07-09 00:41:08 +00:00
|
|
|
->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}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateDigestAction( $title, User $user ) {
|
2015-06-11 07:10:33 +00:00
|
|
|
/*
|
|
|
|
* Linker::link() will try to figure out if $title already exists
|
|
|
|
* (Title::isKnown) and alter the link depending on the outcome
|
|
|
|
* (&action=edit&redlink=1)
|
|
|
|
* Notifications are usually triggered by new content, so we better
|
|
|
|
* make damn sure that slave lag doesn't mess that up. Especially
|
|
|
|
* in emails, which we can't rerender once they've been sent.
|
|
|
|
* I'll force the status for this $title to be read from master, so
|
|
|
|
* Linker::link is guaranteed to get the correct exists() result.
|
|
|
|
*/
|
|
|
|
$title->exists( wfGetLB()->hasOrMadeRecentMasterChanges() ? Title::GAID_FOR_UPDATE : 0 );
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
return Linker::link(
|
|
|
|
$title,
|
2013-07-31 02:02:16 +00:00
|
|
|
EchoEmailMode::message( 'echo-email-batch-link-text-view-all-notifications', $user )->escaped(),
|
2013-07-09 00:41:08 +00:00
|
|
|
array( 'style' => $this->getPrimaryLinkCSS() ),
|
|
|
|
array(),
|
|
|
|
array( 'https' )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateFooter( $address, User $user ) {
|
2013-07-09 00:41:08 +00:00
|
|
|
$title = SpecialPage::getTitleFor( 'Preferences' );
|
|
|
|
$title->setFragment( "#mw-prefsection-echo" );
|
2013-07-31 02:02:16 +00:00
|
|
|
return EchoEmailMode::message( 'echo-email-footer-default-html', $user )
|
2013-07-09 00:41:08 +00:00
|
|
|
->params( $address )
|
|
|
|
->rawParams( $title->getFullURL( '', false, PROTO_HTTPS ) )
|
|
|
|
->text();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
2015-08-13 17:51:33 +00:00
|
|
|
public function decorateSingleAction( $notifFormatter, EchoEvent $event, User $user, $rank, $message ) {
|
2013-07-09 00:41:08 +00:00
|
|
|
if ( $rank === 'primary' ) {
|
|
|
|
$style = $this->getPrimaryLinkCSS();
|
|
|
|
} else {
|
|
|
|
$style = $this->getSecondaryLinkCSS();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $notifFormatter->getLink( $event, $user, $rank, false, false, $style );
|
|
|
|
}
|
|
|
|
|
2013-07-24 03:50:43 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function decorateRevisionSnippet( $snippet ) {
|
|
|
|
return htmlspecialchars( $snippet );
|
|
|
|
}
|
|
|
|
|
2013-07-09 00:41:08 +00:00
|
|
|
/**
|
|
|
|
* {@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;
|
2013-09-17 23:52:57 +00:00
|
|
|
color:#D9EEF7; background:#3366BB; font-family: Arial, Helvetica, sans-serif;font-size: 13px;';
|
2013-07-09 00:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The style for secondary link
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function getSecondaryLinkCSS() {
|
2013-09-17 23:52:57 +00:00
|
|
|
return 'text-decoration: none;font-size: 10px;font-family: Arial, Helvetica, sans-serif; color: #808184';
|
2013-07-09 00:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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">
|
2013-08-21 22:04:44 +00:00
|
|
|
<img src="$icon" width="30" height="30" style="vertical-align:middle;">
|
2013-07-09 00:41:08 +00:00
|
|
|
</td>
|
2013-09-17 23:52:57 +00:00
|
|
|
<td style="font-family: Arial, Helvetica, sans-serif; font-size:13px; color: #58585B;">
|
2013-07-09 00:41:08 +00:00
|
|
|
$text
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|