diff options
Diffstat (limited to 'plugins/EmailSummary')
-rw-r--r-- | plugins/EmailSummary/EmailSummaryPlugin.php | 202 | ||||
-rw-r--r-- | plugins/EmailSummary/Email_summary_status.php | 167 | ||||
-rw-r--r-- | plugins/EmailSummary/sendemailsummary.php | 47 | ||||
-rw-r--r-- | plugins/EmailSummary/siteemailsummaryhandler.php | 96 | ||||
-rw-r--r-- | plugins/EmailSummary/useremailsummaryhandler.php | 226 |
5 files changed, 738 insertions, 0 deletions
diff --git a/plugins/EmailSummary/EmailSummaryPlugin.php b/plugins/EmailSummary/EmailSummaryPlugin.php new file mode 100644 index 000000000..58c40e43c --- /dev/null +++ b/plugins/EmailSummary/EmailSummaryPlugin.php @@ -0,0 +1,202 @@ +<?php +/** + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * Sends an email summary of the inbox to users in the network + * + * PHP version 5 + * + * 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); +} + +/** + * Plugin for sending email summaries to users + * + * @category Email + * @package StatusNet + * @author Brion Vibber <brionv@status.net> + * @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 EmailSummaryPlugin extends Plugin +{ + /** + * Database schema setup + * + * @return boolean hook value + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing user-submitted flags on profiles + + $schema->ensureTable('email_summary_status', + array(new ColumnDef('user_id', 'integer', null, + false, 'PRI'), + new ColumnDef('send_summary', 'tinyint', null, + false, null, 1), + new ColumnDef('last_summary_id', 'integer', null, + true), + new ColumnDef('created', 'datetime', null, + false), + new ColumnDef('modified', 'datetime', null, + false), + ) + ); + return true; + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + * + */ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + 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; + default: + return true; + } + } + + /** + * Version info for this plugin + * + * @param array &$versions array of version data + * + * @return boolean hook value; true means continue processing, false means stop. + * + */ + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'EmailSummary', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:EmailSummary', + 'rawdescription' => + _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; + } + + /** + * Add a checkbox to turn off email summaries + * + * @param Action $action Action being executed (emailsettings) + * + * @return boolean hook value + */ + + function onEndEmailFormData($action) + { + $user = common_current_user(); + + $action->elementStart('li'); + $action->checkbox('emailsummary', + // TRANS: Checkbox label in e-mail preferences form. + _('Send me a periodic summary of updates from my network.'), + Email_summary_status::getSendSummary($user->id)); + $action->elementEnd('li'); + return true; + } + + /** + * Add a checkbox to turn off email summaries + * + * @param Action $action Action being executed (emailsettings) + * + * @return boolean hook value + */ + + function onEndEmailSaveForm($action) + { + $sendSummary = $action->boolean('emailsummary'); + + $user = common_current_user(); + + if (!empty($user)) { + + $ess = Email_summary_status::staticGet('user_id', $user->id); + + if (empty($ess)) { + + $ess = new Email_summary_status(); + + $ess->user_id = $user->id; + $ess->send_summary = $sendSummary; + $ess->created = common_sql_now(); + $ess->modified = common_sql_now(); + + $ess->insert(); + + } else { + + $orig = clone($ess); + + $ess->send_summary = $sendSummary; + $ess->modified = common_sql_now(); + + $ess->update($orig); + } + } + + return true; + } +} diff --git a/plugins/EmailSummary/Email_summary_status.php b/plugins/EmailSummary/Email_summary_status.php new file mode 100644 index 000000000..5b5b231e3 --- /dev/null +++ b/plugins/EmailSummary/Email_summary_status.php @@ -0,0 +1,167 @@ +<?php +/** + * Data class for email summary status + * + * PHP version 5 + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the 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/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for email summaries + * + * Email summary information for users + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +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_id; // int(4) null + public $created; // datetime not_null + public $modified; // datetime not_null + + /** + * Get an instance by key + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return Email_summary_status object found, or null for no hits + * + */ + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('email_summary_status', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'send_summary' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + '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); + } + + /** + * return key definitions for DB_DataObject + * + * @return array list of key field names + */ + + function keys() + { + return array_keys($this->keyTypes()); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. This key information is used to store and clear + * cached data, so be sure to list any key that will be used for static + * lookups. + * + * @return array associative array of key definitions, field name to type: + * 'K' for primary key: for compound keys, add an entry for each component; + * 'U' for unique keys: compound keys are not well supported here. + */ + function keyTypes() + { + return array('user_id' => 'K'); + } + + /** + * Magic formula for non-autoincrementing integer primary keys + * + * @return array magic three-false array that stops auto-incrementing. + */ + + function sequenceKey() + { + return array(false, false, false); + } + + /** + * Helper function + * + * @param integer $user_id ID of the user to get a count for + * + * @return int flag for whether to send this user a summary email + */ + + static function getSendSummary($user_id) + { + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess)) { + return $ess->send_summary; + } else { + return 1; + } + } + + /** + * Get email summary status for a user + * + * @param integer $user_id ID of the user to get a count for + * + * @return Email_summary_status instance for this user, with count already incremented. + */ + + static function getLastSummaryID($user_id) + { + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess)) { + return $ess->last_summary_id; + } else { + 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 +<?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'); +} 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 @@ +<?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; + } +} + diff --git a/plugins/EmailSummary/useremailsummaryhandler.php b/plugins/EmailSummary/useremailsummaryhandler.php new file mode 100644 index 000000000..b1ebd0c42 --- /dev/null +++ b/plugins/EmailSummary/useremailsummaryhandler.php @@ -0,0 +1,226 @@ +<?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())); + + + $out->elementStart('table', array('width' => '541px', 'style' => 'border: none')); + + while ($notice->fetch()) { + + $profile = Profile::staticGet('id', $notice->profile_id); + + if (empty($profile)) { + continue; + } + + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + + $out->elementStart('tr'); + $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'align' => 'left', + 'valign' => 'top')); + $out->element('img', array('src' => ($avatar) ? + $avatar->displayUrl() : + Avatar::defaultImage($avatar_size), + 'class' => 'avatar photo', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => $profile->getBestName())); + $out->elementEnd('td'); + $out->elementStart('td', array('align' => 'left', + 'valign' => 'top')); + $out->element('a', array('href' => $profile->profileurl), + $profile->nickname); + $out->text(' '); + $out->raw($notice->rendered); + $out->element('br'); // yeah, you know it. I just wrote a <br> in the middle of my table layout. + $noticeurl = $notice->bestUrl(); + // above should always return an URL + assert(!empty($noticeurl)); + $out->elementStart('a', array('rel' => 'bookmark', + 'class' => 'timestamp', + 'href' => $noticeurl)); + $dt = common_date_iso8601($notice->created); + $out->element('abbr', array('class' => 'published', + 'title' => $dt), + common_date_string($notice->created)); + $out->elementEnd('a'); + if ($notice->hasConversation()) { + $conv = Conversation::staticGet('id', $notice->conversation); + $convurl = $conv->uri; + if (!empty($convurl)) { + $out->text(' '); + $out->element('a', + array('href' => $convurl.'#notice-'.$notice->id, + 'class' => 'response'), + _('in context')); + } + } + $out->elementEnd('td'); + $out->elementEnd('tr'); + } + + $out->elementEnd('table'); + + $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($orig); + } + + return true; + } +} |