Merge branch '1.0.x' into testing

This commit is contained in:
Evan Prodromou 2011-06-17 09:29:53 -04:00
commit 4989fc72d2
21 changed files with 1043 additions and 229 deletions

View File

@ -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);
}

View File

@ -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');

View File

@ -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.'));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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'),
),
);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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)
{
}
}

View File

@ -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%%

View File

@ -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%%

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1 @@
* * * * * /opt/local/bin/php /path/to/statusnet/plugins/EmailReminder/scripts/sendemailreminder.php -t all -a -q > /tmp/cron.log

View File

@ -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);
}
}

View File

@ -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 <a href="http://xcache.lighttpd.net/">XCache</a> variable cache to cache query results.'));
return true;
}
}

View File

@ -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')
));

View File

@ -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 <patrick@georgi-clan.de>
* @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.
*/

View File

@ -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);
}