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 */
*
* @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) {
}
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);
-
- $subscriber->blowSubscriptionCount();
- $other->blowSubscriberCount();
-
$otherUser = User::staticGet('id', $other->id);
-
- 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()}");
+ 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();
+
+ 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();
+
+ 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()}");
+ }
}
}
Event::handle('EndSubscribe', array($subscriber, $other));
}
- return true;
+ return $sub;
}
/**
function notify()
{
- # XXX: add other notifications (Jabber, SMS) here
- # XXX: queue this and handle it offline
- # XXX: Whatever happens, do it in Twitter-like API, too
+ // XXX: add other notifications (Jabber, SMS) here
+ // XXX: queue this and handle it offline
+ // XXX: Whatever happens, do it in Twitter-like API, too
$this->notifyEmail();
}
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();
'subscribed' => $other->id));
return (empty($sub)) ? false : true;
}
+
+ function asActivity()
+ {
+ $subscriber = Profile::staticGet('id', $this->subscriber);
+ $subscribed = Profile::staticGet('id', $this->subscribed);
+
+ $act = new Activity();
+
+ $act->verb = ActivityVerb::FOLLOW;
+
+ // XXX: rationalize this with the URL
+
+ $act->id = TagURI::mint('follow:%d:%d:%s',
+ $subscriber->id,
+ $subscribed->id,
+ common_date_iso8601($this->created));
+
+ $act->time = strtotime($this->created);
+ // TRANS: Activity title when subscribing to another person.
+ $act->title = _m('TITLE','Follow');
+ // TRANS: Notification given when one person starts following another.
+ // TRANS: %1$s is the subscriber, %2$s is the subscribed.
+ $act->content = sprintf(_('%1$s is now following %2$s.'),
+ $subscriber->getBestName(),
+ $subscribed->getBestName());
+
+ $act->actor = ActivityObject::fromProfile($subscriber);
+ $act->objects[] = ActivityObject::fromProfile($subscribed);
+
+ $url = common_local_url('AtomPubShowSubscription',
+ array('subscriber' => $subscriber->id,
+ 'subscribed' => $subscribed->id));
+
+ $act->selfLink = $url;
+ $act->editLink = $url;
+
+ return $act;
+ }
+
+ /**
+ * Stream of subscriptions with the same subscriber
+ *
+ * Useful for showing pages that list subscriptions in reverse
+ * chronological order. Has offset & limit to make paging
+ * easy.
+ *
+ * @param integer $subscriberId Profile ID of the subscriber
+ * @param integer $offset Offset from latest
+ * @param integer $limit Maximum number to fetch
+ *
+ * @return Subscription stream of subscriptions; use fetch() to iterate
+ */
+ static function bySubscriber($subscriberId,
+ $offset = 0,
+ $limit = PROFILES_PER_PAGE)
+ {
+ if ($offset + $limit > self::CACHE_WINDOW) {
+ return new ArrayWrapper(self::realBySubscriber($subscriberId,
+ $offset,
+ $limit));
+ } else {
+ $key = 'subscription:by-subscriber:'.$subscriberId;
+ $window = self::cacheGet($key);
+ if ($window === false) {
+ $window = self::realBySubscriber($subscriberId,
+ 0,
+ self::CACHE_WINDOW);
+ self::cacheSet($key, $window);
+ }
+ return new ArrayWrapper(array_slice($window,
+ $offset,
+ $limit));
+ }
+ }
+
+ private static function realBySubscriber($subscriberId,
+ $offset,
+ $limit)
+ {
+ $sub = new Subscription();
+
+ $sub->subscriber = $subscriberId;
+
+ $sub->whereAdd('subscribed != ' . $subscriberId);
+
+ $sub->orderBy('created DESC');
+ $sub->limit($offset, $limit);
+
+ $sub->find();
+
+ $subs = array();
+
+ while ($sub->fetch()) {
+ $subs[] = clone($sub);
+ }
+
+ return $subs;
+ }
+
+ /**
+ * Stream of subscriptions with the same subscribed profile
+ *
+ * Useful for showing pages that list subscribers in reverse
+ * chronological order. Has offset & limit to make paging
+ * easy.
+ *
+ * @param integer $subscribedId Profile ID of the subscribed
+ * @param integer $offset Offset from latest
+ * @param integer $limit Maximum number to fetch
+ *
+ * @return Subscription stream of subscriptions; use fetch() to iterate
+ */
+ static function bySubscribed($subscribedId,
+ $offset = 0,
+ $limit = PROFILES_PER_PAGE)
+ {
+ if ($offset + $limit > self::CACHE_WINDOW) {
+ return new ArrayWrapper(self::realBySubscribed($subscribedId,
+ $offset,
+ $limit));
+ } else {
+ $key = 'subscription:by-subscribed:'.$subscribedId;
+ $window = self::cacheGet($key);
+ if ($window === false) {
+ $window = self::realBySubscribed($subscribedId,
+ 0,
+ self::CACHE_WINDOW);
+ self::cacheSet($key, $window);
+ }
+ return new ArrayWrapper(array_slice($window,
+ $offset,
+ $limit));
+ }
+ }
+
+ private static function realBySubscribed($subscribedId,
+ $offset,
+ $limit)
+ {
+ $sub = new Subscription();
+
+ $sub->subscribed = $subscribedId;
+
+ $sub->whereAdd('subscriber != ' . $subscribedId);
+
+ $sub->orderBy('created DESC');
+ $sub->limit($offset, $limit);
+
+ $sub->find();
+
+ $subs = array();
+
+ while ($sub->fetch()) {
+ $subs[] = clone($sub);
+ }
+
+ return $subs;
+ }
+
+ /**
+ * Flush cached subscriptions when subscription is updated
+ *
+ * Because we cache subscriptions, it's useful to flush them
+ * here.
+ *
+ * @param mixed $orig Original version of object
+ *
+ * @return boolean success flag.
+ */
+ function update($orig=null)
+ {
+ $result = parent::update($orig);
+
+ self::blow('subscription:by-subscriber:'.$this->subscriber);
+ self::blow('subscription:by-subscribed:'.$this->subscribed);
+
+ return $result;
+ }
}