useremailsummaryhandler.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. /**
  3. * StatusNet - the distributed open-source microblogging tool
  4. *
  5. * Handler for queue items of type 'usersum', sends an email summaries
  6. * to a particular user.
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. * @category Sample
  22. * @package StatusNet
  23. * @author Evan Prodromou <evan@status.net>
  24. * @copyright 2010 StatusNet, Inc.
  25. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
  26. * @link http://status.net/
  27. */
  28. if (!defined('GNUSOCIAL')) { exit(1); }
  29. /**
  30. * Handler for queue items of type 'usersum', sends an email summaries
  31. * to a particular user.
  32. *
  33. * @category Email
  34. * @package StatusNet
  35. * @author Evan Prodromou <evan@status.net>
  36. * @copyright 2010 StatusNet, Inc.
  37. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
  38. * @link http://status.net/
  39. */
  40. class UserEmailSummaryHandler extends QueueHandler
  41. {
  42. // Maximum number of notices to include by default. This is probably too much.
  43. const MAX_NOTICES = 200;
  44. /**
  45. * Return transport keyword which identifies items this queue handler
  46. * services; must be defined for all subclasses.
  47. *
  48. * Must be 8 characters or less to fit in the queue_item database.
  49. * ex "email", "jabber", "sms", "irc", ...
  50. *
  51. * @return string
  52. */
  53. function transport()
  54. {
  55. return 'usersum';
  56. }
  57. /**
  58. * Send a summary email to the user
  59. *
  60. * @param mixed $object
  61. * @return boolean true on success, false on failure
  62. */
  63. function handle($user_id) : bool
  64. {
  65. // Skip if they've asked not to get summaries
  66. $ess = Email_summary_status::getKV('user_id', $user_id);
  67. if (!empty($ess) && !$ess->send_summary) {
  68. common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id));
  69. return true;
  70. }
  71. $since_id = null;
  72. if (!empty($ess)) {
  73. $since_id = $ess->last_summary_id;
  74. }
  75. $user = User::getKV('id', $user_id);
  76. if (empty($user)) {
  77. common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id));
  78. return true;
  79. }
  80. if (empty($user->email)) {
  81. common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id));
  82. return true;
  83. }
  84. try {
  85. $profile = $user->getProfile();
  86. } catch (UserNoProfileException $e) {
  87. common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id));
  88. return true;
  89. }
  90. // An InboxNoticeStream for a certain user, scoped to its own view
  91. $stream = new InboxNoticeStream($profile, $profile);
  92. $notice = $stream->getNotices(0, self::MAX_NOTICES, $since_id);
  93. if (empty($notice) || $notice->N == 0) {
  94. common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id));
  95. return true;
  96. }
  97. // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like
  98. // figuring out a better way. -ESP
  99. $new_top = null;
  100. if ($notice->fetch()) {
  101. $new_top = $notice->id;
  102. }
  103. // TRANS: Subject for e-mail.
  104. $subject = sprintf(_m('Your latest updates from %s'), common_config('site', 'name'));
  105. $out = new XMLStringer(true);
  106. $out->elementStart('html');
  107. $out->elementStart('head');
  108. $out->element('title', null, $subject);
  109. $out->elementEnd('head');
  110. $out->elementStart('body');
  111. $out->elementStart('div', array('width' => '100%',
  112. 'style' => 'background-color: #ffffff; border: 4px solid #4c609a; padding: 10px;'));
  113. $out->elementStart('div', array('style' => 'color: #ffffff; background-color: #4c609a; font-weight: bold; margin-bottom: 10px; padding: 4px;'));
  114. // TRANS: Text in e-mail summary.
  115. // TRANS: %1$s is the StatusNet sitename, %2$s is the recipient's profile name.
  116. $out->raw(sprintf(_m('Recent updates from %1$s for %2$s:'),
  117. common_config('site', 'name'),
  118. $profile->getBestName()));
  119. $out->elementEnd('div');
  120. $out->elementStart('table', array('width' => '550px',
  121. 'style' => 'border: none; border-collapse: collapse;', 'cellpadding' => '6'));
  122. do {
  123. $profile = Profile::getKV('id', $notice->profile_id);
  124. if (empty($profile)) {
  125. continue;
  126. }
  127. $avatarUrl = $profile->avatarUrl(AVATAR_STREAM_SIZE);
  128. $out->elementStart('tr');
  129. $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE,
  130. 'height' => AVATAR_STREAM_SIZE,
  131. 'align' => 'left',
  132. 'valign' => 'top',
  133. 'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
  134. $out->element('img', array('src' => $avatarUrl,
  135. 'width' => AVATAR_STREAM_SIZE,
  136. 'height' => AVATAR_STREAM_SIZE,
  137. 'alt' => $profile->getBestName()));
  138. $out->elementEnd('td');
  139. $out->elementStart('td', array('align' => 'left',
  140. 'valign' => 'top',
  141. 'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
  142. $out->element('a', array('href' => $profile->profileurl),
  143. $profile->nickname);
  144. $out->text(' ');
  145. $out->raw($notice->getRendered());
  146. $out->elementStart('div', array('style' => 'font-size: 0.8em; padding-top: 4px;'));
  147. $noticeurl = $notice->getLocalUrl();
  148. // above should always return an URL
  149. assert(!empty($noticeurl));
  150. $out->elementStart('a', array('rel' => 'bookmark',
  151. 'href' => $noticeurl));
  152. $dt = common_date_iso8601($notice->created);
  153. $out->element('abbr', array('style' => 'border-bottom: none;',
  154. 'title' => $dt),
  155. common_date_string($notice->created));
  156. $out->elementEnd('a');
  157. $out->element('a', array('href' => $notice->getConversationUrl()),
  158. // TRANS: Link text for link to conversation view.
  159. _m('in context'));
  160. $out->elementEnd('div');
  161. $out->elementEnd('td');
  162. $out->elementEnd('tr');
  163. } while ($notice->fetch());
  164. $out->elementEnd('table');
  165. // TRANS: Link text for link to e-mail settings.
  166. // TRANS: %1$s is a link to the e-mail settings, %2$s is the StatusNet sitename.
  167. $out->raw("<p>" . sprintf(_m('<a href="%1$s">change your email settings for %2$s</a>'),
  168. common_local_url('emailsettings'),
  169. common_config('site', 'name'))."</p>");
  170. $out->elementEnd('div');
  171. $out->elementEnd('body');
  172. $out->elementEnd('html');
  173. $body = $out->getString();
  174. // FIXME: do something for people who don't like HTML email
  175. mail_to_user($user,
  176. $subject,
  177. $body,
  178. array('Content-Type' => 'text/html; charset=utf-8',
  179. 'Mime-Version' => '1.0'));
  180. if (empty($ess)) {
  181. $ess = new Email_summary_status();
  182. $ess->user_id = $user_id;
  183. $ess->created = common_sql_now();
  184. $ess->last_summary_id = $new_top;
  185. $ess->modified = common_sql_now();
  186. $ess->insert();
  187. } else {
  188. $orig = clone($ess);
  189. $ess->last_summary_id = $new_top;
  190. $ess->modified = common_sql_now();
  191. $ess->update($orig);
  192. }
  193. return true;
  194. }
  195. }