From 797059340e304a707dda5596f30651ffc727f3c3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 8 Nov 2010 13:10:09 -0500 Subject: Complete email summary sending system Added the necessary classes to send email summaries. First, added a script to run on a daily basis. Second, added a queue handler for sending email summaries for users, and another to queue summaries for all users on the site. Fixed up the email_summary_status table to store the last-sent notice id, rather than a datetime (since we don't support 'since' parameters anymore). Finally, made the plugin class load the right modules when needed. --- plugins/EmailSummary/EmailSummaryPlugin.php | 21 ++- plugins/EmailSummary/Email_summary_status.php | 18 +-- plugins/EmailSummary/sendemailsummary.php | 47 +++++++ plugins/EmailSummary/siteemailsummaryhandler.php | 96 +++++++++++++ plugins/EmailSummary/useremailsummaryhandler.php | 172 +++++++++++++++++++++++ 5 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 plugins/EmailSummary/sendemailsummary.php create mode 100644 plugins/EmailSummary/siteemailsummaryhandler.php create mode 100644 plugins/EmailSummary/useremailsummaryhandler.php (limited to 'plugins/EmailSummary') diff --git a/plugins/EmailSummary/EmailSummaryPlugin.php b/plugins/EmailSummary/EmailSummaryPlugin.php index dd72329f8..03577bb4a 100644 --- a/plugins/EmailSummary/EmailSummaryPlugin.php +++ b/plugins/EmailSummary/EmailSummaryPlugin.php @@ -63,7 +63,7 @@ class EmailSummaryPlugin extends Plugin false, 'PRI'), new ColumnDef('send_summary', 'tinyint', null, false, null, 1), - new ColumnDef('last_summary', 'datetime', null, + new ColumnDef('last_summary_id', 'integer', null, true), new ColumnDef('created', 'datetime', null, false), @@ -89,6 +89,10 @@ class EmailSummaryPlugin extends Plugin switch ($cls) { + case 'SiteEmailSummaryHandler': + case 'UserEmailSummaryHandler': + include_once $dir . '/'.strtolower($cls).'.php'; + return false; case 'Email_summary_status': include_once $dir . '/'.$cls.'.php'; return false; @@ -116,4 +120,19 @@ class EmailSummaryPlugin extends Plugin _m('Send an email summary of the inbox to users.')); return true; } + + /** + * Register our queue handlers + * + * @param QueueManager $qm Current queue manager + * + * @return boolean hook value + */ + + function onEndInitializeQueueManager($qm) + { + $qm->connect('sitesum', 'SiteEmailSummaryHandler'); + $qm->connect('usersum', 'UserEmailSummaryHandler'); + return true; + } } diff --git a/plugins/EmailSummary/Email_summary_status.php b/plugins/EmailSummary/Email_summary_status.php index a0462fd04..5b5b231e3 100644 --- a/plugins/EmailSummary/Email_summary_status.php +++ b/plugins/EmailSummary/Email_summary_status.php @@ -52,9 +52,9 @@ class Email_summary_status extends Memcached_DataObject public $__table = 'email_summary_status'; // table name public $user_id; // int(4) primary_key not_null public $send_summary; // tinyint not_null - public $last_summary; // int(4) primary_key not_null - public $created; // int(4) primary_key not_null - public $modified; // int(4) primary_key not_null + public $last_summary_id; // int(4) null + public $created; // datetime not_null + public $modified; // datetime not_null /** * Get an instance by key @@ -83,9 +83,9 @@ class Email_summary_status extends Memcached_DataObject { return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, 'send_summary' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, - 'last_summary' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'created' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'modified' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME); + 'last_summary_id' => DB_DATAOBJECT_INT, + 'created' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'modified' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } /** @@ -154,14 +154,14 @@ class Email_summary_status extends Memcached_DataObject * @return Email_summary_status instance for this user, with count already incremented. */ - static function getLastSummary($user_id) + static function getLastSummaryID($user_id) { $ess = Email_summary_status::staticGet('user_id', $user_id); if (!empty($ess)) { - return $ess->last_summary; + return $ess->last_summary_id; } else { - return 1; + return null; } } } diff --git a/plugins/EmailSummary/sendemailsummary.php b/plugins/EmailSummary/sendemailsummary.php new file mode 100644 index 000000000..37bfdcfbd --- /dev/null +++ b/plugins/EmailSummary/sendemailsummary.php @@ -0,0 +1,47 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<enqueue($user->id, 'usersum'); +} catch (NoUserArgumentException $nuae) { + $qm->enqueue(null, 'sitesum'); +} diff --git a/plugins/EmailSummary/siteemailsummaryhandler.php b/plugins/EmailSummary/siteemailsummaryhandler.php new file mode 100644 index 000000000..595c3267a --- /dev/null +++ b/plugins/EmailSummary/siteemailsummaryhandler.php @@ -0,0 +1,96 @@ +. + * + * @category Sample + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * + * Handler for queue items of type 'sitesum', sends email summaries + * to all users on the site. + * + * @category Email + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class SiteEmailSummaryHandler extends QueueHandler +{ + + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ + + function transport() + { + return 'sitesum'; + } + + /** + * Handle the site + * + * @param mixed $object + * @return boolean true on success, false on failure + */ + + function handle($object) + { + $qm = QueueManager::get(); + + try { + // Enqueue a summary for all users + + $user = new User(); + $user->find(); + + while ($user->fetch()) { + try { + $qm->enqueue($user->id, 'usersum'); + } catch (Exception $e) { + common_log(LOG_WARNING, $e->getMessage()); + continue; + } + } + } catch (Exception $e) { + common_log(LOG_WARNING, $e->getMessage()); + } + + return true; + } +} + diff --git a/plugins/EmailSummary/useremailsummaryhandler.php b/plugins/EmailSummary/useremailsummaryhandler.php new file mode 100644 index 000000000..f3151bd47 --- /dev/null +++ b/plugins/EmailSummary/useremailsummaryhandler.php @@ -0,0 +1,172 @@ +. + * + * @category Sample + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Handler for queue items of type 'usersum', sends an email summaries + * to a particular user. + * + * @category Email + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class UserEmailSummaryHandler extends QueueHandler +{ + // Maximum number of notices to include by default. This is probably too much. + + const MAX_NOTICES = 200; + + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ + + function transport() + { + return 'sitesum'; + } + + /** + * Send a summary email to the user + * + * @param mixed $object + * @return boolean true on success, false on failure + */ + + function handle($user_id) + { + // Skip if they've asked not to get summaries + + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess) && !$ess->send_summary) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id)); + return true; + } + + $since_id = null; + + if (!empty($ess)) { + $since_id = $ess->last_summary_id; + } + + $user = User::staticGet('id', $user_id); + + if (empty($user)) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id)); + return true; + } + + if (empty($user->email)) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id)); + return true; + } + + $profile = $user->getProfile(); + + if (empty($profile)) { + common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id)); + return true; + } + + $notice = $user->ownFriendsTimeline(0, self::MAX_NOTICES, $since_id); + + if (empty($notice) || $notice->N == 0) { + common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id)); + return true; + } + + // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like + // figuring out a better way. -ESP + + $new_top = null; + + if ($notice instanceof ArrayWrapper) { + $new_top = $notice->_items[0]->id; + } + + $out = new XMLStringer(); + + $out->raw(sprintf(_('

Recent updates from %1s for %2s:

'), + common_config('site', 'name'), + $profile->getBestName())); + + $nl = new NoticeList($notice, $out); + + // Outputs to the string + + $nl->show(); + + $out->raw(sprintf(_('

change your email settings for %2s

'), + common_local_url('emailsettings'), + common_config('site', 'name'))); + + $body = $out->getString(); + + // FIXME: do something for people who don't like HTML email + + mail_to_user($user, _('Updates from your network'), $body, + array('Content-Type', 'text/html; charset=UTF-8')); + + if (empty($ess)) { + + $ess = new Email_summary_status(); + + $ess->user_id = $user_id; + $ess->created = common_sql_now(); + $ess->last_summary_id = $new_top; + $ess->modified = common_sql_now(); + + $ess->insert(); + + } else { + + $orig = clone($ess); + + $ess->last_summary_id = $new_top; + $ess->modified = common_sql_now(); + + $ess->update(); + } + + return true; + } +} + -- cgit v1.2.3-54-g00ecf