Index: trunk/extensions/MoodBar/include/MoodBarHTMLMailerJob.php |
— | — | @@ -0,0 +1,31 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class MoodBarHTMLMailerJob extends Job { |
| 5 | + |
| 6 | + function __construct( $title, $params, $id = 0 ) { |
| 7 | + parent::__construct( __CLASS__, $title, $params, $id ); |
| 8 | + } |
| 9 | + |
| 10 | + function run() { |
| 11 | + $enotif = new MoodBarHTMLEmailNotification(); |
| 12 | + // Get the user from ID (rename safe). Anons are 0, so defer to name. |
| 13 | + if( isset( $this->params['editorID'] ) && $this->params['editorID'] ) { |
| 14 | + $editor = User::newFromId( $this->params['editorID'] ); |
| 15 | + // B/C, only the name might be given. |
| 16 | + } else { |
| 17 | + # FIXME: newFromName could return false on a badly configured wiki. |
| 18 | + $editor = User::newFromName( $this->params['editor'], false ); |
| 19 | + } |
| 20 | + $enotif->actuallyNotifyOnRespond( |
| 21 | + $editor, |
| 22 | + $this->title, |
| 23 | + $this->params['timestamp'], |
| 24 | + $this->params['feedbackResponse'], |
| 25 | + $this->params['response'] |
| 26 | + ); |
| 27 | + return true; |
| 28 | + } |
| 29 | + |
| 30 | +} |
| 31 | + |
| 32 | +?> |
Index: trunk/extensions/MoodBar/include/MoodBarHTMLEmailNotification.php |
— | — | @@ -0,0 +1,238 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * A custom Mailer for MoodBar that sends HTML Email notification |
| 6 | + * This is a hacksish solution till we revamp the email system |
| 7 | + */ |
| 8 | +class MoodBarHTMLEmailNotification { |
| 9 | + |
| 10 | + protected $to, $subject, $body, $replyto, $from; |
| 11 | + protected $timestamp, $composed_common, $feedbackResponse, $response; |
| 12 | + protected $mime_boundary; |
| 13 | + |
| 14 | + /** |
| 15 | + * @var Title |
| 16 | + */ |
| 17 | + protected $title; |
| 18 | + |
| 19 | + /** |
| 20 | + * @var User |
| 21 | + */ |
| 22 | + protected $editor; |
| 23 | + protected $targetUser; |
| 24 | + |
| 25 | + public function __construct() { |
| 26 | + $this->mime_boundary = 'PHP_Alt_Boundary_'. md5( time() ); |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Send emails corresponding to the user $editor editing the page $title. |
| 31 | + * Also updates wl_notificationtimestamp. |
| 32 | + * |
| 33 | + * May be deferred via the job queue. |
| 34 | + * |
| 35 | + * @param $editor User object |
| 36 | + * @param $title Title object |
| 37 | + * @param $timestamp string Edit timestamp |
| 38 | + * @param $feedbackResponse integer response id |
| 39 | + * @param $response string response text |
| 40 | + */ |
| 41 | + public function notifyOnRespond( $editor, $title, $timestamp, $feedbackResponse, $response ) { |
| 42 | + global $wgEnotifUseJobQ, $wgEnotifUserTalk; |
| 43 | + |
| 44 | + if ( $title->getNamespace() != NS_USER_TALK || !$wgEnotifUserTalk || |
| 45 | + !$this->canSendUserTalkEmail( $editor, $title ) ) { |
| 46 | + return; |
| 47 | + } |
| 48 | + |
| 49 | + if ( $wgEnotifUseJobQ ) { |
| 50 | + $params = array( |
| 51 | + 'editor' => $editor->getName(), |
| 52 | + 'editorID' => $editor->getID(), |
| 53 | + 'timestamp' => $timestamp, |
| 54 | + 'response' => $response, |
| 55 | + 'feedbackResponse' => $feedbackResponse |
| 56 | + ); |
| 57 | + $job = new MoodBarHTMLMailerJob( $title, $params ); |
| 58 | + $job->insert(); |
| 59 | + } else { |
| 60 | + $this->actuallyNotifyOnRespond( $editor, $title, $timestamp, $feedbackResponse, $response ); |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Immediate version of notifyOnRespond(). |
| 66 | + * |
| 67 | + * Send emails corresponding to the user $editor editing the page $title. |
| 68 | + * Also updates wl_notificationtimestamp. |
| 69 | + * |
| 70 | + * @param $editor User object |
| 71 | + * @param $title Title object |
| 72 | + * @param $timestamp string Edit timestamp |
| 73 | + * @param $feedbackResponse integer response id |
| 74 | + * @param $response string response text |
| 75 | + */ |
| 76 | + public function actuallyNotifyOnRespond( $editor, $title, $timestamp, $feedbackResponse, $response ) { |
| 77 | + |
| 78 | + global $wgEnotifUserTalk; |
| 79 | + |
| 80 | + wfProfileIn( __METHOD__ ); |
| 81 | + |
| 82 | + if ( $title->getNamespace() != NS_USER_TALK ) { |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + $this->title = $title; |
| 87 | + $this->timestamp = $timestamp; |
| 88 | + $this->editor = $editor; |
| 89 | + $this->composed_common = false; |
| 90 | + $this->feedbackResponse = $feedbackResponse; |
| 91 | + $this->response = $response; |
| 92 | + |
| 93 | + if ( $wgEnotifUserTalk && $this->canSendUserTalkEmail( $editor, $title ) ) { |
| 94 | + $this->compose( $this->targetUser ); |
| 95 | + } |
| 96 | + |
| 97 | + wfProfileOut( __METHOD__ ); |
| 98 | + |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * @param $editor User |
| 103 | + * @param $title Title bool |
| 104 | + * @return bool |
| 105 | + */ |
| 106 | + protected function canSendUserTalkEmail( $editor, $title ) { |
| 107 | + global $wgEnotifUserTalk; |
| 108 | + |
| 109 | + if ( $wgEnotifUserTalk ) { |
| 110 | + $this->targetUser = User::newFromName( $title->getText() ); |
| 111 | + |
| 112 | + if ( !$this->targetUser || $this->targetUser->isAnon() ) { |
| 113 | + wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" ); |
| 114 | + } elseif ( $this->targetUser->getId() == $editor->getId() ) { |
| 115 | + wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" ); |
| 116 | + } elseif ( $this->targetUser->getOption( 'enotifusertalkpages' ) ) |
| 117 | + { |
| 118 | + if ( $this->targetUser->isEmailConfirmed() ) { |
| 119 | + wfDebug( __METHOD__ . ": sending talk page update notification\n" ); |
| 120 | + return true; |
| 121 | + } else { |
| 122 | + wfDebug( __METHOD__ . ": talk page owner doesn't have validated email\n" ); |
| 123 | + } |
| 124 | + } else { |
| 125 | + wfDebug( __METHOD__ . ": talk page owner doesn't want notifications\n" ); |
| 126 | + } |
| 127 | + } |
| 128 | + return false; |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Generate the generic "this page has been changed" e-mail text. |
| 133 | + */ |
| 134 | + protected function composeCommonMailtext() { |
| 135 | + global $wgPasswordSender, $wgPasswordSenderName, $wgNoReplyAddress; |
| 136 | + global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress; |
| 137 | + global $wgEnotifUseRealName; |
| 138 | + |
| 139 | + $this->composed_common = true; |
| 140 | + |
| 141 | + $keys = array(); |
| 142 | + |
| 143 | + if ( $this->editor->isAnon() ) { |
| 144 | + $pageEditor = wfMsgForContent( 'enotif_anon_editor', $this->editor->getName() ); |
| 145 | + } else { |
| 146 | + $pageEditor = $wgEnotifUseRealName ? $this->editor->getRealName() : $this->editor->getName(); |
| 147 | + } |
| 148 | + |
| 149 | + $talkPageUrl = $this->title->getCanonicalURL() . '#feedback-dashboard-response-' . $this->feedbackResponse; |
| 150 | + |
| 151 | + // build the subject |
| 152 | + $this->subject = wfMessage( 'moodbar-enotif-subject')->params($pageEditor)->escaped(); |
| 153 | + |
| 154 | + // build section for the email body |
| 155 | + $textUserResponseMessage = wfMessage('moodbar-enotif-text-body-user-response-message') |
| 156 | + ->params($this->editor->getName(), $talkPageUrl)->escaped(); |
| 157 | + |
| 158 | + $htmlUserResponseMessage = wfMsgExt('moodbar-enotif-html-body-user-response-message', |
| 159 | + array('parse'), |
| 160 | + $this->editor->getUserPage()->getCanonicalURL(), |
| 161 | + $this->editor->getName(), |
| 162 | + $talkPageUrl); |
| 163 | + |
| 164 | + $userReplyTitle = wfMessage('moodbar-enotif-body-user-response-title')->escaped(); |
| 165 | + |
| 166 | + |
| 167 | + $textResponse = htmlspecialchars( $this->response ); |
| 168 | + |
| 169 | + $messageCache = MessageCache::singleton(); |
| 170 | + $htmlResponse = $messageCache->parse( $this->response )->getText(); |
| 171 | + |
| 172 | + //assemable the email body |
| 173 | + $this->body = <<<HTML |
| 174 | +--$this->mime_boundary |
| 175 | +Content-Type: text/plain; charset=UTF-8 |
| 176 | +Content-Transfer-Encoding: 8bit |
| 177 | + |
| 178 | +$textUserResponseMessage |
| 179 | +$userReplyTitle |
| 180 | +$textResponse |
| 181 | + |
| 182 | +--$this->mime_boundary |
| 183 | +Content-Type: text/html; charset=UTF-8 |
| 184 | +Content-Transfer-Encoding: 8bit |
| 185 | + |
| 186 | +<html> |
| 187 | + <body> |
| 188 | + $htmlUserResponseMessage |
| 189 | + $userReplyTitle |
| 190 | + $htmlResponse |
| 191 | + </body> |
| 192 | +</html> |
| 193 | + |
| 194 | +--$this->mime_boundary-- |
| 195 | +HTML; |
| 196 | + |
| 197 | + # Reveal the page editor's address as REPLY-TO address only if |
| 198 | + # the user has not opted-out and the option is enabled at the |
| 199 | + # global configuration level. |
| 200 | + $adminAddress = new MailAddress( $wgPasswordSender, $wgPasswordSenderName ); |
| 201 | + if ( $wgEnotifRevealEditorAddress |
| 202 | + && ( $this->editor->getEmail() != '' ) |
| 203 | + && $this->editor->getOption( 'enotifrevealaddr' ) ) |
| 204 | + { |
| 205 | + $editorAddress = new MailAddress( $this->editor ); |
| 206 | + if ( $wgEnotifFromEditor ) { |
| 207 | + $this->from = $editorAddress; |
| 208 | + } else { |
| 209 | + $this->from = $adminAddress; |
| 210 | + $this->replyto = $editorAddress; |
| 211 | + } |
| 212 | + } else { |
| 213 | + $this->from = $adminAddress; |
| 214 | + $this->replyto = new MailAddress( $wgNoReplyAddress ); |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + /** |
| 219 | + * Compose a mail to a given user and either queue it for sending, or send it now, |
| 220 | + * depending on settings. |
| 221 | + * @param $user User |
| 222 | + */ |
| 223 | + protected function compose( $user ) { |
| 224 | + |
| 225 | + global $wgContLang, $wgEnotifUseRealName; |
| 226 | + |
| 227 | + if ( !$this->composed_common ) { |
| 228 | + $this->composeCommonMailtext(); |
| 229 | + } |
| 230 | + |
| 231 | + $to = new MailAddress( $user ); |
| 232 | + |
| 233 | + return UserMailer::send( $to, $this->from, $this->subject, $this->body, $this->replyto, $contentType = 'multipart/alternative; boundary=' . $this->mime_boundary ); |
| 234 | + |
| 235 | + } |
| 236 | + |
| 237 | +} |
| 238 | + |
| 239 | +?> |
Index: trunk/extensions/MoodBar/MoodBar.i18n.php |
— | — | @@ -159,6 +159,11 @@ |
160 | 160 | 'response-ajax-success-body' => 'Your response has been added.', |
161 | 161 | 'response-ajax-error-head' => 'Oops!', |
162 | 162 | 'response-ajax-error-body' => 'There was an error adding your response. <br />Please try again later.', |
| 163 | + //Email notification |
| 164 | + 'moodbar-enotif-subject' => '$1 replied to your feedback', |
| 165 | + 'moodbar-enotif-text-body-user-response-message' => '$1 replied to your feedback, see the response: $2', |
| 166 | + 'moodbar-enotif-html-body-user-response-message' => '[$1 $2] replied to your feedback, see the [$3 response] for more detail', |
| 167 | + 'moodbar-enotif-body-user-response-title' => 'User response: ' |
163 | 168 | ); |
164 | 169 | |
165 | 170 | /** Message documentation (Message documentation) |
— | — | @@ -292,6 +297,10 @@ |
293 | 298 | 'response-ajax-success-body' => 'Text for ajax status body on successful response (can be html)', |
294 | 299 | 'response-ajax-error-head' => 'Text for ajax status heading on error', |
295 | 300 | 'response-ajax-error-body' => 'Text for ajax status body on error (can be html)', |
| 301 | + 'moodbar-enotif-subject' => 'Feedback response email subject', |
| 302 | + 'moodbar-enotif-text-body-user-response-message' => 'The text version response message, $1 is the user name, $2 is the link to the response', |
| 303 | + 'moodbar-enotif-html-body-user-response-message' => 'The html version response message, $1 is the the user page link, $2 is user name, $3 is the link to response', |
| 304 | + 'moodbar-enotif-body-user-response-title' => 'User response: ' |
296 | 305 | ); |
297 | 306 | |
298 | 307 | /** Afrikaans (Afrikaans) |
Index: trunk/extensions/MoodBar/ApiFeedbackDashboardResponse.php |
— | — | @@ -2,6 +2,9 @@ |
3 | 3 | |
4 | 4 | class ApiFeedbackDashboardResponse extends ApiBase { |
5 | 5 | |
| 6 | + private $EnotifUserTalk; |
| 7 | + private $EnotifWatchlist; |
| 8 | + |
6 | 9 | public function execute() { |
7 | 10 | global $wgRequest, $wgUser; |
8 | 11 | |
— | — | @@ -40,7 +43,9 @@ |
41 | 44 | |
42 | 45 | $summary = wfMessage('moodbar-feedback-edit-summary')->inContentLanguage()-> |
43 | 46 | rawParams( $item->getProperty('feedback'), $response)->escaped(); |
44 | | - |
| 47 | + |
| 48 | + $this->disableUserTalkEmailNotification(); |
| 49 | + |
45 | 50 | $api = new ApiMain( new FauxRequest( array( |
46 | 51 | 'action' => 'edit', |
47 | 52 | 'title' => $talkPage->getFullText(), |
— | — | @@ -54,12 +59,47 @@ |
55 | 60 | ), true, array( 'wsEditToken' => $wgRequest->getSessionData( 'wsEditToken' ) ) ), true ); |
56 | 61 | |
57 | 62 | $api->execute(); |
| 63 | + |
| 64 | + $this->restoreUserTalkEmailNotification(); |
| 65 | + |
| 66 | + global $wgLang; |
| 67 | + |
| 68 | + $EMailNotif = new MoodBarHTMLEmailNotification(); |
| 69 | + $EMailNotif->notifyOnRespond( $wgUser, $talkPage, wfTimestampNow(), $item->getProperty( 'id' ), $wgLang->truncate( $response, 250 ) ); |
| 70 | + |
58 | 71 | } |
59 | 72 | |
60 | 73 | $result = array( 'result' => 'success' ); |
61 | 74 | $this->getResult()->addValue( null, $this->getModuleName(), $result ); |
62 | 75 | } |
63 | 76 | |
| 77 | + /** |
| 78 | + * temporarily disable the talk page email notification |
| 79 | + * for user and watchers |
| 80 | + */ |
| 81 | + private function disableUserTalkEmailNotification() { |
| 82 | + global $wgEnotifUserTalk, $wgEnotifWatchlist; |
| 83 | + |
| 84 | + $this->EnotifUserTalk = $wgEnotifUserTalk; |
| 85 | + $this->EnotifWatchlist = $wgEnotifWatchlist; |
| 86 | + |
| 87 | + $wgEnotifUserTalk = $wgEnotifWatchlist = false; |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * restore the default state of talk page email notification |
| 92 | + */ |
| 93 | + private function restoreUserTalkEmailNotification() { |
| 94 | + global $wgEnotifUserTalk, $wgEnotifWatchlist; |
| 95 | + |
| 96 | + if ( !is_null( $this->EnotifUserTalk ) ) { |
| 97 | + $wgEnotifUserTalk = $this->EnotifUserTalk; |
| 98 | + } |
| 99 | + if ( !is_null( $this->EnotifWatchlist ) ) { |
| 100 | + $wgEnotifWatchlist = $this->EnotifWatchlist; |
| 101 | + } |
| 102 | + } |
| 103 | + |
64 | 104 | public function needsToken() { |
65 | 105 | return true; |
66 | 106 | } |
Index: trunk/extensions/MoodBar/MoodBar.php |
— | — | @@ -17,6 +17,8 @@ |
18 | 18 | $wgAutoloadClasses['MBFeedbackItem'] = dirname(__FILE__).'/FeedbackItem.php'; |
19 | 19 | $wgAutoloadClasses['MBFeedbackResponseItem'] = dirname(__FILE__).'/FeedbackResponseItem.php'; |
20 | 20 | $wgAutoloadClasses['MoodBarFormatter'] = dirname(__FILE__).'/Formatter.php'; |
| 21 | +$wgAutoloadClasses['MoodBarHTMLEmailNotification'] = dirname(__FILE__).'/include/MoodBarHTMLEmailNotification.php'; |
| 22 | +$wgAutoloadClasses['MoodBarHTMLMailerJob'] = dirname( __FILE__ ) . '/include/MoodBarHTMLMailerJob.php'; |
21 | 23 | |
22 | 24 | // API |
23 | 25 | $wgAutoloadClasses['ApiMoodBar'] = dirname(__FILE__).'/ApiMoodBar.php'; |
— | — | @@ -57,6 +59,9 @@ |
58 | 60 | 'moodbar/restore' => 'moodbar-log-restore', |
59 | 61 | ); |
60 | 62 | |
| 63 | +// Jobs |
| 64 | +$wgJobClasses['MoodBarHTMLMailerJob'] = 'MoodBarHTMLMailerJob'; |
| 65 | + |
61 | 66 | // User rights |
62 | 67 | $wgAvailableRights[] = 'moodbar-view'; |
63 | 68 | $wgAvailableRights[] = 'moodbar-admin'; |