]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
EmailReminder plugin to send reminders about various things
authorZach Copley <zach@status.net>
Fri, 17 Jun 2011 09:24:34 +0000 (02:24 -0700)
committerZach Copley <zach@status.net>
Fri, 17 Jun 2011 09:24:34 +0000 (02:24 -0700)
* Needs some cleanup and testing
* Email templates need work
* More documentation

Squashed commit of the following:

commit 1c7b418dad5ec1b7713d61b6a42d6d7a394d500f
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 17 02:17:31 2011 -0700

    * Set the reminder interval correctly

commit ae0ded8cf95210f54b4cd58dac0eeeedf2d99c67
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 17 02:15:01 2011 -0700

    Send email reminders for invitations

commit 1b596d08f5dbe765a16fbdfbd21e2ad68e8b0058
Author: Zach Copley <zach@status.net>
Date:   Thu Jun 16 23:53:48 2011 -0700

    Handle multiple confirmation types

commit 25d83351d878f39498cd6a14fddde27f1afef2ca
Author: Zach Copley <zach@status.net>
Date:   Thu Jun 16 18:04:57 2011 -0700

    Actually send reminders and record a record of doing so

commit 9ffc2dbee15cacc7e7f9feab492185ee9964a17e
Author: Zach Copley <zach@status.net>
Date:   Thu Jun 16 14:20:16 2011 -0700

    Make the queue handling actually work

commit 2a6ce3c17c045bdb0a3ddf36f2c290c9c48eb003
Author: Zach Copley <zach@status.net>
Date:   Thu Jun 16 13:27:56 2011 -0700

    Fix syntax errors

commit 054b54847dfadc490aa7d7dff12d473af31c99bb
Author: Zach Copley <zach@status.net>
Date:   Thu Jun 16 00:36:37 2011 -0700

    Registration reminders should work now, but code is untested

commit b44117017b64635aae340c260167cf1efab9b2ae
Merge: 9d1441d f74de88
Author: Zach Copley <zach@status.net>
Date:   Tue Jun 14 09:43:19 2011 -0700

    Merge branch 'email-reminder' of gitorious.org:~zcopley/statusnet/zcopleys-clone into email-reminder

    * 'email-reminder' of gitorious.org:~zcopley/statusnet/zcopleys-clone:
      Stubby EmailReminderPlugin and data class
      Remove bogus data class

    Conflicts:
     plugins/EmailReminder/EmailReminderPlugin.php
     plugins/EmailReminder/classes/Email_reminder.php

commit 9d1441d7366df57e38cdfaf96e006f7d2f29d889
Author: Zach Copley <zach@status.net>
Date:   Tue Jun 14 09:23:23 2011 -0700

    Most of the other classes needed to send email reminders

commit 4e9bb11dbb23556bf5c1847e7a127084b5cc217c
Author: Zach Copley <zach@status.net>
Date:   Mon Jun 13 12:10:55 2011 -0700

    size -> length

commit a9ea80ef8abae1e64d5713091baedd931b7184e2
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 10 16:38:06 2011 -0400

    Stubby EmailReminderPlugin and data class

commit 5d893f982209b245cb9113a59e49721dd6e191b6
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 10 14:01:48 2011 -0400

    Remove bogus data class

commit f74de8841a98add73536fd8a4d3cee76035b491c
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 10 16:38:06 2011 -0400

    Stubby EmailReminderPlugin and data class

commit 5b14370918233e5112a95da94567c4ed83429bc9
Author: Zach Copley <zach@status.net>
Date:   Fri Jun 10 14:01:48 2011 -0400

    Remove bogus data class

14 files changed:
plugins/EmailRegistration/Email_confirmation.php [deleted file]
plugins/EmailReminder/EmailReminderPlugin.php [new file with mode: 0644]
plugins/EmailReminder/classes/Email_reminder.php [new file with mode: 0644]
plugins/EmailReminder/lib/siteconfirmreminderhandler.php [new file with mode: 0644]
plugins/EmailReminder/lib/userconfirmregreminderhandler.php [new file with mode: 0644]
plugins/EmailReminder/lib/userinvitereminderhandler.php [new file with mode: 0644]
plugins/EmailReminder/lib/userreminderhandler.php [new file with mode: 0644]
plugins/EmailReminder/mail-src/invite-1 [new file with mode: 0644]
plugins/EmailReminder/mail-src/invite-3 [new file with mode: 0644]
plugins/EmailReminder/mail-src/register-1 [new file with mode: 0644]
plugins/EmailReminder/mail-src/register-3 [new file with mode: 0644]
plugins/EmailReminder/mail-src/register-7 [new file with mode: 0644]
plugins/EmailReminder/scripts/cron.sample [new file with mode: 0644]
plugins/EmailReminder/scripts/sendemailreminder.php [new file with mode: 0644]

diff --git a/plugins/EmailRegistration/Email_confirmation.php b/plugins/EmailRegistration/Email_confirmation.php
deleted file mode 100644 (file)
index 949556f..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-<?php
-/**
- * Data class for counting greetings
- *
- * 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) 2009, 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 counting greetings
- *
- * We use the DB_DataObject framework for data classes in StatusNet. Each
- * table maps to a particular data class, making it easier to manipulate
- * data.
- *
- * Data classes should extend Memcached_DataObject, the (slightly misnamed)
- * extension of DB_DataObject that provides caching, internationalization,
- * and other bits of good functionality to StatusNet-specific data classes.
- *
- * @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 User_greeting_count extends Memcached_DataObject
-{
-    public $__table = 'user_greeting_count'; // table name
-    public $user_id;                         // int(4)  primary_key not_null
-    public $greeting_count;                  // int(4)
-
-    /**
-     * Get an instance by key
-     *
-     * This is a utility method to get a single instance with a given key value.
-     *
-     * @param string $k Key to use to lookup (usually 'user_id' for this class)
-     * @param mixed  $v Value to lookup
-     *
-     * @return User_greeting_count object found, or null for no hits
-     *
-     */
-    function staticGet($k, $v=null)
-    {
-        return Memcached_DataObject::staticGet('User_greeting_count', $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,
-                     'greeting_count' => DB_DATAOBJECT_INT);
-    }
-
-    /**
-     * return key definitions for DB_DataObject
-     *
-     * DB_DataObject needs to know about keys that the table has, since it
-     * won't appear in StatusNet's own keys list. In most cases, this will
-     * simply reference your keyTypes() function.
-     *
-     * @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
-     *
-     * If a table has a single integer column as its primary key, DB_DataObject
-     * assumes that the column is auto-incrementing and makes a sequence table
-     * to do this incrementation. Since we don't need this for our class, we
-     * overload this method and return the magic formula that DB_DataObject needs.
-     *
-     * @return array magic three-false array that stops auto-incrementing.
-     */
-    function sequenceKey()
-    {
-        return array(false, false, false);
-    }
-
-    /**
-     * Increment a user's greeting count and return instance
-     *
-     * This method handles the ins and outs of creating a new greeting_count for a
-     * user or fetching the existing greeting count and incrementing its value.
-     *
-     * @param integer $user_id ID of the user to get a count for
-     *
-     * @return User_greeting_count instance for this user, with count already incremented.
-     */
-    static function inc($user_id)
-    {
-        $gc = User_greeting_count::staticGet('user_id', $user_id);
-
-        if (empty($gc)) {
-
-            $gc = new User_greeting_count();
-
-            $gc->user_id        = $user_id;
-            $gc->greeting_count = 1;
-
-            $result = $gc->insert();
-
-            if (!$result) {
-                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
-                // TRANS: %d is a user ID (number).
-                throw Exception(sprintf(_m("Could not save new greeting count for %d."),
-                                        $user_id));
-            }
-        } else {
-            $orig = clone($gc);
-
-            $gc->greeting_count++;
-
-            $result = $gc->update($orig);
-
-            if (!$result) {
-                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
-                // TRANS: %d is a user ID (number).
-                throw Exception(sprintf(_m('Could not increment greeting count for %d.'),
-                                        $user_id));
-            }
-        }
-
-        return $gc;
-    }
-}
diff --git a/plugins/EmailReminder/EmailReminderPlugin.php b/plugins/EmailReminder/EmailReminderPlugin.php
new file mode 100644 (file)
index 0000000..0bb1092
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Plugin for sending email reminders about various things
+ *
+ * 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  OnDemand
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Email reminder plugin
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class EmailReminderPlugin extends Plugin
+{
+    /**
+     * Set up email_reminder table
+     *
+     * @see Schema
+     * @see ColumnDef
+     *
+     * @return boolean hook value; true means continue processing, false means stop.
+     */
+    function onCheckSchema()
+    {
+        $schema = Schema::get();
+        $schema->ensureTable('email_reminder', Email_reminder::schemaDef());
+        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) {
+        $base = dirname(__FILE__);
+        $lower = strtolower($cls);
+
+        $files = array("$base/classes/$cls.php",
+            "$base/lib/$lower.php");
+        if (substr($lower, -6) == 'action') {
+            $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
+        }
+        foreach ($files as $file) {
+            if (file_exists($file)) {
+                include_once $file;
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Register our queue handlers
+     *
+     * @param QueueManager $qm Current queue manager
+     *
+     * @return boolean hook value
+     */
+    function onEndInitializeQueueManager($qm)
+    {
+        $qm->connect('siterem', 'SiteConfirmReminderHandler');
+        $qm->connect('uregrem', 'UserConfirmRegReminderHandler');
+        $qm->connect('uinvrem', 'UserInviteReminderHandler');
+
+        return true;
+    }
+
+    function onEndDocFileForTitle($title, $paths, &$filename)
+    {
+        if (empty($filename)) {
+            $filename = dirname(__FILE__) . '/mail-src/' . $title;
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     *
+     * @param type $object
+     * @param type $subject
+     * @param type $day
+     */
+    static function sendReminder($type, $object, $subject, $day)
+    {
+        common_debug("QQQQQ sendReminder() enter ... ", __FILE__);
+
+        $title = "{$type}-{$day}";
+
+        common_debug("QQQQ title = {$title}", __FILE__);
+
+        // Record the fact that we sent a reminder
+        if (self::sendReminderEmail($type, $object, $subject, $title)) {
+            common_debug("Recording reminder record for {$object->address}", __FILE__);
+            try {
+                Email_reminder::recordReminder($type, $object, $day);
+            } catch (Exception $e) {
+                // oh noez
+                common_log(LOG_ERR, $e->getMessage(), __FILE__);
+            }
+        }
+
+        common_debug("QQQQQ sendReminder() exit ... ", __FILE__);
+
+        return true;
+    }
+
+    /**
+     *
+     * @param type $object
+     * @param type $subject
+     * @param type $title
+     * @return type
+     */
+    static function sendReminderEmail($type, $object, $subject, $title=null) {
+
+        $sitename   = common_config('site', 'name');
+        $recipients = array($object->address);
+        $inviter    = null;
+        $inviterurl = null;
+
+        if ($type == UserInviteReminderHandler::INVITE_REMINDER) {
+            $user = User::staticGet($object->user_id);
+            if (!empty($user)) {
+                $profile    = $user->getProfile();
+                $inviter    = $profile->getBestName();
+                $inviterUrl = $profile->profileurl;
+            }
+        }
+
+        $headers['From'] = mail_notify_from();
+        $headers['To']   = trim($object->address);
+        // TRANS: Subject for confirmation e-mail.
+        // TRANS: %s is the StatusNet sitename.
+        $headers['Subject']      = $subject;
+        $headers['Content-Type'] = 'text/html; charset=UTF-8';
+
+        $confirmUrl = common_local_url('register', array('code' => $object->code));
+
+        $template = DocFile::forTitle($title, DocFile::mailPaths());
+
+        $body = $template->toHTML(
+            array(
+                'confirmurl' => $confirmUrl,
+                'inviter'    => $inviter,
+                'inviterurl' => $inviterUrl
+                // @todo private invitation message
+            )
+        );
+
+        return mail_send($recipients, $headers, $body);
+    }
+
+    /**
+     *
+     * @param type $versions
+     * @return type
+     */
+    function onPluginVersion(&$versions)
+    {
+        $versions[] = array(
+            'name'           => 'EmailReminder',
+            'version'        => STATUSNET_VERSION,
+            'author'         => 'Zach Copley',
+            'homepage'       => 'http://status.net/wiki/Plugin:EmailReminder',
+            'rawdescription' => _m('Send email reminders for various things.')
+        );
+        return true;
+    }
+    
+}
diff --git a/plugins/EmailReminder/classes/Email_reminder.php b/plugins/EmailReminder/classes/Email_reminder.php
new file mode 100644 (file)
index 0000000..2a82e6c
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Data class for email reminders
+ *
+ * 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  Data
+ * @package   EmailReminder
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class Email_reminder extends Managed_DataObject
+{
+    const INVITE_REMINDER   = 'invite'; // @todo Move this to the invite reminder handler
+
+    public $__table = 'email_reminder';
+
+    public $type;     // type of reminder
+    public $code;     // confirmation code
+    public $days;     // number of days after code was created
+    public $sent;     // timestamp
+    public $created;  // timestamp
+    public $modified; // timestamp
+
+    /**
+     * Get an instance by key
+     *
+     * This is a utility method to get a single instance with a given key value.
+     *
+     * @param string $k Key to use to lookup
+     * @param mixed  $v Value to lookup
+     *
+     * @return QnA_Answer object found, or null for no hits
+     *
+     */
+    function staticGet($k, $v=null)
+    {
+        return Memcached_DataObject::staticGet('email_reminder', $k, $v);
+    }
+
+    /**
+     *
+     * @param type $type
+     * @param type $confirm
+     * @param type $day
+     * @return type
+     */
+    static function needsReminder($type, $confirm, $days) {
+
+        $reminder        = new Email_reminder();
+        $reminder->type  = $type;
+        $reminder->code  = $confirm->code;
+        $reminder->days  = $days;
+
+        $result = $reminder->find(true);
+
+        if (empty($result)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     *
+     * @param type $type
+     * @param type $confirm
+     * @param type $day
+     * @return type
+     */
+    static function recordReminder($type, $confirm, $days) {
+
+        $reminder        = new Email_reminder();
+        $reminder->type  = $type;
+        $reminder->code  = $confirm->code;
+        $reminder->days  = $days;
+        $reminder->sent  = $reminder->created = common_sql_now();
+        $result          = $reminder->insert();
+
+        if (empty($result)) {
+            common_log_db_error($reminder, 'INSERT', __FILE__);
+                throw new ServerException(
+                    _m('Database error inserting reminder record.')
+            );
+        }
+
+        return $result;
+    }
+
+    /**
+     * Data definition for email reminders
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'Record of email reminders that have been sent',
+            'fields'      => array(
+                'type'     => array(
+                    'type'          => 'varchar',
+                    'length'        => 255,
+                    'not null'      => true,
+                    'description'   => 'type of reminder'
+                ),
+                'code' => array(
+                    'type'        => 'varchar',
+                    'not null'    => 'true',
+                    'length'      => 255,
+                    'description' => 'confirmation code'
+                 ),
+                'days' => array(
+                    'type'        => 'int',
+                    'not null'    => 'true',
+                    'description' => 'number of days since code creation'
+                 ),
+                'sent' => array(
+                    'type'        => 'datetime',
+                    'not null'    => true,
+                    'description' => 'Date and time the reminder was sent'
+                ),
+                'created' => array(
+                    'type'        => 'datetime',
+                    'not null'    => true,
+                    'description' => 'Date and time the record was created'
+                ),
+                'modified'        => array(
+                    'type'        => 'timestamp',
+                    'not null'    => true,
+                    'description' => 'Date and time the record was last modified'
+                ),
+            ),
+            'primary key' => array('type', 'code', 'days'),
+            'indexes' => array(
+                'sent_idx' => array('sent'),
+             ),
+        );
+    }
+}
diff --git a/plugins/EmailReminder/lib/siteconfirmreminderhandler.php b/plugins/EmailReminder/lib/siteconfirmreminderhandler.php
new file mode 100644 (file)
index 0000000..e5b5618
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Handler for reminder queue items which send reminder emails to all users
+ * we would like to complete a given process (e.g.: registration).
+ *
+ * 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  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 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 reminder queue items which send reminder emails to all users
+ * we would like to complete a given process (e.g.: registration)
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class SiteConfirmReminderHandler 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 'siterem';
+    }
+
+    /**
+     * Handle the site
+     *
+     * @param string $reminderType type of reminder to send
+     * @return boolean true on success, false on failure
+     */
+    function handle($reminderType)
+    {
+        $qm = QueueManager::get();
+
+        try {
+            switch($reminderType) {
+            case UserConfirmRegReminderHandler::REGISTER_REMINDER:
+                $confirm               = new Confirm_address();
+                $confirm->address_type = $object;
+                $confirm->find();
+                while ($confirm->fetch()) {
+                    try {
+                        $qm->enqueue($confirm, 'uregrem');
+                    } catch (Exception $e) {
+                        common_log(LOG_WARNING, $e->getMessage());
+                        continue;
+                    }
+                }
+                break;
+            case UserInviteReminderHandler::INVITE_REMINDER:
+                $invitation = new Invitation();
+                $invitation->find();
+                while ($invitation->fetch()) {
+                    try {
+                        $qm->enqueue($invitation, 'uinvrem');
+                    } catch (Exception $e) {
+                        common_log(LOG_WARNING, $e->getMessage());
+                        continue;
+                    }
+                }
+                break;
+            default:
+                // WTF?
+                common_log(
+                    LOG_ERR,
+                    "Received unknown confirmation address type",
+                    __FILE__
+                );
+            }
+        } catch (Exception $e) {
+            common_log(LOG_ERR, $e->getMessage());
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/EmailReminder/lib/userconfirmregreminderhandler.php b/plugins/EmailReminder/lib/userconfirmregreminderhandler.php
new file mode 100644 (file)
index 0000000..5f1bbd6
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Handler for queue items of type 'uregem' - sends email registration
+ * confirmation reminders 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  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 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 'uregrem'
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class UserConfirmRegReminderHandler extends UserReminderHandler {
+
+    const REGISTER_REMINDER = 'register';
+
+    /**
+     * 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 'uregrem';
+    }
+
+    /**
+     * Send an email registration confirmation reminder until the user
+     * confirms her registration. We'll send a reminder after one day,
+     * three days, and a full week.
+     *
+     * @todo abstract this bit further
+     *
+     * @param Confirm_address $confirm
+     * @return boolean success value
+     */
+    function sendNextReminder($confirm)
+    {
+        $regDate = strtotime($confirm->modified); // Seems like my best bet
+        $now     = strtotime('now');
+
+        // Days since registration
+        $days = ($now - $regDate) / 86499; // 60*60*24 = 86499
+
+        // Welcome to one of the ugliest switch statement I've ever written
+
+        switch($days) {
+        case ($days > 1 && $days < 2):
+            if (Email_reminder::needsReminder(self::REGISTER_REMINDER, $confirm, 1)) {
+                common_log(LOG_INFO, "Sending one day registration confirmation reminder to {$confirm->address}", __FILE__);
+                $subject = _m("Reminder - please confirm your registration!");
+                return EmailReminderPlugin::sendReminder(
+                    self::REGISTER_REMINDER,
+                    $confirm,
+                    $subject,
+                1);
+            } else {
+                return true;
+            }
+            break;
+        case ($days > 3 && $days < 4):
+            if (Email_reminder::needsReminder(self::REGISTER_REMINDER, $confirm, 3)) {
+                common_log(LOG_INFO, "Sending three day registration confirmation reminder to {$confirm->address}", __FILE__);
+                $subject = _m("Second reminder - please confirm your registration!");
+                    return EmailReminderPlugin::sendReminder(
+                        self::REGISTER_REMINDER,
+                        $confirm,
+                        $subject,
+                        3
+                    );
+                } else {
+                    return true;
+                }
+            break;
+        case ($days > 7 && $days < 8):
+            if (Email_reminder::needsReminder(self::REGISTER_REMINDER, $confirm, 7)) {
+                common_log(LOG_INFO, "Sending one week registration confirmation reminder to {$confirm->address}", __FILE__);
+                $subject = _m("Final reminder - please confirm your registration!");
+                return EmailReminderPlugin::sendReminder(
+                    self::REGISTER_REMINDER,
+                    $confirm,
+                    $subject,
+                    7
+                );
+            } else {
+                return true;
+            }
+            break;
+        }
+        return true;
+    }
+
+}
diff --git a/plugins/EmailReminder/lib/userinvitereminderhandler.php b/plugins/EmailReminder/lib/userinvitereminderhandler.php
new file mode 100644 (file)
index 0000000..c4acecc
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Handler for queue items of type 'uinvrem' - sends an email reminder to
+ * an email address of someone who has been invited to 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  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 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 'uinvrem' (user invite reminder)
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class UserInviteReminderHandler extends UserReminderHandler {
+
+    const INVITE_REMINDER = 'invite';
+
+    /**
+     * 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 'uinvrem';
+    }
+
+    /**
+     * Send an invitation reminder. We'll send one after one day, and then
+     * one after three days.
+     *
+     * @todo Abstract this stuff further
+     *
+     * @param Invitation $invitation
+     * @return boolean success value
+     */
+    function sendNextReminder($invitation)
+    {
+        $invDate = strtotime($invitation->created);
+        $now     = strtotime('now');
+        
+        // Days since first invitation was sent
+        $days = ($now - $invDate) / 86499; // 60*60*24 = 86499
+
+        $siteName = common_config('site', 'name');
+
+        switch($days) {
+        case ($days > 1 && $days < 2):
+            if (Email_reminder::needsReminder(self::INVITE_REMINDER, $invitation, 1)) {
+                common_log(LOG_INFO, "Sending one day invitation reminder to {$invitation->address}", __FILE__);
+                $subject = _m("Reminder - You have been invited to join {$siteName}!");
+                return EmailReminderPlugin::sendReminder(
+                    self::INVITE_REMINDER,
+                    $invitation,
+                    $subject,
+                1);
+            } else {
+                return true;
+            }
+            break;
+        case ($days > 3 && $days < 4):
+            if (Email_reminder::needsReminder(self::INVITE_REMINDER, $invitation, 3)) {
+                common_log(LOG_INFO, "Sending three day invitation reminder to {$invitation->address}", __FILE__);
+                $subject = _m("Final reminder - you have been invited to join {$siteName}!");
+                    return EmailReminderPlugin::sendReminder(
+                        self::INVITE_REMINDER,
+                        $invitation,
+                        $subject,
+                        3
+                    );
+                } else {
+                    return true;
+                }
+            break;        
+        }
+        return true;
+    }
+
+}
diff --git a/plugins/EmailReminder/lib/userreminderhandler.php b/plugins/EmailReminder/lib/userreminderhandler.php
new file mode 100644 (file)
index 0000000..22f4987
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Base handler for email reminders
+ *
+ * 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  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 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 email reminder queue items
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class UserReminderHandler extends QueueHandler {
+
+    /**
+     * Send the next email reminder to the confirm address
+     *
+     * @param Confirm_address $confirm the confirmation email/code
+     * @return boolean true on success, false on failure
+     */
+    function handle($confirm) {
+        return $this->sendNextReminder($confirm);
+    }
+
+    /**
+     * Send the next reminder if one needs sending.
+     * Subclasses must override
+     *
+     * @param Confirm_address $confirm the confirmation email/code
+     */
+    function sendNextReminder($confirm)
+    {
+    }
+}
diff --git a/plugins/EmailReminder/mail-src/invite-1 b/plugins/EmailReminder/mail-src/invite-1
new file mode 100644 (file)
index 0000000..f1abf0b
--- /dev/null
@@ -0,0 +1,26 @@
+First reminder...
+
+%%arg.inviter%% has invited you to join them on %%site.name%%.
+
+%%site.name%% is a micro-blogging service that lets you keep
+up-to-date with people you know and people who interest you.
+
+You can also share news about yourself, your thoughts, or your life
+online with people who know about you.
+
+It's great for meeting new people who share your interests.
+
+You can see %%arg.inviter%%'s profile page on %%site.name%% here:
+
+> [%%arg.inviterurl%%](%%arg.inviterurl%%)
+
+If you'd like to try the service, click on the link below to accept
+the invitation.
+
+> [%%arg.confirmurl%%](%%arg.confirmurl%%)
+
+If not, you can ignore this message. Thanks for your patience and your time.
+
+Sincerely,
+
+%%site.name%%
diff --git a/plugins/EmailReminder/mail-src/invite-3 b/plugins/EmailReminder/mail-src/invite-3
new file mode 100644 (file)
index 0000000..8955443
--- /dev/null
@@ -0,0 +1,26 @@
+Second and final reminder...
+
+%%arg.inviter%% has invited you to join them on %%site.name%%.
+
+%%site.name%% is a micro-blogging service that lets you keep
+up-to-date with people you know and people who interest you.
+
+You can also share news about yourself, your thoughts, or your life
+online with people who know about you.
+
+It's great for meeting new people who share your interests.
+
+You can see %%arg.inviter%%'s profile page on %%site.name%% here:
+
+> [%%arg.inviterurl%%](%%arg.inviterurl%%)
+
+If you'd like to try the service, click on the link below to accept
+the invitation.
+
+> [%%arg.confirmurl%%](%%arg.confirmurl%%)
+
+If not, you can ignore this message. Thanks for your patience and your time.
+
+Sincerely,
+
+%%site.name%%
diff --git a/plugins/EmailReminder/mail-src/register-1 b/plugins/EmailReminder/mail-src/register-1
new file mode 100644 (file)
index 0000000..9461842
--- /dev/null
@@ -0,0 +1,9 @@
+Hey, it's been a whole day!
+
+Someone (probably you) has requested an account on %%site.name%% using this email address.
+
+To confirm the address, click the following URL or copy it into the address bar of your browser.
+
+> [%%arg.confirmurl%%](%%arg.confirmurl%%)
+
+If it was not you, you can safely ignore this message.
diff --git a/plugins/EmailReminder/mail-src/register-3 b/plugins/EmailReminder/mail-src/register-3
new file mode 100644 (file)
index 0000000..3065622
--- /dev/null
@@ -0,0 +1,9 @@
+Hey, it's been three days!!
+
+Someone (probably you) has requested an account on %%site.name%% using this email address.
+
+To confirm the address, click the following URL or copy it into the address bar of your browser.
+
+> [%%arg.confirmurl%%](%%arg.confirmurl%%)
+
+If it was not you, you can safely ignore this message.
diff --git a/plugins/EmailReminder/mail-src/register-7 b/plugins/EmailReminder/mail-src/register-7
new file mode 100644 (file)
index 0000000..75db0eb
--- /dev/null
@@ -0,0 +1,9 @@
+IT'S BEEN A WHOLE WEEK! Final reminder...
+
+Someone (probably you) has requested an account on %%site.name%% using this email address.
+
+To confirm the address, click the following URL or copy it into the address bar of your browser.
+
+> [%%arg.confirmurl%%](%%arg.confirmurl%%)
+
+If it was not you, you can safely ignore this message.
diff --git a/plugins/EmailReminder/scripts/cron.sample b/plugins/EmailReminder/scripts/cron.sample
new file mode 100644 (file)
index 0000000..91981e7
--- /dev/null
@@ -0,0 +1 @@
+* * * * * /opt/local/bin/php /path/to/statusnet/plugins/EmailReminder/scripts/sendemailreminder.php -t all -a -q > /tmp/cron.log
diff --git a/plugins/EmailReminder/scripts/sendemailreminder.php b/plugins/EmailReminder/scripts/sendemailreminder.php
new file mode 100644 (file)
index 0000000..4cb7087
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env php
+<?php
+/*
+* StatusNet - a distributed open-source microblogging tool
+* Copyright (C) 2011, 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 = 't:e:au';
+$longoptions = array('type=', 'email=', 'all', 'universe');
+
+$helptext = <<<END_OF_SENDEMAILREMINDER_HELP
+sendemailreminder.php [options]
+Send an email summary of the inbox to users
+
+ -t --type     type of reminder to send (register | invite | all)
+ -e --email    email address to send reminder to
+ -a --all      send reminder to all addresses
+ -u --universe send reminder to all addresses on all sites
+
+END_OF_SENDEMAILREMINDER_HELP;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+
+$quiet = have_option('q', 'quiet');
+
+$types = array(
+    // registration confirmation reminder
+    'register' => array(
+        'type'       => 'register',
+        'className'  => 'Confirm_address',
+        'utransport' => 'uregrem'
+     ),
+    // invitation confirmation reminder
+    'invite'   => array(
+        'type'       => 'invite',
+        'className'  => 'Invitation',
+        'utransport' => 'uinvrem'
+    )
+    // ... add more here
+);
+
+$type = null;
+
+if (have_option('t', 'type')) {
+    $type = trim(get_option_value('t', 'type'));
+    if (!in_array($type, array_keys($types)) && $type !== 'all') {
+       print _m("Unknown reminder type: {$type}.\n");
+       exit(1);
+    }
+} else {
+   show_help();
+   exit(1);
+}
+
+$reminders = array();
+
+switch($type) {
+case 'register':
+    $reminders[] = $types['register'];
+    break;
+case 'invite':
+    $reminders[] = $types['invite'];
+    break;
+case 'all':
+    $reminders = $types;
+    break;
+}
+
+if (have_option('u', 'universe')) {
+    $sn = new Status_network();
+    if ($sn->find()) {
+        while ($sn->fetch()) {
+            $server = $sn->getServerName();
+            StatusNet::init($server);
+            // Different queue manager, maybe!
+            $qm = QueueManager::get();
+            foreach ($reminders as $reminder) {
+                extract($reminder);
+                $qm->enqueue($type, 'siterem');
+                if (!$quiet) { print "Sent pending {$type} reminders to all unconfirmed addresses in the known universe.\n"; }
+            }
+        }
+    }
+} else {
+    $qm = QueueManager::get();
+    try {
+        // enqueue reminder for specific email address or all unconfirmed addresses
+        if (have_option('e', 'email')) {
+            $address = trim(get_option_value('e', 'email'));
+            foreach ($reminders as $reminder) {
+                // real bad voodoo here
+                extract($reminder);
+                $confirm = new $className;
+                $confirm->address = $address;
+                $result = $confirm->find(true);
+                if (empty($result)) {
+                    throw new Exception("No confirmation code found for {$address}.");
+                }
+                $qm->enqueue($confirm, $utransport);
+                if (!$quiet) { print "Sent all pending {$type} reminder to {$address}.\n"; }
+            }
+        } else if (have_option('a', 'all')) {
+            foreach ($reminders as $reminder) {
+                extract($reminder);
+                $qm->enqueue($type, 'siterem');
+                if (!$quiet) { print "Sent pending {$type} reminders to all unconfirmed addresses on the site.\n"; }
+            }
+        } else {
+            show_help();
+            exit(1);
+        }
+    } catch (Exception $e) {
+        if (!$quiet) { print $e->getMessage() . "\n"; }
+        common_log(LOG_ERR, $e->getMessage(), __FILE__);
+        exit(1);
+    }
+}