diff --git a/actions/approvesub.php b/actions/approvesub.php
index be07b6e877..5fbb2149bd 100644
--- a/actions/approvesub.php
+++ b/actions/approvesub.php
@@ -73,6 +73,7 @@ class ApprovesubAction extends Action
if (empty($this->request)) {
// TRANS: Client error displayed trying to approve subscription for a non-existing request.
+ // TRANS: %s is a user nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for your subscriptions.'), $this->profile->nickname), 403);
}
diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php
index 8d731cb871..d81c13b005 100644
--- a/actions/recoverpassword.php
+++ b/actions/recoverpassword.php
@@ -260,7 +260,7 @@ class RecoverpasswordAction extends Action
$this->elementStart('li');
// TRANS: Field label for password reset form where the password has to be typed again.
$this->password('confirm', _('Confirm'),
- // TRANS: Ttile for field label for password reset form where the password has to be typed again.
+ // TRANS: Title for field label for password reset form where the password has to be typed again.
_('Same as password above.'));
$this->elementEnd('li');
$this->elementEnd('ul');
diff --git a/actions/siteadminpanel.php b/actions/siteadminpanel.php
index 11636b0b5c..bc96a6d73e 100644
--- a/actions/siteadminpanel.php
+++ b/actions/siteadminpanel.php
@@ -157,13 +157,13 @@ class SiteadminpanelAction extends AdminPanelAction
// Validate logos
if (!empty($values['site']['logo']) &&
!Validate::uri($values['site']['logo'], array('allowed_schemes' => array('http', 'https')))) {
- // TRANS: Client error displayed when a logo URL does is not valid.
+ // TRANS: Client error displayed when a logo URL is not valid.
$this->clientError(_('Invalid logo URL.'));
}
if (!empty($values['site']['ssllogo']) &&
!Validate::uri($values['site']['ssllogo'], array('allowed_schemes' => array('https')))) {
- // TRANS: Client error displayed when an SSL logo URL is invalid.
+ // TRANS: Client error displayed when a SSL logo URL is invalid.
$this->clientError(_('Invalid SSL logo URL.'));
}
@@ -196,7 +196,7 @@ class SiteadminpanelAction extends AdminPanelAction
if (!Validate::number($values['site']['dupelimit'], array('min' => 1))) {
// TRANS: Client error displayed trying to save site settings with a text limit below 1.
- $this->clientError(_("Dupe limit must be one or more seconds."));
+ $this->clientError(_('Dupe limit must be one or more seconds.'));
}
}
}
@@ -302,7 +302,7 @@ class SiteAdminPanelForm extends AdminForm
_('Default language'),
get_nice_language_list(),
// TRANS: Dropdown title on site settings panel.
- _('Site language when autodetection from browser settings is not available'),
+ _('The site language when autodetection from browser settings is not available.'),
false, $this->value('language'));
$this->unli();
@@ -374,6 +374,6 @@ class SiteAdminPanelForm extends AdminForm
'submit',
null,
// TRANS: Button title for saving site settings.
- _('Save site settings'));
+ _('Save the site settings.'));
}
}
diff --git a/plugins/EmailRegistration/Email_confirmation.php b/plugins/EmailRegistration/Email_confirmation.php
deleted file mode 100644
index 949556fc10..0000000000
--- a/plugins/EmailRegistration/Email_confirmation.php
+++ /dev/null
@@ -1,183 +0,0 @@
-
- * @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 .
- */
-
-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
- * @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
index 0000000000..0bb10921b0
--- /dev/null
+++ b/plugins/EmailReminder/EmailReminderPlugin.php
@@ -0,0 +1,209 @@
+.
+ *
+ * @category OnDemand
+ * @package StatusNet
+ * @author Zach Copley
+ * @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
+ * @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
index 0000000000..2a82e6c4d8
--- /dev/null
+++ b/plugins/EmailReminder/classes/Email_reminder.php
@@ -0,0 +1,154 @@
+.
+ *
+ * @category Data
+ * @package EmailReminder
+ * @author Zach Copley
+ * @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
index 0000000000..e5b561827b
--- /dev/null
+++ b/plugins/EmailReminder/lib/siteconfirmreminderhandler.php
@@ -0,0 +1,112 @@
+.
+ *
+ * @category Email
+ * @package StatusNet
+ * @author Zach Copley
+ * @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
+ * @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
index 0000000000..5f1bbd62d9
--- /dev/null
+++ b/plugins/EmailReminder/lib/userconfirmregreminderhandler.php
@@ -0,0 +1,126 @@
+.
+ *
+ * @category Email
+ * @package StatusNet
+ * @author Zach Copley
+ * @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
+ * @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
index 0000000000..c4acecc62f
--- /dev/null
+++ b/plugins/EmailReminder/lib/userinvitereminderhandler.php
@@ -0,0 +1,110 @@
+.
+ *
+ * @category Email
+ * @package StatusNet
+ * @author Zach Copley
+ * @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
+ * @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
index 0000000000..22f498762b
--- /dev/null
+++ b/plugins/EmailReminder/lib/userreminderhandler.php
@@ -0,0 +1,63 @@
+.
+ *
+ * @category Email
+ * @package StatusNet
+ * @author Zach Copley
+ * @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
+ * @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
index 0000000000..f1abf0b5d6
--- /dev/null
+++ b/plugins/EmailReminder/mail-src/invite-1
@@ -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
index 0000000000..8955443b24
--- /dev/null
+++ b/plugins/EmailReminder/mail-src/invite-3
@@ -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
index 0000000000..9461842407
--- /dev/null
+++ b/plugins/EmailReminder/mail-src/register-1
@@ -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
index 0000000000..3065622477
--- /dev/null
+++ b/plugins/EmailReminder/mail-src/register-3
@@ -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
index 0000000000..75db0ebfeb
--- /dev/null
+++ b/plugins/EmailReminder/mail-src/register-7
@@ -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
index 0000000000..91981e73d3
--- /dev/null
+++ b/plugins/EmailReminder/scripts/cron.sample
@@ -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
index 0000000000..4cb7087fc7
--- /dev/null
+++ b/plugins/EmailReminder/scripts/sendemailreminder.php
@@ -0,0 +1,132 @@
+#!/usr/bin/env php
+.
+*/
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+
+$shortoptions = 't:e:au';
+$longoptions = array('type=', 'email=', 'all', 'universe');
+
+$helptext = << 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);
+ }
+}
diff --git a/plugins/XCache/XCachePlugin.php b/plugins/XCache/XCachePlugin.php
index 2baa290ed2..532714315f 100644
--- a/plugins/XCache/XCachePlugin.php
+++ b/plugins/XCache/XCachePlugin.php
@@ -117,8 +117,8 @@ class XCachePlugin extends Plugin
'author' => 'Craig Andrews',
'homepage' => 'http://status.net/wiki/Plugin:XCache',
'rawdescription' =>
+ // TRANS: Plugin description.
_m('Use the XCache variable cache to cache query results.'));
return true;
}
}
-
diff --git a/plugins/Xmpp/README b/plugins/Xmpp/README
index 9bd71e9807..96b0f3291a 100644
--- a/plugins/Xmpp/README
+++ b/plugins/Xmpp/README
@@ -1,4 +1,5 @@
-The XMPP plugin allows users to send and receive notices over the XMPP/Jabber/GTalk network.
+The XMPP plugin allows users to send and receive notices over the
+XMPP/Jabber/GTalk network.
Installation
============
@@ -6,9 +7,10 @@ add "addPlugin('xmpp',
array('setting'=>'value', 'setting2'=>'value2', ...);"
to the bottom of your config.php
-The daemon included with this plugin must be running. It will be started by
-the plugin along with their other daemons when you run scripts/startdaemons.sh.
-See the StatusNet README for more about queuing and daemons.
+The daemon included with this plugin must be running. It will be
+started by the plugin along with their other daemons when you run
+scripts/startdaemons.sh. See the StatusNet README for more about queuing and
+daemons.
Settings
========
@@ -32,4 +34,3 @@ addPlugin('xmpp', array(
'password'=>'...',
'public'=>array('bob@aol.com', 'sue@google.com')
));
-
diff --git a/plugins/Xmpp/XmppPlugin.php b/plugins/Xmpp/XmppPlugin.php
index f7df6812cf..ece4acac37 100644
--- a/plugins/Xmpp/XmppPlugin.php
+++ b/plugins/Xmpp/XmppPlugin.php
@@ -44,7 +44,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
-
class XmppPlugin extends ImPlugin
{
public $server = null;
@@ -59,22 +58,22 @@ class XmppPlugin extends ImPlugin
public $transport = 'xmpp';
function getDisplayName(){
+ // TRANS: Plugin display name.
return _m('XMPP/Jabber/GTalk');
}
/**
* Splits a Jabber ID (JID) into node, domain, and resource portions.
- *
+ *
* Based on validation routine submitted by:
* @copyright 2009 Patrick Georgi
- * @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
+ * @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
*
* @param string $jid string to check
*
* @return array with "node", "domain", and "resource" indices
* @throws Exception if input is not valid
*/
-
protected function splitJid($jid)
{
$chars = '';
@@ -102,11 +101,11 @@ class XmppPlugin extends ImPlugin
$chars .= "\x{340}\x{341}\x{200e}\x{200f}\x{202a}-\x{202e}\x{206a}-\x{206f}";
/* C9 - Tagging characters */
$chars .= "\x{e0001}\x{e0020}-\x{e007f}";
-
+
/* Nodeprep forbids some more characters */
$nodeprepchars = $chars;
$nodeprepchars .= "\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}";
-
+
$parts = explode("/", $jid, 2);
if (count($parts) > 1) {
$resource = $parts[1];
@@ -117,10 +116,11 @@ class XmppPlugin extends ImPlugin
} else {
$resource = null;
}
-
+
$node = explode("@", $parts[0]);
if ((count($node) > 2) || (count($node) == 0)) {
- throw new Exception("Invalid JID: too many @s");
+ // TRANS: Exception thrown when using too many @ signs in a Jabber ID.
+ throw new Exception(_m('Invalid JID: too many @s.'));
} else if (count($node) == 1) {
$domain = $node[0];
$node = null;
@@ -128,47 +128,57 @@ class XmppPlugin extends ImPlugin
$domain = $node[1];
$node = $node[0];
if ($node == '') {
- throw new Exception("Invalid JID: @ but no node");
+ // TRANS: Exception thrown when using @ sign not followed by a Jabber ID.
+ throw new Exception(_m('Invalid JID: @ but no node'));
}
}
-
+
// Length limits per http://xmpp.org/rfcs/rfc3920.html#addressing
if ($node !== null) {
if (strlen($node) > 1023) {
- throw new Exception("Invalid JID: node too long.");
+ // TRANS: Exception thrown when using too long a Jabber ID (>1023).
+ throw new Exception(_m('Invalid JID: node too long.'));
}
if (preg_match("/[".$nodeprepchars."]/u", $node)) {
- throw new Exception("Invalid JID node '$node'");
+ // TRANS: Exception thrown when using an invalid Jabber ID.
+ // TRANS: %s is the invalid Jabber ID.
+ throw new Exception(sprintf(_m('Invalid JID node "%s".'),$node));
}
}
-
+
if (strlen($domain) > 1023) {
- throw new Exception("Invalid JID: domain too long.");
+ // TRANS: Exception thrown when using too long a Jabber domain (>1023).
+ throw new Exception(_m('Invalid JID: domain too long.'));
}
if (!common_valid_domain($domain)) {
- throw new Exception("Invalid JID domain name '$domain'");
+ // TRANS: Exception thrown when using an invalid Jabber domain name.
+ // TRANS: %s is the invalid domain name.
+ throw new Exception(sprintf(_m('Invalid JID domain name "%s".'),$domain));
}
-
+
if ($resource !== null) {
if (strlen($resource) > 1023) {
+ // TRANS: Exception thrown when using too long a resource (>1023).
throw new Exception("Invalid JID: resource too long.");
}
if (preg_match("/[".$chars."]/u", $resource)) {
- throw new Exception("Invalid JID resource '$resource'");
+ // TRANS: Exception thrown when using an invalid Jabber resource.
+ // TRANS: %s is the invalid resource.
+ throw new Exception(sprintf(_m('Invalid JID resource "%s".'),$resource));
}
}
-
+
return array('node' => is_null($node) ? null : mb_strtolower($node),
'domain' => is_null($domain) ? null : mb_strtolower($domain),
'resource' => $resource);
}
-
+
/**
* Checks whether a string is a syntactically valid Jabber ID (JID),
* either with or without a resource.
- *
+ *
* Note that a bare domain can be a valid JID.
- *
+ *
* @param string $jid string to check
* @param bool $check_domain whether we should validate that domain...
*
@@ -188,15 +198,15 @@ class XmppPlugin extends ImPlugin
return false;
}
}
-
+
/**
* Checks whether a string is a syntactically valid base Jabber ID (JID).
* A base JID won't include a resource specifier on the end; since we
* take it off when reading input we can't really use them reliably
* to direct outgoing messages yet (sorry guys!)
- *
+ *
* Note that a bare domain can be a valid JID.
- *
+ *
* @param string $jid string to check
* @param bool $check_domain whether we should validate that domain...
*
@@ -225,7 +235,6 @@ class XmppPlugin extends ImPlugin
*
* @return string an equivalent JID in normalized (lowercase) form
*/
-
function normalize($jid)
{
try {
@@ -308,7 +317,7 @@ class XmppPlugin extends ImPlugin
function microiduri($screenname)
{
- return 'xmpp:' . $screenname;
+ return 'xmpp:' . $screenname;
}
function sendMessage($screenname, $body)
@@ -320,7 +329,7 @@ class XmppPlugin extends ImPlugin
{
$msg = $this->formatNotice($notice);
$entry = $this->format_entry($notice);
-
+
$this->queuedConnection()->message($screenname, $msg, 'chat', null, $entry);
return true;
}
@@ -333,7 +342,6 @@ class XmppPlugin extends ImPlugin
*
* @return string Extra information (Atom, HTML, addresses) in string format
*/
-
function format_entry($notice)
{
$profile = $notice->getProfile();
@@ -355,6 +363,7 @@ class XmppPlugin extends ImPlugin
$xs->element('a', array(
'href'=>common_local_url('conversation',
array('id' => $notice->conversation)).'#notice-'.$notice->id),
+ // TRANS: Link description to notice in conversation.
// TRANS: %s is a notice ID.
sprintf(_m('[%s]'),$notice->id));
$xs->elementEnd('body');
@@ -380,14 +389,14 @@ class XmppPlugin extends ImPlugin
}
$this->handleIncoming($from, $pl['body']);
-
+
return true;
}
/**
* Build a queue-proxied XMPP interface object. Any outgoing messages
* will be run back through us for enqueing rather than sent directly.
- *
+ *
* @return Queued_XMPP
* @throws Exception if server settings are invalid.
*/
diff --git a/plugins/Xmpp/xmppmanager.php b/plugins/Xmpp/xmppmanager.php
index 4aaed677b5..f6a9b40a1d 100644
--- a/plugins/Xmpp/xmppmanager.php
+++ b/plugins/Xmpp/xmppmanager.php
@@ -29,16 +29,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
* In a multi-site queuedaemon.php run, one connection will be instantiated
* for each site being handled by the current process that has XMPP enabled.
*/
-
class XmppManager extends ImManager
{
protected $lastping = null;
protected $pingid = null;
public $conn = null;
-
+
const PING_INTERVAL = 120;
-
/**
* Initialize connection to server.
@@ -114,7 +112,7 @@ class XmppManager extends ImManager
*
* Side effect: kills process on exception from XMPP library.
*
- * @fixme non-dying error handling
+ * @todo FIXME: non-dying error handling
*/
public function idle($timeout=0)
{
@@ -165,6 +163,7 @@ class XmppManager extends ImManager
}
$this->conn->processUntil('session_start');
+ // TRANS: Presence announcement for XMPP.
$this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
}
return $this->conn;
@@ -204,6 +203,7 @@ class XmppManager extends ImManager
common_log(LOG_NOTICE, 'XMPP reconnected');
$this->conn->processUntil('session_start');
+ // TRANS: Message for XMPP reconnect.
$this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
}