summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/EmailSummary/EmailSummaryPlugin.php138
-rw-r--r--plugins/EmailSummary/Email_summary_status.php167
-rw-r--r--plugins/EmailSummary/sendemailsummary.php47
-rw-r--r--plugins/EmailSummary/siteemailsummaryhandler.php96
-rw-r--r--plugins/EmailSummary/useremailsummaryhandler.php226
5 files changed, 674 insertions, 0 deletions
diff --git a/plugins/EmailSummary/EmailSummaryPlugin.php b/plugins/EmailSummary/EmailSummaryPlugin.php
new file mode 100644
index 000000000..03577bb4a
--- /dev/null
+++ b/plugins/EmailSummary/EmailSummaryPlugin.php
@@ -0,0 +1,138 @@
+<?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;
+ }
+}
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..fb004b555
--- /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();
+ }
+
+ return true;
+ }
+}