From 7dd06b524668c441301c83bada570a5eda231403 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 28 Mar 2011 12:20:00 -0400 Subject: [PATCH 1/8] ShortenAction::handle() had bad args --- plugins/ClientSideShorten/shorten.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ClientSideShorten/shorten.php b/plugins/ClientSideShorten/shorten.php index 53c2cf5d1a..3e7121db8b 100644 --- a/plugins/ClientSideShorten/shorten.php +++ b/plugins/ClientSideShorten/shorten.php @@ -57,7 +57,7 @@ class ShortenAction extends Action return true; } - function handle($args) + function handle($args=null) { parent::handle($args); header('Content-Type: text/plain'); From 4eb02c624e152e58b37a3999f8570b49166361be Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 23 Mar 2011 16:42:36 -0700 Subject: [PATCH 2/8] Subscription_queue class for subscription approval --- classes/Subscription_queue.php | 69 ++++++++++++++++++++++++++++++++++ db/core.php | 2 + 2 files changed, 71 insertions(+) create mode 100644 classes/Subscription_queue.php diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php new file mode 100644 index 0000000000..6bf4a681b2 --- /dev/null +++ b/classes/Subscription_queue.php @@ -0,0 +1,69 @@ + 'Holder for subscription requests awaiting moderation.', + 'fields' => array( + 'subscriber' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'), + 'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile being subscribed to'), + 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), + ), + 'primary key' => array('subscriber', 'subscribed'), + 'indexes' => array( + 'group_join_queue_profile_id_created_idx' => array('subscriber', 'created'), + 'group_join_queue_group_id_created_idx' => array('subscribed', 'created'), + ), + 'foreign keys' => array( + 'group_join_queue_subscriber_fkey' => array('profile', array('subscriber' => 'id')), + 'group_join_queue_subscribed_fkey' => array('profile', array('subscribed' => 'id')), + ) + ); + } + + public static function saveNew(Profile $subscriber, Profile $other) + { + $rq = new Group_join_queue(); + $rq->subscriber = $subscriber->id; + $rq->subscribed = $subscribed->id; + $rq->created = common_sql_now(); + $rq->insert(); + return $rq; + } + + /** + * Send notifications via email etc to group administrators about + * this exciting new pending moderation queue item! + */ + public function notify() + { + $subscriber = Profile::staticGet('id', $this->subscriber); + $subscribed = Profile::staticGet('id', $this->subscribed); + mail_notify_subscription_pending($subscribed, $subscriber); + } +} diff --git a/db/core.php b/db/core.php index 928186d94d..dfba0f8cd4 100644 --- a/db/core.php +++ b/db/core.php @@ -1028,3 +1028,5 @@ $schema['schema_version'] = array( ); $schema['group_join_queue'] = Group_join_queue::schemaDef(); + +$schema['subscription_queue'] = Subscription_queue::schemaDef(); From 11b40ddb1b4d95d90cc6ae032672deb08de13e77 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 23 Mar 2011 17:05:55 -0700 Subject: [PATCH 3/8] work in progress... --- actions/subscribe.php | 12 ++++++++---- classes/Profile.php | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/actions/subscribe.php b/actions/subscribe.php index 3837915d53..fad153fc6e 100644 --- a/actions/subscribe.php +++ b/actions/subscribe.php @@ -139,8 +139,8 @@ class SubscribeAction extends Action { // Throws exception on error - Subscription::start($this->user->getProfile(), - $this->other); + $sub = Subscription::start($this->user->getProfile(), + $this->other); if ($this->boolean('ajax')) { $this->startHTML('text/xml;charset=utf-8'); @@ -149,8 +149,12 @@ class SubscribeAction extends Action $this->element('title', null, _('Subscribed')); $this->elementEnd('head'); $this->elementStart('body'); - $unsubscribe = new UnsubscribeForm($this, $this->other); - $unsubscribe->show(); + if ($sub instanceof Subscription) { + $form = new UnsubscribeForm($this, $this->other); + } else { + $form = new CancelSubscriptionForm($this, $this->other); + } + $form->show(); $this->elementEnd('body'); $this->elementEnd('html'); } else { diff --git a/classes/Profile.php b/classes/Profile.php index b582451350..9a145a0018 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -363,6 +363,47 @@ class Profile extends Memcached_DataObject } } + /** + * Request a subscription to another local or remote profile. + * This will result in either the subscription going through + * immediately, being queued for approval, or being rejected + * immediately. + * + * @param Profile $profile + * @return mixed: Subscription or Subscription_queue object on success + * @throws Exception of various types on invalid state + */ + function subscribe($profile) + { + // + } + + /** + * Cancel an outstanding subscription request to the other profile. + * + * @param Profile $profile + */ + function cancelSubscribe($profile) + { + $request = Subscribe_join_queue::pkeyGet(array('subscriber' => $this->id, + 'subscribed' => $profile->id)); + if ($request) { + if (Event::handle('StartCancelSubscription', array($this, $profile))) { + $request->delete(); + Event::handle('EndCancelSubscription', array($this, $profile)); + } + } + } + + /** + * + * @param $profile + */ + function completeSubscribe($profile) + { + + } + function getSubscriptions($offset=0, $limit=null) { $subs = Subscription::bySubscriber($this->id, From df5def8ce4b9644cb03340de23e1fe90585218aa Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Mar 2011 15:13:59 -0700 Subject: [PATCH 4/8] Work in progress: subscription approval policy field in place on user, hooked up to settings. Queue not 100% tidied up, no UI for queue or management yet. --- actions/approvegroup.php | 4 +- actions/cancelgroup.php | 2 +- actions/profilesettings.php | 17 ++++++- classes/Group_join_queue.php | 65 ++++++++++++++++++++++++++ classes/Profile.php | 84 ---------------------------------- classes/Subscription.php | 48 ++++++++++--------- classes/Subscription_queue.php | 29 ++++++++++++ classes/User.php | 4 ++ classes/statusnet.ini | 1 + db/core.php | 1 + 10 files changed, 145 insertions(+), 110 deletions(-) diff --git a/actions/approvegroup.php b/actions/approvegroup.php index 5039cfae6b..95338a4af3 100644 --- a/actions/approvegroup.php +++ b/actions/approvegroup.php @@ -152,9 +152,9 @@ class ApprovegroupAction extends Action try { if ($this->approve) { - $this->profile->completeJoinGroup($this->group); + $this->request->complete(); } elseif ($this->cancel) { - $this->profile->cancelJoinGroup($this->group); + $this->request->abort(); } } catch (Exception $e) { common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage()); diff --git a/actions/cancelgroup.php b/actions/cancelgroup.php index 57df1a10a7..15eb2b5dc3 100644 --- a/actions/cancelgroup.php +++ b/actions/cancelgroup.php @@ -139,7 +139,7 @@ class CancelgroupAction extends Action parent::handle($args); try { - $this->profile->cancelJoinGroup($this->group); + $this->request->abort(); } catch (Exception $e) { common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage()); // TRANS: Server error displayed when cancelling a queued group join request fails. diff --git a/actions/profilesettings.php b/actions/profilesettings.php index e1d686ca29..7aa987f3a5 100644 --- a/actions/profilesettings.php +++ b/actions/profilesettings.php @@ -192,6 +192,17 @@ class ProfilesettingsAction extends SettingsAction ($this->arg('autosubscribe')) ? $this->boolean('autosubscribe') : $user->autosubscribe); $this->elementEnd('li'); + $this->elementStart('li'); + $this->dropdown('subscribe_policy', + // TRANS: Dropdown field label on profile settings, for what policies to apply when someone else tries to subscribe to your updates. + _('Subscription policy'), + array(User::SUBSCRIBE_POLICY_OPEN => _('Let anyone follow me'), + User::SUBSCRIBE_POLICY_MODERATE => _('Ask me first')), + // TRANS: Dropdown field title on group edit form. + _('Whether other users need your permission to follow your updates.'), + false, + (empty($user->subscribe_policy)) ? User::SUBSCRIBE_POLICY_OPEN : $user->subscribe_policy); + $this->elementEnd('li'); } $this->elementEnd('ul'); // TRANS: Button to save input in profile settings. @@ -234,6 +245,7 @@ class ProfilesettingsAction extends SettingsAction $bio = $this->trimmed('bio'); $location = $this->trimmed('location'); $autosubscribe = $this->boolean('autosubscribe'); + $subscribe_policy = $this->trimmed('subscribe_policy'); $language = $this->trimmed('language'); $timezone = $this->trimmed('timezone'); $tagstring = $this->trimmed('tags'); @@ -333,11 +345,12 @@ class ProfilesettingsAction extends SettingsAction } // XXX: XOR - if ($user->autosubscribe ^ $autosubscribe) { + if (($user->autosubscribe ^ $autosubscribe) || $user->subscribe_policy != $subscribe_policy) { $original = clone($user); $user->autosubscribe = $autosubscribe; + $user->subscribe_policy = $subscribe_policy; $result = $user->update($original); @@ -345,7 +358,7 @@ class ProfilesettingsAction extends SettingsAction common_log_db_error($user, 'UPDATE', __FILE__); // TRANS: Server error thrown when user profile settings could not be updated to // TRANS: automatically subscribe to any subscriber. - $this->serverError(_('Could not update user for autosubscribe.')); + $this->serverError(_('Could not update user for autosubscribe or subscribe_policy.')); return; } } diff --git a/classes/Group_join_queue.php b/classes/Group_join_queue.php index 48b36cae2d..acf3a13957 100644 --- a/classes/Group_join_queue.php +++ b/classes/Group_join_queue.php @@ -56,6 +56,71 @@ class Group_join_queue extends Managed_DataObject return $rq; } + function getMember() + { + $member = Profile::staticGet('id', $this->profile_id); + + if (empty($member)) { + // TRANS: Exception thrown providing an invalid profile ID. + // TRANS: %s is the invalid profile ID. + throw new Exception(sprintf(_("Profile ID %s is invalid."),$this->profile_id)); + } + + return $member; + } + + function getGroup() + { + $group = User_group::staticGet('id', $this->group_id); + + if (empty($group)) { + // TRANS: Exception thrown providing an invalid group ID. + // TRANS: %s is the invalid group ID. + throw new Exception(sprintf(_("Group ID %s is invalid."),$this->group_id)); + } + + return $group; + } + + /** + * Abort the pending group join... + * + * @param User_group $group + */ + function abort() + { + $profile = $this->getMember(); + $group = $this->getGroup(); + if ($request) { + if (Event::handle('StartCancelJoinGroup', array($profile, $group))) { + $this->delete(); + Event::handle('EndCancelJoinGroup', array($profile, $group)); + } + } + } + + /** + * Complete a pending group join... + * + * @return Group_member object on success + */ + function complete(User_group $group) + { + $join = null; + $profile = $this->getMember(); + $group = $this->getGroup(); + if (Event::handle('StartJoinGroup', array($profile, $group))) { + $join = Group_member::join($group->id, $profile->id); + $this->delete(); + Event::handle('EndJoinGroup', array($profile, $group)); + } + if (!$join) { + throw new Exception('Internal error: group join failed.'); + } + $join->notify(); + return $join; + } + /** * Send notifications via email etc to group administrators about * this exciting new pending moderation queue item! diff --git a/classes/Profile.php b/classes/Profile.php index 9a145a0018..98fe9ede2f 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -297,49 +297,6 @@ class Profile extends Memcached_DataObject return $join; } - /** - * Cancel a pending group join... - * - * @param User_group $group - */ - function cancelJoinGroup(User_group $group) - { - $request = Group_join_queue::pkeyGet(array('profile_id' => $this->id, - 'group_id' => $group->id)); - if ($request) { - if (Event::handle('StartCancelJoinGroup', array($group, $this))) { - $request->delete(); - Event::handle('EndCancelJoinGroup', array($group, $this)); - } - } - } - - /** - * Complete a pending group join on our end... - * - * @param User_group $group - */ - function completeJoinGroup(User_group $group) - { - $join = null; - $request = Group_join_queue::pkeyGet(array('profile_id' => $this->id, - 'group_id' => $group->id)); - if ($request) { - if (Event::handle('StartJoinGroup', array($group, $this))) { - $join = Group_member::join($group->id, $this->id); - $request->delete(); - Event::handle('EndJoinGroup', array($group, $this)); - } - } else { - // TRANS: Exception thrown trying to approve a non-existing group join request. - throw new Exception(_('Invalid group join approval: not pending.')); - } - if ($join) { - $join->notify(); - } - return $join; - } - /** * Leave a group that this profile is a member of. * @@ -363,47 +320,6 @@ class Profile extends Memcached_DataObject } } - /** - * Request a subscription to another local or remote profile. - * This will result in either the subscription going through - * immediately, being queued for approval, or being rejected - * immediately. - * - * @param Profile $profile - * @return mixed: Subscription or Subscription_queue object on success - * @throws Exception of various types on invalid state - */ - function subscribe($profile) - { - // - } - - /** - * Cancel an outstanding subscription request to the other profile. - * - * @param Profile $profile - */ - function cancelSubscribe($profile) - { - $request = Subscribe_join_queue::pkeyGet(array('subscriber' => $this->id, - 'subscribed' => $profile->id)); - if ($request) { - if (Event::handle('StartCancelSubscription', array($this, $profile))) { - $request->delete(); - Event::handle('EndCancelSubscription', array($this, $profile)); - } - } - } - - /** - * - * @param $profile - */ - function completeSubscribe($profile) - { - - } - function getSubscriptions($offset=0, $limit=null) { $subs = Subscription::bySubscriber($this->id, diff --git a/classes/Subscription.php b/classes/Subscription.php index 797e6fef1c..70d351a0f7 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -27,6 +27,7 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; class Subscription extends Memcached_DataObject { const CACHE_WINDOW = 201; + const FORCE = true; ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -58,11 +59,12 @@ class Subscription extends Memcached_DataObject * * @param Profile $subscriber party to receive new notices * @param Profile $other party sending notices; publisher + * @param bool $force pass Subscription::FORCE to override local subscription approval * - * @return Subscription new subscription + * @return mixed Subscription or Subscription_queue: new subscription info */ - static function start($subscriber, $other) + static function start($subscriber, $other, $force=false) { // @fixme should we enforce this as profiles in callers instead? if ($subscriber instanceof User) { @@ -88,28 +90,32 @@ class Subscription extends Memcached_DataObject } if (Event::handle('StartSubscribe', array($subscriber, $other))) { - $sub = self::saveNew($subscriber->id, $other->id); - $sub->notify(); - - self::blow('user:notices_with_friends:%d', $subscriber->id); - - self::blow('subscription:by-subscriber:'.$subscriber->id); - self::blow('subscription:by-subscribed:'.$other->id); - - $subscriber->blowSubscriptionCount(); - $other->blowSubscriberCount(); - $otherUser = User::staticGet('id', $other->id); + if ($otherUser && $otherUser->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE && !$force) { + $sub = Subscription_queue::saveNew($subscriber, $other); + $sub->notify(); + } else { + $sub = self::saveNew($subscriber->id, $other->id); + $sub->notify(); - if (!empty($otherUser) && - $otherUser->autosubscribe && - !self::exists($other, $subscriber) && - !$subscriber->hasBlocked($other)) { + self::blow('user:notices_with_friends:%d', $subscriber->id); - try { - self::start($other, $subscriber); - } catch (Exception $e) { - common_log(LOG_ERR, "Exception during autosubscribe of {$other->nickname} to profile {$subscriber->id}: {$e->getMessage()}"); + self::blow('subscription:by-subscriber:'.$subscriber->id); + self::blow('subscription:by-subscribed:'.$other->id); + + $subscriber->blowSubscriptionCount(); + $other->blowSubscriberCount(); + + if (!empty($otherUser) && + $otherUser->autosubscribe && + !self::exists($other, $subscriber) && + !$subscriber->hasBlocked($other)) { + + try { + self::start($other, $subscriber); + } catch (Exception $e) { + common_log(LOG_ERR, "Exception during autosubscribe of {$other->nickname} to profile {$subscriber->id}: {$e->getMessage()}"); + } } } diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php index 6bf4a681b2..e7572ae725 100644 --- a/classes/Subscription_queue.php +++ b/classes/Subscription_queue.php @@ -56,6 +56,35 @@ class Subscription_queue extends Managed_DataObject return $rq; } + /** + * Complete a pending subscription, as we've got approval of some sort. + * + * @return Subscription + */ + public function complete() + { + $subscriber = Profile::staticGet('id', $this->subscriber); + $subscribed = Profile::staticGet('id', $this->subscribed); + $sub = Subscription::start($subscriber, $other, Subscription::FORCE); + if ($sub) { + $this->delete(); + } + return $sub; + } + + /** + * Cancel an outstanding subscription request to the other profile. + */ + public function abort($profile) + { + $subscriber = Profile::staticGet('id', $this->subscriber); + $subscribed = Profile::staticGet('id', $this->subscribed); + if (Event::handle('StartCancelSubscription', array($subscriber, $subscribed))) { + $this->delete(); + Event::handle('EndCancelSubscription', array($subscriber, $subscribed)); + } + } + /** * Send notifications via email etc to group administrators about * this exciting new pending moderation queue item! diff --git a/classes/User.php b/classes/User.php index 1a3a7dfd72..5945456b18 100644 --- a/classes/User.php +++ b/classes/User.php @@ -30,6 +30,9 @@ require_once 'Validate.php'; class User extends Memcached_DataObject { + const SUBSCRIBE_POLICY_OPEN = 0; + const SUBSCRIBE_POLICY_MODERATE = 1; + ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -55,6 +58,7 @@ class User extends Memcached_DataObject public $smsemail; // varchar(255) public $uri; // varchar(255) unique_key public $autosubscribe; // tinyint(1) + public $subscribe_policy; // tinyint(1) public $urlshorteningservice; // varchar(50) default_ur1.ca public $inboxed; // tinyint(1) public $design_id; // int(4) diff --git a/classes/statusnet.ini b/classes/statusnet.ini index f648fb3fbf..ab2c3d02ef 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -590,6 +590,7 @@ smsreplies = 17 smsemail = 2 uri = 2 autosubscribe = 17 +subscribe_policy = 17 urlshorteningservice = 2 inboxed = 17 design_id = 1 diff --git a/db/core.php b/db/core.php index dfba0f8cd4..f44a85cad7 100644 --- a/db/core.php +++ b/db/core.php @@ -118,6 +118,7 @@ $schema['user'] = array( 'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'), 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'), 'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'), + 'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'), 'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'), 'inboxed' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'has an inbox been created for this user?'), 'design_id' => array('type' => 'int', 'description' => 'id of a design'), From a70e68e09c4d936a88847d7623b43264050c7312 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Mar 2011 16:12:51 -0700 Subject: [PATCH 5/8] Work in progress: can create & cancel sub requests --- classes/Profile.php | 11 +++++++++++ classes/Subscription_queue.php | 21 ++++++++++++++------- classes/User.php | 6 ++++++ lib/accountprofileblock.php | 3 +++ lib/router.php | 3 ++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/classes/Profile.php b/classes/Profile.php index 98fe9ede2f..94e709b508 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -413,6 +413,17 @@ class Profile extends Memcached_DataObject { return Subscription::exists($this, $other); } + + /** + * Check if a pending subscription request is outstanding for this... + * + * @param Profile $other + * @return boolean + */ + function hasPendingSubscription($other) + { + return Subscription_queue::exists($this, $other); + } /** * Are these two profiles subscribed to each other? diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php index e7572ae725..3e254dfce1 100644 --- a/classes/Subscription_queue.php +++ b/classes/Subscription_queue.php @@ -36,19 +36,19 @@ class Subscription_queue extends Managed_DataObject ), 'primary key' => array('subscriber', 'subscribed'), 'indexes' => array( - 'group_join_queue_profile_id_created_idx' => array('subscriber', 'created'), - 'group_join_queue_group_id_created_idx' => array('subscribed', 'created'), + 'subscription_queue_subscriber_created_idx' => array('subscriber', 'created'), + 'subscription_queue_subscribed_created_idx' => array('subscribed', 'created'), ), 'foreign keys' => array( - 'group_join_queue_subscriber_fkey' => array('profile', array('subscriber' => 'id')), - 'group_join_queue_subscribed_fkey' => array('profile', array('subscribed' => 'id')), + 'subscription_queue_subscriber_fkey' => array('profile', array('subscriber' => 'id')), + 'subscription_queue_subscribed_fkey' => array('profile', array('subscribed' => 'id')), ) ); } - public static function saveNew(Profile $subscriber, Profile $other) + public static function saveNew(Profile $subscriber, Profile $subscribed) { - $rq = new Group_join_queue(); + $rq = new Subscription_queue(); $rq->subscriber = $subscriber->id; $rq->subscribed = $subscribed->id; $rq->created = common_sql_now(); @@ -56,6 +56,13 @@ class Subscription_queue extends Managed_DataObject return $rq; } + function exists($subscriber, $other) + { + $sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->id, + 'subscribed' => $other->id)); + return (empty($sub)) ? false : true; + } + /** * Complete a pending subscription, as we've got approval of some sort. * @@ -93,6 +100,6 @@ class Subscription_queue extends Managed_DataObject { $subscriber = Profile::staticGet('id', $this->subscriber); $subscribed = Profile::staticGet('id', $this->subscribed); - mail_notify_subscription_pending($subscribed, $subscriber); + //mail_notify_subscription_pending($subscribed, $subscriber); } } diff --git a/classes/User.php b/classes/User.php index 5945456b18..f395dc1e49 100644 --- a/classes/User.php +++ b/classes/User.php @@ -90,6 +90,12 @@ class User extends Memcached_DataObject return $profile->isSubscribed($other); } + function hasPendingSubscription($other) + { + $profile = $this->getProfile(); + return $profile->hasPendingSubscription($other); + } + // 'update' won't write key columns, so we have to do it ourselves. function updateKeys(&$orig) diff --git a/lib/accountprofileblock.php b/lib/accountprofileblock.php index a8bdb4715b..6a4021bf04 100644 --- a/lib/accountprofileblock.php +++ b/lib/accountprofileblock.php @@ -144,6 +144,9 @@ class AccountProfileBlock extends ProfileBlock if ($cur->isSubscribed($this->profile)) { $usf = new UnsubscribeForm($this->out, $this->profile); $usf->show(); + } else if ($cur->hasPendingSubscription($this->profile)) { + $sf = new CancelSubscriptionForm($this->out, $this->profile); + $sf->show(); } else { $sf = new SubscribeForm($this->out, $this->profile); $sf->show(); diff --git a/lib/router.php b/lib/router.php index fa9fe9aee1..2cbb1ad649 100644 --- a/lib/router.php +++ b/lib/router.php @@ -199,7 +199,8 @@ class Router // main stuff is repetitive $main = array('login', 'logout', 'register', 'subscribe', - 'unsubscribe', 'confirmaddress', 'recoverpassword', + 'unsubscribe', 'cancelsubscription', + 'confirmaddress', 'recoverpassword', 'invite', 'favor', 'disfavor', 'sup', 'block', 'unblock', 'subedit', 'groupblock', 'groupunblock', From e5b5c256a37f57f596e2db0959fad1d40f3bef35 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Mar 2011 17:06:02 -0700 Subject: [PATCH 6/8] Working subscription approval! --- actions/approvegroup.php | 2 +- actions/approvesub.php | 145 +++++++++++++++++++++++++++++++++ actions/cancelgroup.php | 2 +- actions/cancelsubscription.php | 119 +++++++++++++++++++++++++++ actions/groupqueue.php | 2 +- actions/subqueue.php | 142 ++++++++++++++++++++++++++++++++ classes/Profile.php | 30 +++++++ classes/Subscription.php | 2 +- classes/Subscription_queue.php | 4 +- lib/approvesubform.php | 114 ++++++++++++++++++++++++++ lib/cancelsubscriptionform.php | 129 +++++++++++++++++++++++++++++ lib/router.php | 9 +- lib/subgroupnav.php | 21 +++++ 13 files changed, 714 insertions(+), 7 deletions(-) create mode 100644 actions/approvesub.php create mode 100644 actions/cancelsubscription.php create mode 100644 actions/subqueue.php create mode 100644 lib/approvesubform.php create mode 100644 lib/cancelsubscriptionform.php diff --git a/actions/approvegroup.php b/actions/approvegroup.php index 95338a4af3..55a0a6c3fd 100644 --- a/actions/approvegroup.php +++ b/actions/approvegroup.php @@ -157,7 +157,7 @@ class ApprovegroupAction extends Action $this->request->abort(); } } catch (Exception $e) { - common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage()); + common_log(LOG_ERR, "Exception canceling group sub: " . $e->getMessage()); // TRANS: Server error displayed when cancelling a queued group join request fails. // TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed. $this->serverError(sprintf(_('Could not cancel request for user %1$s to join group %2$s.'), diff --git a/actions/approvesub.php b/actions/approvesub.php new file mode 100644 index 0000000000..d30da25d32 --- /dev/null +++ b/actions/approvesub.php @@ -0,0 +1,145 @@ +. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Leave a group + * + * This is the action for leaving a group. It works more or less like the subscribe action + * for users. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class ApprovesubAction extends Action +{ + var $profile = null; + + /** + * Prepare to run + */ + function prepare($args) + { + parent::prepare($args); + + $cur = common_current_user(); + if (empty($cur)) { + // TRANS: Client error displayed trying to approve group membership while not logged in. + $this->clientError(_('Must be logged in.'), 403); + return false; + } + if ($this->arg('profile_id')) { + $this->profile = Profile::staticGet('id', $this->arg('profile_id')); + } else { + // TRANS: Client error displayed trying to approve subscriptionswithout specifying a profile to approve. + $this->clientError(_('Must specify a profile.')); + return false; + } + + $this->request = Subscription_queue::pkeyGet(array('subscriber' => $this->profile->id, + 'subscribed' => $cur->id)); + + if (empty($this->request)) { + // TRANS: Client error displayed trying to approve subscription for a non-existing request. + $this->clientError(sprintf(_('%s is not in the moderation queue for your subscriptions.'), $this->profile->nickname), 403); + } + + $this->approve = (bool)$this->arg('approve'); + $this->cancel = (bool)$this->arg('cancel'); + if (!$this->approve && !$this->cancel) { + // TRANS: Client error displayed trying to approve/deny subscription. + $this->clientError(_('Internal error: received neither cancel nor abort.')); + } + if ($this->approve && $this->cancel) { + // TRANS: Client error displayed trying to approve/deny subscription + $this->clientError(_('Internal error: received both cancel and abort.')); + } + return true; + } + + /** + * Handle the request + * + * On POST, add the current user to the group + * + * @param array $args unused + * + * @return void + */ + function handle($args) + { + parent::handle($args); + $cur = common_current_user(); + + try { + if ($this->approve) { + $this->request->complete(); + } elseif ($this->cancel) { + $this->request->abort(); + } + } catch (Exception $e) { + common_log(LOG_ERR, "Exception canceling sub: " . $e->getMessage()); + // TRANS: Server error displayed when cancelling a queued subscription request fails. + // TRANS: %1$s is the leaving user's nickname, $2$s is the nickname for which the leave failed. + $this->serverError(sprintf(_('Could not cancel or approve request for user %1$s to join group %2$s.'), + $this->profile->nickname, $cur->nickname)); + return; + } + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + // TRANS: Title for subscription approval ajax return + // TRANS: %1$s is the approved user's nickname + $this->element('title', null, sprintf(_m('TITLE','%1$s\'s request'), + $this->profile->nickname)); + $this->elementEnd('head'); + $this->elementStart('body'); + if ($this->approve) { + // TRANS: Message on page for user after approving a subscription request. + $this->element('p', 'success', _('Subscription approved.')); + } elseif ($this->cancel) { + // TRANS: Message on page for user after rejecting a subscription request. + $this->element('p', 'success', _('Subscription canceled.')); + } + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect(common_local_url('subqueue', array('nickname' => + $cur->nickname)), + 303); + } + } +} diff --git a/actions/cancelgroup.php b/actions/cancelgroup.php index 15eb2b5dc3..3074e3ffa3 100644 --- a/actions/cancelgroup.php +++ b/actions/cancelgroup.php @@ -141,7 +141,7 @@ class CancelgroupAction extends Action try { $this->request->abort(); } catch (Exception $e) { - common_log(LOG_ERROR, "Exception canceling group sub: " . $e->getMessage()); + common_log(LOG_ERR, "Exception canceling group sub: " . $e->getMessage()); // TRANS: Server error displayed when cancelling a queued group join request fails. // TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed. $this->serverError(sprintf(_('Could not cancel request for user %1$s to join group %2$s.'), diff --git a/actions/cancelsubscription.php b/actions/cancelsubscription.php new file mode 100644 index 0000000000..367fbfa138 --- /dev/null +++ b/actions/cancelsubscription.php @@ -0,0 +1,119 @@ +. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Leave a group + * + * This is the action for leaving a group. It works more or less like the subscribe action + * for users. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class CancelsubscriptionAction extends Action +{ + + function handle($args) + { + parent::handle($args); + if ($this->boolean('ajax')) { + StatusNet::setApi(true); + } + if (!common_logged_in()) { + $this->clientError(_('Not logged in.')); + return; + } + + $user = common_current_user(); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('subscriptions', + array('nickname' => $user->nickname))); + return; + } + + /* Use a session token for CSRF protection. */ + + $token = $this->trimmed('token'); + + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token. ' . + 'Try again, please.')); + return; + } + + $other_id = $this->arg('unsubscribeto'); + + if (!$other_id) { + $this->clientError(_('No profile ID in request.')); + return; + } + + $other = Profile::staticGet('id', $other_id); + + if (!$other) { + $this->clientError(_('No profile with that ID.')); + return; + } + + $this->request = Subscription_queue::pkeyGet(array('subscriber' => $user->id, + 'subscribed' => $other->id)); + + if (empty($this->request)) { + // TRANS: Client error displayed when trying to approve a non-existing group join request. + // TRANS: %s is a user nickname. + $this->clientError(sprintf(_('%s is not in the moderation queue for this group.'), $this->profile->nickname), 403); + } + + $this->request->abort(); + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, _('Unsubscribed')); + $this->elementEnd('head'); + $this->elementStart('body'); + $subscribe = new SubscribeForm($this, $other); + $subscribe->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect(common_local_url('subscriptions', + array('nickname' => $user->nickname)), + 303); + } + } +} diff --git a/actions/groupqueue.php b/actions/groupqueue.php index dca0ff7bd5..c6f54d57d7 100644 --- a/actions/groupqueue.php +++ b/actions/groupqueue.php @@ -156,7 +156,7 @@ class GroupqueueAction extends GroupDesignAction $members->free(); $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, - $this->page, 'groupmembers', + $this->page, 'groupqueue', array('nickname' => $this->group->nickname)); } } diff --git a/actions/subqueue.php b/actions/subqueue.php new file mode 100644 index 0000000000..38bc16e562 --- /dev/null +++ b/actions/subqueue.php @@ -0,0 +1,142 @@ +. + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once(INSTALLDIR.'/lib/profilelist.php'); + +/** + * List of group members + * + * @category Group + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class SubqueueAction extends GalleryAction +{ + var $page = null; + + function isReadOnly($args) + { + return true; + } + + // @todo FIXME: most of this belongs in a base class, sounds common to most group actions? + function prepare($args) + { + parent::prepare($args); + + $cur = common_current_user(); + if (!$cur || $cur->id != $this->profile->id) { + // TRANS: Client error displayed when trying to approve group applicants without being a group administrator. + $this->clientError(_('You may only approve your own pending subscriptions.')); + return false; + } + return true; + } + + function title() + { + if ($this->page == 1) { + // TRANS: Title of the first page showing pending subscribers still awaiting approval. + // TRANS: %s is the name of the user. + return sprintf(_('%s subscribers awaiting approval'), + $this->profile->nickname); + } else { + // TRANS: Title of all but the first page showing pending subscribersmembers still awaiting approval. + // TRANS: %1$s is the name of the user, %2$d is the page number of the members list. + return sprintf(_('%1$s subscribers awaiting approval, page %2$d'), + $this->profile->nickname, + $this->page); + } + } + + function showPageNotice() + { + $this->element('p', 'instructions', + // TRANS: Page notice for group members page. + _('A list of users awaiting approval to subscribe to you.')); + } + + + function showContent() + { + $offset = ($this->page-1) * PROFILES_PER_PAGE; + $limit = PROFILES_PER_PAGE + 1; + + $cnt = 0; + + $members = $this->profile->getRequests($offset, $limit); + + if ($members) { + // @fixme change! + $member_list = new SubQueueList($members, $this); + $cnt = $member_list->show(); + } + + $members->free(); + + $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, + $this->page, 'subqueue', + array('nickname' => $this->profile->nickname)); // urgh + } +} + +class SubQueueList extends ProfileList +{ + function newListItem($profile) + { + return new SubQueueListItem($profile, $this->action); + } +} + +class SubQueueListItem extends ProfileListItem +{ + function showActions() + { + $this->startActions(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showApproveButtons(); + Event::handle('EndProfileListItemActionElements', array($this)); + } + $this->endActions(); + } + + function showApproveButtons() + { + $this->out->elementStart('li', 'entity_approval'); + $form = new ApproveSubForm($this->out, $this->profile); + $form->show(); + $this->out->elementEnd('li'); + } +} diff --git a/classes/Profile.php b/classes/Profile.php index 94e709b508..3d989dd974 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -356,6 +356,36 @@ class Profile extends Memcached_DataObject return new ArrayWrapper($profiles); } + /** + * Get pending subscribers, who have not yet been approved. + * + * @param int $offset + * @param int $limit + * @return Profile + */ + function getRequests($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN subscription_queue '. + 'ON profile.id = subscription_queue.subscriber ' . + 'WHERE subscription_queue.subscribed = %d ' . + 'ORDER BY subscription_queue.created DESC '; + + if ($limit != null) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $members = new Profile(); + + $members->query(sprintf($qry, $this->id)); + return $members; + } + function subscriptionCount() { $c = Cache::instance(); diff --git a/classes/Subscription.php b/classes/Subscription.php index 70d351a0f7..438f1dfe50 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -122,7 +122,7 @@ class Subscription extends Memcached_DataObject Event::handle('EndSubscribe', array($subscriber, $other)); } - return true; + return $sub; } /** diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php index 3e254dfce1..41c0a54fa8 100644 --- a/classes/Subscription_queue.php +++ b/classes/Subscription_queue.php @@ -72,7 +72,7 @@ class Subscription_queue extends Managed_DataObject { $subscriber = Profile::staticGet('id', $this->subscriber); $subscribed = Profile::staticGet('id', $this->subscribed); - $sub = Subscription::start($subscriber, $other, Subscription::FORCE); + $sub = Subscription::start($subscriber, $subscribed, Subscription::FORCE); if ($sub) { $this->delete(); } @@ -82,7 +82,7 @@ class Subscription_queue extends Managed_DataObject /** * Cancel an outstanding subscription request to the other profile. */ - public function abort($profile) + public function abort() { $subscriber = Profile::staticGet('id', $this->subscriber); $subscribed = Profile::staticGet('id', $this->subscribed); diff --git a/lib/approvesubform.php b/lib/approvesubform.php new file mode 100644 index 0000000000..820f648d0f --- /dev/null +++ b/lib/approvesubform.php @@ -0,0 +1,114 @@ +. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for leaving a group + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see UnsubscribeForm + */ +class ApproveSubForm extends Form +{ + var $profile = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Profile $profile user whose request to accept or drop + */ + function __construct($out=null, $profile=null) + { + parent::__construct($out); + + $this->profile = $profile; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + function id() + { + return 'sub-queue-' . $this->profile->id; + } + + /** + * class of the form + * + * @return string of the form class + */ + function formClass() + { + return 'form_sub_queue ajax'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + function action() + { + $params = array(); + if ($this->profile) { + $params['profile_id'] = $this->profile->id; + } + return common_local_url('approvesub', + array(), $params); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + // TRANS: Submit button text to accept a subscription request on approve sub form. + $this->out->submit('approve', _m('BUTTON','Accept')); + // TRANS: Submit button text to reject a subscription request on approve sub form. + $this->out->submit('cancel', _m('BUTTON','Reject')); + } +} diff --git a/lib/cancelsubscriptionform.php b/lib/cancelsubscriptionform.php new file mode 100644 index 0000000000..1173152c63 --- /dev/null +++ b/lib/cancelsubscriptionform.php @@ -0,0 +1,129 @@ +. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for leaving a group + * + * @category Form + * @package StatusNet + * @author Evan Prodromou + * @author Sarven Capadisli + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see UnsubscribeForm + */ + +class CancelSubscriptionForm extends Form +{ + /** + * user being subscribed to + */ + + var $profile = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Profile $profile being subscribed to + */ + + function __construct($out=null, $profile=null) + { + parent::__construct($out); + + $this->profile = $profile; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + return 'subscription-cancel-' . $this->profile->id; + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_unsubscribe ajax'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('cancelsubscription', + array(), + array('id' => $this->profile->id)); + } + + /** + * Data elements of the form + * + * @return void + */ + function formData() + { + $this->out->hidden('unsubscribeto-' . $this->profile->id, + $this->profile->id, + 'unsubscribeto'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Cancel sub request')); + } +} diff --git a/lib/router.php b/lib/router.php index 2cbb1ad649..156bc10c7f 100644 --- a/lib/router.php +++ b/lib/router.php @@ -199,7 +199,7 @@ class Router // main stuff is repetitive $main = array('login', 'logout', 'register', 'subscribe', - 'unsubscribe', 'cancelsubscription', + 'unsubscribe', 'cancelsubscription', 'approvesub', 'confirmaddress', 'recoverpassword', 'invite', 'favor', 'disfavor', 'sup', 'block', 'unblock', 'subedit', @@ -844,6 +844,10 @@ class Router array('tag' => self::REGEX_TAG)); } + $m->connect('subscribers/pending', + array('action' => 'subqueue', + 'nickname' => $nickname)); + foreach (array('rss', 'groups') as $a) { $m->connect($a, array('action' => 'user'.$a, @@ -900,6 +904,9 @@ class Router array('action' => $a), array('nickname' => Nickname::DISPLAY_FMT)); } + $m->connect(':nickname/subscribers/pending', + array('action' => 'subqueue'), + array('nickname' => Nickname::DISPLAY_FMT)); foreach (array('subscriptions', 'subscribers') as $a) { $m->connect(':nickname/'.$a.'/:tag', diff --git a/lib/subgroupnav.php b/lib/subgroupnav.php index ee4b0a8dff..b0ec364bff 100644 --- a/lib/subgroupnav.php +++ b/lib/subgroupnav.php @@ -96,6 +96,20 @@ class SubGroupNav extends Menu $this->user->nickname), $action == 'subscribers', 'nav_subscribers'); + if ($cur && $cur->id == $this->user->id) { + // Possibly site admins should be able to get in here too + $pending = $this->countPendingSubs(); + if ($pending || $cur->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE) { + $this->out->menuItem(common_local_url('subqueue', + array('nickname' => + $this->user->nickname)), + sprintf(_('Pending (%d)'), $pending), + sprintf(_('Approve pending subscription requests'), + $this->user->nickname), + $action == 'subqueueaction', + 'nav_subscribers'); + } + } $this->out->menuItem(common_local_url('usergroups', array('nickname' => $this->user->nickname)), @@ -118,4 +132,11 @@ class SubGroupNav extends Menu $this->out->elementEnd('ul'); } + + function countPendingSubs() + { + $req = new Subscription_queue(); + $req->subscribed = $this->user->id; + return $req->count(); + } } From 5d31dd259a4daf05d02302ffcf1d48b3c9322e47 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Mar 2011 17:15:48 -0700 Subject: [PATCH 7/8] Subscription pending notification mail --- classes/Subscription_queue.php | 6 ++--- lib/mail.php | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php index 41c0a54fa8..3c67790271 100644 --- a/classes/Subscription_queue.php +++ b/classes/Subscription_queue.php @@ -98,8 +98,8 @@ class Subscription_queue extends Managed_DataObject */ public function notify() { - $subscriber = Profile::staticGet('id', $this->subscriber); - $subscribed = Profile::staticGet('id', $this->subscribed); - //mail_notify_subscription_pending($subscribed, $subscriber); + $listenee = User::staticGet('id', $this->subscriber); + $other = Profile::staticGet('id', $this->subscribed); + mail_subscribe_pending_notify_profile($listenee, $other); } } diff --git a/lib/mail.php b/lib/mail.php index 708e5349b1..e8c5d3c4e7 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -259,6 +259,49 @@ function mail_subscribe_notify_profile($listenee, $other) } } +function mail_subscribe_pending_notify_profile($listenee, $other) +{ + if ($other->hasRight(Right::EMAILONSUBSCRIBE) && + $listenee->email && $listenee->emailnotifysub) { + + $profile = $listenee->getProfile(); + + $name = $profile->getBestName(); + + $long_name = ($other->fullname) ? + ($other->fullname . ' (' . $other->nickname . ')') : $other->nickname; + + $recipients = $listenee->email; + + // use the recipient's localization + common_switch_locale($listenee->language); + + $headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname); + $headers['From'] = mail_notify_from(); + $headers['To'] = $name . ' <' . $listenee->email . '>'; + // TRANS: Subject of pending new-subscriber notification e-mail. + // TRANS: %1$s is the subscribing user's nickname, %2$s is the StatusNet sitename. + $headers['Subject'] = sprintf(_('%1$s would like to listen to '. + 'your notices on %2$s.'), + $other->getBestName(), + common_config('site', 'name')); + + // TRANS: Main body of pending new-subscriber notification e-mail. + // TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename. + $body = sprintf(_('%1$s would like to listen to your notices on %2$s. ' . + 'You may approve or reject their subscription at %3$s'), + $long_name, + common_config('site', 'name'), + common_local_url('subqueue', array('nickname' => $listenee->nickname))) . + mail_profile_block($other) . + mail_footer_block(); + + // reset localization + common_switch_locale(); + mail_send($recipients, $headers, $body); + } +} + function mail_footer_block() { // TRANS: Common footer block for StatusNet notification emails. From c1683d9925ba710c44ce0a7adec46703e9b292fd Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Mar 2011 17:22:37 -0700 Subject: [PATCH 8/8] Durr... got items in wrong order :D Fixed email notification for pending subscribes --- classes/Subscription_queue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php index 3c67790271..19cd71c6a8 100644 --- a/classes/Subscription_queue.php +++ b/classes/Subscription_queue.php @@ -98,8 +98,8 @@ class Subscription_queue extends Managed_DataObject */ public function notify() { - $listenee = User::staticGet('id', $this->subscriber); - $other = Profile::staticGet('id', $this->subscribed); + $other = Profile::staticGet('id', $this->subscriber); + $listenee = User::staticGet('id', $this->subscribed); mail_subscribe_pending_notify_profile($listenee, $other); } }