/** * Create or update subscription */ function Streams_subscription_put($params) { $items = array(); $subscribed = 'no'; $updateTemplate = true; $streamName = Streams::requestedName(); $publisherId = Streams::requestedPublisherId(true); $user = Users::loggedInUser(true); extract($_REQUEST); $items = json_decode($items, true); $stream = Streams::fetchOne($user->id, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => compact('publisherId', 'streamName'))); } $rules = Streams_Rule::select('*')->where(array('ofUserId' => $user->id, 'publisherId' => $publisherId, 'streamName' => $streamName))->fetchDbRows(null, '', 'ordinal'); $types = Q_Config::get('Streams', 'types', $stream->type, 'messages', array()); if ($subscribed !== 'no') { // update rules while ($item = array_pop($items)) { // join "grouped" message types to $items foreach ($types as $type => $msg) { if ($msg['title'] == $item['filter']->labels and $type != $item['filter']->types) { $items[] = (object) array('deliver' => $item->deliver, 'filter' => array('types' => $type, 'labels' => $msg['title'], 'notifications' => $item['filter']->notifications)); } } if (!($rule = array_pop($rules))) { $rule = new Streams_Rule(); $rule->ofUserId = $user->id; $rule->publisherId = $publisherId; $rule->streamName = $streamName; $rule->relevance = 1; } $rule->filter = Q::json_encode($item['filter']); $rule->deliver = Q::json_encode($item['deliver']); $rule->save(); } } foreach ($rules as $rule) { $rule->remove(); } $streams_subscription = new Streams_Subscription(); $streams_subscription->streamName = $streamName; $streams_subscription->publisherId = $publisherId; $streams_subscription->ofUserId = $user->id; $streams_subscription->filter = Q::json_encode(array()); $streams_subscription->retrieve(); $streams_participant = new Streams_Participant(); $streams_participant->publisherId = $publisherId; $streams_participant->streamName = $streamName; $streams_participant->userId = $user->id; $streams_participant->state = 'participating'; $streams_participant->reason = ''; $streams_participant->retrieve(); $streams_participant->subscribed = $subscribed; $streams_participant->save(); if ($subscribed === 'yes') { $stream->subscribe(array('skipRules' => true)); } else { $stream->unsubscribe(); } }
/** * If the user is subscribed, get the Streams_Subscription object. * Otherwise, returns false, or null if the user isn't logged in. * @param {string} $ofUserId Defaults to logged-in user's id, if any. * @return {Streams_Subscription|false|null} */ function subscription($ofUserId = null) { if (!isset($ofUserId)) { $user = Users::loggedInUser(); if (!$user) { return null; } $ofUserId = $user->id; } $s = new Streams_Subscription(); $s->publisherId = $this->publisherId; $s->streamName = $this->name; $s->ofUserId = $ofUserId; return $s->retrieve(); }
/** * Subscribe to one or more streams, to start receiving notifications. * Posts "Streams/subscribe" message to the streams. * Also posts "Streams/subscribed" messages to user's "Streams/participating" stream. * If options are not given check the subscription templates: * 1. generic publisher id and generic user * 2. exact publisher id and generic user * 3. generic publisher id and exact user * default is to subscribe to ALL messages. * If options are supplied - skip templates and use options. * Using subscribe if subscription is already active will modify existing * subscription - change type(s) or modify notifications * @method subscribe * @static * @param {string} $asUserId The id of the user that is joining. Pass null here to use the logged-in user's id. * @param {string} $publisherId The id of the user publishing all the streams * @param {array} $streams An array of Streams_Stream objects or stream names * @param {array} [$options=array()] * @param {array} [$options.filter] optional array with two keys * @param {array} [$options.filter.types] array of message types, if this is empty then subscribes to all types * @param {array} [$options.filter.notifications=0] limit number of notifications, 0 means no limit * @param {datetime} [$options.untilTime=null] time limit, if any for subscription * @param {array} [$options.rule=array()] optionally override the rule for new subscriptions * @param {array} [$options.rule.deliver=array('to'=>'default')] under "to" key, * named the field under Streams/rules/deliver config, which will contain the names of destinations, * which can include "email", "mobile", "email+pending", "mobile+pending" * @param {datetime} [$options.rule.readyTime] time from which user is ready to receive notifications again * @param {array} [$options.rule.filter] optionally set a filter for the rules to add * @param {boolean} [$options.skipRules] if true, do not attempt to create rules for new subscriptions * @param {boolean} [$options.skipAccess] if true, skip access check for whether user can join and subscribe * @return {array} An array of Streams_Participant rows from the database. */ static function subscribe($asUserId, $publisherId, $streams, $options = array()) { $streams2 = self::_getStreams($asUserId, $publisherId, $streams); $streamNames = array(); foreach ($streams2 as $s) { $streamNames[] = $s->name; } if (empty($options['skipAccess'])) { self::_accessExceptions($streams2, $streamNames, 'join'); } $participants = Streams::join($asUserId, $publisherId, $streams2, array('subscribed' => true, 'noVisit' => true, 'skipAccess' => Q::ifset($options, 'skipAccess', false))); $shouldUpdate = false; if (isset($options['filter'])) { $filter = Q::json_encode($options['filter']); $shouldUpdate = true; } $db = Streams_Subscription::db(); if (isset($options['untilTime'])) { $untilTime = $db->toDateTime($options['untilTime']); $shouldUpdate = true; } $subscriptions = array(); $rows = Streams_Subscription::select('*')->where(array('publisherId' => $publisherId, 'streamName' => $streamNames, 'ofUserId' => $asUserId))->fetchAll(PDO::FETCH_ASSOC); foreach ($rows as $row) { $sn = $row['streamName']; $subscriptions[$sn] = $row; } $messages = array(); $pMessages = array(); $streamNamesMissing = array(); $streamNamesUpdate = array(); foreach ($streamNames as $sn) { $messages[$publisherId][$sn] = array('type' => 'Streams/subscribe'); $pMessages[] = array('type' => 'Streams/subscribed', 'instructions' => array('publisherId' => $publisherId, 'streamName' => $sn)); if (empty($subscriptions[$sn])) { $streamNamesMissing[] = $sn; continue; } if ($shouldUpdate) { $streamNamesUpdate[] = $sn; } } if ($streamNamesUpdate) { Streams_Subscription::update()->set(compact('filter', 'untilTime'))->where(array('publisherId' => $publisherId, 'streamName' => $streamNamesUpdate, 'ofUserId' => $asUserId))->execute(); } $rules = array(); if ($streamNamesMissing) { $types = array(); foreach ($streamNamesMissing as $sn) { $stream = $streams2[$sn]; $types[$stream->type][] = $sn; } $subscriptionRows = array(); $ruleRows = array(); foreach ($types as $type => $sns) { // insert subscriptions if (!isset($filter) or !isset($untilTime)) { $templates = Streams_Subscription::select('*')->where(array('publisherId' => array('', $publisherId), 'streamName' => $type . '/', 'ofUserId' => array('', $asUserId)))->fetchAll(PDO::FETCH_ASSOC); $template = null; foreach ($templates as $t) { if (!$template or $template['publisherId'] == '' and $t['publisherId'] !== '' or $template['userId'] == '' and $t['userId'] !== '') { $template = $t; } } } if (!isset($filter)) { $filter = Q::json_encode($template ? Q::json_decode($template['filter']) : Streams_Stream::getConfigField($type, array('subscriptions', 'filter'), array("types" => array("^(?!(Users/)|(Streams/)).*/", "Streams/relatedTo", "Streams/chat/message"), "notifications" => 0))); } if (!isset($untilTime)) { $untilTime = ($template and $template['duration'] > 0) ? new Db_Expression("CURRENT_TIMESTAMP + INTERVAL {$template['duration']} SECOND") : null; } foreach ($sns as $sn) { $subscriptions[$sn] = $subscriptionRows[] = new Streams_Subscription(array('publisherId' => $publisherId, 'streamName' => $sn, 'ofUserId' => $asUserId, 'untilTime' => $untilTime, 'filter' => $filter)); } if (!empty($options['skipRules'])) { continue; } // insert up to one rule per subscription $rule = null; if (isset($options['rule'])) { $rule = $options['rule']; if (isset($rule['readyTime'])) { $rule['readyTime'] = $db->toDateTime($rule['readyTime']); } if (isset($rule['filter']) and is_array($rule['filter'])) { $rule['filter'] = Q::json_encode($rule['filter']); } if (isset($rule['deliver']) and is_array($rule['deliver'])) { $rule['deliver'] = Q::json_encode($rule['deliver']); } } if (!isset($rule)) { $templates = Streams_Rule::select('*')->where(array('ofUserId' => array('', $asUserId), 'publisherId' => array('', $publisherId), 'streamName' => $type . '/', 'ordinal' => 1))->fetchAll(PDO::FETCH_ASSOC); foreach ($templates as $t) { if (!$rule or $rule['userId'] == '' and $t['userId'] !== '' or $rule['publisherId'] == '' and $t['publisherId'] !== '') { $rule = $t; } } } if (!isset($rule)) { $rule = array('deliver' => '{"to": "default"}', 'filter' => '{"types": [], "labels": []}'); } if ($rule) { $rule['ofUserId'] = $asUserId; $rule['publisherId'] = $publisherId; if (empty($rule['readyTime'])) { $rule['readyTime'] = new Db_Expression("CURRENT_TIMESTAMP"); } foreach ($sns as $sn) { $row = $rule; $row['streamName'] = $sn; $row['ordinal'] = 1; $row['filter'] = ''; $rules[$sn] = $ruleRows[] = $row; $messages[$publisherId][$sn]['instructions'] = Q::json_encode(array('rule' => $row)); } } } Streams_Subscription::insertManyAndExecute($subscriptionRows); Streams_Rule::insertManyAndExecute($ruleRows); } foreach ($streamNames as $sn) { $subscription = $subscriptions[$sn]; $stream = $streams2[$sn]; // skip error testing for rule save BUT inform node. // Node can notify user to check the rules Q_Utils::sendToNode(array("Q/method" => "Streams/Stream/subscribe", "subscription" => Q::json_encode($subscription), "stream" => Q::json_encode($stream->toArray()), "rule" => isset($rules[$sn]) ? Q::json_encode($rules[$sn]) : null)); } Streams_Message::postMessages($asUserId, $messages, true); Streams_Message::postMessages($asUserId, array($asUserId => array('Streams/participating' => $pMessages)), true); return $participants; }