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.
This commit is contained in:
Evan Prodromou 2010-11-08 13:10:09 -05:00
parent 9003769609
commit 797059340e
5 changed files with 344 additions and 10 deletions

View File

@ -63,7 +63,7 @@ class EmailSummaryPlugin extends Plugin
false, 'PRI'), false, 'PRI'),
new ColumnDef('send_summary', 'tinyint', null, new ColumnDef('send_summary', 'tinyint', null,
false, null, 1), false, null, 1),
new ColumnDef('last_summary', 'datetime', null, new ColumnDef('last_summary_id', 'integer', null,
true), true),
new ColumnDef('created', 'datetime', null, new ColumnDef('created', 'datetime', null,
false), false),
@ -89,6 +89,10 @@ class EmailSummaryPlugin extends Plugin
switch ($cls) switch ($cls)
{ {
case 'SiteEmailSummaryHandler':
case 'UserEmailSummaryHandler':
include_once $dir . '/'.strtolower($cls).'.php';
return false;
case 'Email_summary_status': case 'Email_summary_status':
include_once $dir . '/'.$cls.'.php'; include_once $dir . '/'.$cls.'.php';
return false; return false;
@ -116,4 +120,19 @@ class EmailSummaryPlugin extends Plugin
_m('Send an email summary of the inbox to users.')); _m('Send an email summary of the inbox to users.'));
return true; 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;
}
} }

View File

@ -52,9 +52,9 @@ class Email_summary_status extends Memcached_DataObject
public $__table = 'email_summary_status'; // table name public $__table = 'email_summary_status'; // table name
public $user_id; // int(4) primary_key not_null public $user_id; // int(4) primary_key not_null
public $send_summary; // tinyint not_null public $send_summary; // tinyint not_null
public $last_summary; // int(4) primary_key not_null public $last_summary_id; // int(4) null
public $created; // int(4) primary_key not_null public $created; // datetime not_null
public $modified; // int(4) primary_key not_null public $modified; // datetime not_null
/** /**
* Get an instance by key * 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, return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'send_summary' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, 'send_summary' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'last_summary' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'last_summary_id' => DB_DATAOBJECT_INT,
'created' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'created' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
'modified' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME); '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. * @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); $ess = Email_summary_status::staticGet('user_id', $user_id);
if (!empty($ess)) { if (!empty($ess)) {
return $ess->last_summary; return $ess->last_summary_id;
} else { } else {
return 1; return null;
} }
} }
} }

View File

@ -0,0 +1,47 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
$shortoptions = 'i:n:a';
$longoptions = array('id=', 'nickname=', 'all');
$helptext = <<<END_OF_SENDEMAILSUMMARY_HELP
sendemailsummary.php [options]
Send an email summary of the inbox to users
-i --id ID of user to send summary to
-n --nickname nickname of the user to send summary to
-a --all send summary to all users
END_OF_SENDEMAILSUMMARY_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$qm = QueueManager::get();
// enqueue summary for user or all users
try {
$user = getUser();
$qm->enqueue($user->id, 'usersum');
} catch (NoUserArgumentException $nuae) {
$qm->enqueue(null, 'sitesum');
}

View File

@ -0,0 +1,96 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
*
* Handler for queue items of type 'sitesum', sends email summaries
* to all users on the site.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Sample
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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 <evan@status.net>
* @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;
}
}

View File

@ -0,0 +1,172 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
*
* Handler for queue items of type 'usersum', sends an email summaries
* to a particular user.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Sample
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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 <evan@status.net>
* @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(_('<p>Recent updates from %1s for %2s:</p>'),
common_config('site', 'name'),
$profile->getBestName()));
$nl = new NoticeList($notice, $out);
// Outputs to the string
$nl->show();
$out->raw(sprintf(_('<p><a href="%1s">change your email settings for %2s</a></p>'),
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;
}
}